
bool Board::MakeMove(Move *m, undo_t *undo) {
	register uint from = m->From();
	register uint to = m->To();	
	register BitBoard bb_from = BIT(from);         
	register BitBoard bb_to = BIT(to);
	register uint piece_moving = m->Piece();
	register uint removal = m->Removal();
	uint8 old_castle = castle;

	undo->ep_file = ep_file;
	undo->castle = castle;
	undo->ksq[0] = ksq[0];
	undo->ksq[1] = ksq[1];	
	undo->ply = ply;
	undo->stm = stm;
	undo->key = key;	
	undo->fifty = fifty++;	
	undo->mat_diff = mat_diff;
	undo->pawn_key = pawn_key;
	undo->mat_key = mat_key;	
	undo->opening = opening;
	undo->endgame = endgame;
			
	uint sntm = stm^1; 
	if(m->IsIrreversible())		fifty = 0;

	keys[ply++] = key;	
	// key: turn
	key ^= STM_HASH_KEY;

	// castle
	castle &= CastleMask[from] & CastleMask[to];
	key ^= CastleHashKey[castle ^ old_castle];

	// EP
	if(ep_file) {
		key ^= EP_HASH_KEY(ep_file);
		ep_file = 0;
	}

	// prevent crashes in 64-bit mode
	// bugfixed by Dann Corbit
	volatile int pm = piece_moving; 
	volatile int fm = from; 
	// clear from square 
	key ^= PIECE_HASH_KEY(stm, type[fm], fm); 
	// material values
	opening -= PST_OP(stm, pm, fm); 
	endgame -= PST_EG(stm, pm, fm); 

	// special case: Promotes 

	if(m->IsPromote()) {
		type[to] = PAWN;
		piece[PAWN] ^= bb_from; 		
		piece_moving = PromoteToPiece[m->Promote()]; // hack for hash
		piece[piece_moving] ^= bb_to; 
		type[to] = piece_moving;
		
		pawn_key ^= PAWN_HASH_KEY(stm, from);
		mat_key += MAT_KEY_VALUE_PROMO(stm, piece_moving);
		mat_diff += MAT_DIFF_VALUE_PROMO(stm, piece_moving);
									
	} else { // other moves
		piece[piece_moving] ^= bb_from^bb_to; 
		type[to] = piece_moving;

		// special case: Two Forward
		if(piece_moving == PAWN) {
			pawn_key ^= PAWN_HASH_KEY(stm, from) ^ PAWN_HASH_KEY(stm, to);
			if(m->IsTwoForward()) {				
				if (Pawn[WHITE][to-8] & Occupied(sntm) & piece[PAWN]) { 	
				ep_file = File(to)+1;
				key ^= EP_HASH_KEY(ep_file);
			}
		}

		// special case: King and Castle
		} else if(piece_moving == KING) { 		 
			ksq[stm] = to;				

			if(m->IsCastle()) {
				uint rook_from=0, rook_to=0;
				switch(to) {
					case g1: rook_from = h1; rook_to = f1; break;
					case g8: rook_from = h8; rook_to = f8; break;
					case c1: rook_from = a1; rook_to = d1; break;					
					case c8: rook_from = a8; rook_to = d8; break;					
				}					
				type[rook_from] = EMPTY;
				type[rook_to] = ROOK;
				piece[ROOK] ^= BIT(rook_from)^BIT(rook_to);
				occupied_by[stm] ^= BIT(rook_from)^BIT(rook_to);
				occupied_rl90 ^= BIT_L90(rook_from)^BIT_L90(rook_to);
				occupied_rl45 ^= BIT_L45(rook_from)^BIT_L45(rook_to);
				occupied_rr45 ^= BIT_R45(rook_from)^BIT_R45(rook_to);

				// hash
				key ^= PIECE_HASH_KEY(stm, ROOK, rook_from);
				key ^= PIECE_HASH_KEY(stm, ROOK, rook_to);

				opening += PST_OP(stm, ROOK, rook_to) - PST_OP(stm, ROOK, rook_from);
				endgame += PST_EG(stm, ROOK, rook_to) - PST_EG(stm, ROOK, rook_from);
			}
		}
	}

	// hash
	key ^= PIECE_HASH_KEY(stm, piece_moving, to);
	opening += PST_OP(stm, piece_moving, to);
	endgame += PST_EG(stm, piece_moving, to);

	// Capture
	if(removal == EMPTY) {					
		occupied_rr45 ^= BIT_R45(to);			
		occupied_rl45 ^= BIT_L45(to);
		occupied_rl90 ^= BIT_L90(to);

	} else if(removal == EN_PASSANT) {
		uint epsq = to+8-((stm^1)<<4);             
		type[epsq] = EMPTY;
		piece[PAWN] ^= BIT(epsq);
		occupied_by[sntm] ^= BIT(epsq);        
		occupied_rl90 ^= BIT_L90(to)^BIT_L90(epsq);
		occupied_rl45 ^= BIT_L45(to)^BIT_L45(epsq);
		occupied_rr45 ^= BIT_R45(to)^BIT_R45(epsq);
		key ^= PIECE_HASH_KEY(sntm, PAWN, epsq);
		pawn_key ^= PAWN_HASH_KEY(sntm, epsq);

		mat_key -= MAT_KEY_VALUE(sntm, PAWN);
		mat_diff -= MAT_DIFF_VALUE(sntm, PAWN); 
		opening -= PST_OP(sntm, PAWN, epsq);
		endgame -= PST_EG(sntm, PAWN, epsq);

	} else {  // Other 
		piece[removal] ^= bb_to;
		occupied_by[sntm] ^= bb_to;
		key ^= PIECE_HASH_KEY(sntm, removal, to);
		if(removal == PAWN)	pawn_key ^= PAWN_HASH_KEY(sntm, to);

		mat_key -= MAT_KEY_VALUE(sntm, removal);
		mat_diff -= MAT_DIFF_VALUE(sntm, removal);
		opening -= PST_OP(sntm, removal, to);
		endgame -= PST_EG(sntm, removal, to);
	} 

	// Final
	occupied_by[stm] ^= bb_from ^ bb_to;	
	type[from] = EMPTY;
	occupied = White() | Black();	
	occupied_rl90 ^= BIT_L90(from);		
	occupied_rl45 ^= BIT_L45(from);
	occupied_rr45 ^= BIT_R45(from);

	ASSERT(!InCheck());	
	
	stm ^= 1;		
	
	if(DEBUG) {
		if(key != InitHashKey()) {
			Print();
			m->Print();
			ASSERT(false);
		}
	}
	return true;
}

bool Board::UnmakeMove(Move *m, undo_t *undo) {

	register uint from = m->From();
	register uint to = m->To();
	register BitBoard bb_from = BIT(from);         
	register BitBoard bb_to = BIT(to);
	register uint removal = m->Removal();
	register uint piece_moving = m->Piece();
	
	uint sntm; 		
	ply = undo->ply;
	ep_file = undo->ep_file;
	stm = undo->stm;
	sntm = stm^1;
	key = undo->key;
	fifty = undo->fifty;
	castle = undo->castle; 
	mat_diff = undo->mat_diff;
	pawn_key = undo->pawn_key;
	mat_key = undo->mat_key;	
	opening = undo->opening;
	endgame = undo->endgame;
		
	if(piece_moving == PAWN) {		   
		piece[PAWN] ^= bb_from; 
		type[from] = PAWN;
		if(m->IsPromote())	piece_moving = PromoteToPiece[m->Promote()];
		piece[piece_moving] ^= bb_to; 						
		
	} else if(piece_moving == KING) {   
		piece[KING] ^= bb_from^bb_to; 
		ksq[stm] = undo->ksq[stm];
		type[from] = KING;
		
		if(m->IsCastle()) {
			uint rook_from=0, rook_to=0;			       
			switch (to) {
				case c1: rook_from = a1; rook_to = d1; break;
				case g1: rook_from = h1; rook_to = f1; break;
				case c8: rook_from = a8; rook_to = d8; break;
				case g8: rook_from = h8; rook_to = f8; break;
			}					
			type[rook_to] = EMPTY;
			type[rook_from] = ROOK;
			piece[ROOK] ^= BIT(rook_from)^BIT(rook_to);
			occupied_by[stm] ^= BIT(rook_from)^BIT(rook_to);
			occupied_rl90 ^= BIT_L90(rook_from)^BIT_L90(rook_to);
			occupied_rl45 ^= BIT_L45(rook_from)^BIT_L45(rook_to);
			occupied_rr45 ^= BIT_R45(rook_from)^BIT_R45(rook_to);
		}	

	} else { // other pieces
		piece[piece_moving] ^= bb_from^bb_to;
		type[from] = piece_moving;
	}

	// Stage 2:

	if(removal == EMPTY) {		
		occupied_rl90 ^= BIT_L90(to);          
		occupied_rl45 ^= BIT_L45(to);     
		occupied_rr45 ^= BIT_R45(to);
		type[to] = EMPTY;	

	} else if(removal == EN_PASSANT) {
		uint epsq = to+8-(sntm<<4);              
		type[epsq] = PAWN;
		type[to] = EMPTY; 
		piece[PAWN] ^= BIT(epsq);
		occupied_by[sntm] ^= BIT(epsq);        
		occupied_rl90 ^= BIT_L90(to)^BIT_L90(epsq);
		occupied_rl45 ^= BIT_L45(to)^BIT_L45(epsq);
		occupied_rr45 ^= BIT_R45(to)^BIT_R45(epsq);

	} else {   
		piece[removal] ^= bb_to;
		occupied_by[sntm] ^= bb_to;
		type[to] = removal;
	}                                               
		
	occupied_by[stm] ^= bb_from ^ bb_to;
	occupied = White() | Black();
	occupied_rl90 ^= BIT_L90(from);		
	occupied_rl45 ^= BIT_L45(from);
	occupied_rr45 ^= BIT_R45(from);	

	return true;
}

void Board::MakeNullMove(undo_t *undo) {
	// save
	undo->ep_file = ep_file;	
	undo->ply = ply;
	undo->stm = stm;
	undo->key = key;
	undo->fifty = fifty;
	
	fifty = 0; // hack
	keys[ply++] = key;								
	stm ^= 1;		

	key ^= STM_HASH_KEY;
	if(ep_file) {
		key ^= EP_HASH_KEY(ep_file);
		ep_file = 0;
	}	
	
	if(DEBUG) {
		if(key != InitHashKey()) {
			Print();			
			ASSERT(false);
		}
	}	
}

void Board::UnmakeNullMove(undo_t *undo) {
	// load	
	ep_file = undo->ep_file;
	ply		= undo->ply;	
	stm		= undo->stm;	
	key		= undo->key;
	fifty	= undo->fifty;
}
