From 48619c5113897977d037350abc2a20c317504f46 Mon Sep 17 00:00:00 2001 From: Jonathan Good Date: Mon, 7 Oct 2024 12:36:24 +0100 Subject: [PATCH] Basic play demo --- include/fsm.hpp | 2 - lib/chess/include/chess.hpp | 18 +++-- lib/chess/src/chess.cpp | 56 +++++++++++---- src/bin/main.cpp | 82 ++++++++++++++++++---- src/controller.cpp | 1 + src/fsm.cpp | 134 +++++++++++++++++++++--------------- src/reed.cpp | 2 + 7 files changed, 203 insertions(+), 92 deletions(-) diff --git a/include/fsm.hpp b/include/fsm.hpp index eed74d6..d461518 100644 --- a/include/fsm.hpp +++ b/include/fsm.hpp @@ -25,5 +25,3 @@ typedef struct { FSMState update_state(Chess *chess, int instruction, FSMState state, State_Memory *state_memory, Move_List *move_list, Move_List *current_square_moves); - -void print_bitboard(unsigned long long bitboard); \ No newline at end of file diff --git a/lib/chess/include/chess.hpp b/lib/chess/include/chess.hpp index 40042bc..fc2b5ef 100644 --- a/lib/chess/include/chess.hpp +++ b/lib/chess/include/chess.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include typedef int Square; typedef int Player; @@ -56,7 +56,11 @@ typedef struct { std::string square_to_string(const Square square); std::string move_to_string(const Move move); -extern Move_List chess_move_lists[3]; +int to_square(int file, int rank); +int to_square(int index); +int to_index(int square); + +extern Move_List chess_move_lists[4]; class Chess { private: @@ -79,17 +83,19 @@ class Chess { uint64_t get_black(); int get_piece_on_square(int square); void print_board(); - Move parse_move(const char *move_string, Move_List *move_list); + Move parse_move(const char *move_string); + Move parse_move(Square source_square, Square target_square); + Move parse_move(Square source_square, Square target_square, + char promoted_piece); int search_position(Player side, int alpha, int beta, int depth, - Search_Info *search_info, Move_List *move_lists); + Search_Info *search_info); int quiescence_search(Player side, int alpha, int beta); int generate_moves(Move_List *move_list, bool only_captures = false); inline int evaluate_position(Player side); void unmake_move(Move move, Square old_ep); void make_move(Move move); - void get_best_move(int depth, Search_Info *search_info, - Move_List *move_lists); + void get_best_move(int depth, Search_Info *search_info); char square_char(int square); std::string get_fen(); diff --git a/lib/chess/src/chess.cpp b/lib/chess/src/chess.cpp index 626bea3..dfecac4 100644 --- a/lib/chess/src/chess.cpp +++ b/lib/chess/src/chess.cpp @@ -198,6 +198,8 @@ static const int move_offsets[] = { static const int piece_weights[] = {0, 0, -100, 0, -300, -350, -500, -900, 0, 100, 0, 0, 300, 350, 500, 900}; +Move_List chess_move_lists[4]; + Chess::Chess() : side(WHITE), en_passant(128) { // initialize board } @@ -454,9 +456,9 @@ int Chess::quiescence_search(Player side, int alpha, } int Chess::search_position(Player side, int alpha, int beta, int depth, - Search_Info *search_info, Move_List *move_lists) { + Search_Info *search_info) { // Move_List move_list[1]; - Move_List *move_list = &move_lists[depth - 1]; + Move_List *move_list = &chess_move_lists[depth - 1]; int old_alpha = alpha; Square old_ep = en_passant; Move move; @@ -478,8 +480,8 @@ int Chess::search_position(Player side, int alpha, int beta, int depth, } make_move(move_list->moves[i]); // make move - int score = -search_position(24 - side, -beta, -alpha, depth - 1, - search_info, move_lists); + int score = + -search_position(24 - side, -beta, -alpha, depth - 1, search_info); unmake_move(move_list->moves[i], old_ep); // take back search_info->best_move = move_list->moves[i]; // store best move so far @@ -500,9 +502,8 @@ int Chess::search_position(Player side, int alpha, int beta, int depth, return alpha; } -void Chess::get_best_move(int depth, Search_Info *search_info, - Move_List *move_lists) { - search_position(side, -10000, 10000, depth, search_info, move_lists); +void Chess::get_best_move(int depth, Search_Info *search_info) { + search_position(side, -10000, 10000, depth, search_info); } /*********************************************************************************\ @@ -511,31 +512,53 @@ void Chess::get_best_move(int depth, Search_Info *search_info, ;---------------------------------------------------------------------------------; \*********************************************************************************/ -Move Chess::parse_move(const char *move_string, Move_List *move_list) { +Move Chess::parse_move(Square source_square, Square target_square, + char promoted_piece) { Move move; + Move_List *move_list = &chess_move_lists[0]; generate_moves(move_list, false); for (int i = 0; i < move_list->length; i++) { move = move_list->moves[i]; - if (move.source_square == (move_string[0] - 'a') + - (7 - (move_string[1] - '0' - 1)) * 16 && - move.target_square == (move_string[2] - 'a') + - (7 - (move_string[3] - '0' - 1)) * 16) { - if (move.promoted_piece) { - if (promoted_pieces[move.promoted_piece] == move_string[4]) + if (move.source_square == source_square && + move.target_square == target_square) { + // Check promotion piece if provided + if (promoted_piece) { + if (move.promoted_piece && + promoted_pieces[move.promoted_piece] == promoted_piece) { return move; + } continue; } + // Return move if no promotion check is needed return move; } } + // Return an invalid move if no match is found move.promoted_piece = move.target_square = move.source_square = 0; return move; } +Move Chess::parse_move(Square source_square, Square target_square) { + return parse_move(source_square, target_square, 0); +} + +Move Chess::parse_move(const char *move_string) { + Square source_square = + (move_string[0] - 'a') + (7 - (move_string[1] - '0' - 1)) * 16; + Square target_square = + (move_string[2] - 'a') + (7 - (move_string[3] - '0' - 1)) * 16; + + // Check if the move string includes a promotion character + char promoted_piece = move_string[4]; + + // Delegate to the overloaded parse_move function + return parse_move(source_square, target_square, promoted_piece); +} + void Chess::print_board() { for (int i = 0; i < 128; i++) { if (!(i % 16)) printf(" %d ", 8 - (i / 16)); @@ -605,3 +628,8 @@ std::string move_to_string(const Move move) { str += square_to_string(move.target_square); return str; } + +// do the to square with the 7 - rank to get the correct square +int to_square(int file, int rank) { return ((7 - rank) << 4) + file; } +int to_square(int index) { return to_square(index & 7, index >> 3); } +int to_index(int square) { return (7 - (square >> 4)) * 8 + (square & 7); } diff --git a/src/bin/main.cpp b/src/bin/main.cpp index 1d8fcff..cbf6c8b 100644 --- a/src/bin/main.cpp +++ b/src/bin/main.cpp @@ -2,6 +2,7 @@ #include +#include "bluetooth.hpp" #include "controller.hpp" #include "fsm.hpp" @@ -52,16 +53,21 @@ std::vector game = { // clang-format on FSMState state; -Move_List move_list[4]; void replay() { for (std::string moveStr : game) { - Move move = chess.parse_move(moveStr.c_str(), move_list); + Move move = chess.parse_move(moveStr.c_str()); controller.makeMove(move, 500); delay(1000); } } +uint64_t prevBoard; + +State_Memory state_memory; +Move_List cur; +Move_List other; + void setup() { Serial.begin(9600); delay(1000); @@ -75,16 +81,20 @@ void setup() { // controller.makeMove(move, 500); // board.gotoSquare(SQB4); // replay(); - state = FSMState::EnemyPU; + + prevBoard = detection.read(); + // state = FSMState::EnemyPU; + state = FSMState::Idle; ble.setFSMState(state); - ble.setReedSwitchValue(0); + ble.setReedSwitchValue(prevBoard); } void enemy() { if (ble.getMoveUpdated()) { delay(10); // FIX: delay stops ble crash std::string moveStr = ble.readMove(); - Move move = chess.parse_move(moveStr.c_str(), move_list); + Serial.println(moveStr.c_str()); + Move move = chess.parse_move(moveStr.c_str()); chess.make_move(move); state = FSMState::FriendlyPU; ble.setFEN(chess.get_fen()); @@ -95,7 +105,7 @@ void enemy() { void friendly() { Search_Info search_info; - chess.get_best_move(3, &search_info, move_list); + chess.get_best_move(3, &search_info); Move move = search_info.best_move; chess.make_move(move); state = FSMState::EnemyPU; @@ -104,13 +114,59 @@ void friendly() { ble.setFSMState(state); } +int countOnes(uint64_t value) { + return __builtin_popcountll(value); // Works for uint64_t +} + +int findLSBIndex(uint64_t value) { + if (value == 0) return -1; // No bits are set, return -1 + // ffsll returns 1-based index, so subtract 1 for 0-based index + return __builtin_ffsll(value) - 1; +} + void loop() { - switch (state) { - case FSMState::EnemyPU: - enemy(); - break; - case FSMState::FriendlyPU: - friendly(); - break; + // switch (state) { + // case FSMState::EnemyPU: + // enemy(); + // break; + // case FSMState::FriendlyPU: + // friendly(); + // break; + // } + uint64_t currentBoard = detection.read(); + ble.setReedSwitchValue(currentBoard); + delay(10); + uint64_t diff = currentBoard ^ prevBoard; + if (diff && countOnes(diff) == 1) { + bool pickedUp = prevBoard & diff; + // xor and find the changed bit and its index + int sq8x8 = findLSBIndex(diff); + int square = to_square(sq8x8); + if (pickedUp) { + Serial.println("Picked up: " + String(sq8x8) + + " Square: " + String(square)); + } else { + Serial.println("Put down: " + String(sq8x8) + + " Square: " + String(square)); + } + + chess.generate_moves(&cur); + state = + update_state(&chess, square, state, &state_memory, &cur, &other); + + if (state == FSMState::MoveComplete) { + ble.setFEN(chess.get_fen()); + state = FSMState::Idle; + state_memory.length = 0; + } + + ble.setFSMState(state); + Serial.println(String(fsm_state_string(state).c_str()) + " " + + String(state_memory.length)); + } else if (state == FSMState::Error && + currentBoard == chess.get_occupied()) { + state = FSMState::Idle; + ble.setFSMState(state); } + prevBoard = currentBoard; } diff --git a/src/controller.cpp b/src/controller.cpp index 2f360ff..6436d3a 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -12,6 +12,7 @@ Controller::Controller(Board* board, ReedMatrix* detection, Chess* chess, void Controller::init() { board->init(); + detection->init(); indicator->init(); indicator->set(Color::RED); bluetooth->begin("Gambit"); diff --git a/src/fsm.cpp b/src/fsm.cpp index 0343510..52cbc22 100644 --- a/src/fsm.cpp +++ b/src/fsm.cpp @@ -3,8 +3,10 @@ #include #include +// #include +#include + #include "chess.hpp" -#include /*********************************************************************************\ ;---------------------------------------------------------------------------------; @@ -12,21 +14,7 @@ ;---------------------------------------------------------------------------------; \*********************************************************************************/ -void print_bitboard(unsigned long long bitboard) { - for (int i = 0; i < 64; i++) { - if (i % 8 == 0) { - std::cout << std::endl; - } - if (bitboard & 1ull << (63 - i)) { - std::cout << "X "; - } else { - std::cout << ". "; - } - - } -} - -unsigned long long get_bitboard_from_square(int square) { +uint64_t get_bitboard_from_square(int square) { int mod = square % 8; int div = square / 8; return 1ull << (63 - ((div / 2 * 8) + mod)); @@ -34,18 +22,26 @@ unsigned long long get_bitboard_from_square(int square) { void get_moves_from_square(Move_List *move_list, Move_List *current_square_moves, int square) { + Serial.println("SQUARE: " + String(square) + + " MOVE LIST LENGTH: " + String(move_list->length)); current_square_moves->length = 0; for (int i = 0; i < move_list->length; i++) { // std::cout << "SEARCHING " << i << std::endl; + Serial.println("OPTION: " + String(move_list->moves[i].source_square)); if (move_list->moves[i].source_square == square) { - std::cout << "FOUND" << std::endl; - std::cout << "Target square: " << move_list->moves[i].target_square << std::endl; + Serial.println(move_list->moves[i].target_square); + + // std::cout << "FOUND" << std::endl; + // std::cout << "Target square: " << + // move_list->moves[i].target_square + // << std::endl; current_square_moves->moves[current_square_moves->length] = move_list->moves[i]; current_square_moves->length++; } } - std::cout << "Searching complete, found " << current_square_moves->length << " moves" << std::endl; + // std::cout << "Searching complete, found " << current_square_moves->length + // << " moves" << std::endl; } void get_attacks_on_square(Move_List *move_list, @@ -54,28 +50,25 @@ void get_attacks_on_square(Move_List *move_list, for (int i = 0; i < move_list->length; i++) { // std::cout << "SEARCHING " << i << std::endl; if (move_list->moves[i].target_square == square) { - std::cout << "FOUND" << std::endl; - std::cout << "Source square: " << move_list->moves[i].source_square << std::endl; + // std::cout << "FOUND" << std::endl; + // std::cout << "Source square: " << + // move_list->moves[i].source_square + // << std::endl; current_square_moves->moves[current_square_moves->length] = move_list->moves[i]; current_square_moves->length++; } } - std::cout << "Searching complete, found " << current_square_moves->length << " moves" << std::endl; + // std::cout << "Searching complete, found " << current_square_moves->length + // << " moves" << std::endl; } bool is_friendly(Chess *chess, int square) { int piece = chess->get_piece_on_square(square); if (chess->get_side() == WHITE) { - if (piece > 0 && piece < 18 || piece == 43 || piece == 46) { - return true; - } else - return false; + return piece > 0 && piece < 18 || piece == 43 || piece == 46; } else { - if (piece >= 18 && piece < 24 || piece == 51 || piece == 54) { - return true; - } else - return false; + return piece >= 18 && piece < 24 || piece == 51 || piece == 54; } } @@ -85,14 +78,26 @@ bool is_friendly(Chess *chess, int square) { ;---------------------------------------------------------------------------------; \*********************************************************************************/ +void show_uint64(uint64_t value) { + for (int i = 0; i < 64; i++) { + if (i % 8 == 0) { + Serial.println(); + } + Serial.print((value & (1ull << (63 - i))) ? "1" : "0"); + } + Serial.println(); +} + FSMState update_state(Chess *chess, int instruction, FSMState state, State_Memory *state_memory, Move_List *move_list, Move_List *current_square_moves) { - // chess.print_board(); - unsigned long long square = get_bitboard_from_square(instruction); - unsigned long long occupied = chess->get_occupied(); - unsigned long long friendlies; - unsigned long long enemies; + // uint64_t square = get_bitboard_from_square(instruction); + int sq8x8 = to_index(instruction); + Serial.println("Index: " + String(sq8x8)); + uint64_t square = 1ULL << sq8x8; + uint64_t occupied = chess->get_occupied(); + uint64_t friendlies; + uint64_t enemies; if (chess->get_side() == WHITE) { // white turn friendlies = chess->get_white(); @@ -100,21 +105,26 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, } else { // black turn friendlies = chess->get_black(); enemies = chess->get_white(); + Serial.println("BLACK TURN"); + show_uint64(square); + show_uint64(friendlies); + show_uint64(enemies); } switch (state) { case Idle: + Serial.println(friendlies & square); if ((friendlies & square) != 0ULL) { get_moves_from_square(move_list, current_square_moves, instruction); state_memory->memory[0] = instruction; - state_memory->length++; + state_memory->length = 1; return FSMState::FriendlyPU; } else if ((enemies & square) != 0ULL) { get_attacks_on_square(move_list, current_square_moves, instruction); state_memory->memory[0] = instruction; - state_memory->length++; + state_memory->length = 1; if (current_square_moves->length > 0) { return FSMState::EnemyPU; } else { @@ -128,12 +138,14 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, if (instruction == state_memory->memory[0]) { state_memory->length--; return FSMState::Idle; - } else if (chess->get_piece_on_square(instruction) == 43 || chess->get_piece_on_square(instruction) == 51) { // king + } else if (chess->get_piece_on_square(instruction) == 43 || + chess->get_piece_on_square(instruction) == 51) { // king state_memory->memory[1] = instruction; state_memory->length++; return FSMState::Castling; - } else if (chess->get_piece_on_square(instruction) == 46 || chess->get_piece_on_square(instruction) == 54) { // rook + } else if (chess->get_piece_on_square(instruction) == 46 || + chess->get_piece_on_square(instruction) == 54) { // rook state_memory->memory[1] = instruction; state_memory->length++; return FSMState::Castling; @@ -143,26 +155,32 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, state_memory->length++; return FSMState::InvalidPiecePU; } else { + Serial.println("FriendlyPU"); + Serial.println(instruction); + Serial.println(current_square_moves->length); for (int i = 0; i < current_square_moves->length; i++) { ///////////////////////////////// - //this is for not allowing castling without the apropriate states - //this is dumb but I dont care + // this is for not allowing castling without the apropriate + // states this is dumb but I dont care int curr = current_square_moves->moves[i].target_square; int pieceId = current_square_moves->moves[i].piece; - if (pieceId == 43 && (curr == 118 || curr == 114) || pieceId == 51 && (curr == 6 || curr == 2)) { + if (pieceId == 43 && (curr == 118 || curr == 114) || + pieceId == 51 && (curr == 6 || curr == 2)) { state_memory->memory[1] = instruction; state_memory->length++; return FSMState::InvalidMove; } ///////////////////////////////// - if (current_square_moves->moves[i].target_square == instruction) { + if (current_square_moves->moves[i].target_square == + instruction) { if (current_square_moves->moves[i].capture != 0) { state_memory->memory[1] = instruction; state_memory->length++; return FSMState::FriendlyAndEnemyPU; } chess->make_move(current_square_moves->moves[i]); - std::cout << "MOVE COMMITTED" << std::endl; + // std::cout << "MOVE COMMITTED" << std::endl; + Serial.println("MOVE COMMITTED"); return FSMState::MoveComplete; } } @@ -206,14 +224,14 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, state_memory->length--; return FSMState::EnemyPU; } else if (instruction == prev_enemy_square) { - std::cout << "CAPTURED!" << std::endl; + // std::cout << "CAPTURED!" << std::endl; for (int i = 0; i < current_square_moves->length; i++) { if (current_square_moves->moves[i].target_square == prev_enemy_square && current_square_moves->moves[i].source_square == prev_friendly_square) { chess->make_move(current_square_moves->moves[i]); - std::cout << "MOVE COMMITTED" << std::endl; + // std::cout << "MOVE COMMITTED" << std::endl; return FSMState::MoveComplete; } } @@ -229,14 +247,14 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, if (instruction == 114) { state_memory->memory[1] = instruction; state_memory->memory[2] = 115; - state_memory->length+=2; - //chess->make_move(chess->parse_move("e1c1")); + state_memory->length += 2; + // chess->make_move(chess->parse_move("e1c1")); return FSMState::CastlingPutRookDown; } else if (instruction == 118) { state_memory->memory[1] = instruction; state_memory->memory[2] = 117; - state_memory->length+=2; - //chess->make_move(chess->parse_move("e1g1")); + state_memory->length += 2; + // chess->make_move(chess->parse_move("e1g1")); return FSMState::CastlingPutRookDown; } else { return FSMState::Error; @@ -246,18 +264,18 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, state_memory->memory[1] = instruction; state_memory->memory[2] = 3; state_memory->length++; - //chess->make_move(chess->parse_move("e8c8")); + // chess->make_move(chess->parse_move("e8c8")); return FSMState::CastlingPutRookDown; } else if (instruction == 6) { state_memory->memory[1] = instruction; state_memory->memory[2] = 5; state_memory->length++; - //chess->make_move(chess->parse_move("e8g8")); + // chess->make_move(chess->parse_move("e8g8")); return FSMState::CastlingPutRookDown; } else { return FSMState::Error; } - }else { + } else { return FSMState::Error; } break; @@ -267,15 +285,17 @@ FSMState update_state(Chess *chess, int instruction, FSMState state, return FSMState::Castling; } else if (instruction == state_memory->memory[2]) { for (int i = 0; i < current_square_moves->length; i++) { - if (current_square_moves->moves[i].target_square == state_memory->memory[1]) { - // std::cout << current_square_moves->moves[i] << std::endl; + if (current_square_moves->moves[i].target_square == + state_memory->memory[1]) { + // std::cout << current_square_moves->moves[i] << + // std::endl; chess->make_move(current_square_moves->moves[i]); - std::cout << "MOVE COMMITTED" << std::endl; + // std::cout << "MOVE COMMITTED" << std::endl; return FSMState::MoveComplete; } } return FSMState::Error; - }else { + } else { return FSMState::Error; } break; diff --git a/src/reed.cpp b/src/reed.cpp index d79b700..8211478 100644 --- a/src/reed.cpp +++ b/src/reed.cpp @@ -20,7 +20,9 @@ uint64_t ReedMatrix::read() { uint64_t board; for (int row = 7; row >= 0; row--) { shiftOut->write(1 << row); + delayMicroseconds(delayInMicroseconds); byte data = shiftIn->read(); + delayMicroseconds(delayInMicroseconds); board = board << 8 | data; } return board;