#ifndef __BOARD_H_INC #define __BOARD_H_INC #include "Types.h" #include "Panic.h" #include "Move.h" #define BOARD_DEFAULT_VALUE { \ 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, \ }; // 0x88-fill definitions #define PTR_SIDE_AND_CASTLERIGHT 0x08 //byte (1=side, 2,4=white castle, 8,16=black) // CAN FILL 0x09 #define PTR_W_KING 0x0A // byte (points to index or maybe 64-arr index) #define PTR_B_KING 0x0B // (PTR_W_KING | COLOR or PTR_W_KING + COLOR) #define PTR_ZOBRIST 0x0C // 4 bytes // 0x0D // 0x0E // 0x0F #define PTR_ENPASSANT 0x18 #define PTR_REVMOV 0x19 // free space #define PTR_UNMOVE_START 0x28 #define PTR_UNMOVE_LAST 0x7F byte field[128]; byte PTR_UNMOVE; const byte field_default_value[] PROGMEM = BOARD_DEFAULT_VALUE; void board_init() { for(int i = 0; i < 128; i++) { field[i] = pgm_read_byte_near(field_default_value + i); } PTR_UNMOVE = PTR_UNMOVE_START; field[PTR_SIDE_AND_CASTLERIGHT] = 0b11110; // all castle rights allowed, white to move field[PTR_W_KING] = 0x04; // e1 field[PTR_B_KING] = 0x74; // e8 long* zob = (long*)&field[PTR_ZOBRIST]; *zob = 0xDEADBEEF; } struct Unmove { byte sq_from; // 0b(1kingside_castle?)(3rank)(1queenside_castle?)(3file) byte sq_to; // 0b(1promoted?)(3rank)(1ep_capture?)(3file) byte captured; // 0b(4enpassantinfo)(1color)(3piecetype) byte revmov; // 8bit integer }; 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; } void next_unmove() { PTR_UNMOVE++; if(PTR_UNMOVE > PTR_UNMOVE_LAST) { panic(F("Unmove stack overflow")); } if(!(PTR_UNMOVE & 0x8)) { PTR_UNMOVE += 0x8; } } void prev_unmove() { PTR_UNMOVE--; if(PTR_UNMOVE < PTR_UNMOVE_START) { panic(F("Unmaking from empty stack")); } if(!(PTR_UNMOVE & 0x8)) { PTR_UNMOVE -= 0x8; } } void store_unmove(Unmove u) { byte *ub = (byte*) &u; for(byte i = 0; i < sizeof(u); i++) { field[PTR_UNMOVE] = ub[i]; next_unmove(); } } Unmove read_unmove() { Unmove u; byte* ptr = (byte*) &u; for(int i = sizeof(u) - 1; i >= 0; i--) { prev_unmove(); ptr[i] = field[PTR_UNMOVE]; #ifdef _ACF_CLEAR_UNMOVE field[PTR_UNMOVE] = 0; #endif } return u; } void print() { Serial.println(F("BOARD:")); for(char i = 7; i >= 0; i--) { for(byte j = 0; j < 16; j++) { if(j == 8) Serial.print(F("| ")); Serial.print(field[i*16 + j], HEX); Serial.print(F(" ")); } Serial.println(); } } void make(Move m) { // TODO zobrist? // fill unmove struct with basic data Unmove u; u.revmov = field[PTR_REVMOV]; u.captured = field[m.sq_to] | (field[PTR_ENPASSANT] << 4); u.sq_from = m.sq_from; u.sq_to = m.sq_to; byte piece_type = field[m.sq_from] & 0x7; byte color = black_moving(); if(field[m.sq_to] || piece_type == W_PAWN) { field[PTR_REVMOV] = 0; } else { field[PTR_REVMOV]++; } // Calculate the move 'amount' (unique signature for dx,dy) int sq_diff = (int)m.sq_to - (int)m.sq_from; int sq_diff_abs = abs(sq_diff); // TODO test the castling code more extensively // 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 = (color ? 0x70 : 0x0); 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 // First store the current rights in the unmove byte our_rights = field[PTR_SIDE_AND_CASTLERIGHT] >> (color ? 3 : 1); if(our_rights & 0b10) // kingside allowed u.sq_from |= 0x80; if(our_rights & 0b01) // queenside allowed u.sq_from |= 0x08; // We are doing the simple way: // unset it any time a move is made from the original position. if(m.sq_from == 0x00) // white queenside rook field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00010; else if(m.sq_from == 0x07) // white kingside rook field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00100; else if(m.sq_from == 0x04) // white king field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b00110; else if(m.sq_from == 0x70) // black queenside rook field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b01000; else if(m.sq_from == 0x77) // black kingside rook field[PTR_SIDE_AND_CASTLERIGHT] &= ~0b10000; else if(m.sq_from == 0x74) // black king 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) == (color ? 0x20 : 0x50) ) { // all EP-conditions are met // therefore, delete the EP-vurnerable pawn byte ep_field = m.sq_to + (color ? 16 : -16); field[ep_field] = P_EMPTY; // also put information that we did an EP-capture u.sq_to |= 0x08; } // 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(m.pc_prom != P_EMPTY) { // promoting; indicate this in the sq_to byte in unmove. field[m.sq_to] = m.pc_prom; u.sq_to |= 0x80; } else { // not promoting; so keep the same piece type field[m.sq_to] = field[m.sq_from]; } // then delete the original copy. field[m.sq_from] = P_EMPTY; // Switch sides field[PTR_SIDE_AND_CASTLERIGHT] ^= 0x01; store_unmove(u); } void unmake() { Unmove u = read_unmove(); field[PTR_REVMOV] = u.revmov; byte sq_from = u.sq_from & 0x77; byte sq_to = u.sq_to & 0x77; byte prom_ep_capt = u.sq_to & 0x88; if(prom_ep_capt == 0) { // regular move field[sq_from] = field[sq_to]; } else if (prom_ep_capt == 0x80) { // piece was promoted // so the source is a pawn field[sq_from] = W_PAWN | (field[sq_to] & 0b1000); } else if (prom_ep_capt == 0x08) { // we did an enpassant capture byte ep_sq = (sq_to & 0x07) | (sq_from & 0x70); field[ep_sq] = W_PAWN | black_moving() << 3; // also undo the regular move field[sq_from] = field[sq_to]; } byte castleright_offset = 3 - 2*black_moving(); if(u.sq_from & 0x80) { // restore king side castling rights field[PTR_SIDE_AND_CASTLERIGHT] |= 0b10 << castleright_offset; } if(u.sq_from & 0x08) { field[PTR_SIDE_AND_CASTLERIGHT] |= 0b01 << castleright_offset; } int sq_diff = (int)sq_to - (int)sq_from; int sq_diff_abs = abs(sq_diff); if((field[sq_from] & 0x7) == W_KING && sq_diff_abs == 2) { // we castled byte castle_source = 0x70*!black_moving(); if(sq_diff == 2) { castle_source += 0x7; } byte castle_target = sq_from + (sq_diff/2); // move rook back to original position field[castle_source] = field[castle_target]; // and clear where it was put field[castle_target] = P_EMPTY; } field[sq_to] = u.captured & 0b1111; field[PTR_SIDE_AND_CASTLERIGHT] ^= 0x01; field[PTR_ENPASSANT] = u.captured >> 4; } #endif