diff --git a/plugin/chess/__init__.py b/plugin/chess/__init__.py index 21ddb6f..b15730a 100644 --- a/plugin/chess/__init__.py +++ b/plugin/chess/__init__.py @@ -1,5 +1,6 @@ from os import path from enum import Enum, auto +import time import re import itertools from cinput import ControlInput, Button @@ -9,37 +10,56 @@ from . import sunfish class GameState(Enum): MAIN_MENU = auto() - PLAYING = auto() THINKING = auto() CHOOSE_ROW = auto() CHOOSE_COL = auto() GAME_OVER = auto() SAVE_FILE = path.join("data", "chess_state.fen") +MAX_THINKING_SECONDS = 3 def execute(cinput: ControlInput, graphics: Graphics, _): graphics.clear() draw = Draw(graphics) - pos: sunfish.Position + hist: list[sunfish.Position] + searcher = sunfish.Searcher() if path.exists(SAVE_FILE): with open(SAVE_FILE, "r") as fen: fenstr = fen.read() - pos = parseFEN(fenstr) + hist = [parseFEN(fenstr)] else: - pos = sunfish.Position( - sunfish.initial, 0, (True,True), (True,True), 0, 0) + hist = [sunfish.Position( + sunfish.initial, 0, (True,True), (True,True), 0, 0)] - draw.draw_board(pos.board) + player_color = 'wb'[get_color(hist[-1])] + draw.draw_board(hist[-1].board if player_color == 'w' else hist[-1].rotate().board) state = GameState.MAIN_MENU menu_index = 0 + move = None + score = None + last_move = "Begin!" while True: if state == GameState.MAIN_MENU: draw.draw_menu(menu_index) - elif state == GameState.PLAYING: - draw.draw_state() + elif state == GameState.THINKING: + draw.draw_thinking() + 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]) + 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 key = cinput.get_one_shot() if state == GameState.MAIN_MENU: @@ -53,20 +73,22 @@ def execute(cinput: ControlInput, graphics: Graphics, _): menu_index = 3 elif key == Button.BTN_B: if menu_index == 0: - state = GameState.PLAYING + state = GameState.CHOOSE_ROW elif menu_index == 1: with open(SAVE_FILE, "w") as fen: - fen.write(renderFEN(pos)) + fen.write(renderFEN(hist[-1])) return elif menu_index == 2 or menu_index == 3: pos = sunfish.Position( sunfish.initial, 0, (True,True), (True,True), 0, 0) if menu_index == 3: - pos = pos.rotate() + state = GameState.THINKING + else: + state = GameState.CHOOSE_ROW + hist = [pos] draw.draw_board(pos.board) - state = GameState.PLAYING - elif state == GameState.PLAYING: + else: if key == Button.BTN_A: state = GameState.MAIN_MENU @@ -74,7 +96,6 @@ def get_color(pos): return 1 if pos.board.startswith('\n') else 0 def parseFEN(fen): - """ Parses a string in Forsyth-Edwards Notation into a Position """ board, color, castling, enpas, _, _ = fen.split() board = re.sub(r'\d', (lambda m: '.'*int(m.group(0))), board) board = list(21*' ' + ' '.join(board.split('/')) + 21*' ') @@ -90,7 +111,7 @@ def parseFEN(fen): def renderFEN(pos, half_move_clock=0, full_move_clock=1): color = 'wb'[get_color(pos)] - if get_color(pos) == 1: + if color == 'b': pos = pos.rotate() board = '/'.join(pos.board.split()) board = re.sub(r'\.+', (lambda m: str(len(m.group(0)))), board) diff --git a/plugin/chess/draw.py b/plugin/chess/draw.py index 80dc862..2d9ff45 100644 --- a/plugin/chess/draw.py +++ b/plugin/chess/draw.py @@ -80,7 +80,6 @@ class Draw: def draw_board(self, board: str): ic(board) - is_black = board.startswith('\n') self._graphics.fill_rect(0, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0) nb = "".join(board.split()) c = True @@ -93,7 +92,7 @@ class Draw: p = nb[(row * 8) + col] if p.lower() in self.PIECES: self._draw_piece( - self.PIECES[p.lower()], p.isupper() if not is_black else p.islower(), col, row, c) + self.PIECES[p.lower()], p.isupper(), col, row, c) c = not c c = not c self._graphics.show() @@ -106,6 +105,21 @@ class Draw: self._graphics.text(marker + self.MENU[i], 12, i + 2, 1) self._graphics.show() - def draw_state(self): + def draw_thinking(self): self._clear_info() + self._graphics.text("Shh, I'm", 12, 0, 1) + self._graphics.text("thinking!", 12, 1, 1) + self._graphics.show() + + def draw_checkmate(self, move: str, player_won: bool): + self._clear_info() + self._graphics.text(move, 12, 0, 1) + self._graphics.text("Checkmate", 12, 1, 1) + self._graphics.text( + "you win!" if player_won else "you lose!", 12, 2, 1) + self._graphics.show() + + def draw_rowsel(self, move: str): + self._clear_info() + self._graphics.text(move, 12, 0, 1) self._graphics.show() diff --git a/plugin/chess/sunfish.py b/plugin/chess/sunfish.py index 2418721..d786862 100644 --- a/plugin/chess/sunfish.py +++ b/plugin/chess/sunfish.py @@ -378,10 +378,6 @@ class Searcher: # User interface ############################################################################### -# Python 2 compatability -if sys.version_info[0] == 2: - input = raw_input - def parse(c): fil, rank = ord(c[0]) - ord('a'), int(c[1]) - 1 @@ -393,15 +389,6 @@ def render(i): return chr(fil + ord('a')) + str(-rank + 1) -def print_pos(pos): - print() - uni_pieces = {'R':'♜', 'N':'♞', 'B':'♝', 'Q':'♛', 'K':'♚', 'P':'♟', - 'r':'♖', 'n':'♘', 'b':'♗', 'q':'♕', 'k':'♔', 'p':'♙', '.':'·'} - for i, row in enumerate(pos.board.split()): - print(' ', 8-i, ' '.join(uni_pieces.get(p, p) for p in row)) - print(' a b c d e f g h \n\n') - - def main(): hist = [Position(initial, 0, (True,True), (True,True), 0, 0)] searcher = Searcher()