/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * This file is part of SanKit -- A multiplatform set of C code
 * for reading and writing PGN formatted chess games. In addition
 * it can be used as an engine to determine in real time if proposed
 * moves are legal or not. It appears to generate near perfect PGN
 * from incorrectly formatted files.
 *
 * Copyright 1994 Steven J. Edwards All Rights Reserved.
 * Copyright 2009-2011 David Hoerl All Rights Reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*>>> sanhsh.c: SAN project hash code generation */

/* Revised: 1993.09.14 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "sandef.h"
#include "sanvar.h"

#include "sandsp.h"
#include "sanhsh.h"
#include "sanutl.h"

/**** Locals */

/* the psuedorandom generator seed */

static uliT hash_ps_seed;

/* macro for calculating a main hash xor record address */

#define mhxpoint(cp, sq) (mhxp_base + (((cp) << sqQ) + (sq)))

/* main hash table exclusive-or record storage of color-piece/square codes */

static hxptrT mhxp_base;

/* main hash table exclusive-or record storage of castling availabilities */

static hxT mhxp_caiv[caiL];

/* main hash table exclusive-or record storage of en passant target files */

static hxT mhxp_eptf[fileL];

/*--> SANHashColorPieceSq: change single index/position signature pair */
nonstatic
void SANHashColorPieceSq(cpT cp, sqT sq)
{
	hxptrT hxptr;

/* important: cp_wp <= cp <= cp_bk */

	hxptr = mhxpoint(cp, sq);
	curr_e.e_mhca ^= hxptr->hx_hcpa;
	curr_e.e_mhcs ^= hxptr->hx_hcps;

	return;
}

/*--> SANHashCastlingAvailability: change castling availability signatures */
nonstatic
void SANHashCastlingAvailability(caiT cai)
{
	curr_e.e_mhca ^= mhxp_caiv[cai].hx_hcpa;
	curr_e.e_mhcs ^= mhxp_caiv[cai].hx_hcps;

	return;
}

/*--> SANHashEnPassantTargetFile: change en passant file signatures */
nonstatic
void SANHashEnPassantTargetFile(void)
{
	hxptrT hxptr;

/* note that the current ep target square must not be nil */

	hxptr = &mhxp_eptf[map_file_sq(curr_e.e_epsq)];
	curr_e.e_mhca ^= hxptr->hx_hcpa;
	curr_e.e_mhcs ^= hxptr->hx_hcps;

	return;
}

/*--> SANHashColorPieceSqSq: change two index/position signatures */
nonstatic
void SANHashColorPieceSqSq(cpT cp, sqT sq0, sqT sq1)
{
	hxptrT hxptr;

/* important: cp_wp <= cp <= cp_bk */

	hxptr = mhxpoint(cp, sq0);
	curr_e.e_mhca ^= hxptr->hx_hcpa;
	curr_e.e_mhcs ^= hxptr->hx_hcps;

	hxptr = mhxpoint(cp, sq1);
	curr_e.e_mhca ^= hxptr->hx_hcpa;
	curr_e.e_mhcs ^= hxptr->hx_hcps;

	return;
}

/*--> SANHashCreate: create main hash subsystem index/position signatures */
nonstatic
void SANHashCreate(void)
{
	cpT cp;
	sqT sq;
	caiT cai;

/* clear the main hash address and signature */

	curr_e.e_mhca = 0;
	curr_e.e_mhcs = 0;

/* create the main hash address and signature from the local board */

	for(sq = sq_a1; sq <= sq_h8; sq++) {
		if((cp = gb.bv[sq]) != cp_v0) {
			SANHashColorPieceSq(cp, sq);
		}
	}

/* fold in castling availability */

	for(cai = cai_wk; cai <= cai_bq; cai++) {
		if(curr_e.e_cast & (bit << cai)) {
			SANHashCastlingAvailability(cai);
		}
	}

/* fold in en passant target file */

	if(curr_e.e_epsq != sq_nil) {
		SANHashEnPassantTargetFile();
	}

	return;
}

/*--> SANHashDisplayGenerationTables: display the hash generation table set */
nonstatic
void SANHashDisplayGenerationTables(void)
{
	cT c;
	pT p;
	cpT cp;
	fileT file;
	rankT rank;
	sqT sq;
	caiT cai;
	hxptrT hxptr;
	hcpaT hcpa;
	hcpsT hcps;

	SANDspLine("castling   Address  Signature");
	SANDspLine("--         -------- --------");
	for(cai = cai_wk; cai <= cai_bq; cai++) {
		hcpa = mhxp_caiv[cai].hx_hcpa;
		hcps = mhxp_caiv[cai].hx_hcps;
		SANDspColor(cai / flankL);
		SANDspPiece((cai % flankL) ? p_q : p_k);
		SANDspStr("         ");
		sprintf(stv, "%08x", hcpa);
		SANDspSTV();
		SANDspChar(' ');
		sprintf(stv, "%08x", hcps);
		SANDspSTV();
		SANDspNL();
	}
	SANDspNL();

	SANDspLine("ep file    Address  Signature");
	SANDspLine("-          -------- --------");
	for(file = file_a; file <= file_h; file++) {
		hcpa = mhxp_eptf[file].hx_hcpa;
		hcps = mhxp_eptf[file].hx_hcps;
		SANDspChar(acfv[file]);
		SANDspStr("          ");
		sprintf(stv, "%08x", hcpa);
		SANDspSTV();
		SANDspChar(' ');
		sprintf(stv, "%08x", hcps);
		SANDspSTV();
		SANDspNL();
	}
	SANDspNL();

	SANDspLine("cp sq      Address  Signature");
	SANDspLine("-- --      -------- --------");
	for(c = c_w; c <= c_b; c++) {
		for(p = p_p; p <= p_k; p++) {
			cp = cv_cp_cpv[c][p];
			for(file = file_a; file <= file_h; file++) {
				for(rank = rank_1; rank <= rank_8; rank++) {
					sq = map_sq_rank_file(rank, file);
					hxptr = mhxpoint(cp, sq);
					hcpa = hxptr->hx_hcpa;
					hcps = hxptr->hx_hcps;
					SANDspColor(c);
					SANDspPiece(p);
					SANDspChar(' ');
					SANDspSq(sq);
					SANDspStr("      ");
					sprintf(stv, "%08x", hcpa);
					SANDspSTV();
					SANDspChar(' ');
					sprintf(stv, "%08x", hcps);
					SANDspSTV();
					SANDspNL();
				}
			}
		}
	}
	SANDspNL();

	return;
}

/*--> SANHashPSNonNegativeWord: psuedorandom positive 16 bit word */
static
siT SANHashPSNonNegativeWord(void)
{
	hash_ps_seed = (hash_ps_seed * 1103515245L) + 12345L;

	return (siT) ((hash_ps_seed >> wordW) & 0x7fff);
}

/*--> SANHashPSByte: return psuedorandom 8 bit byte (in a word) */
static
siT SANHashPSByte(void)
{
	return (SANHashPSNonNegativeWord() >> nybbW) & byteM;
}

/*--> SANHashPSWord: return psuedorandom 16 bit word (in a long word) */
static
liT SANHashPSWord(void)
{
	liT n;

	n = (((liT) SANHashPSByte()) << byteW) | ((liT) SANHashPSByte());

	return n;
}

/*--> SANHashPSLongWord: return psuedorandom 32 bit word */
static
liT SANHashPSLongWord(void)
{
	liT n;

	n = (SANHashPSWord() << wordW) | (SANHashPSWord() & wordM);

	return n;
}

/*--> SANHashInit: initialize main hash table subsystem (called once) */
nonstatic
void SANHashInit(void)
{
	hxptrT hxptr;
	cpT cp;
	sqT sq;
	caiT cai;
	fileT file;

/* set the hash psuedorandom seed to generate the canonical table sequence */

	hash_ps_seed = 1;

/* allocate hash color-piece/square xor data */

	mhxp_base = (hxptrT) SANMemGrab(sizeof(hxT) * rcpL * sqL);

/* initialize (constant) color-piece/square hash xor data */

	for(cp = cp_wp; cp <= cp_bk; cp++) {
		for(sq = sq_a1; sq <= sq_h8; sq++) {
			hxptr = mhxpoint(cp, sq);
			hxptr->hx_hcpa = SANHashPSLongWord();
			hxptr->hx_hcps = SANHashPSLongWord();
		}
	}

/* initialize (constant) castling availability hash xor data */

	for(cai = cai_wk; cai <= cai_bq; cai++) {
		mhxp_caiv[cai].hx_hcpa = SANHashPSLongWord();
		mhxp_caiv[cai].hx_hcps = SANHashPSLongWord();
	}

/* initialize (constant) en passant target file hash xor data */

	for(file = file_a; file <= file_h; file++) {
		mhxp_eptf[file].hx_hcpa = SANHashPSLongWord();
		mhxp_eptf[file].hx_hcps = SANHashPSLongWord();
	}

	return;
}

/*--> SANHashTerm: terminate main hash table subsystem (called once) */
nonstatic
void SANHashTerm(void)
{
/* release hash xor storage */

	SANMemFree(mhxp_base);

	return;
}

/*<<< sanhsh.c: EOF */
