From 2c06b5c3270a8887426c0208afe44ff2e4d9b9c0 Mon Sep 17 00:00:00 2001 From: Christian Hagenest Date: Fri, 23 Dec 2022 19:45:44 +0100 Subject: [PATCH] wip --- blunderboard-py/.idea/.gitignore | 8 ++ blunderboard-py/.idea/blunderboard-py.iml | 10 +++ .../inspectionProfiles/profiles_settings.xml | 6 ++ blunderboard-py/.idea/misc.xml | 4 + blunderboard-py/.idea/modules.xml | 8 ++ blunderboard-py/.idea/vcs.xml | 6 ++ blunderboard-py/blunderboard.py | 80 ++++++++++++------ blunderboard-py/requirements.txt | 4 +- ... Cries - #402 Kricketune [4dFn2r-iq_4].mp3 | Bin 0 -> 18261 bytes 9 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 blunderboard-py/.idea/.gitignore create mode 100644 blunderboard-py/.idea/blunderboard-py.iml create mode 100644 blunderboard-py/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 blunderboard-py/.idea/misc.xml create mode 100644 blunderboard-py/.idea/modules.xml create mode 100644 blunderboard-py/.idea/vcs.xml create mode 100644 blunderboard-py/sounds/Pokemon Cries - #402 Kricketune [4dFn2r-iq_4].mp3 diff --git a/blunderboard-py/.idea/.gitignore b/blunderboard-py/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/blunderboard-py/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/blunderboard-py/.idea/blunderboard-py.iml b/blunderboard-py/.idea/blunderboard-py.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/blunderboard-py/.idea/blunderboard-py.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/blunderboard-py/.idea/inspectionProfiles/profiles_settings.xml b/blunderboard-py/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/blunderboard-py/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/blunderboard-py/.idea/misc.xml b/blunderboard-py/.idea/misc.xml new file mode 100644 index 0000000..e9e4ed3 --- /dev/null +++ b/blunderboard-py/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/blunderboard-py/.idea/modules.xml b/blunderboard-py/.idea/modules.xml new file mode 100644 index 0000000..8d7d182 --- /dev/null +++ b/blunderboard-py/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/blunderboard-py/.idea/vcs.xml b/blunderboard-py/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/blunderboard-py/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/blunderboard-py/blunderboard.py b/blunderboard-py/blunderboard.py index 7d70f09..dcc492e 100644 --- a/blunderboard-py/blunderboard.py +++ b/blunderboard-py/blunderboard.py @@ -2,6 +2,9 @@ from stockfish import Stockfish from playsound import playsound from pathlib import Path import random +from pgn_parser import parser, pgn + +# import RPi.GPIO as GPIO settings = { "Debug Log File": "", @@ -46,39 +49,66 @@ class Game: A class representing the game state """ - def __init__(self, settings: dict): - self.engine = Stockfish("/usr/local/bin/stockfish") - self.settings = settings + def __init__(self, engine_settings: dict): + self.engine = Stockfish("/usr/bin/stockfish") + self.settings = engine_settings self.engine.update_engine_parameters(self.settings) self.matrix = BoardReader() - self.current_evaluation = self.engine.get_evaluation() + self.current_evaluation = self.engine.get_evaluation() # This is not necessary, now that I think about it. self.evaluations = [] self.engine.set_position() - def make_move(move) -> None: - """ - Makes a move on the board and updates the game state - :param move: - :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) - if move_was_blunder(): - # If the played move was a blunder play a random sound from the sound path - playsound(random.choice(sound_path)) - - def move_was_blunder() -> bool: - """ - Returns true if the last move was a blunder - :return: bool - """ + def move_was_blunder(self) -> bool: + """ + Returns true if the last move was a blunder + :return: bool + """ + if len(self.evaluations) > 1: previous_evaluation = self.evaluations[-1] - return self.current_evaluation["value"] < (previous_evaluation["value"] + 3) # TODO This is not a - # particularly good way to identify a blunder + return abs(self.current_evaluation["value"] - previous_evaluation["value"]) > 300 + # TODO This is not a particularly good way to identify a blunder + else: + return False + + def make_move(self, move) -> None: + """ + Makes a move on the board and updates the game state + :param move: + :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) + if self.move_was_blunder(): + # If the played move was a blunder play a random sound from the sound path + # playsound(random.choice(sound_path)) + print("Blunder!") def __str__(self): return "" +def get_moves_from_pgn(pgn_file: Path): + """ + Returns a list of moves from a PGN file + :param pgn_file: str + :return: list + """ + with open(pgn_file, "r") as f: + pgn_file = f.read() + return parser.parse(pgn_file, actions=pgn.Actions()) + + +test_game = Game(settings) + +pgn_path = Path("../spongeboyahoy_vs_tomlx.pgn") +moves = get_moves_from_pgn(pgn_path) +for i in range(1, 250): + print(str(moves.move(i)).split(". ")[1].split(" ")[0]) + test_game.make_move(str(moves.move(i)).split(". ")[1].split(" ")[0]) + print(test_game.current_evaluation) + test_game.make_move(str(moves.move(i)).split(". ")[1].split(" ")[1]) + print(test_game.current_evaluation) + + diff --git a/blunderboard-py/requirements.txt b/blunderboard-py/requirements.txt index 51ee0ef..56a03b1 100644 --- a/blunderboard-py/requirements.txt +++ b/blunderboard-py/requirements.txt @@ -1,2 +1,4 @@ stockfish -playsound \ No newline at end of file +playsound +pygobject +pgn_parser \ No newline at end of file diff --git a/blunderboard-py/sounds/Pokemon Cries - #402 Kricketune [4dFn2r-iq_4].mp3 b/blunderboard-py/sounds/Pokemon Cries - #402 Kricketune [4dFn2r-iq_4].mp3 new file mode 100644 index 0000000000000000000000000000000000000000..65eab25b2265217b4d3e195956703fe0bbe64526 GIT binary patch literal 18261 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 i5a2)k|9?J5