wip: implementing chess
This commit is contained in:
parent
94befd04ee
commit
38a2854378
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
Reference in New Issue