/* * Copyright (c) 2018. Developed by Hedgecode. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hedgecode.chess.tcd; import org.hedgecode.chess.position.Castle; import org.hedgecode.chess.position.Color; import org.hedgecode.chess.position.ParseException; import org.hedgecode.chess.position.Parser; import org.hedgecode.chess.position.Position; import org.hedgecode.chess.position.Positions; import org.hedgecode.chess.position.Square; /** * Tiny Chess Diagram (TCD) parser. * * @author Dmitry Samoshin aka gotty */ public final class TCDParser implements Parser { private static Parser _instance = new TCDParser(); private TCDParser() { } private final class TCDIterator { private byte[] bytes; private int pos; private boolean isHighPart; TCDIterator(byte[] bytes) { this.bytes = bytes; this.pos = 0; this.isHighPart = true; } boolean hasNext() { return (bytes.length > pos + 1) || (isHighPart && bytes.length > pos); } boolean isLast() { return !isHighPart && (bytes.length - 1 == pos); } byte next() throws ParseException { isHighPart = !isHighPart; if (isHighPart) pos++; return current(); } byte current() throws ParseException { if (bytes.length <= pos) throw new ParseException("parse.tcd.index.out.of.bounds"); byte frame = bytes[pos]; if (isHighPart) frame = (byte) (frame >> TCD.FRAME_LENGTH); return (byte) (frame & TCD.FRAME_MASK); } } @Override public Position parse(String string) throws ParseException { return parse( string.getBytes() ); } public Position parse(byte[] bytes) throws ParseException { TCD.replaceFrom(bytes); if (!TCD.isValid(bytes)) throw new ParseException("parse.tcd.invalid.bytes"); TCDIterator iterator = new TCDIterator(bytes); return _parse(iterator); } private Position _parse(TCDIterator iterator) throws ParseException { Position position; byte curr = iterator.current(); byte next = iterator.next(); if (TCD.isBlackWhite(curr, next)) { position = Positions.INITIAL.getPosition(); curr = iterator.next(); next = iterator.next(); } else { position = Positions.EMPTY.getPosition(); boolean isWhiteAssigned = false, isBlackAssigned = false; while (!isWhiteAssigned || !isBlackAssigned) { switch (curr) { case TCD.WHITE: _parsePieces(Color.WHITE, position, iterator); isWhiteAssigned = true; break; case TCD.BLACK: _parsePieces(Color.BLACK, position, iterator); isBlackAssigned = true; break; default: throw new ParseException("parse.tcd.incorrect.color"); } curr = iterator.current(); next = iterator.next(); } } if (!TCD.isWhiteBlack(curr, next)) throw new ParseException("parse.tcd.incorrect.end"); _parseAddition(position, iterator); return position; } private void _parsePieces(Color color, Position position, TCDIterator iterator) throws ParseException { byte piece = iterator.current(); while (TCD.isNotColor(piece)) { int pieceCount = iterator.next() + 1; for (int i = 0; i < pieceCount; ++i) { Square square = _parseSquare(iterator); switch (piece) { case TCD.PAWN: position.setPawn(color, square); break; case TCD.KNIGHT: position.setKnight(color, square); break; case TCD.BISHOP: position.setBishop(color, square); break; case TCD.ROOK: position.setRook(color, square); break; case TCD.QUEEN: position.setQueen(color, square); break; case TCD.KING: position.setKing(color, square); break; default: throw new ParseException("parse.tcd.incorrect.piece"); } } piece = iterator.next(); } } private void _parseAddition(Position position, TCDIterator iterator) throws ParseException { if (iterator.hasNext()) { byte frame = iterator.next(); if (iterator.isLast() && frame != TCD.END) { throw new ParseException("parse.tcd.incorrect.end"); } else { boolean isEndPosition = false; while (!isEndPosition) { switch (frame) { case TCD.MOVE: position.setMove( _parseMove(iterator) ); break; case TCD.CASTLE_WHITE: position.setCastle( Color.WHITE, _parseCastle(iterator) ); break; case TCD.CASTLE_BLACK: position.setCastle( Color.BLACK, _parseCastle(iterator) ); break; case TCD.EN_PASSANT: position.setEnPassant( _parseSquare(iterator) ); break; case TCD.HALFMOVE: position.setHalfMove( _parseNumber(iterator) ); break; case TCD.FULLMOVE: position.setFullMove( _parseNumber(iterator) ); break; default: throw new ParseException("parse.tcd.incorrect.addition"); } isEndPosition = true; if (iterator.hasNext()) { frame = iterator.next(); if (iterator.isLast() && frame != TCD.END) { throw new ParseException("parse.tcd.incorrect.end"); } isEndPosition = iterator.isLast(); } } } } } private Square _parseSquare(TCDIterator iterator) throws ParseException { byte vl = iterator.next(); byte hl = iterator.next(); return Square.getSquare(vl, hl); } private Color _parseMove(TCDIterator iterator) throws ParseException { byte frame = iterator.next(); if (TCD.isNotColor(frame)) throw new ParseException("parse.tcd.incorrect.move.color"); return frame == TCD.WHITE ? Color.WHITE : Color.BLACK; } private Castle _parseCastle(TCDIterator iterator) throws ParseException { byte frame = iterator.next(); if (TCD.isNotCastle(frame)) throw new ParseException("parse.tcd.incorrect.castle"); Castle castle = Castle.NONE; switch (frame) { case TCD.CASTLE_KING: castle = Castle.KING; break; case TCD.CASTLE_QUEEN: castle = Castle.QUEEN; break; case TCD.CASTLE_BOTH: castle = Castle.BOTH; break; } return castle; } private int _parseNumber(TCDIterator iterator) throws ParseException { return iterator.next() << (2 * TCD.FRAME_LENGTH) | iterator.next() << TCD.FRAME_LENGTH | iterator.next(); } public static Parser getInstance() { return _instance; } public static void main(String[] args) throws ParseException { //Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFD0OvFV+4fnNQwOYoWaG-h5pgG"); //Position position = getInstance().parse("xG"); Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFDGOvFV+4fnNQwOYoWaG-h5pgG"); System.out.println(position); } }