package chess4j.hash;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import org.testng.annotations.Test;

import chess4j.Color;
import chess4j.board.CastlingRights;
import chess4j.board.Square;
import chess4j.hash.Zobrist;
import chess4j.pieces.Bishop;
import chess4j.pieces.King;
import chess4j.pieces.Knight;
import chess4j.pieces.Pawn;
import chess4j.pieces.Piece;
import chess4j.pieces.Queen;
import chess4j.pieces.Rook;

public class ZobristTest {

	@Test
	public void testHammingDistances() {
		int wKey=Zobrist.getPlayerKey(Color.WHITE);
		int bKey=Zobrist.getPlayerKey(Color.BLACK);
		System.out.println("wKey=" + String.valueOf(wKey));
		System.out.println("bKey=" + String.valueOf(bKey));
		String strWKey=DecimalToBinaryString.integerToBinary(wKey);
		String strBKey=DecimalToBinaryString.integerToBinary(bKey);
		System.out.println("strWKey=" + strWKey);
		System.out.println("strBKey=" + strBKey);
		Hamming h = new Hamming(strWKey,strBKey);
		int hd=h.getHammingDistance();
		System.out.println("hamming distance=" + hd);
	}
	
	@Test
	public void testHammingDistances2() {
		List<String> keys = getStringKeys();
		List<Integer> hammingDistances = new ArrayList<Integer>();
		for (int i=0;i<keys.size();i++) {
			String key = keys.get(i);
			for (int j=i+1;j<keys.size();j++) {
				String key2=keys.get(j);
				int hd = new Hamming(key,key2).getHammingDistance();
				hammingDistances.add(hd);
			}
		}
		double[] hds = new double[hammingDistances.size()];
		for (int i=0;i<hammingDistances.size();i++) {
			hds[i] = Double.valueOf(hammingDistances.get(i));
		}
		double minHD = StdStats.min(hds);
		double maxHD = StdStats.max(hds);
		double meanHD = StdStats.mean(hds);
		double stdDevHD = StdStats.stddev(hds);
		System.out.println("min hd: " + minHD);
		System.out.println("max hd: " + maxHD);
		System.out.println("mean: " + meanHD);
		System.out.println("stddev: " + stdDevHD);
		assert(minHD >= 3);
		assert(meanHD >= 15.0 && meanHD <= 17.0);
		assert(stdDevHD < 3.0);
	}
	
	private List<String> getStringKeys() {
		List<String> skeys = new ArrayList<String>();
		List<Integer> ikeys = getKeys();
		for (Integer ikey : ikeys) {
			skeys.add(DecimalToBinaryString.integerToBinary(ikey));
		}
		return skeys;
	}
	
	private List<Integer> getKeys() {
		List<Integer> keys = new ArrayList<Integer>();
		
		// add colors
		keys.add(Zobrist.getPlayerKey(Color.WHITE));
		keys.add(Zobrist.getPlayerKey(Color.BLACK));
		
		// add ep squares
		List<Square> sqs = Square.allSquares();
		for (Square sq : sqs) {
			keys.add(Zobrist.getEnPassantKey(sq));
		}
		
		// add castling rights
		Set<CastlingRights> crs = EnumSet.allOf(CastlingRights.class);
		for (CastlingRights cr : crs) {
			keys.add(Zobrist.getCastlingKey(cr));
		}
		
		// add piece/square keys
		addToKeys(keys,Pawn.BLACK_PAWN);
		addToKeys(keys,Pawn.WHITE_PAWN);
		addToKeys(keys,Rook.BLACK_ROOK);
		addToKeys(keys,Rook.WHITE_ROOK);
		addToKeys(keys,Knight.BLACK_KNIGHT);
		addToKeys(keys,Knight.WHITE_KNIGHT);
		addToKeys(keys,Bishop.BLACK_BISHOP);
		addToKeys(keys,Bishop.WHITE_BISHOP);
		addToKeys(keys,Queen.BLACK_QUEEN);
		addToKeys(keys,Queen.WHITE_QUEEN);
		addToKeys(keys,King.BLACK_KING);
		addToKeys(keys,King.WHITE_KING);
		
		return keys;
	}
	
	private void addToKeys(List<Integer> keys,Piece p) {
		List<Square> sqs = Square.allSquares();
		for (Square sq : sqs) {
			keys.add(Zobrist.getPieceKey(sq, p));
		}		
	}
}
