2 * Copyright (c) 2018. Developed by Hedgecode.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.hedgecode.chess.tcd;
19 import org.hedgecode.chess.position.Castle;
20 import org.hedgecode.chess.position.Color;
21 import org.hedgecode.chess.position.ParseException;
22 import org.hedgecode.chess.position.Parser;
23 import org.hedgecode.chess.position.Position;
24 import org.hedgecode.chess.position.Positions;
25 import org.hedgecode.chess.position.Square;
28 * Tiny Chess Diagram (TCD) parser.
30 * @author Dmitry Samoshin aka gotty
32 public final class TCDParser implements Parser {
34 private static Parser _instance = new TCDParser();
39 private final class TCDIterator {
43 private boolean isHighPart;
45 TCDIterator(byte[] bytes) {
48 this.isHighPart = true;
52 return (bytes.length > pos + 1) || (isHighPart && bytes.length > pos);
56 return !isHighPart && (bytes.length - 1 == pos);
59 byte next() throws ParseException {
60 isHighPart = !isHighPart;
66 byte current() throws ParseException {
67 if (bytes.length <= pos)
68 throw new ParseException("parse.tcd.index.out.of.bounds");
69 byte frame = bytes[pos];
71 frame = (byte) (frame >> TCD.FRAME_LENGTH);
72 return (byte) (frame & TCD.FRAME_MASK);
77 public Position parse(String string) throws ParseException {
83 public Position parse(byte[] bytes) throws ParseException {
84 TCD.replaceFrom(bytes);
85 if (!TCD.isValid(bytes))
86 throw new ParseException("parse.tcd.invalid.bytes");
87 TCDIterator iterator = new TCDIterator(bytes);
88 return _parse(iterator);
91 private Position _parse(TCDIterator iterator) throws ParseException {
94 byte curr = iterator.current();
95 byte next = iterator.next();
97 if (TCD.isBlackWhite(curr, next)) {
98 position = Positions.INITIAL.getPosition();
99 curr = iterator.next();
100 next = iterator.next();
102 position = Positions.EMPTY.getPosition();
103 boolean isWhiteAssigned = false, isBlackAssigned = false;
104 while (!isWhiteAssigned || !isBlackAssigned) {
107 _parsePieces(Color.WHITE, position, iterator);
108 isWhiteAssigned = true;
111 _parsePieces(Color.BLACK, position, iterator);
112 isBlackAssigned = true;
115 throw new ParseException("parse.tcd.incorrect.color");
117 curr = iterator.current();
118 next = iterator.next();
122 if (!TCD.isWhiteBlack(curr, next))
123 throw new ParseException("parse.tcd.incorrect.end");
125 _parseAddition(position, iterator);
130 private void _parsePieces(Color color, Position position, TCDIterator iterator) throws ParseException {
131 byte piece = iterator.current();
132 while (TCD.isNotColor(piece)) {
133 int pieceCount = iterator.next() + 1;
134 for (int i = 0; i < pieceCount; ++i) {
135 Square square = _parseSquare(iterator);
138 position.setPawn(color, square);
141 position.setKnight(color, square);
144 position.setBishop(color, square);
147 position.setRook(color, square);
150 position.setQueen(color, square);
153 position.setKing(color, square);
156 throw new ParseException("parse.tcd.incorrect.piece");
159 piece = iterator.next();
163 private void _parseAddition(Position position, TCDIterator iterator) throws ParseException {
164 if (iterator.hasNext()) {
165 byte frame = iterator.next();
166 if (iterator.isLast() && frame != TCD.END) {
167 throw new ParseException("parse.tcd.incorrect.end");
169 boolean isEndPosition = false;
170 while (!isEndPosition) {
177 case TCD.CASTLE_WHITE:
179 Color.WHITE, _parseCastle(iterator)
182 case TCD.CASTLE_BLACK:
184 Color.BLACK, _parseCastle(iterator)
188 position.setEnPassant(
189 _parseSquare(iterator)
193 position.setHalfMove(
194 _parseNumber(iterator)
198 position.setFullMove(
199 _parseNumber(iterator)
203 throw new ParseException("parse.tcd.incorrect.addition");
205 isEndPosition = true;
206 if (iterator.hasNext()) {
207 frame = iterator.next();
208 if (iterator.isLast() && frame != TCD.END) {
209 throw new ParseException("parse.tcd.incorrect.end");
211 isEndPosition = iterator.isLast();
218 private Square _parseSquare(TCDIterator iterator) throws ParseException {
219 byte vl = iterator.next();
220 byte hl = iterator.next();
221 return Square.getSquare(vl, hl);
224 private Color _parseMove(TCDIterator iterator) throws ParseException {
225 byte frame = iterator.next();
226 if (TCD.isNotColor(frame))
227 throw new ParseException("parse.tcd.incorrect.move.color");
228 return frame == TCD.WHITE ? Color.WHITE : Color.BLACK;
231 private Castle _parseCastle(TCDIterator iterator) throws ParseException {
232 byte frame = iterator.next();
233 if (TCD.isNotCastle(frame))
234 throw new ParseException("parse.tcd.incorrect.castle");
235 Castle castle = Castle.NONE;
237 case TCD.CASTLE_KING:
238 castle = Castle.KING;
240 case TCD.CASTLE_QUEEN:
241 castle = Castle.QUEEN;
243 case TCD.CASTLE_BOTH:
244 castle = Castle.BOTH;
250 private int _parseNumber(TCDIterator iterator) throws ParseException {
251 return iterator.next() << (2 * TCD.FRAME_LENGTH)
252 | iterator.next() << TCD.FRAME_LENGTH
256 public static Parser getInstance() {
261 public static void main(String[] args) throws ParseException {
262 //Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFD0OvFV+4fnNQwOYoWaG-h5pgG");
263 //Position position = getInstance().parse("xG");
264 Position position = getInstance().parse("A7ONMILHJJIFCJEDO0ECFDGOvFV+4fnNQwOYoWaG-h5pgG");
265 System.out.println(position);