/* * 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 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()) ); */ } }