+/*
+ * 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 java.util.Map;
+
+import org.hedgecode.chess.Builders;
+import org.hedgecode.chess.Parsers;
+import org.hedgecode.chess.position.Builder;
+import org.hedgecode.chess.position.Castle;
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+
+/**
+ * Tiny Chess Diagram (TCD) builder.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class TCDBuilder implements Builder {
+
+ private static final byte[] EMPTY = {};
+
+ private static Builder _instance = new TCDBuilder();
+
+ private TCDBuilder() {
+ }
+
+ @Override
+ public String build(Position position) {
+ return _build(
+ position
+ );
+ }
+
+ private String _build(Position position) {
+ // todo: isInitial
+ byte[] white = _buildColor(Color.WHITE, position);
+ byte[] black = _buildColor(Color.BLACK, position);
+ byte[] addition = _buildAddition(position);
+
+ byte[] bytes;
+ int length = white.length + black.length + addition.length;
+ if (length % 2 == 0) {
+ bytes = _concat(white, black, addition);
+ } else {
+ byte[] end = {TCD.END};
+ bytes = _concat(white, black, addition, end);
+ }
+
+ bytes = _compress(bytes);
+ TCD.replaceTo(bytes);
+
+ return new String(
+ bytes, TCD.OUTPUT_CHARSET
+ );
+ }
+
+ private byte[] _buildColor(Color color, Position position) {
+ byte[] bytes = {Color.WHITE.equals(color) ? TCD.WHITE : TCD.BLACK};
+ for (Piece piece : Piece.values()) {
+ Map<Square, ColorPiece> squarePieces = position.getSquarePieces(color, piece);
+ if (!squarePieces.isEmpty()) {
+ byte[] pieces = new byte[2 * (squarePieces.size() + 1)];
+ int i = 0;
+ switch (piece) {
+ case PAWN:
+ pieces[i++] = TCD.PAWN;
+ break;
+ case KNIGHT:
+ pieces[i++] = TCD.KNIGHT;
+ break;
+ case BISHOP:
+ pieces[i++] = TCD.BISHOP;
+ break;
+ case ROOK:
+ pieces[i++] = TCD.ROOK;
+ break;
+ case QUEEN:
+ pieces[i++] = TCD.QUEEN;
+ break;
+ case KING:
+ pieces[i++] = TCD.KING;
+ break;
+ }
+ pieces[i++] = (byte) (squarePieces.size() - 1);
+ for (Square square : squarePieces.keySet()) {
+ pieces[i++] = (byte) square.getV();
+ pieces[i++] = (byte) square.getH();
+ }
+ bytes = _concat(bytes, pieces);
+ }
+ }
+ return bytes;
+ }
+
+ private byte[] _buildAddition(Position position) {
+ byte[] bytes = {TCD.WHITE, TCD.BLACK};
+ if (!TCD.isDiagram() && !position.isDiagram()) {
+ bytes = _concat(
+ bytes,
+ _buildMove(position),
+ _buildCastle(Color.WHITE, position),
+ _buildCastle(Color.BLACK, position),
+ _buildEnPassant(position),
+ _buildHalfMove(position),
+ _buildFullMove(position)
+ );
+ }
+ return bytes;
+ }
+
+ private byte[] _buildMove(Position position) {
+ if (position.getMove() != null) {
+ return new byte[] {
+ TCD.MOVE,
+ Color.WHITE.equals(position.getMove()) ? TCD.WHITE : TCD.BLACK
+ };
+ }
+ return EMPTY;
+ }
+
+ private byte[] _buildCastle(Color color, Position position) {
+ Castle castle = position.getCastle(color);
+ if (castle != null) {
+ byte castleByte;
+ switch (castle) {
+ case KING:
+ castleByte = TCD.CASTLE_KING;
+ break;
+ case QUEEN:
+ castleByte = TCD.CASTLE_QUEEN;
+ break;
+ case BOTH:
+ castleByte = TCD.CASTLE_BOTH;
+ break;
+ default:
+ castleByte = TCD.CASTLE_NONE;
+ }
+ return new byte[] {
+ Color.WHITE.equals(color) ? TCD.CASTLE_WHITE : TCD.CASTLE_BLACK,
+ castleByte
+ };
+ }
+ return EMPTY;
+ }
+
+ private byte[] _buildEnPassant(Position position) {
+ Square square = position.getEnPassant();
+ if (square != null) {
+ return new byte[] {
+ TCD.EN_PASSANT,
+ (byte) square.getV(),
+ (byte) square.getH()
+ };
+ }
+ return EMPTY;
+ }
+
+ private byte[] _buildHalfMove(Position position) {
+ int halfMove = position.getHalfMove();
+ return new byte[] {
+ TCD.HALFMOVE,
+ (byte) (halfMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+ (byte) (halfMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+ (byte) (halfMove & TCD.FRAME_MASK)
+ };
+ }
+
+ private byte[] _buildFullMove(Position position) {
+ int fullMove = position.getFullMove();
+ return new byte[] {
+ TCD.FULLMOVE,
+ (byte) (fullMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+ (byte) (fullMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
+ (byte) (fullMove & TCD.FRAME_MASK),
+ };
+ }
+
+ private byte[] _concat(byte[]... byteArrays) {
+ int length = 0;
+ for (byte[] bytes : byteArrays) {
+ length += bytes.length;
+ }
+ byte[] concatBytes = new byte[length];
+ length = 0;
+ for (byte[] bytes : byteArrays) {
+ if (bytes.length == 0)
+ continue;
+ System.arraycopy(bytes, 0, concatBytes, length, bytes.length);
+ length += bytes.length;
+ }
+ return concatBytes;
+ }
+
+ private byte[] _compress(byte[] bytes) {
+ byte[] cmpBytes = new byte[bytes.length / 2];
+ byte high, low;
+ int i = 0;
+ for (int j = 0; j < cmpBytes.length; ++j) {
+ high = (byte) ((bytes[i++] & TCD.FRAME_MASK) << TCD.FRAME_LENGTH);
+ low = (byte) (bytes[i++] & TCD.FRAME_MASK);
+ cmpBytes[j] = (byte) (TCD.BYTE_MASK | high | low);
+ }
+ return cmpBytes;
+ }
+
+ public static Builder getInstance() {
+ return _instance;
+ }
+
+
+ public static void main(String[] args) throws ParseException {
+ Position position = Positions.EMPTY.getPosition();
+
+/*
+ position.setKing(Color.WHITE, Square.C1);
+ position.setQueen(Color.WHITE, Square.B3);
+ position.setRook(Color.WHITE, Square.D1);
+ position.setBishop(Color.WHITE, Square.G5);
+ position.setPawn(Color.WHITE, Square.A2);
+ position.setPawn(Color.WHITE, Square.B2);
+ position.setPawn(Color.WHITE, Square.C2);
+ position.setPawn(Color.WHITE, Square.E4);
+ position.setPawn(Color.WHITE, Square.F2);
+ position.setPawn(Color.WHITE, Square.G2);
+ position.setPawn(Color.WHITE, Square.H2);
+
+ position.setKing(Color.BLACK, Square.E8);
+ position.setQueen(Color.BLACK, Square.E6);
+ position.setRook(Color.BLACK, Square.H8);
+ position.setBishop(Color.BLACK, Square.F8);
+ position.setKnight(Color.BLACK, Square.D7);
+ position.setPawn(Color.BLACK, Square.A7);
+ position.setPawn(Color.BLACK, Square.E5);
+ position.setPawn(Color.BLACK, Square.F7);
+ position.setPawn(Color.BLACK, Square.G7);
+ position.setPawn(Color.BLACK, Square.H7);
+*/
+
+/*
+ position.setKing(Color.WHITE, Square.G1);
+ position.setQueen(Color.WHITE, Square.A4);
+ position.setRook(Color.WHITE, Square.D1);
+ position.setBishop(Color.WHITE, Square.A3);
+ position.setBishop(Color.WHITE, Square.D3);
+ position.setPawn(Color.WHITE, Square.A2);
+ position.setPawn(Color.WHITE, Square.C3);
+ position.setPawn(Color.WHITE, Square.F6);
+ position.setPawn(Color.WHITE, Square.F2);
+ position.setPawn(Color.WHITE, Square.G2);
+ position.setPawn(Color.WHITE, Square.H2);
+
+ position.setKing(Color.BLACK, Square.E8);
+ position.setQueen(Color.BLACK, Square.F3);
+ position.setRook(Color.BLACK, Square.B8);
+ position.setRook(Color.BLACK, Square.G8);
+ position.setBishop(Color.BLACK, Square.B7);
+ position.setBishop(Color.BLACK, Square.B6);
+ position.setKnight(Color.BLACK, Square.E7);
+ position.setPawn(Color.BLACK, Square.A7);
+ position.setPawn(Color.BLACK, Square.C7);
+ position.setPawn(Color.BLACK, Square.D7);
+ position.setPawn(Color.BLACK, Square.F7);
+ position.setPawn(Color.BLACK, Square.H7);
+*/
+
+ position.setKing(Color.WHITE, Square.H1);
+ position.setQueen(Color.WHITE, Square.E3);
+ position.setRook(Color.WHITE, Square.E1);
+ position.setRook(Color.WHITE, Square.G1);
+ position.setBishop(Color.WHITE, Square.D2);
+ position.setBishop(Color.WHITE, Square.G2);
+ position.setKnight(Color.WHITE, Square.B1);
+ position.setPawn(Color.WHITE, Square.A2);
+ position.setPawn(Color.WHITE, Square.B2);
+ position.setPawn(Color.WHITE, Square.D4);
+ position.setPawn(Color.WHITE, Square.G3);
+ position.setPawn(Color.WHITE, Square.H3);
+
+ position.setKing(Color.BLACK, Square.G8);
+ position.setQueen(Color.BLACK, Square.D7);
+ position.setRook(Color.BLACK, Square.F5);
+ position.setRook(Color.BLACK, Square.F2);
+ position.setBishop(Color.BLACK, Square.D6);
+ position.setBishop(Color.BLACK, Square.D3);
+ position.setPawn(Color.BLACK, Square.A6);
+ position.setPawn(Color.BLACK, Square.B4);
+ position.setPawn(Color.BLACK, Square.D5);
+ position.setPawn(Color.BLACK, Square.E6);
+ position.setPawn(Color.BLACK, Square.E4);
+ position.setPawn(Color.BLACK, Square.G7);
+ position.setPawn(Color.BLACK, Square.H6);
+
+/*
+ position.setKing(Color.WHITE, Square.H5);
+ position.setPawn(Color.WHITE, Square.C6);
+ position.setKing(Color.BLACK, Square.A6);
+ position.setPawn(Color.BLACK, Square.F6);
+ position.setPawn(Color.BLACK, Square.G7);
+ position.setPawn(Color.BLACK, Square.H6);
+*/
+
+
+ position.setMove(Color.WHITE);
+
+ position.setCastle(Color.WHITE, Castle.NONE);
+ position.setCastle(Color.BLACK, Castle.NONE);
+
+ //position.setEnPassant(Square.A3);
+
+ position.setHalfMove(0);
+ position.setFullMove(26);
+
+ TCD.setDiagram(false);
+
+ String tcd = getInstance().build(position);
+
+ System.out.println(
+ tcd
+ );
+
+ Position tcdPosition = Parsers.TCD.parser().parse(tcd);
+
+ System.out.println(
+ Builders.ASCII.builder().build(tcdPosition)
+ );
+
+ System.out.println(
+ getInstance().build(tcdPosition)
+ );
+
+ System.out.println(
+ Builders.FEN.builder().build(tcdPosition)
+ );
+
+/*
+ System.out.println(
+ getInstance().build(Positions.INITIAL.getPosition())
+ );
+*/
+ }
+
+}