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].pull = Pull.UP
def get_one_shot(self, timeout = 0):
def get_one_shot(self, timeout: float = 0):
started = time.time()
while True:
time.sleep(0.05)

View File

@ -21,8 +21,8 @@ class Graphics:
def text(self, text, x, y, c):
self._display.text(text, x * self.CHAR_WIDTH, y * self.LINE_HEIGHT, c)
def pixel(self, x, y, c):
self._display.pixel(x, y, c)
def pixel(self, x, y, c = None):
return self._display.pixel(x, y, c)
def line(self, 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 graphics import Graphics
from chess import Board, Color, WHITE
import chess
class Draw:
BOARD_SIZE = 64
@ -79,11 +80,49 @@ class Draw:
self._graphics.fill_rect(
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)
nb = "".join(str(board).split()).replace(" ", "")
nb = nb if player_color == WHITE else nb[::-1]
ic(player_color, nb)
nb = nb if player_color == chess.WHITE else nb[::-1]
c = True
for row in range(8):
for col in range(8):
@ -109,7 +148,7 @@ class Draw:
def draw_thinking(self, last_move: str):
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("thinking!", 12, 3, 1)
self._graphics.show()
@ -122,12 +161,32 @@ class Draw:
"you win!" if player_won else "you lose!", 12, 2, 1)
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._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)
if dst == None:
self._graphics.text("<" + src + "> - __", 12, 3, 1)
if src != None and dst == None:
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:
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()

View File

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