removed chess, added music menu

This commit is contained in:
Rudis Muiznieks 2022-12-27 13:13:51 -06:00
parent c2c401a75b
commit 5999509f7d
Signed by: rudism
GPG key ID: CABF2F86EF7884F9
5 changed files with 39 additions and 456 deletions

View file

@ -9,44 +9,36 @@ from cinput import ControlInput
from graphics import Graphics
menu_config = [
MenuItem("Apps",
MenuItem("Music",
MenuType.SUB_MENU,
{"sub_menu": [
MenuItem("Chess Game",
MenuItem("Artists",
MenuType.PLUGIN,
{"plugin": "chess",
"arg": "game"}),
MenuItem("Chess Puzzles",
{"plugin": "music",
"arg": "artists"}),
MenuItem("Genres",
MenuType.PLUGIN,
{"plugin": "chess",
"arg": "puzzles"}),
MenuItem("Cube Timer",
{"plugin": "music",
"arg": "genres"}),
MenuItem("Playlists",
MenuType.PLUGIN,
{"plugin": "cube",
"arg": None}),
{"plugin": "music",
"arg": "playlists"}),
]}),
MenuItem("Information",
MenuType.PLUGIN,
{"plugin": "info",
"arg": None}),
MenuItem("Config",
MenuItem("System",
MenuType.SUB_MENU,
{"sub_menu": [
MenuItem("Brightness",
MenuItem("Information",
MenuType.PLUGIN,
{"plugin": "config",
"arg": "brightness"}),
MenuItem("Wifi",
MenuType.PLUGIN,
{"plugin": "config",
"arg": "wifi"}),
{"plugin": "info",
"arg": None}),
MenuItem("Reboot",
MenuType.EXIT_CMD,
{"command": "sudo reboot"}),
MenuItem("Shutdown",
MenuType.EXIT_CMD,
{"command": "sudo shutdown now"})
]}),
MenuItem("Reboot",
MenuType.EXIT_CMD,
{"command": "sudo reboot"}),
MenuItem("Shutdown",
MenuType.EXIT_CMD,
{"command": "sudo shutdown now"})]
cinput = ControlInput()
graphics = Graphics()

View file

@ -1,7 +0,0 @@
from cinput import ControlInput
from graphics import Graphics
from .game import ChessGame
def execute(cinput: ControlInput, graphics: Graphics, arg: str):
if arg == "game":
ChessGame(cinput, graphics).run()

View file

@ -1,194 +0,0 @@
from typing import Optional
from icecream import ic
from graphics import Graphics
from chess import Board, Outcome, Color, WHITE
class Draw:
BOARD_SIZE = 64
SQUARE_SIZE = 8
PIECES = {
"p": [
" . . . . . . ",
" . . ■ ■ . . ",
" . ■ - - ■ . ",
" . ■ - - ■ . ",
" . . ■ ■ . . ",
" . . . . . . "],
"r": [
" ■ ■ ■ ■ ■ ■ ",
" ■ - - - - ■ ",
" . ■ - - ■ . ",
" . ■ - - ■ . ",
" ■ - - - - ■ ",
" ■ ■ ■ ■ ■ ■ "],
"n": [
" . . ■ ■ ■ . ",
" . ■ - - - ■ ",
" ■ - - - - ■ ",
" . ■ - - - ■ ",
" . . ■ - - ■ ",
" . . ■ ■ ■ ■ "],
"b": [
" . . ■ ■ . . ",
" . ■ - - ■ . ",
" . ■ - - ■ . ",
" . ■ - - ■ . ",
" ■ - - - - ■ ",
" ■ ■ ■ ■ ■ ■ "],
"q": [
" ■ . . . . ■ ",
" ■ ■ . . ■ ■ ",
" ■ - ■ ■ - ■ ",
" ■ - - - - ■ ",
" ■ - - - - ■ ",
" ■ ■ ■ ■ ■ ■ "],
"k": [
" . . ■ ■ . . ",
" ■ ■ - - ■ ■ ",
" ■ - - - - ■ ",
" ■ ■ - - ■ ■ ",
" . ■ - - ■ . ",
" . ■ ■ ■ ■ . "],
}
MENU = [
"Play",
"Quit",
"New (W)",
"New (B)"]
def __init__(self, graphics: Graphics):
self._graphics = graphics
def _draw_piece(self, piece: list[str], white: bool, sqx, sqy, sqc):
c = 0 if sqc == 1 else 1
filled = (white and sqc == 0) or (not white and sqc == 1)
for row in range(len(piece)):
pixels = piece[row].replace(" ", "")
for col in range(len(pixels)):
x = (sqx * self.SQUARE_SIZE) + col + 1
y = (sqy * self.SQUARE_SIZE) + row + 1
pixel = pixels[col]
if pixel == '':
self._graphics.pixel(x, y, c)
elif pixel =='-':
self._graphics.pixel(x, y, c if filled else sqc)
def _clear_info(self):
self._graphics.fill_rect(
self.BOARD_SIZE, 0, self.BOARD_SIZE, self.BOARD_SIZE, 0)
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 = 0 if c != 0 else 1
self._graphics.pixel(x, y, c)
def _cursor_clear(self, except_for: list[str], player_color: Color):
c = 1
for row in range(8):
for col in range(8): # 0,0 w = a8 b = h1 ... 7,7 w = h1 b = a8
sqx = col if player_color == WHITE else 7 - col
sqy = 7 - row if player_color == WHITE else row
square = chr(sqx + 97) + str(sqy + 1)
if not (square in except_for):
# see if square is right color
sqc = self._graphics.pixel(col * 8, row * 8)
if sqc != c:
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: Color):
sqx = ord(square[0]) - 97
sqy = int(square[1]) - 1
if player_color == WHITE:
sqy = 7 - sqy
else:
sqx = 7 - sqx
for i in range(8):
# top and bottom row
self._invert_pixel(sqx * 8 + i, sqy * 8)
self._invert_pixel(sqx * 8 + i, sqy * 8 + 7)
if i > 0 and i < 7:
# sides (skipping top and bottom row)
self._invert_pixel(sqx * 8, sqy * 8 + i)
self._invert_pixel(sqx * 8 + 7, sqy * 8 + i)
def draw_board(self, board: Board, player_color: 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]
c = True
for row in range(8):
for col in range(8):
if c:
x = col * self.SQUARE_SIZE
y = row * self.SQUARE_SIZE
self._graphics.fill_rect(x, y, self.SQUARE_SIZE, self.SQUARE_SIZE, 1)
p = nb[(row * 8) + col]
if p.lower() in self.PIECES:
self._draw_piece(
self.PIECES[p.lower()], p.isupper(), col, row, c)
c = not c
c = not c
self._graphics.show()
def draw_menu(self, menu_index: int):
self._clear_info()
self._graphics.text("Menu:", 12, 0, 1)
for i in range(len(self.MENU)):
marker = "> " if i == menu_index else " "
self._graphics.text(marker + self.MENU[i], 12, i + 2, 1)
self._graphics.show()
def draw_thinking(self, last_move: str):
self._clear_info()
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()
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_select(self, last_move: str, player_color: Color, src: Optional[str], dst: Optional[str] = None):
self._clear_info()
self._graphics.text(self._format_move(last_move), 12, 0, 1)
self._graphics.text("You move:", 12, 2, 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._cursor_clear(list(), player_color)
self._graphics.show()
def draw_game_over(self, outcome: Optional[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 == WHITE else "Black!",
12, 3, 1)
self._graphics.show()

View file

@ -1,227 +0,0 @@
from os import path
from typing import Optional
from enum import Enum, auto
import shutil
from icecream import ic
from cinput import ControlInput, Button
from graphics import Graphics
from .draw import Draw
from chess import Board, Move, WHITE, BLACK
from stockfish import Stockfish
class GameState(Enum):
MAIN_MENU = auto()
THINKING = auto()
CHOOSE_SRC = auto()
CHOOSE_DST = auto()
GAME_OVER = auto()
class ChessGame:
SAVE_FILE = path.join("data", "chess_state.fen")
STOCKFISH_MOVE_TIME = 3
def __init__(self, cinput: ControlInput, graphics: Graphics):
self._sf = Stockfish(shutil.which("stockfish") or "stockfish")
self._cinput = cinput
self._draw = Draw(graphics)
self._state = GameState.MAIN_MENU
self._menu_index = 0
def _init_new_game(self, fen = None, player_color = None):
self._board = Board() if fen == None else Board(fen)
self._move = "Begin!"
self._all_moves = dict()
self._src_idx = 0
self._dst_idx = 0
self._player_color = player_color if player_color != None else self._board.turn
self._sf.set_fen_position(self._board.fen())
self._draw.draw_board(self._board, self._player_color)
def _load_saved_or_init(self):
if path.exists(self.SAVE_FILE):
with open(self.SAVE_FILE, "r") as fen:
fen = fen.read()
self._init_new_game(fen)
else:
self._init_new_game()
def _save_game(self):
with open(self.SAVE_FILE, "w") as fen:
fen.write(self._board.fen())
def _make_move(self, move: str):
self._move = move
self._board.push(Move.from_uci(move))
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: 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)
move = self._sf.get_best_move_time(self.STOCKFISH_MOVE_TIME * 1000)
if move != None:
self._make_move(move)
# reset user moves and move state to user's turn
self._all_moves = dict()
self._src_idx = 0
self._dst_idx = 0
self._state = GameState.CHOOSE_SRC
else:
self._state = GameState.GAME_OVER
# user picks source piece
elif self._state == GameState.CHOOSE_SRC:
if len(self._get_sources()) > 0:
src = self._get_sources()[self._src_idx]
self._draw.draw_select(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 = self._get_sources()[self._src_idx]
dst = self._get_dests()[self._dst_idx]
self._draw.draw_select(self._move, self._player_color, src, dst)
key = self._cinput.get_one_shot(0.1)
# game has ended
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:
# menu cursor up and down
if key == Button.DIR_U:
self._menu_index -= 1
if self._menu_index < 0:
self._menu_index = 0
elif key == Button.DIR_D:
self._menu_index += 1
if self._menu_index > 3:
self._menu_index = 3
# select current menu item
elif key == Button.BTN_B:
if self._menu_index == 0: # play current game
self._state = GameState.CHOOSE_SRC
elif self._menu_index == 1: # save and quit
self._save_game()
return
elif self._menu_index == 2 or self._menu_index == 3: # new game
if self._menu_index == 2:
self._init_new_game(player_color=WHITE)
self._state = GameState.CHOOSE_SRC
else:
self._init_new_game(player_color=BLACK)
self._state = GameState.THINKING
# handle user input when selecting source piece
# 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()):
self._src_idx = 0
elif key == Button.DIR_U or key == Button.DIR_L:
self._src_idx -= 1
if self._src_idx < 0:
self._src_idx = len(list(self._all_moves)) - 1
# back out to main menu
elif key == Button.BTN_A:
self._state = GameState.MAIN_MENU
# 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
elif self._state == GameState.CHOOSE_DST:
# move between dest squares for the given source
if key == Button.DIR_D or key == Button.DIR_R:
self._dst_idx += 1
if self._dst_idx >= len(self._all_moves[src]):
self._dst_idx = 0
elif key == Button.DIR_U or key == Button.DIR_L:
self._dst_idx -= 1
if self._dst_idx < 0:
self._dst_idx = len(self._all_moves[src]) - 1
# back out to choose a different source piece
elif key == Button.BTN_A:
self._dst_idx = 0
self._state = GameState.CHOOSE_SRC
# make the move
elif key == Button.BTN_B:
src = self._get_sources()[self._src_idx]
dst = self._get_dests()[self._dst_idx]
self._make_move(src + dst)
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

19
requirements.txt Normal file
View file

@ -0,0 +1,19 @@
Adafruit-Blinka==8.10.0
adafruit-circuitpython-busdevice==5.2.3
adafruit-circuitpython-framebuf==1.5.0
adafruit-circuitpython-requests==1.12.11
adafruit-circuitpython-ssd1306==2.12.12
adafruit-circuitpython-typing==1.8.3
Adafruit-PlatformDetect==3.38.0
Adafruit-PureIO==1.1.9
asttokens==2.2.1
colorama==0.4.6
executing==1.2.0
icecream==2.1.3
pyftdi==0.54.0
Pygments==2.13.0
pyserial==3.5
pyusb==1.2.1
RPi.GPIO==0.7.1
six==1.16.0
typing_extensions==4.4.0