diff --git a/sounds/blunder/tts_what_a_blunder.mp3 b/sounds/blunder/tts_what_a_blunder.mp3 new file mode 100644 index 0000000..a28a064 Binary files /dev/null and b/sounds/blunder/tts_what_a_blunder.mp3 differ diff --git a/sounds/illegal/alarm.mp3 b/sounds/illegal/alarm.mp3 new file mode 100644 index 0000000..7462201 Binary files /dev/null and b/sounds/illegal/alarm.mp3 differ diff --git a/src/blunderboard/blunderboard.py b/src/blunderboard/blunderboard.py index ed923da..2291ba1 100644 --- a/src/blunderboard/blunderboard.py +++ b/src/blunderboard/blunderboard.py @@ -28,7 +28,7 @@ settings = { "NNUE": "true", } -sound_path = Path("sounds") +sound_path = Path("../../sounds") def play_sound() -> None: diff --git a/src/blunderboard/blunderevaluator.py b/src/blunderboard/blunderevaluator.py index a4be47e..5563dfc 100644 --- a/src/blunderboard/blunderevaluator.py +++ b/src/blunderboard/blunderevaluator.py @@ -1,6 +1,109 @@ -class BlunderEvaluator: - def reset(self) -> None: - pass +import os +from pathlib import Path +from pygame import mixer +import random +from stockfish import Stockfish +import time +import movegenerator - def move(self, move: str) -> None: - pass +sound_path = Path("../../sounds") + + +settings = { # TODO Move to a config file + "Debug Log File": "stocklog.txt", + "Contempt": 0, + "Min Split Depth": 0, + "Threads": 1, + # More threads will make the engine stronger, but should be kept at less than the + # number of logical processors on your computer. + "Ponder": "false", + "Hash": 256, + # Default size is 16 MB. It's recommended that you increase this value, but keep it + # as some power of 2. E.g., if you're fine using 2 GB of RAM, set Hash to 2048 + # (11th power of 2). + "MultiPV": 1, + "Skill Level": 20, + "Move Overhead": 10, + "Minimum Thinking Time": 20, + "Slow Mover": 100, + "UCI_Chess960": "false", + "UCI_LimitStrength": "false", + "UCI_Elo": 1350, + # "NNUE": "true", # TODO Find out if NNUE can be used with the python wrapper +} + +class BlunderEvaluator: + + def __init__(self, engine_settings: dict): + self.engine = Stockfish("/usr/bin/stockfish") + self.settings = engine_settings + self.engine.update_engine_parameters(self.settings) + self.current_evaluation = ( + self.engine.get_evaluation() + ) # This is not necessary, now that I think about it. + self.evaluations: list[dict] = [] + self.current_wdl = self.engine.get_wdl_stats() + self.wdls: list[tuple[int, int, int]] = [] + self.engine.set_position() + + def reset(self): + self.engine.set_position() + + def make_move(self, move) -> None: + """ + Makes a move on the board and updates the game state + :param move: str + :return: None + """ + if self.engine.is_move_correct(move): + self.engine.make_moves_from_current_position([move]) + self.current_evaluation = self.engine.get_evaluation() + self.evaluations.append(self.current_evaluation) + self.current_wdl = self.engine.get_wdl_stats() + self.wdls.append(self.current_wdl) + print(self.current_wdl) + print(self.current_evaluation) + if self.move_was_blunder(): + # If the played move was a blunder play a random sound from the blunder path + self.play_sound("blunder") + print("Blunder!") + else: + print("Invalid move") + self.play_sound("illegal") + + def move_was_blunder(self) -> bool: + """ + Returns true if the last move was a blunder + :return: bool + """ + if len(self.wdls) > 1: # Don't check for blunders on the first move + previous_wdl = self.wdls[len(self.evaluations) - 2] + if abs(previous_wdl[0] - self.current_wdl[0]) > 300: + return True + elif abs(previous_wdl[2] - self.current_wdl[2]) > 300: + return True + else: + return False + return False + + @staticmethod + def play_sound(move_type: str) -> None: + """ + Plays a random sound for the type of move (blunder, illegal) + :param move_type: str + :return: None + """ + path = sound_path / move_type + mixer.init() + mixer.music.load("sounds/" + random.choice(os.listdir(path))) + mixer.music.play() + # while mixer.music.get_busy(): + # time.sleep(0.) + # I guess we won't want this, since it will block the main thread. + + def get_board(self) -> str: + """ + Returns the current board state + :return: str + """ + return self.engine.get_board_visual() \ No newline at end of file