#ifndef __BOARD_H_INC #define __BOARD_H_INC #include "Types.h" #include "Panic.h" #include "Move.h" struct Unmove { byte sq_from; // 0b(1unused)(3rank)(1unused)(3file) byte sq_to; // 0b(1promoted?)(3rank)(1unused)(3file) byte captured; // 0b(4unused)(1color)(3piecetype) byte enpassant; // store enpassant state byte revmov; // 8bit integer }; class Board { public: Board(); void make(Move m); void unmake(); void print(); bool black_moving() { return field[PTR_SIDE_AND_CASTLERIGHT] & 0x1; } unsigned long get_zobrist() { long* addr = (long*) &field[PTR_ZOBRIST]; return *addr; } void reset_unmake_stack() { PTR_UNMOVE = PTR_UNMOVE_START; } byte field[128] = { W_ROOK, W_KNGT, W_BSHP, W_QUEN, W_KING, W_BSHP, W_KNGT, W_ROOK, 0, 0, 0, 0, 0, 0, 0, 0, W_PAWN, W_PAWN, W_PAWN, W_PAWN, W_PAWN, W_PAWN, W_PAWN, W_PAWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, B_PAWN, B_PAWN, B_PAWN, B_PAWN, B_PAWN, B_PAWN, B_PAWN, B_PAWN, 0, 0, 0, 0, 0, 0, 0, 0, B_ROOK, B_KNGT, B_BSHP, B_QUEN, B_KING, B_BSHP, B_KNGT, B_ROOK, 0, 0, 0, 0, 0, 0, 0, 0, }; private: // Private function defs void next_unmove(); void prev_unmove(); void store_unmove(Unmove u); Unmove read_unmove(); // 0x88-fill definitions static const byte PTR_SIDE_AND_CASTLERIGHT = 0x08; //byte (1=side, 2,4=white castle, 8,16=black) // CAN FILL 0x09 static const byte PTR_W_KING = 0x0A; // byte (points to index or maybe 64-arr index) // const byte PTR_B_KING = 0x0B; (PTR_W_KING | COLOR or PTR_W_KING + COLOR) static const byte PTR_ZOBRIST = 0x0C; // 4 bytes // 0x0D // 0x0E // 0x0F static const byte PTR_ENPASSANT = 0x18; static const byte PTR_REVMOV = 0x19; // free space static const byte PTR_UNMOVE_START = 0x28; static const byte PTR_UNMOVE_LAST = 0x7F; byte PTR_UNMOVE = PTR_UNMOVE_START; }; Board::Board() { // Then, other data we need to store is assigned 0-fields. field[PTR_ZOBRIST+1] = 1; } void Board::print() { for(char i = 7; i >= 0; i--) { for(byte j = 0; j < 16; j++) { Serial.print(field[i*16 + j], HEX); Serial.print(" "); } Serial.println(); } } void Board::make(Move m) { // TODO handle revmov clock // TODO zobrist? // fill unmove struct with basic data Unmove u; u.revmov = field[PTR_REVMOV]; u.captured = field[m.sq_to]; u.sq_from = m.sq_from; u.sq_to = m.sq_to; byte piece_type = field[m.sq_from] & 0x7; if(u.captured || piece_type == W_PAWN) { field[PTR_REVMOV]++; } else { field[PTR_REVMOV] = 0; } // Calculate the move 'amount' (unique signature for dx,dy) int sq_diff = (int)m.sq_from - (int)m.sq_to; int sq_diff_abs = abs(sq_diff); // TODO test the csatling code // Handle castling if(piece_type == W_KING && sq_diff_abs == 2) { // We are castling! After all, a king cannot move // more than one position except when castling. // Since we don't care about legality; just do it byte castle_source = 0x70*black_moving(); if(sq_diff == 2) { castle_source += 0x7; } byte castle_target = m.sq_from + (sq_diff/2); field[castle_target] = field[castle_source]; field[castle_source] = P_EMPTY; } // Handle castling rights // We are doing the simple way: // unset it any time a move is made from the original position. if(m.sq_from == 0x00) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00010; if(m.sq_from == 0x07) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00100; if(m.sq_from == 0x04) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00110; if(m.sq_from == 0x70) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b01000; if(m.sq_from == 0x77) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b10000; if(m.sq_from == 0x74) field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b11000; // TODO: test enpassant code more than basics // handle enpassant capture if( field[PTR_ENPASSANT] && piece_type == W_PAWN && (m.sq_to & 0x7) == (field[PTR_ENPASSANT] & 0x7) && (m.sq_to & 0x70) == (0x50 - 0x30*black_moving()) ) { // we are also going to the correct square to do EP. // therefore, delete the EP-vurnerable pawn byte ep_field = m.sq_to - 16 + 32*black_moving(); field[ep_field] = P_EMPTY; } // Store the current enpassant situation u.enpassant = field[PTR_ENPASSANT]; // handle enpassant setup (double pawn move) if( piece_type == W_PAWN && (sq_diff_abs == 32) ) { // we are doing a pawn double-move. // therefore, it allows enpassant in the next move. field[PTR_ENPASSANT] = 0b1000 | (m.sq_from & 0x7); } else { // no enpassant in the next turn. field[PTR_ENPASSANT] = 0; } // are we promoting? byte new_val = m.pc_prom & 0b1111; if(new_val != P_EMPTY) { // promoting; indicate this in the sq_to byte in unmove. u.sq_to |= 0x80; } else { // not promoting; so keep the same piece type new_val = field[m.sq_from]; } // copy or promote our piece. field[m.sq_to] = new_val; // then delete the original copy. field[m.sq_from] = P_EMPTY; // Switch sides field[PTR_SIDE_AND_CASTLERIGHT] ^= 0x01; store_unmove(u); } void Board::unmake() { Unmove u = read_unmove(); byte sq_to = u.sq_to & 0x77; byte prom_ep_capt = u.sq_to & 0x88; if(prom_ep_capt == 0) { // regular move field[u.sq_from] = field[u.sq_to]; } else if (prom_ep_capt == 0x80) { // piece was promoted // so the source is a pawn field[u.sq_from] = W_PAWN | (field[u.sq_to] & 0b1000); } else if (prom_ep_capt == 0x08) { // we did an enpassant capture byte ep_sq = (sq_to & 0x07) | (u.sq_from & 0x70); field[ep_sq] = W_PAWN | black_moving() << 3; } field[u.sq_to] = u.captured & 0b1111; field[PTR_SIDE_AND_CASTLERIGHT] ^= 0x01; } void Board::next_unmove() { PTR_UNMOVE++; if(PTR_UNMOVE > PTR_UNMOVE_LAST) { panic(F("Unmove stack overflow")); } if(!(PTR_UNMOVE & 0x8)) { PTR_UNMOVE += 0x8; } } void Board::prev_unmove() { PTR_UNMOVE--; if(PTR_UNMOVE < PTR_UNMOVE_START) { panic(F("Unmaking from empty stack")); } if(!(PTR_UNMOVE & 0x8)) { PTR_UNMOVE -= 0x8; } } void Board::store_unmove(Unmove u) { byte *ub = (byte*) &u; for(size_t i = 0; i < sizeof(u); i++) { next_unmove(); field[PTR_UNMOVE] = ub[i]; } } Unmove Board::read_unmove() { Unmove u; byte* ptr = (byte*) &u; for(int i = sizeof(u) - 1; i >= 0; i--) { ptr[i] = field[PTR_UNMOVE]; prev_unmove(); } return u; } #endif