[LIB-9] Separate chesshog-hedgefish module
[chesshog.git] / chesshog-format / src / main / java / org / hedgecode / chess / tcd / TCDBuilder.java
1 /*
2  * Copyright (c) 2018-2019. Developed by Hedgecode.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.hedgecode.chess.tcd;
18
19 import java.util.Map;
20
21 import org.hedgecode.chess.Builders;
22 import org.hedgecode.chess.Parsers;
23 import org.hedgecode.chess.position.Builder;
24 import org.hedgecode.chess.position.Castle;
25 import org.hedgecode.chess.position.Color;
26 import org.hedgecode.chess.position.ColorPiece;
27 import org.hedgecode.chess.position.ParseException;
28 import org.hedgecode.chess.position.Piece;
29 import org.hedgecode.chess.position.Position;
30 import org.hedgecode.chess.position.Positions;
31 import org.hedgecode.chess.position.Square;
32
33 /**
34  * Tiny Chess Diagram (TCD) builder.
35  *
36  * @author Dmitry Samoshin aka gotty
37  */
38 public final class TCDBuilder implements Builder {
39
40     private static final byte[] EMPTY = {};
41
42     private static Builder _instance = new TCDBuilder();
43
44     private TCDBuilder() {
45     }
46
47     @Override
48     public String build(Position position) {
49         return _build(
50                 position
51         );
52     }
53
54     private String _build(Position position) {
55         // todo: isInitial
56         byte[] white = _buildColor(Color.WHITE, position);
57         byte[] black = _buildColor(Color.BLACK, position);
58         byte[] addition = _buildAddition(position);
59
60         byte[] bytes;
61         int length = white.length + black.length + addition.length;
62         if (length % 2 == 0) {
63             bytes = _concat(white, black, addition);
64         } else {
65             byte[] end = {TCD.END};
66             bytes = _concat(white, black, addition, end);
67         }
68
69         bytes = _compress(bytes);
70         TCD.replaceTo(bytes);
71
72         return new String(
73                 bytes, TCD.OUTPUT_CHARSET
74         );
75     }
76
77     private byte[] _buildColor(Color color, Position position) {
78         byte[] bytes = {Color.WHITE.equals(color) ? TCD.WHITE : TCD.BLACK};
79         for (Piece piece : Piece.values()) {
80             Map<Square, ColorPiece> squarePieces = position.getSquarePieces(color, piece);
81             if (!squarePieces.isEmpty()) {
82                 byte[] pieces = new byte[2 * (squarePieces.size() + 1)];
83                 int i = 0;
84                 switch (piece) {
85                     case Piece.PAWN:
86                         pieces[i++] = TCD.PAWN;
87                         break;
88                     case Piece.KNIGHT:
89                         pieces[i++] = TCD.KNIGHT;
90                         break;
91                     case Piece.BISHOP:
92                         pieces[i++] = TCD.BISHOP;
93                         break;
94                     case Piece.ROOK:
95                         pieces[i++] = TCD.ROOK;
96                         break;
97                     case Piece.QUEEN:
98                         pieces[i++] = TCD.QUEEN;
99                         break;
100                     case Piece.KING:
101                         pieces[i++] = TCD.KING;
102                         break;
103                 }
104                 pieces[i++] = (byte) (squarePieces.size() - 1);
105                 for (Square square : squarePieces.keySet()) {
106                     pieces[i++] = (byte) square.getV();
107                     pieces[i++] = (byte) square.getH();
108                 }
109                 bytes = _concat(bytes, pieces);
110             }
111         }
112         return bytes;
113     }
114
115     private byte[] _buildAddition(Position position) {
116         byte[] bytes = {TCD.WHITE, TCD.BLACK};
117         if (!TCD.isDiagram() && !position.isDiagram()) {
118             bytes = _concat(
119                     bytes,
120                     _buildMove(position),
121                     _buildCastle(Color.WHITE, position),
122                     _buildCastle(Color.BLACK, position),
123                     _buildEnPassant(position),
124                     _buildHalfMove(position),
125                     _buildFullMove(position)
126             );
127         }
128         return bytes;
129     }
130
131     private byte[] _buildMove(Position position) {
132         if (position.getMove() != null) {
133             return new byte[] {
134                     TCD.MOVE,
135                     Color.WHITE.equals(position.getMove()) ? TCD.WHITE : TCD.BLACK
136             };
137         }
138         return EMPTY;
139     }
140
141     private byte[] _buildCastle(Color color, Position position) {
142         Castle castle = position.getCastle(color);
143         if (castle != null) {
144             byte castleByte;
145             switch (castle) {
146                 case Castle.KING:
147                     castleByte = TCD.CASTLE_KING;
148                     break;
149                 case Castle.QUEEN:
150                     castleByte = TCD.CASTLE_QUEEN;
151                     break;
152                 case Castle.BOTH:
153                     castleByte = TCD.CASTLE_BOTH;
154                     break;
155                 default:
156                     castleByte = TCD.CASTLE_NONE;
157             }
158             return new byte[] {
159                     Color.WHITE.equals(color) ? TCD.CASTLE_WHITE : TCD.CASTLE_BLACK,
160                     castleByte
161             };
162         }
163         return EMPTY;
164     }
165
166     private byte[] _buildEnPassant(Position position) {
167         Square square = position.getEnPassant();
168         if (square != null) {
169             return new byte[] {
170                     TCD.EN_PASSANT,
171                     (byte) square.getV(),
172                     (byte) square.getH()
173             };
174         }
175         return EMPTY;
176     }
177
178     private byte[] _buildHalfMove(Position position) {
179         int halfMove = position.getHalfMove();
180         return new byte[] {
181                 TCD.HALFMOVE,
182                 (byte) (halfMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
183                 (byte) (halfMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
184                 (byte) (halfMove & TCD.FRAME_MASK)
185         };
186     }
187
188     private byte[] _buildFullMove(Position position) {
189         int fullMove = position.getFullMove();
190         return new byte[] {
191                 TCD.FULLMOVE,
192                 (byte) (fullMove >> 2 * TCD.FRAME_LENGTH & TCD.FRAME_MASK),
193                 (byte) (fullMove >> TCD.FRAME_LENGTH & TCD.FRAME_MASK),
194                 (byte) (fullMove & TCD.FRAME_MASK),
195         };
196     }
197
198     private byte[] _concat(byte[]... byteArrays) {
199         int length = 0;
200         for (byte[] bytes : byteArrays) {
201             length += bytes.length;
202         }
203         byte[] concatBytes = new byte[length];
204         length = 0;
205         for (byte[] bytes : byteArrays) {
206             if (bytes.length == 0)
207                 continue;
208             System.arraycopy(bytes, 0, concatBytes, length, bytes.length);
209             length += bytes.length;
210         }
211         return concatBytes;
212     }
213
214     private byte[] _compress(byte[] bytes) {
215         byte[] cmpBytes = new byte[bytes.length / 2];
216         byte high, low;
217         int i = 0;
218         for (int j = 0; j < cmpBytes.length; ++j) {
219             high = (byte) ((bytes[i++] & TCD.FRAME_MASK) << TCD.FRAME_LENGTH);
220             low  = (byte) (bytes[i++] & TCD.FRAME_MASK);
221             cmpBytes[j] = (byte) (TCD.BYTE_MASK | high | low);
222         }
223         return cmpBytes;
224     }
225
226     public static Builder getInstance() {
227         return _instance;
228     }
229
230
231     public static void main(String[] args) throws ParseException {
232         Position position = Positions.EMPTY.getPosition();
233
234 /*
235         position.setKing(Color.WHITE, Square.C1);
236         position.setQueen(Color.WHITE, Square.B3);
237         position.setRook(Color.WHITE, Square.D1);
238         position.setBishop(Color.WHITE, Square.G5);
239         position.setPawn(Color.WHITE, Square.A2);
240         position.setPawn(Color.WHITE, Square.B2);
241         position.setPawn(Color.WHITE, Square.C2);
242         position.setPawn(Color.WHITE, Square.E4);
243         position.setPawn(Color.WHITE, Square.F2);
244         position.setPawn(Color.WHITE, Square.G2);
245         position.setPawn(Color.WHITE, Square.H2);
246
247         position.setKing(Color.BLACK, Square.E8);
248         position.setQueen(Color.BLACK, Square.E6);
249         position.setRook(Color.BLACK, Square.H8);
250         position.setBishop(Color.BLACK, Square.F8);
251         position.setKnight(Color.BLACK, Square.D7);
252         position.setPawn(Color.BLACK, Square.A7);
253         position.setPawn(Color.BLACK, Square.E5);
254         position.setPawn(Color.BLACK, Square.F7);
255         position.setPawn(Color.BLACK, Square.G7);
256         position.setPawn(Color.BLACK, Square.H7);
257 */
258
259 /*
260         position.setKing(Color.WHITE, Square.G1);
261         position.setQueen(Color.WHITE, Square.A4);
262         position.setRook(Color.WHITE, Square.D1);
263         position.setBishop(Color.WHITE, Square.A3);
264         position.setBishop(Color.WHITE, Square.D3);
265         position.setPawn(Color.WHITE, Square.A2);
266         position.setPawn(Color.WHITE, Square.C3);
267         position.setPawn(Color.WHITE, Square.F6);
268         position.setPawn(Color.WHITE, Square.F2);
269         position.setPawn(Color.WHITE, Square.G2);
270         position.setPawn(Color.WHITE, Square.H2);
271
272         position.setKing(Color.BLACK, Square.E8);
273         position.setQueen(Color.BLACK, Square.F3);
274         position.setRook(Color.BLACK, Square.B8);
275         position.setRook(Color.BLACK, Square.G8);
276         position.setBishop(Color.BLACK, Square.B7);
277         position.setBishop(Color.BLACK, Square.B6);
278         position.setKnight(Color.BLACK, Square.E7);
279         position.setPawn(Color.BLACK, Square.A7);
280         position.setPawn(Color.BLACK, Square.C7);
281         position.setPawn(Color.BLACK, Square.D7);
282         position.setPawn(Color.BLACK, Square.F7);
283         position.setPawn(Color.BLACK, Square.H7);
284 */
285
286         position.setKing(Color.WHITE, Square.H1);
287         position.setQueen(Color.WHITE, Square.E3);
288         position.setRook(Color.WHITE, Square.E1);
289         position.setRook(Color.WHITE, Square.G1);
290         position.setBishop(Color.WHITE, Square.D2);
291         position.setBishop(Color.WHITE, Square.G2);
292         position.setKnight(Color.WHITE, Square.B1);
293         position.setPawn(Color.WHITE, Square.A2);
294         position.setPawn(Color.WHITE, Square.B2);
295         position.setPawn(Color.WHITE, Square.D4);
296         position.setPawn(Color.WHITE, Square.G3);
297         position.setPawn(Color.WHITE, Square.H3);
298
299         position.setKing(Color.BLACK, Square.G8);
300         position.setQueen(Color.BLACK, Square.D7);
301         position.setRook(Color.BLACK, Square.F5);
302         position.setRook(Color.BLACK, Square.F2);
303         position.setBishop(Color.BLACK, Square.D6);
304         position.setBishop(Color.BLACK, Square.D3);
305         position.setPawn(Color.BLACK, Square.A6);
306         position.setPawn(Color.BLACK, Square.B4);
307         position.setPawn(Color.BLACK, Square.D5);
308         position.setPawn(Color.BLACK, Square.E6);
309         position.setPawn(Color.BLACK, Square.E4);
310         position.setPawn(Color.BLACK, Square.G7);
311         position.setPawn(Color.BLACK, Square.H6);
312
313 /*
314         position.setKing(Color.WHITE, Square.H5);
315         position.setPawn(Color.WHITE, Square.C6);
316         position.setKing(Color.BLACK, Square.A6);
317         position.setPawn(Color.BLACK, Square.F6);
318         position.setPawn(Color.BLACK, Square.G7);
319         position.setPawn(Color.BLACK, Square.H6);
320 */
321
322
323         position.setMove(Color.WHITE);
324
325         position.setCastle(Color.WHITE, Castle.NONE);
326         position.setCastle(Color.BLACK, Castle.NONE);
327
328         //position.setEnPassant(Square.A3);
329
330         position.setHalfMove(0);
331         position.setFullMove(26);
332
333         TCD.setDiagram(false);
334
335         String tcd = getInstance().build(position);
336
337         System.out.println(
338                 tcd
339         );
340
341         Position tcdPosition = Parsers.TCD.parser().parse(tcd);
342
343         System.out.println(
344                 Builders.ASCII.builder().build(tcdPosition)
345         );
346
347         System.out.println(
348                 getInstance().build(tcdPosition)
349         );
350
351         System.out.println(
352                 Builders.FEN.builder().build(tcdPosition)
353         );
354
355 /*
356         System.out.println(
357                 getInstance().build(Positions.INITIAL.getPosition())
358         );
359 */
360     }
361
362 }