From 38a2854378d58759c15e774272ed22e24876dbc9 Mon Sep 17 00:00:00 2001 From: Rudis Muiznieks Date: Sun, 24 Apr 2022 23:08:37 -0500 Subject: [PATCH] wip: implementing chess --- plugin/chess/__init__.py | 87 +++++++++++++++++++++++++++++++++------- plugin/chess/draw.py | 23 +++++++---- plugin/chess/sunfish.py | 2 +- 3 files changed, 89 insertions(+), 23 deletions(-) diff --git a/plugin/chess/__init__.py b/plugin/chess/__init__.py index b15730a..75adc79 100644 --- a/plugin/chess/__init__.py +++ b/plugin/chess/__init__.py @@ -3,6 +3,7 @@ from enum import Enum, auto import time import re import itertools +from icecream import ic from cinput import ControlInput, Button from graphics import Graphics from .draw import Draw @@ -11,8 +12,8 @@ from . import sunfish class GameState(Enum): MAIN_MENU = auto() THINKING = auto() - CHOOSE_ROW = auto() - CHOOSE_COL = auto() + CHOOSE_SRC = auto() + CHOOSE_DST = auto() GAME_OVER = auto() SAVE_FILE = path.join("data", "chess_state.fen") @@ -33,35 +34,51 @@ def execute(cinput: ControlInput, graphics: Graphics, _): sunfish.initial, 0, (True,True), (True,True), 0, 0)] player_color = 'wb'[get_color(hist[-1])] - draw.draw_board(hist[-1].board if player_color == 'w' else hist[-1].rotate().board) + draw.draw_board(hist[-1] if player_color == "w" else hist[-1].rotate(), player_color) state = GameState.MAIN_MENU menu_index = 0 move = None score = None last_move = "Begin!" + all_moves = dict() + src_idx = 0 + dst_idx = 0 + src = "" while True: + key = None if state == GameState.MAIN_MENU: draw.draw_menu(menu_index) + key = cinput.get_one_shot() elif state == GameState.THINKING: - draw.draw_thinking() + draw.draw_thinking(last_move) start = time.time() for _, move, score in searcher.search(hist[-1], hist): if time.time() - start > MAX_THINKING_SECONDS: break hist.append(hist[-1].move(move)) - draw.draw_board(hist[-1].board if player_color == 'b' else hist[-1].rotate().board) - shift = -119 if player_color == 'b' else 0 - last_move = sunfish.render(shift+move[0]) + " -> " + sunfish.render(shift+move[1]) + draw.draw_board(hist[-1].rotate(), player_color) + last_move = move_str(move, player_color == "w") if score == sunfish.MATE_UPPER: draw.draw_checkmate(last_move, False) state = GameState.GAME_OVER else: - draw.draw_rowsel(last_move) - state = GameState.CHOOSE_ROW + all_moves = dict() + state = GameState.CHOOSE_SRC + elif state == GameState.CHOOSE_SRC: + if len(list(all_moves)) == 0: + all_moves = get_all_moves(hist[-1]) + src = list(all_moves)[src_idx] + draw.draw_select(last_move, list(all_moves)[src_idx]) + key = cinput.get_one_shot() + elif state == GameState.CHOOSE_DST: + src = list(all_moves)[src_idx] + draw.draw_select(last_move, src, all_moves[src][dst_idx]) + key = cinput.get_one_shot() + elif state == GameState.GAME_OVER: + key = cinput.get_one_shot() - key = cinput.get_one_shot() if state == GameState.MAIN_MENU: if key == Button.DIR_U: menu_index -= 1 @@ -73,7 +90,7 @@ def execute(cinput: ControlInput, graphics: Graphics, _): menu_index = 3 elif key == Button.BTN_B: if menu_index == 0: - state = GameState.CHOOSE_ROW + state = GameState.CHOOSE_SRC elif menu_index == 1: with open(SAVE_FILE, "w") as fen: fen.write(renderFEN(hist[-1])) @@ -82,16 +99,58 @@ def execute(cinput: ControlInput, graphics: Graphics, _): pos = sunfish.Position( sunfish.initial, 0, (True,True), (True,True), 0, 0) if menu_index == 3: + player_color = "b" state = GameState.THINKING else: - state = GameState.CHOOSE_ROW + player_color = "w" + state = GameState.CHOOSE_SRC hist = [pos] - draw.draw_board(pos.board) - + draw.draw_board(hist[-1], player_color) + elif state == GameState.CHOOSE_SRC: + if key == Button.DIR_D or key == Button.DIR_R: + src_idx += 1 + if src_idx >= len(all_moves.keys()): + src_idx = 0 + elif key == Button.DIR_U or key == Button.DIR_L: + src_idx -= 1 + if src_idx < 0: + src_idx = len(list(all_moves)) - 1 + elif key == Button.BTN_A: + state = GameState.MAIN_MENU + elif key == Button.BTN_B: + state = GameState.CHOOSE_DST + elif state == GameState.CHOOSE_DST: + if key == Button.DIR_D or key == Button.DIR_R: + dst_idx += 1 + if dst_idx >= len(all_moves[src]): + dst_idx = 0 + elif key == Button.DIR_U or key == Button.DIR_L: + dst_idx -= 1 + if dst_idx < 0: + dst_idx = len(all_moves[src]) - 1 + elif key == Button.BTN_A: + state = GameState.CHOOSE_SRC + elif key == Button.BTN_B: + dst = all_moves[src][dst_idx] + last_move = src + " - " + dst + sfmove = sunfish.parse(src), sunfish.parse(dst) + hist.append(hist[-1].move(sfmove)) + draw.draw_board(hist[-1].rotate(), player_color) + state = GameState.THINKING else: if key == Button.BTN_A: state = GameState.MAIN_MENU +def get_all_moves(pos: sunfish.Position): + all_moves = dict() + for src, dst in pos.gen_moves(): + all_moves.setdefault(sunfish.render(src), []).append(sunfish.render(dst)) + return all_moves + +def move_str(move, shift = False): + s = -119 if shift else 0 + return sunfish.render(s + move[0]) + " - " + sunfish.render(s + move[1]) + def get_color(pos): return 1 if pos.board.startswith('\n') else 0 diff --git a/plugin/chess/draw.py b/plugin/chess/draw.py index 2d9ff45..0f2dd3e 100644 --- a/plugin/chess/draw.py +++ b/plugin/chess/draw.py @@ -1,5 +1,6 @@ from icecream import ic from graphics import Graphics +from .sunfish import Position class Draw: BOARD_SIZE = 64 @@ -78,10 +79,10 @@ class Draw: self._graphics.fill_rect( self.BOARD_SIZE, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0) - def draw_board(self, board: str): - ic(board) + def draw_board(self, pos: Position, player_color: str): self._graphics.fill_rect(0, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0) - nb = "".join(board.split()) + nb = "".join(pos.board.split()) + nb = nb[::-1] if player_color == "b" else nb c = True for row in range(8): for col in range(8): @@ -105,10 +106,11 @@ class Draw: self._graphics.text(marker + self.MENU[i], 12, i + 2, 1) self._graphics.show() - def draw_thinking(self): + def draw_thinking(self, last_move: str): self._clear_info() - self._graphics.text("Shh, I'm", 12, 0, 1) - self._graphics.text("thinking!", 12, 1, 1) + self._graphics.text(last_move, 12, 0, 1) + self._graphics.text("Shh, I'm", 12, 2, 1) + self._graphics.text("thinking!", 12, 3, 1) self._graphics.show() def draw_checkmate(self, move: str, player_won: bool): @@ -119,7 +121,12 @@ class Draw: "you win!" if player_won else "you lose!", 12, 2, 1) self._graphics.show() - def draw_rowsel(self, move: str): + def draw_select(self, last_move: str, src: str, dst = None): self._clear_info() - self._graphics.text(move, 12, 0, 1) + self._graphics.text(last_move, 12, 0, 1) + self._graphics.text("You move:", 12, 2, 1) + if dst == None: + self._graphics.text("<" + src + "> - __", 12, 3, 1) + else: + self._graphics.text(src + " - <" + dst + ">", 12, 3, 1) self._graphics.show() diff --git a/plugin/chess/sunfish.py b/plugin/chess/sunfish.py index d786862..68ebd59 100644 --- a/plugin/chess/sunfish.py +++ b/plugin/chess/sunfish.py @@ -295,7 +295,7 @@ class Searcher: # Note, we don't have to check for legality, since we've already done it # before. Also note that in QS the killer must be a capture, otherwise we # will be non deterministic. - killer = self.tp_move.get(pos) + killer = selfctp_move.get(pos) if killer and (depth > 0 or pos.value(killer) >= QS_LIMIT): yield killer, -self.bound(pos.move(killer), 1-gamma, depth-1, root=False) # Then all the other moves