[LIB-9] Separate chesshog-format module
[chesshog.git] / chesshog-format / src / main / java / org / hedgecode / chess / tcd / TCDBuilder.java
diff --git a/chesshog-format/src/main/java/org/hedgecode/chess/tcd/TCDBuilder.java b/chesshog-format/src/main/java/org/hedgecode/chess/tcd/TCDBuilder.java
new file mode 100644 (file)
index 0000000..b72372c
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2018-2019. 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 Piece.PAWN:
+                        pieces[i++] = TCD.PAWN;
+                        break;
+                    case Piece.KNIGHT:
+                        pieces[i++] = TCD.KNIGHT;
+                        break;
+                    case Piece.BISHOP:
+                        pieces[i++] = TCD.BISHOP;
+                        break;
+                    case Piece.ROOK:
+                        pieces[i++] = TCD.ROOK;
+                        break;
+                    case Piece.QUEEN:
+                        pieces[i++] = TCD.QUEEN;
+                        break;
+                    case Piece.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 Castle.KING:
+                    castleByte = TCD.CASTLE_KING;
+                    break;
+                case Castle.QUEEN:
+                    castleByte = TCD.CASTLE_QUEEN;
+                    break;
+                case Castle.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())
+        );
+*/
+    }
+
+}