added cpu chess moving

This commit is contained in:
Rudis Muiznieks 2022-04-24 18:18:50 -05:00
parent 39a3e33a06
commit 94befd04ee
Signed by: rudism
GPG Key ID: CABF2F86EF7884F9
3 changed files with 53 additions and 31 deletions

View File

@ -1,5 +1,6 @@
from os import path from os import path
from enum import Enum, auto from enum import Enum, auto
import time
import re import re
import itertools import itertools
from cinput import ControlInput, Button from cinput import ControlInput, Button
@ -9,37 +10,56 @@ from . import sunfish
class GameState(Enum): class GameState(Enum):
MAIN_MENU = auto() MAIN_MENU = auto()
PLAYING = auto()
THINKING = auto() THINKING = auto()
CHOOSE_ROW = auto() CHOOSE_ROW = auto()
CHOOSE_COL = auto() CHOOSE_COL = auto()
GAME_OVER = auto() GAME_OVER = auto()
SAVE_FILE = path.join("data", "chess_state.fen") SAVE_FILE = path.join("data", "chess_state.fen")
MAX_THINKING_SECONDS = 3
def execute(cinput: ControlInput, graphics: Graphics, _): def execute(cinput: ControlInput, graphics: Graphics, _):
graphics.clear() graphics.clear()
draw = Draw(graphics) draw = Draw(graphics)
pos: sunfish.Position hist: list[sunfish.Position]
searcher = sunfish.Searcher()
if path.exists(SAVE_FILE): if path.exists(SAVE_FILE):
with open(SAVE_FILE, "r") as fen: with open(SAVE_FILE, "r") as fen:
fenstr = fen.read() fenstr = fen.read()
pos = parseFEN(fenstr) hist = [parseFEN(fenstr)]
else: else:
pos = sunfish.Position( hist = [sunfish.Position(
sunfish.initial, 0, (True,True), (True,True), 0, 0) 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 state = GameState.MAIN_MENU
menu_index = 0 menu_index = 0
move = None
score = None
last_move = "Begin!"
while True: while True:
if state == GameState.MAIN_MENU: if state == GameState.MAIN_MENU:
draw.draw_menu(menu_index) draw.draw_menu(menu_index)
elif state == GameState.PLAYING: elif state == GameState.THINKING:
draw.draw_state() 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() key = cinput.get_one_shot()
if state == GameState.MAIN_MENU: if state == GameState.MAIN_MENU:
@ -53,20 +73,22 @@ def execute(cinput: ControlInput, graphics: Graphics, _):
menu_index = 3 menu_index = 3
elif key == Button.BTN_B: elif key == Button.BTN_B:
if menu_index == 0: if menu_index == 0:
state = GameState.PLAYING state = GameState.CHOOSE_ROW
elif menu_index == 1: elif menu_index == 1:
with open(SAVE_FILE, "w") as fen: with open(SAVE_FILE, "w") as fen:
fen.write(renderFEN(pos)) fen.write(renderFEN(hist[-1]))
return return
elif menu_index == 2 or menu_index == 3: elif menu_index == 2 or menu_index == 3:
pos = sunfish.Position( pos = sunfish.Position(
sunfish.initial, 0, (True,True), (True,True), 0, 0) sunfish.initial, 0, (True,True), (True,True), 0, 0)
if menu_index == 3: if menu_index == 3:
pos = pos.rotate() state = GameState.THINKING
else:
state = GameState.CHOOSE_ROW
hist = [pos]
draw.draw_board(pos.board) draw.draw_board(pos.board)
state = GameState.PLAYING
elif state == GameState.PLAYING: else:
if key == Button.BTN_A: if key == Button.BTN_A:
state = GameState.MAIN_MENU state = GameState.MAIN_MENU
@ -74,7 +96,6 @@ def get_color(pos):
return 1 if pos.board.startswith('\n') else 0 return 1 if pos.board.startswith('\n') else 0
def parseFEN(fen): def parseFEN(fen):
""" Parses a string in Forsyth-Edwards Notation into a Position """
board, color, castling, enpas, _, _ = fen.split() board, color, castling, enpas, _, _ = fen.split()
board = re.sub(r'\d', (lambda m: '.'*int(m.group(0))), board) board = re.sub(r'\d', (lambda m: '.'*int(m.group(0))), board)
board = list(21*' ' + ' '.join(board.split('/')) + 21*' ') 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): def renderFEN(pos, half_move_clock=0, full_move_clock=1):
color = 'wb'[get_color(pos)] color = 'wb'[get_color(pos)]
if get_color(pos) == 1: if color == 'b':
pos = pos.rotate() pos = pos.rotate()
board = '/'.join(pos.board.split()) board = '/'.join(pos.board.split())
board = re.sub(r'\.+', (lambda m: str(len(m.group(0)))), board) board = re.sub(r'\.+', (lambda m: str(len(m.group(0)))), board)

View File

@ -80,7 +80,6 @@ class Draw:
def draw_board(self, board: str): def draw_board(self, board: str):
ic(board) ic(board)
is_black = board.startswith('\n')
self._graphics.fill_rect(0, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0) self._graphics.fill_rect(0, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0)
nb = "".join(board.split()) nb = "".join(board.split())
c = True c = True
@ -93,7 +92,7 @@ class Draw:
p = nb[(row * 8) + col] p = nb[(row * 8) + col]
if p.lower() in self.PIECES: if p.lower() in self.PIECES:
self._draw_piece( 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
c = not c c = not c
self._graphics.show() self._graphics.show()
@ -106,6 +105,21 @@ class Draw:
self._graphics.text(marker + self.MENU[i], 12, i + 2, 1) self._graphics.text(marker + self.MENU[i], 12, i + 2, 1)
self._graphics.show() self._graphics.show()
def draw_state(self): def draw_thinking(self):
self._clear_info() 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() self._graphics.show()

View File

@ -378,10 +378,6 @@ class Searcher:
# User interface # User interface
############################################################################### ###############################################################################
# Python 2 compatability
if sys.version_info[0] == 2:
input = raw_input
def parse(c): def parse(c):
fil, rank = ord(c[0]) - ord('a'), int(c[1]) - 1 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) 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(): def main():
hist = [Position(initial, 0, (True,True), (True,True), 0, 0)] hist = [Position(initial, 0, (True,True), (True,True), 0, 0)]
searcher = Searcher() searcher = Searcher()