Compare commits

..

2 Commits

Author SHA1 Message Date
Rudis Muiznieks 93e941a79e
wip: cursor blinking when selecting pieces 2022-04-30 17:16:13 -05:00
Rudis Muiznieks 0d5a55088f
handle game over state 2022-04-30 11:00:14 -05:00
4 changed files with 125 additions and 34 deletions

View File

@ -30,7 +30,7 @@ class ControlInput:
self._buttons[button].direction = Direction.INPUT self._buttons[button].direction = Direction.INPUT
self._buttons[button].pull = Pull.UP self._buttons[button].pull = Pull.UP
def get_one_shot(self, timeout = 0): def get_one_shot(self, timeout: float = 0):
started = time.time() started = time.time()
while True: while True:
time.sleep(0.05) time.sleep(0.05)

View File

@ -21,8 +21,8 @@ class Graphics:
def text(self, text, x, y, c): def text(self, text, x, y, c):
self._display.text(text, x * self.CHAR_WIDTH, y * self.LINE_HEIGHT, c) self._display.text(text, x * self.CHAR_WIDTH, y * self.LINE_HEIGHT, c)
def pixel(self, x, y, c): def pixel(self, x, y, c = None):
self._display.pixel(x, y, c) return self._display.pixel(x, y, c)
def line(self, x1, y1, x2, y2, c): def line(self, x1, y1, x2, y2, c):
self._display.line(x1, y1, x2, y2, c) self._display.line(x1, y1, x2, y2, c)

View File

@ -1,6 +1,7 @@
from typing import Optional
from icecream import ic from icecream import ic
from graphics import Graphics from graphics import Graphics
from chess import Board, Color, WHITE import chess
class Draw: class Draw:
BOARD_SIZE = 64 BOARD_SIZE = 64
@ -79,11 +80,49 @@ 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: Board, player_color: Color): def _format_move(self, move: str):
if len(move) == 4:
return move.upper()[:2] + "-" + move.upper()[2:]
return move
def _invert_pixel(self, x: int, y: int):
c = self._graphics.pixel(x, y)
c = None if c != None else 1
self._graphics.pixel(x, y, c)
def _cursor_clear(self, except_for: list[str], player_color: chess.Color):
c = 0
for row in range(8):
for col in range(8):
sqx = col if player_color == chess.WHITE else 7 - col
sqy = row if player_color == chess.BLACK else 7 - row
square = chr(sqx + 97) + str(sqy + 1)
if not (square in except_for):
# see if square is right color
sqc = self._graphics.pixel(sqx * 8, sqy * 8)
if sqc != c:
ic(square)
self._cursor(square, player_color);
c = 0 if c == 1 else 1
c = 0 if c == 1 else 1
def _cursor(self, square: str, player_color: chess.Color):
sqx = ord(square[0]) - 97
sqy = int(square[1]) - 1
if player_color == chess.WHITE:
sqy = 7 - sqy
else:
sqx = 7 - sqx
for i in range(7):
self._invert_pixel(sqx * 8 + i, sqy * 8)
self._invert_pixel(sqx * 8 + i, sqy * 8 + 7)
self._invert_pixel(sqx * 8, sqy * 8 + i)
self._invert_pixel(sqx * 8 + 7, sqy * 8 + i)
def draw_board(self, board: chess.Board, player_color: chess.Color):
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(str(board).split()).replace(" ", "") nb = "".join(str(board).split()).replace(" ", "")
nb = nb if player_color == WHITE else nb[::-1] nb = nb if player_color == chess.WHITE else nb[::-1]
ic(player_color, 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):
@ -109,7 +148,7 @@ class Draw:
def draw_thinking(self, last_move: str): def draw_thinking(self, last_move: str):
self._clear_info() self._clear_info()
self._graphics.text(last_move, 12, 0, 1) self._graphics.text(self._format_move(last_move), 12, 0, 1)
self._graphics.text("Shh, I'm", 12, 2, 1) self._graphics.text("Shh, I'm", 12, 2, 1)
self._graphics.text("thinking!", 12, 3, 1) self._graphics.text("thinking!", 12, 3, 1)
self._graphics.show() self._graphics.show()
@ -122,12 +161,32 @@ 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_select(self, last_move: str, src: str, dst = None): def draw_select(self, board: chess.Board, last_move: str, player_color: chess.Color, src: Optional[str], dst: Optional[str] = None):
self._clear_info() self._clear_info()
self._graphics.text(last_move, 12, 0, 1) self._graphics.text(self._format_move(last_move), 12, 0, 1)
self._graphics.text("You move:", 12, 2, 1) self._graphics.text("You move:", 12, 2, 1)
if dst == None: if src != None and dst == None:
self._graphics.text("<" + src + "> - __", 12, 3, 1) self._graphics.text("<" + src.upper() + "> - __", 12, 3, 1)
#self._cursor_clear([src], player_color)
self._cursor(src, player_color)
elif src != None and dst != None:
self._graphics.text(src.upper() + " - <" + dst.upper() + ">", 12, 3, 1)
#self._cursor_clear([src, dst], player_color)
self._cursor(src, player_color)
self._cursor(dst, player_color)
else: else:
self._graphics.text(src + " - <" + dst + ">", 12, 3, 1) self._cursor_clear(list(), player_color)
self._graphics.show()
def draw_game_over(self, outcome: Optional[chess.Outcome]):
self._clear_info()
self._graphics.text("Game over", 12, 0, 1)
if outcome != None:
if outcome.winner == None:
self._graphics.text("Draw!", 12, 2, 1)
else:
self._graphics.text("Winner:", 12, 2, 1)
self._graphics.text("White!" if outcome.winner == chess.WHITE else "Black!",
12, 3, 1)
self._graphics.show() self._graphics.show()

View File

@ -1,4 +1,5 @@
from os import path from os import path
from typing import Optional
from enum import Enum, auto from enum import Enum, auto
import shutil import shutil
from icecream import ic from icecream import ic
@ -54,27 +55,56 @@ class ChessGame:
self._sf.make_moves_from_current_position([move]) self._sf.make_moves_from_current_position([move])
self._draw.draw_board(self._board, self._player_color) self._draw.draw_board(self._board, self._player_color)
def _square_sort_key(self, square: str):
# convert to number between 00 to 88
key = (int(square[1]) - 1) * 10 + ord(square[0]) - 97
return key if self._player_color == WHITE else 88 - key
def _get_sources(self):
if len(list(self._all_moves)) == 0:
moves = self._board.generate_legal_moves()
mvarray = map(lambda m: [Move.uci(m)[0:2], Move.uci(m)[2:4]], moves)
for src, dst in list(mvarray):
self._all_moves.setdefault(src, []).append(dst)
if len(list(self._all_moves)) > 0:
return sorted(
list(self._all_moves),
key=lambda x: self._square_sort_key(x))
return list[str]()
def _get_dests(self):
if self._src_idx < len(list(self._all_moves)):
src = self._get_sources()[self._src_idx]
return sorted(
self._all_moves[src],
key=lambda x: self._square_sort_key(x))
return list[str]()
def run(self): def run(self):
# either load the save game or start a new one # either load the save game or start a new one
self._load_saved_or_init() self._load_saved_or_init()
while True: while True:
key = None key: Optional[Button] = None
src = None src: Optional[str] = None
################ ################
# HANDLE STATE # # HANDLE STATE #
################ ################
if self._board.is_game_over() and self._state != GameState.MAIN_MENU:
self._state = GameState.GAME_OVER
# draw menu # draw menu
if self._state == GameState.MAIN_MENU: if self._state == GameState.MAIN_MENU:
self._draw.draw_board(self._board, self._player_color)
self._draw.draw_menu(self._menu_index) self._draw.draw_menu(self._menu_index)
key = self._cinput.get_one_shot() key = self._cinput.get_one_shot()
# computer makes a move # computer makes a move
elif self._state == GameState.THINKING: elif self._state == GameState.THINKING:
self._draw.draw_board(self._board, self._player_color)
self._draw.draw_thinking(self._move) self._draw.draw_thinking(self._move)
# TODO: check game over
move = self._sf.get_best_move_time(self.STOCKFISH_MOVE_TIME * 1000) move = self._sf.get_best_move_time(self.STOCKFISH_MOVE_TIME * 1000)
if move != None: if move != None:
self._make_move(move) self._make_move(move)
@ -88,33 +118,34 @@ class ChessGame:
# user picks source piece # user picks source piece
elif self._state == GameState.CHOOSE_SRC: elif self._state == GameState.CHOOSE_SRC:
if len(list(self._all_moves)) == 0: srces = self._get_sources()
moves = self._board.generate_legal_moves() if len(srces) > 0:
mvarray = map(lambda m: [Move.uci(m)[0:2], Move.uci(m)[2:4]], moves) src = srces[self._src_idx]
for src, dst in list(mvarray): self._draw.draw_select(self._board, self._move, self._player_color, src)
self._all_moves.setdefault(src, []).append(dst) key = self._cinput.get_one_shot(0.1)
if len(list(self._all_moves)) > 0:
src = list(self._all_moves)[self._src_idx]
self._draw.draw_select(self._move, src)
key = self._cinput.get_one_shot()
else: else:
self._state = GameState.GAME_OVER self._state = GameState.GAME_OVER
# user picks dest square # user picks dest square
elif self._state == GameState.CHOOSE_DST: elif self._state == GameState.CHOOSE_DST:
src = list(self._all_moves)[self._src_idx] src = self._get_sources()[self._src_idx]
dst = self._all_moves[src][self._dst_idx] dst = self._get_dests()[self._dst_idx]
self._draw.draw_select(self._move, src, dst) self._draw.draw_select(self._board, self._move, self._player_color, src, dst)
key = self._cinput.get_one_shot() key = self._cinput.get_one_shot(0.1)
# game has ended # game has ended
elif self._state == GameState.GAME_OVER: else:
self._draw.draw_board(self._board, self._player_color)
self._draw.draw_game_over(self._board.outcome())
key = self._cinput.get_one_shot() key = self._cinput.get_one_shot()
################ ################
# HANDLE INPUT # # HANDLE INPUT #
################ ################
if key == None:
continue
# handle user input on main menu # handle user input on main menu
if self._state == GameState.MAIN_MENU: if self._state == GameState.MAIN_MENU:
@ -147,6 +178,8 @@ class ChessGame:
# TODO: some kind of cursor indicator on board # TODO: some kind of cursor indicator on board
elif self._state == GameState.CHOOSE_SRC: elif self._state == GameState.CHOOSE_SRC:
# move between source pieces # move between source pieces
cur_src = self._get_sources()[self._src_idx]
if key == Button.DIR_D or key == Button.DIR_R: if key == Button.DIR_D or key == Button.DIR_R:
self._src_idx += 1 self._src_idx += 1
if self._src_idx >= len(self._all_moves.keys()): if self._src_idx >= len(self._all_moves.keys()):
@ -162,6 +195,7 @@ class ChessGame:
# move to destination picker # move to destination picker
elif key == Button.BTN_B: elif key == Button.BTN_B:
self._dst_idx = 0
self._state = GameState.CHOOSE_DST self._state = GameState.CHOOSE_DST
# handle user input when selecting dest piece # handle user input when selecting dest piece
@ -183,14 +217,12 @@ class ChessGame:
# make the move # make the move
elif key == Button.BTN_B: elif key == Button.BTN_B:
dst = self._all_moves[src][self._dst_idx] src = self._get_sources()[self._src_idx]
dst = self._get_dests()[self._dst_idx]
self._make_move(src + dst) self._make_move(src + dst)
# TODO: check game over
self._state = GameState.THINKING self._state = GameState.THINKING
# handle user input on game over # handle user input on game over
else: else:
if key == Button.BTN_A or key == Button.BTN_B: if key == Button.BTN_A or key == Button.BTN_B:
self._state = GameState.MAIN_MENU self._state = GameState.MAIN_MENU