
/***************************************************************************

                                 ChessV

                   COPYRIGHT (C) 2005 BY GREGORY STRONG

This file is part of ChessV.  ChessV is free software; you can redistribute
it and/or modify it under the terms of the GNU General Public License as 
published by the Free Software Foundation; either version 2 of the License, 
or (at your option) any later version.

ChessV is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
more details; the file 'COPYING' contains the License text, but if for
some reason you need a copy, please write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

****************************************************************************/


#include "StdAfx.h"
#include "../../ChessV.h"
#include "../../PieceType.h"
#include "../../Piece.h"
#include "../../Direction.h"
#include "../../Rand.h"
#include "../../GameParameters.h"
#include "../../boards/DecimalBoard.h"
#include "OdinsRuneGame.h"
#include "Decimal_Types.h"

#include "Decimal_Data.h"
//	this include file contains data matricies for square bonuses,
//	outpost_10x10 bonuses, etc.  it also contains some macros used here.


// *** DEBUG MEMORY ALLOCATION *** //
#ifdef _DEBUG
#define new SAFE_NEW
#endif


DecimalValkyrie DecimalValkyrie::decimalValkyrie;
DecimalForestOx DecimalForestOx::decimalForestOx;
DecimalOdinsKing DecimalOdinsKing::decimalOdinsKing;
DecimalOdinsPawn DecimalOdinsPawn::decimalOdinsPawn;


Game *CreateOdinsRuneGame( Board &brd, Player &plr0, Player &plr1 )
{
	return new OdinsRuneGame( brd, plr0, plr1 );
}

OdinsRuneGame::OdinsRuneGame( Board &board, Player &whitePlayer, Player &blackPlayer ):
	Game(board, whitePlayer, blackPlayer)
{
	// ***************************************** //
	// ***                                   *** //
	// ***  set rules and evaluation values  *** //
	// ***                                   *** //
	// ***************************************** //

	//	this game has a different victory condition; disable checkmate
	goalIsCheckmate = false;

	//	turn off the static exchange evaluator; it cannot be used 
	//	because of the Forest Ox's power of igui capture
	useStaticExchangeEvaluation = false;

	//	implement 50-move draw rule (100 half-moves)
	autoDrawPeriod = 100;

	//	turn on use of pawn structure evaluation
	usePawnStructureEvaluation = false;

	//	turn off null-move after 24 pieces have been captured
	endgameCaptureThreshold = 24;

	//	razoring and futility pruning margins
	razorMargin = 10000;
	futilityMargin = 3750;
	extendedFutilityMargin = 6250;


	// *** PHASES *** //

	//	we have to set up the phases we wish to have in this game;
	//	we will keep it simple and have 3: opening, midgame, and endgame
	nPhases = 3;

	//	settings for phase 0 (opening)
	phases[0].SetNumber( 0 );
	phases[0].SetMobilityFactor( 0 );
	phases[0].SetPawnDeficiencyFactor( 0 );
	phases[0].SetSquareValuesFactor( 1 );
	phases[0].SetKingSafetyFactor( 0 );
	phases[0].SetTropismFactor( 0 );

	//	settings for phase 1 (midgame)
	phases[1].SetNumber( 1 );
	phases[1].SetMobilityFactor( 0 );
	phases[1].SetPawnDeficiencyFactor( 0 );
	phases[1].SetSquareValuesFactor( 1 );
	phases[1].SetKingSafetyFactor( 0 );
	phases[1].SetTropismFactor( 0 );
	
	//	settings for phase 2 (endgame)
	phases[2].SetNumber( 2 );
	phases[2].SetMobilityFactor( 0 );
	phases[2].SetPawnDeficiencyFactor( 0 );
	phases[2].SetSquareValuesFactor( 1 );
	phases[2].SetKingSafetyFactor( 0 );
	phases[2].SetTropismFactor( 0 );


	// *** INITIALIZATION *** //
	board.Initialize( this, BITBOARD_128 );
}

void OdinsRuneGame::AddPlayerPieceTypes( char *gameName )
{
	//	add the piece types that each player may have
	board.AddPlayerPieceTypeBothPlayers( DecimalRook::decimalRook );
	board.AddPlayerPieceTypeBothPlayers( DecimalBishop::decimalBishop );
	board.AddPlayerPieceTypeBothPlayers( DecimalForestOx::decimalForestOx );
	board.AddPlayerPieceTypeBothPlayers( DecimalValkyrie::decimalValkyrie );
	board.AddPlayerPieceTypeBothPlayers( DecimalOdinsKing::decimalOdinsKing );
	board.AddPlayerPieceTypeBothPlayers( DecimalOdinsPawn::decimalOdinsPawn );
}

void OdinsRuneGame::ChangeRulesByVariant
	( char *gameName,
	  char *&array,
	  char *&book )
{
	//	clear out all piece references
	for( int player = 0; player < 2; player++ )
	{
		for( int x = 0; x < 2; x++ )
		{
			rooks[player][x] = NULL;
			oxen[player][x] = NULL;
			bishops[player][x] = NULL;
			valkyries[player][x] = NULL;
			kings[player][x] = NULL;
		}
		for( int y = 0; y < 4; y++ )
			minorPieces[player][y] = NULL;
	}
	for( int i = 0; i < 10; i++ )
	{
		pawns[0][i] = NULL;
		pawns[1][i] = NULL;
	}

	//	place pieces
	array = "rfbvkkvbfr/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/RFBVKKVBFR";
	//	set name of opening book
	book = "openings\\OdinsRune.txt";
}

Piece *OdinsRuneGame::AddPiece
	( PieceType &pieceType,
	  int nPlayer,
	  int nRank,
	  int nFile )
{
	Piece *newPiece = Game::AddPiece( pieceType, nPlayer, nRank, nFile );

	if( pieceType == DecimalValkyrie::decimalValkyrie )
	{
		AddPieceToSet( newPiece, valkyries, 2 );
	}
	else if( pieceType == DecimalRook::decimalRook )
	{
		AddPieceToSet( newPiece, rooks, 2 );
	}
	else if( pieceType == DecimalBishop::decimalBishop )
	{
		AddPieceToSet( newPiece, bishops, 2 );
		AddPieceToSet( newPiece, minorPieces, 4 );
	}
	else if( pieceType == DecimalForestOx::decimalForestOx )
	{
		AddPieceToSet( newPiece, oxen, 2 );
		AddPieceToSet( newPiece, minorPieces, 4 );
	}
	else if( pieceType == DecimalOdinsPawn::decimalOdinsPawn )
	{
		AddPieceToSet( newPiece, pawns, 10 );
	}
	else if( pieceType == DecimalOdinsKing::decimalOdinsKing )
	{
		AddPieceToSet( newPiece, kings, 2 );
		board.SetKing( nPlayer, newPiece );
	}
	return newPiece;
}

void OdinsRuneGame::DeletePiece
	( Piece *piece )
{
	RemovePieceFromSet( piece, rooks, 2 );
	RemovePieceFromSet( piece, oxen, 2 );
	RemovePieceFromSet( piece, bishops, 2 );
	RemovePieceFromSet( piece, valkyries, 2 );
	RemovePieceFromSet( piece, kings, 2 );
	RemovePieceFromSet( piece, pawns, 10 );
	RemovePieceFromSet( piece, minorPieces, 4 );
}

void OdinsRuneGame::AddMovesForOdinsPawn
	( int currentPlayer, 
	  MovementList &list, 
	  int pawnSquare,
	  bool quiescentSearch )
{
	if( currentPlayer == 0 )
	{
		if( pawnSquare < 80 )
		{
			if( pawnSquare % 10 > 0 && 
				board.GetSquareContents( pawnSquare + 9 ) == NULL )
			{
				if( board.GetSquareContents( pawnSquare + 20 ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( pawnSquare, pawnSquare + 20 );
					return;
				}
				else if( board.GetSquareContents( pawnSquare + 20 )->GetPlayerNumber() == 1 )
				{
					list.AddCapture( pawnSquare, pawnSquare + 20 );
					return;
				}
			}
			if( pawnSquare % 10 < 9 && 
				board.GetSquareContents( pawnSquare + 11 ) == NULL )
			{
				if( board.GetSquareContents( pawnSquare + 20 ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( pawnSquare, pawnSquare + 20 );
					return;
				}
				else if( board.GetSquareContents( pawnSquare + 20 )->GetPlayerNumber() == 1 )
				{
					list.AddCapture( pawnSquare, pawnSquare + 20 );
					return;
				}
			}
		}
	}
	else
	{
		if( pawnSquare >= 20 )
		{
			if( pawnSquare % 10 > 0 && 
				board.GetSquareContents( pawnSquare - 11 ) == NULL )
			{
				if( board.GetSquareContents( pawnSquare - 20 ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( pawnSquare, pawnSquare - 20 );
					return;
				}
				else if( board.GetSquareContents( pawnSquare - 20 )->GetPlayerNumber() == 0 )
				{
					list.AddCapture( pawnSquare, pawnSquare - 20 );
					return;
				}
			}
			if( pawnSquare % 10 < 9 && 
				board.GetSquareContents( pawnSquare - 9 ) == NULL )
			{
				if( board.GetSquareContents( pawnSquare - 20 ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( pawnSquare, pawnSquare - 20 );
					return;
				}
				else if( board.GetSquareContents( pawnSquare - 20 )->GetPlayerNumber() == 0 )
				{
					list.AddCapture( pawnSquare, pawnSquare - 20 );
					return;
				}
			}
		}
	}
}

void OdinsRuneGame::AddMovesForForestOx
	( int currentPlayer, 
	  MovementList &list, 
	  int oxSquare,
	  bool quiescentSearch )
{
	bool isKing = &(board.GetSquareContents( oxSquare )->GetType()) == 
		&DecimalOdinsKing::decimalOdinsKing;
	for( int dir = 8; dir < 16; dir++ )
	{
		int square = board.GetMovementMatrix( dir )[oxSquare];
		if( square >= 0 && 
			(board.GetSquareContents( square ) == NULL || 
				board.GetSquareContents( square )->GetPlayerNumber() != currentPlayer) )
		{
			if( isKing )
			{
				if( board.GetSquareContents( square ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( oxSquare, square );
				}
				else if( board.GetSquareContents( square )->GetPlayerNumber() != currentPlayer )
					list.AddCapture( oxSquare, square );
			}
			BitBoard128 adjacentSquares = static_cast<DecimalBoard &>(board).GetAdjacencyBitBoard( square );
			BitBoard128 adjacentEnemies = adjacentSquares & 
				board.BB128_GetFriends( FLIP(currentPlayer) );
			while( adjacentEnemies )
			{
				int enemySquare = adjacentEnemies.GetFirstBit();
				adjacentEnemies.ClearBit( enemySquare );
				if( board.GetSquareContents( square ) != NULL )
				{
					list.BeginMoveAdd( UserMove1, oxSquare, square, enemySquare );
					list.AddPickUp( oxSquare );
					list.AddPickUp( square );
					list.AddPickUp( enemySquare );
					list.AddDrop( board.GetSquareContents( oxSquare ), square );
					list.EndMoveAdd( board.GetSquareContents( square )->GetBaseValue() + 
						board.GetSquareContents( enemySquare )->GetBaseValue() + 5000 );
				}
				else
				{
					list.BeginMoveAdd( UserMove1, oxSquare, square, enemySquare );
					list.AddPickUp( oxSquare );
					list.AddPickUp( enemySquare );
					list.AddDrop( board.GetSquareContents( oxSquare ), square );
					list.EndMoveAdd( board.GetSquareContents( enemySquare )->GetBaseValue() + 3000 );
				}
			}
		}
	}
}

void OdinsRuneGame::AddSpecialMoves
	( int currentPlayer, 
	  MovementList &list, 
	  GameRec &gameRecord, 
	  bool quiescentSearch )
{
	// *** PAWNS *** //
	BitBoard128 pawns = DecimalOdinsPawn::decimalOdinsPawn.GetPieces( currentPlayer );
	while( pawns )
	{
		int square = pawns.GetFirstBit();
		pawns.ClearBit( square );
		AddMovesForOdinsPawn( currentPlayer, list, square, quiescentSearch );
	}

	// *** FOREST OXEN *** //
	BitBoard128 oxen = DecimalForestOx::decimalForestOx.GetPieces( currentPlayer );
	while( oxen )
	{
		int oxSquare = oxen.GetFirstBit();
		oxen.ClearBit( oxSquare );
		AddMovesForForestOx( currentPlayer, list, oxSquare, quiescentSearch );
	}

	// *** VALKYRIES *** //
	BitBoard128 valkyries = DecimalValkyrie::decimalValkyrie.GetPieces( currentPlayer );
	while( valkyries )
	{
		int startSquare = valkyries.GetFirstBit();
		valkyries.ClearBit( startSquare );
		for( int dir = 0; dir < 8; dir++ )
		{
			int *matrix = board.GetMovementMatrix( dir );
			int square = matrix[startSquare];
			while( square != -1 )
			{
				if( board.GetSquareContents( square ) == NULL )
				{
					if( !quiescentSearch )
						list.AddMove( startSquare, square );
				}
				else
				{
					if( board.GetSquareContents( square )->GetPlayerNumber() != currentPlayer )
					{
						list.AddCapture( startSquare, square );
						square = -1;
					}
					else
					{
						if( quiescentSearch )
							square = -1;
						else
						{
							if( &(board.GetSquareContents( square )->GetType()) != 
								&DecimalValkyrie::decimalValkyrie )
							{
								int *reverseMatrix = board.GetOppositeMovementMatrix( dir );
								int sq = reverseMatrix[square];
								while( sq != -1 )
								{
									list.BeginMoveAdd( UserMove2, startSquare, square, sq );
									list.AddPickUp( startSquare );
									list.AddPickUp( square );
									list.AddDrop( board.GetSquareContents( startSquare ), square );
									list.AddDrop( board.GetSquareContents( square ), sq );
									list.EndMoveAdd( 0 ); // TODO: improve this evaluation!
									if( sq == startSquare )
										sq = -1;
									else
										sq = reverseMatrix[sq];
								}
							}
							square = -1;
						}
					}
				}
				if( square != -1 )
					square = matrix[square];
			}
		}
	}

	// *** KINGS *** //
	BitBoard128 kings = DecimalOdinsKing::decimalOdinsKing.GetPieces( currentPlayer );
	while( kings )
	{
		int startSquare = kings.GetFirstBit();
		kings.ClearBit( startSquare );

		//	step 1: find out what types of pieces are adjacent to the King
		int orthogonal = 0;
		int diagonal = 0;
		bool forestOx = false;
		bool pawn = false;
		BitBoard128 adjacentSquares = static_cast<DecimalBoard &>(board).GetAdjacencyBitBoard( startSquare );
		BitBoard128 adjacentFriends = adjacentSquares & board.BB128_GetFriends( currentPlayer );
		while( adjacentFriends )
		{
			int friendSquare = adjacentFriends.GetFirstBit();
			adjacentFriends.ClearBit( friendSquare );
			Piece *piece = board.GetSquareContents( friendSquare );
			if( &(piece->GetType()) == &DecimalRook::decimalRook )
				orthogonal = MAX(orthogonal, 1);
			else if( &(piece->GetType()) == &DecimalBishop::decimalBishop )
				diagonal = MAX(diagonal, 1);
			else if( &(piece->GetType()) == &DecimalValkyrie::decimalValkyrie )
			{
				orthogonal = 2;
				diagonal = 2;
			}
			else if( &(piece->GetType()) == &DecimalForestOx::decimalForestOx )
				forestOx = true;
			else if( &(piece->GetType()) == &DecimalOdinsPawn::decimalOdinsPawn )
				pawn = true;
		}

		//	step 2: now that we know how it can move, we shall add the appropriate moves
		bool isOrthogonal[8] = { true, false, false, true, false, false, true, true };
		for( int dir = 0; dir < 8; dir++ )
		{
			if( (isOrthogonal[dir] && orthogonal) || 
				(!isOrthogonal[dir] && diagonal) )
			{
				int *matrix = board.GetMovementMatrix( dir );
				int square = matrix[startSquare];
				while( square != -1 )
				{
					if( board.GetSquareContents( square ) == NULL )
					{
						if( !quiescentSearch )
							list.AddMove( startSquare, square );
					}
					else
					{
						if( board.GetSquareContents( square )->GetPlayerNumber() != currentPlayer )
						{
							list.AddCapture( startSquare, square );
							square = -1;
						}
						else
						{
							if( quiescentSearch )
								square = -1;
							else
							{
								if( ((isOrthogonal[dir] && orthogonal == 2) || 
									 (!isOrthogonal[dir] && diagonal == 2)) &&
									(&(board.GetSquareContents( square )->GetType()) != 
									 &DecimalOdinsKing::decimalOdinsKing) )
								{
									int *reverseMatrix = board.GetOppositeMovementMatrix( dir );
									int sq = reverseMatrix[square];
									while( sq != -1 )
									{
										list.BeginMoveAdd( UserMove2, startSquare, square, sq );
										list.AddPickUp( startSquare );
										list.AddPickUp( square );
										list.AddDrop( board.GetSquareContents( startSquare ), square );
										list.AddDrop( board.GetSquareContents( square ), sq );
										list.EndMoveAdd( 0 ); // TODO: improve this evaluation!
										if( sq == startSquare )
											sq = -1;
										else
											sq = reverseMatrix[sq];
									}
								}
								square = -1;
							}
						}
					}
					if( square != -1 )
						square = matrix[square];
				}
			}
		}
		if( forestOx )
			AddMovesForForestOx( currentPlayer, list, startSquare, quiescentSearch );
		if( pawn )
		{
			if( !diagonal )
			{
				int targetSquare;
				targetSquare = board.GetMovementMatrix( DIRECTION_NE )[startSquare];
				if( targetSquare != -1 )
				{
					if( board.GetSquareContents( targetSquare ) == NULL )
					{
						if( !quiescentSearch )
							list.AddMove( startSquare, targetSquare );
					}
					else if( board.GetSquareContents( targetSquare )->GetPlayerNumber() != currentPlayer )
						list.AddCapture( startSquare, targetSquare );
				}
				targetSquare = board.GetMovementMatrix( DIRECTION_SE )[startSquare];
				if( targetSquare != -1 )
				{
					if( board.GetSquareContents( targetSquare ) == NULL )
					{
						if( !quiescentSearch )
							list.AddMove( startSquare, targetSquare );
					}
					else if( board.GetSquareContents( targetSquare )->GetPlayerNumber() != currentPlayer )
						list.AddCapture( startSquare, targetSquare );
				}
				targetSquare = board.GetMovementMatrix( DIRECTION_NW )[startSquare];
				if( targetSquare != -1 )
				{
					if( board.GetSquareContents( targetSquare ) == NULL )
					{
						if( !quiescentSearch )
							list.AddMove( startSquare, targetSquare );
					}
					else if( board.GetSquareContents( targetSquare )->GetPlayerNumber() != currentPlayer )
						list.AddCapture( startSquare, targetSquare );
				}
				targetSquare = board.GetMovementMatrix( DIRECTION_SW )[startSquare];
				if( targetSquare != -1 )
				{
					if( board.GetSquareContents( targetSquare ) == NULL )
					{
						if( !quiescentSearch )
							list.AddMove( startSquare, targetSquare );
					}
					else if( board.GetSquareContents( targetSquare )->GetPlayerNumber() != currentPlayer )
						list.AddCapture( startSquare, targetSquare );
				}
			}
			AddMovesForOdinsPawn( currentPlayer, list, startSquare, quiescentSearch );
		}
	}
}

bool OdinsRuneGame::MoveBeingMade
	( MoveInfo &moveInfo,
	  GameRec &gameRecord )
{
	// *** UPDATE gameInt1 *** //

	//	gameInt1 is a 'user' field in the game records that a game can use
	//	for any purpose.  we will use it to track the forward progress of 
	//	the game, so we can determine which Phase we are in ('opening', 
	//	'midgame', or 'endgame' in our case)

	if( gameRecord.gameInt1 < 16 )
	{
		//	we are in the opening until gameInt1 reaches 16;
		//	in the opening, we increment gameInt1 for the following moves:
		//		- any capture
		//		- the first move of any piece
		//		- any pawn push to rank 6+ (code value of 5+)
		if( gameRecord.materialCaptured > 0 ||
			(gameRecord.pieceMoved->GetFlags() & (FLAGS_HAS_MOVED | FLAGS_HAS_MOVED_TWICE)) == FLAGS_HAS_MOVED ||
			(gameRecord.pieceMoved->IsPawn() && 
				board.ranks[gameRecord.pieceMoved->GetPlayerNumber()][gameRecord.pieceMoved->GetSquareNumber()] >= 4) )
			gameRecord.gameInt1++;
	}

	return true;
}

Phase &OdinsRuneGame::AdjustEvaluation
	( int &eval,
	  PawnHash *pPawnHash )
{
	Phase *currentPhase = phases;

	if( board.GetCurrentGameRecord().gameInt1 < 16 )
	{
		// *** The OPENING *** //

		//	in the opening we consider the following things:
		//		- give a penalty for moving major pieces in the opening
		//		- give a penalty for blocking center pawns in the opening
		//		- give a penalty for moving the king to the back rank between 
		//			the rooks
		//		- give a bonus for moving the king to the back rank if not 
		//			between the rooks (the Grand Chess equivilant of castling)

		//	use this "factor" to scale down the adjustments to be applied here, 
		//	so that the adjustments become smaller and smaller as we leave the 
		//	opening and enter the mid-game
		int factor = MAX(board.GetCurrentGameRecord().gameInt1 - 6, 0);

		int factor6 = 640 - (factor << 6);	//   \ 
		int factor5 = 320 - (factor << 5);  //    \_  different adjustments
		int factor4 = 160 - (factor << 4);  //    /   from large to small
		int factor3 = 80 - (factor << 3);   //   / 

		if( testingPosition )
			eval += PenalizePiecesForMovingMultipleTimes();
	}
	else
		currentPhase = phases + 1;


	// *** ROOK ON OPEN and SEMI-OPEN FILE BONUS *** //
	if( pPawnHash != NULL )
	{
		if( rooks[0][0] != NULL &&
			!rooks[0][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][rooks[0][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 50;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 20;
		}
		if( rooks[0][1] != NULL &&
			!rooks[0][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[0][board.files[0][rooks[0][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval += 50;
			if( pPawnHash->nPawnsPerFile[1][board.files[0][rooks[0][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval += 20;
		}

		if( rooks[1][0] != NULL &&
			!rooks[1][0]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][rooks[1][0]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 50;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][0]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 20;
		}
		if( rooks[1][1] != NULL &&
			!rooks[1][1]->IsCaptured() &&
			pPawnHash->nPawnsPerFile[1][board.files[0][rooks[1][1]->GetSquareNumber()]] == 0 )
		{
			//	file at least semi-open
			eval -= 50;
			if( pPawnHash->nPawnsPerFile[0][board.files[0][rooks[1][1]->GetSquareNumber()]] == 0 )
				//	fully open file
				eval -= 20;
		}
	}

	//	TWO-BISHOPS BONUS
	if( bishops[0][0] != NULL && bishops[0][1] != NULL &&
		!bishops[0][0]->IsCaptured() && !bishops[0][1]->IsCaptured() )
		eval += 150;
	if( bishops[1][0] != NULL && bishops[1][1] != NULL && 
		!bishops[1][0]->IsCaptured() && !bishops[1][1]->IsCaptured() )
		eval -= 150;

	return *currentPhase;
}

void OdinsRuneGame::DescribeMove
	( MoveInfo &move,
	  char *queryDescription,
	  char *description,
	  char *notation )
{
	if( move.type == UserMove1 )
	{
		int fromRank = move.fromSquare / board.GetNumberOfFiles();
		int fromFile = move.fromSquare % board.GetNumberOfFiles();
		int toRank = move.toSquare / board.GetNumberOfFiles();
		int toFile = move.toSquare % board.GetNumberOfFiles();
		int iguiRank = move.tag / board.GetNumberOfFiles();
		int iguiFile = move.tag % board.GetNumberOfFiles();

		if( queryDescription != NULL )
		{
			sprintf( queryDescription, "Capture %s on %c%d", 
				board.GetSquareContents( move.tag )->GetType().GetFullName(),
				'a' + iguiFile, 1 + iguiRank );
		}
		if( description != NULL )
		{
			sprintf( description, "%s %c%d-%c%d ! %c%d", 
				&(move.pPieceMoved->GetType()) == &DecimalForestOx::decimalForestOx ? "Forest Ox" : "King",
				'a' + fromFile, 1 + fromRank, 
				'a' + toFile, 1 + toRank, 
				'a' + iguiFile, 1 + iguiRank );
		}
		if( notation != NULL )
		{
			sprintf( notation, "%c%d%c%d!%c%d", 
				'a' + fromFile, 1 + fromRank, 
				'a' + toFile, 1 + toRank, 
				'a' + iguiFile, 1 + iguiRank );
		}
	}
	else if( move.type == UserMove2 )
	{
		int fromRank = move.fromSquare / board.GetNumberOfFiles();
		int fromFile = move.fromSquare % board.GetNumberOfFiles();
		int toRank = move.toSquare / board.GetNumberOfFiles();
		int toFile = move.toSquare % board.GetNumberOfFiles();
		int relocateRank = move.tag / board.GetNumberOfFiles();
		int relocateFile = move.tag % board.GetNumberOfFiles();

		if( queryDescription != NULL )
		{
			sprintf( queryDescription, "Relocate %s to %c%d", 
				board.GetSquareContents( move.toSquare )->GetType().GetFullName(),
				'a' + relocateFile, 1 + relocateRank );
		}
		if( description != NULL )
		{
			sprintf( description, "%s %c%d-%c%d * %c%d", 
				&(move.pPieceMoved->GetType()) == &DecimalValkyrie::decimalValkyrie ? "Valkyrie" : "King",
				'a' + fromFile, 1 + fromRank, 
				'a' + toFile, 1 + toRank, 
				'a' + relocateFile, 1 + relocateRank );
		}
		if( notation != NULL )
		{
			sprintf( notation, "%c%d%c%d*%c%d", 
				'a' + fromFile, 1 + fromRank, 
				'a' + toFile, 1 + toRank, 
				'a' + relocateFile, 1 + relocateRank );
		}
	}
	else if( queryDescription != NULL && 
		&(board.GetSquareContents( move.fromSquare )->GetType()) == 
		&DecimalForestOx::decimalForestOx )
	{
		sprintf( queryDescription, "normal move" );
	}
	else
		Game::DescribeMove( move, queryDescription, description, notation );
}

void OdinsRuneGame::DescribeMove
	( Movement &movement,
	  char *description )
{
	if( movement.GetMoveType() == UserMove1 )
	{
		int fromRank = movement.GetFromSquare() / board.GetNumberOfFiles();
		int fromFile = movement.GetFromSquare() % board.GetNumberOfFiles();
		int toRank = movement.GetToSquare() / board.GetNumberOfFiles();
		int toFile = movement.GetToSquare() % board.GetNumberOfFiles();
		int iguiRank = movement.GetTag() / board.GetNumberOfFiles();
		int iguiFile = movement.GetTag() % board.GetNumberOfFiles();
		sprintf( description, "%c%d-%c%d!%c%d", 
			'a' + fromFile, 1 + fromRank, 
			'a' + toFile, 1 + toRank, 
			'a' + iguiFile, 1 + iguiRank );
	}
	else if( movement.GetMoveType() == UserMove2 )
	{
		int fromRank = movement.GetFromSquare() / board.GetNumberOfFiles();
		int fromFile = movement.GetFromSquare() % board.GetNumberOfFiles();
		int toRank = movement.GetToSquare() / board.GetNumberOfFiles();
		int toFile = movement.GetToSquare() % board.GetNumberOfFiles();
		int relocateRank = movement.GetTag() / board.GetNumberOfFiles();
		int relocateFile = movement.GetTag() % board.GetNumberOfFiles();
		sprintf( description, "%c%d-%c%d*%c%d", 
			'a' + fromFile, 1 + fromRank, 
			'a' + toFile, 1 + toRank, 
			'a' + relocateFile, 1 + relocateRank );
	}
	else
		Game::DescribeMove( movement, description );
}

bool OdinsRuneGame::TestForWinLossDraw
	( int &eval )
{
	if( !DecimalOdinsKing::decimalOdinsKing.GetPieces( 0 ) )
	{
		//	white has no kings, and has lost
		if( board.GetCurrentPlayerNumber() == 0 )
			eval = -INFINITY;
		else
			eval = INFINITY;
		return true;
	}
	else if( !DecimalOdinsKing::decimalOdinsKing.GetPieces( 1 ) )
	{
		//	white has no kings, and has lost
		if( board.GetCurrentPlayerNumber() == 1 )
			eval = -INFINITY;
		else
			eval = INFINITY;
		return true;
	}
	return false;
}

int OdinsRuneGame::TranslateMove
	( MovementList &list,
	  char *notation )
{
	int len = 0;
	while( notation[len] != ' ' && notation[len] != '\0' )
		len++;
	if( len > 6 )
	{
		//	this is either a Forest Ox move with 
		//	an igui capture, or a Valkyrie move which
		//	is relocating a friendly piece.
		int x;
		for( x = len - 1; x > 0; x-- )
		{
			if( notation[x] == '*' )
				break;
			if( notation[x] == '!' )
				break;
		}
		if( x == 0 )
		{
			ASSERT(FALSE);
		}
		int specialFile = notation[x+1] - 'a';
		int specialRank = atoi( notation + x + 2 ) - 1;
		int specialSquare = specialRank * 10 + specialFile;
		int cursor = 0;
		int fromSquare;
		int toSquare;
		fromSquare = board.GetFileByChar( notation[cursor] ) + 
			(atoi( notation + cursor + 1 ) - 1) * 10;
		cursor++;
		while( notation[cursor] >= '0' && notation[cursor] <= '9' )
			cursor++;
		toSquare = board.GetFileByChar( notation[cursor] ) + 
			(atoi( notation + cursor + 1 ) - 1) * 10;
		cursor++;
		while( notation[cursor] >= '0' && notation[cursor] <= '9' )
			cursor++;
		//	now look for the move
		for( int x = 0; x < list.GetCount(); x++ )
		{
			MoveInfo &move = list.GetMove( x );
			if( move.fromSquare == fromSquare &&
				move.toSquare == toSquare && 
				move.tag == specialSquare )
				//	found it!
				return x;
		}
		ASSERT(FALSE);
	}
	//	this is a normal move, so let the 
	//	standard game class handle it
	return Game::TranslateMove( list, notation );
}

void OdinsRuneGame::DefaultSettings()
{
	squareColor1 = RGB(255, 255, 234);
	squareColor2 = RGB(0, 128, 0);
	pieceColor1 = RGB(255, 255, 153);
	pieceColor2 = RGB(153, 204, 204);
	borderColor = RGB(192, 192, 192);
	boardDisplayType = BOARD_IS_CHECKERED;
	selectedPieceSet = PIECE_SET_RUNES;
}
