+/*
+ * 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.service;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hedgecode.chess.position.Color;
+import org.hedgecode.chess.position.ColorPiece;
+import org.hedgecode.chess.position.Piece;
+import org.hedgecode.chess.position.Position;
+import org.hedgecode.chess.position.Positions;
+import org.hedgecode.chess.position.Square;
+import org.hedgecode.chess.position.SquareSort;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class EtudeUtils {
+
+ private static final byte PIECE_MASK = 0b1111;
+ private static final byte PIECE_LENGTH = 4;
+
+ private static final byte NUM_WHITE_PAWN = 0;
+ private static final byte NUM_WHITE_KNIGHT = 1;
+ private static final byte NUM_WHITE_BISHOP = 2;
+ private static final byte NUM_WHITE_ROOK = 3;
+ private static final byte NUM_WHITE_QUEEN = 4;
+
+ private static final byte NUM_BLACK_PAWN = 5;
+ private static final byte NUM_BLACK_KNIGHT = 6;
+ private static final byte NUM_BLACK_BISHOP = 7;
+ private static final byte NUM_BLACK_ROOK = 8;
+ private static final byte NUM_BLACK_QUEEN = 9;
+
+ private static final byte BLOB_PIECE_EMPTY = 0b0000;
+
+ private static final byte BLOB_WHITE_PAWN = 0b0001;
+ private static final byte BLOB_WHITE_KNIGHT = 0b0010;
+ private static final byte BLOB_WHITE_BISHOP = 0b0011;
+ private static final byte BLOB_WHITE_ROOK = 0b0100;
+ private static final byte BLOB_WHITE_QUEEN = 0b0101;
+ private static final byte BLOB_WHITE_KING = 0b0110;
+
+ private static final byte BLOB_BLACK_PAWN = 0b1001;
+ private static final byte BLOB_BLACK_KNIGHT = 0b1010;
+ private static final byte BLOB_BLACK_BISHOP = 0b1011;
+ private static final byte BLOB_BLACK_ROOK = 0b1100;
+ private static final byte BLOB_BLACK_QUEEN = 0b1101;
+ private static final byte BLOB_BLACK_KING = 0b1110;
+
+ private static final Map<ColorPiece, Byte> PIECE_NUMBERS = new HashMap<ColorPiece, Byte>() {
+ {
+ put(ColorPiece.WHITE_PAWN, NUM_WHITE_PAWN); put(ColorPiece.BLACK_PAWN, NUM_BLACK_PAWN);
+ put(ColorPiece.WHITE_KNIGHT, NUM_WHITE_KNIGHT); put(ColorPiece.BLACK_KNIGHT, NUM_BLACK_KNIGHT);
+ put(ColorPiece.WHITE_BISHOP, NUM_WHITE_BISHOP); put(ColorPiece.BLACK_BISHOP, NUM_BLACK_BISHOP);
+ put(ColorPiece.WHITE_ROOK, NUM_WHITE_ROOK); put(ColorPiece.BLACK_ROOK, NUM_BLACK_ROOK);
+ put(ColorPiece.WHITE_QUEEN, NUM_WHITE_QUEEN); put(ColorPiece.BLACK_QUEEN, NUM_BLACK_QUEEN);
+ }
+ };
+
+ private static final Map<ColorPiece, Byte> BLOB_PIECES = new HashMap<ColorPiece, Byte>() {
+ {
+ put(ColorPiece.WHITE_PAWN, BLOB_WHITE_PAWN); put(ColorPiece.BLACK_PAWN, BLOB_BLACK_PAWN);
+ put(ColorPiece.WHITE_KNIGHT, BLOB_WHITE_KNIGHT); put(ColorPiece.BLACK_KNIGHT, BLOB_BLACK_KNIGHT);
+ put(ColorPiece.WHITE_BISHOP, BLOB_WHITE_BISHOP); put(ColorPiece.BLACK_BISHOP, BLOB_BLACK_BISHOP);
+ put(ColorPiece.WHITE_ROOK, BLOB_WHITE_ROOK); put(ColorPiece.BLACK_ROOK, BLOB_BLACK_ROOK);
+ put(ColorPiece.WHITE_QUEEN, BLOB_WHITE_QUEEN); put(ColorPiece.BLACK_QUEEN, BLOB_BLACK_QUEEN);
+ put(ColorPiece.WHITE_KING, BLOB_WHITE_KING); put(ColorPiece.BLACK_KING, BLOB_BLACK_KING);
+ }
+ };
+
+ public static BigInteger hashPosition(Position position) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(
+ numSquare(
+ kingSquare(Color.WHITE, position)
+ )
+ ).append(
+ numSquare(
+ kingSquare(Color.BLACK, position)
+ )
+ );
+ int empty = 0;
+ for (Map.Entry<Square, ColorPiece> entry : position.getSquares(SquareSort.A8H1).entrySet()) {
+ ColorPiece colorPiece = entry.getValue();
+ if (colorPiece != null && !Piece.KING.equals(colorPiece.piece())) {
+ if (empty > 0)
+ sb.append(empty);
+ sb.append(
+ PIECE_NUMBERS.get(colorPiece)
+ );
+ empty = 0;
+ } else {
+ ++empty;
+ }
+ }
+ if (empty > 0)
+ sb.append(empty);
+
+ return new BigInteger(
+ sb.toString() /* todo: SQL: NUMERIC(64,0) */
+ );
+ }
+
+ public static byte[] blobPosition(Position position) {
+ byte[] blob = new byte[Square.getSize() * Square.getSize()];
+ int i = 0;
+ for (Map.Entry<Square, ColorPiece> entry : position.getSquares(SquareSort.A8H1).entrySet()) {
+ ColorPiece colorPiece = entry.getValue();
+ blob[i++] = colorPiece != null ? BLOB_PIECES.get(colorPiece) : BLOB_PIECE_EMPTY;
+ }
+ return compress(blob);
+ }
+
+ private static Square kingSquare(Color color, Position position) {
+ Square kingSquare = null;
+
+ Map<Square, ColorPiece> king = position.getSquarePieces(color, Piece.KING);
+
+ if (king.isEmpty() || king.size() > 1)
+ throw new RuntimeException("Incorrect position"); // todo
+
+ for (Square square : king.keySet()) {
+ kingSquare = square;
+ }
+ return kingSquare;
+ }
+
+ private static int numSquare(Square square) {
+ int v = square.getV() + 1;
+ int h = Square.getSize() - square.getH();
+ return Square.getSize() * (h - 1) + v;
+ }
+
+ private static 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++] & PIECE_MASK) << PIECE_LENGTH);
+ low = (byte) (bytes[i++] & PIECE_MASK);
+ cmpBytes[j] = (byte) (high | low);
+ }
+ return cmpBytes;
+ }
+
+ private EtudeUtils() {
+ throw new AssertionError(
+ "No org.hedgecode.chess.service.EtudeUtils instances!"
+ );
+ }
+
+
+ public static void main(String... args) {
+ System.out.println(
+ hashPosition(Positions.INITIAL.getPosition())
+ );
+ System.out.println(
+ Arrays.toString(blobPosition(Positions.INITIAL.getPosition()))
+ );
+ }
+
+}