wip: implementing chess

This commit is contained in:
Rudis Muiznieks 2022-04-24 23:08:37 -05:00
parent 94befd04ee
commit 38a2854378
Signed by: rudism
GPG Key ID: CABF2F86EF7884F9
3 changed files with 89 additions and 23 deletions

View File

@ -3,6 +3,7 @@ from enum import Enum, auto
import time import time
import re import re
import itertools import itertools
from icecream import ic
from cinput import ControlInput, Button from cinput import ControlInput, Button
from graphics import Graphics from graphics import Graphics
from .draw import Draw from .draw import Draw
@ -11,8 +12,8 @@ from . import sunfish
class GameState(Enum): class GameState(Enum):
MAIN_MENU = auto() MAIN_MENU = auto()
THINKING = auto() THINKING = auto()
CHOOSE_ROW = auto() CHOOSE_SRC = auto()
CHOOSE_COL = auto() CHOOSE_DST = auto()
GAME_OVER = auto() GAME_OVER = auto()
SAVE_FILE = path.join("data", "chess_state.fen") 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)] sunfish.initial, 0, (True,True), (True,True), 0, 0)]
player_color = 'wb'[get_color(hist[-1])] 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 state = GameState.MAIN_MENU
menu_index = 0 menu_index = 0
move = None move = None
score = None score = None
last_move = "Begin!" last_move = "Begin!"
all_moves = dict()
src_idx = 0
dst_idx = 0
src = ""
while True: while True:
key = None
if state == GameState.MAIN_MENU: if state == GameState.MAIN_MENU:
draw.draw_menu(menu_index) draw.draw_menu(menu_index)
key = cinput.get_one_shot()
elif state == GameState.THINKING: elif state == GameState.THINKING:
draw.draw_thinking() draw.draw_thinking(last_move)
start = time.time() start = time.time()
for _, move, score in searcher.search(hist[-1], hist): for _, move, score in searcher.search(hist[-1], hist):
if time.time() - start > MAX_THINKING_SECONDS: if time.time() - start > MAX_THINKING_SECONDS:
break break
hist.append(hist[-1].move(move)) hist.append(hist[-1].move(move))
draw.draw_board(hist[-1].board if player_color == 'b' else hist[-1].rotate().board) draw.draw_board(hist[-1].rotate(), player_color)
shift = -119 if player_color == 'b' else 0 last_move = move_str(move, player_color == "w")
last_move = sunfish.render(shift+move[0]) + " -> " + sunfish.render(shift+move[1])
if score == sunfish.MATE_UPPER: if score == sunfish.MATE_UPPER:
draw.draw_checkmate(last_move, False) draw.draw_checkmate(last_move, False)
state = GameState.GAME_OVER state = GameState.GAME_OVER
else: else:
draw.draw_rowsel(last_move) all_moves = dict()
state = GameState.CHOOSE_ROW 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() 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()
if state == GameState.MAIN_MENU: if state == GameState.MAIN_MENU:
if key == Button.DIR_U: if key == Button.DIR_U:
menu_index -= 1 menu_index -= 1
@ -73,7 +90,7 @@ 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.CHOOSE_ROW state = GameState.CHOOSE_SRC
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(hist[-1])) fen.write(renderFEN(hist[-1]))
@ -82,16 +99,58 @@ def execute(cinput: ControlInput, graphics: Graphics, _):
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:
player_color = "b"
state = GameState.THINKING state = GameState.THINKING
else: else:
state = GameState.CHOOSE_ROW player_color = "w"
state = GameState.CHOOSE_SRC
hist = [pos] 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: else:
if key == Button.BTN_A: if key == Button.BTN_A:
state = GameState.MAIN_MENU 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): def get_color(pos):
return 1 if pos.board.startswith('\n') else 0 return 1 if pos.board.startswith('\n') else 0

View File

@ -1,5 +1,6 @@
from icecream import ic from icecream import ic
from graphics import Graphics from graphics import Graphics
from .sunfish import Position
class Draw: class Draw:
BOARD_SIZE = 64 BOARD_SIZE = 64
@ -78,10 +79,10 @@ class Draw:
self._graphics.fill_rect( self._graphics.fill_rect(
self.BOARD_SIZE, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0) self.BOARD_SIZE, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0)
def draw_board(self, board: str): def draw_board(self, pos: Position, player_color: str):
ic(board)
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(pos.board.split())
nb = nb[::-1] if player_color == "b" else nb
c = True c = True
for row in range(8): for row in range(8):
for col 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.text(marker + self.MENU[i], 12, i + 2, 1)
self._graphics.show() self._graphics.show()
def draw_thinking(self): def draw_thinking(self, last_move: str):
self._clear_info() self._clear_info()
self._graphics.text("Shh, I'm", 12, 0, 1) self._graphics.text(last_move, 12, 0, 1)
self._graphics.text("thinking!", 12, 1, 1) self._graphics.text("Shh, I'm", 12, 2, 1)
self._graphics.text("thinking!", 12, 3, 1)
self._graphics.show() self._graphics.show()
def draw_checkmate(self, move: str, player_won: bool): 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) "you win!" if player_won else "you lose!", 12, 2, 1)
self._graphics.show() self._graphics.show()
def draw_rowsel(self, move: str): def draw_select(self, last_move: str, src: str, dst = None):
self._clear_info() 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() self._graphics.show()

View File

@ -295,7 +295,7 @@ class Searcher:
# Note, we don't have to check for legality, since we've already done it # 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 # before. Also note that in QS the killer must be a capture, otherwise we
# will be non deterministic. # 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): if killer and (depth > 0 or pos.value(killer) >= QS_LIMIT):
yield killer, -self.bound(pos.move(killer), 1-gamma, depth-1, root=False) yield killer, -self.bound(pos.move(killer), 1-gamma, depth-1, root=False)
# Then all the other moves # Then all the other moves