From 38c83fbe51de8e503aaf91ef595a2c8323683290 Mon Sep 17 00:00:00 2001 From: Greg Hendrickson Date: Tue, 27 Jan 2026 20:35:31 +0000 Subject: [PATCH] Large terminal-filling board with background shading - Big board (7-char cells, 3 rows tall) for 80x40+ terminals - Compact board (5-char cells, 2 rows tall) for smaller - Dark square shading with ANSI 256-color backgrounds - Better visual hierarchy and spacing - Simpler prompt (>) --- src/shellmate/ssh/server.py | 161 ++++++++++++++++++++++++++---------- 1 file changed, 117 insertions(+), 44 deletions(-) diff --git a/src/shellmate/ssh/server.py b/src/shellmate/ssh/server.py index 9ef2217..9d2db51 100644 --- a/src/shellmate/ssh/server.py +++ b/src/shellmate/ssh/server.py @@ -282,83 +282,156 @@ async def run_chess_game(process, session: TerminalSession, username: str, oppon } def render_board(): - """Clean Gambit-style board rendering - simple and elegant.""" + """Large, terminal-filling chess board.""" nonlocal status_msg session._update_size() session.clear() + # Determine cell size based on terminal + # Big board for terminals 80+ wide, 40+ tall + big = session.width >= 80 and session.height >= 40 + lines = [] # Title lines.append("") - lines.append("\033[1;32m♔ S H E L L M A T E ♔\033[0m") + if big: + lines.append("\033[1;32m♔ S H E L L M A T E ♔\033[0m") + lines.append("\033[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m") + else: + lines.append("\033[1;32m♔ SHELLMATE ♔\033[0m") lines.append("") - # Column labels - lines.append(" A B C D E F G H") - - # Top border - lines.append(" ┌───┬───┬───┬───┬───┬───┬───┬───┐") - - for rank in range(7, -1, -1): - row = f" \033[36m{rank + 1}\033[0m │" + if big: + # BIG BOARD - 7 char wide cells, 3 rows tall + cell_w = 7 + lines.append(" A B C D E F G H") + lines.append(" ╔═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╗") - for file in range(8): - square = chess.square(file, rank) - piece = board.piece_at(square) - is_light = (rank + file) % 2 == 1 + for rank in range(7, -1, -1): + # Top padding row + pad_row = " ║" + for file in range(8): + is_light = (rank + file) % 2 == 1 + fill = " " if is_light else "\033[48;5;236m \033[0m" + pad_row += fill + if file < 7: + pad_row += "│" + pad_row += "║" + lines.append(pad_row) - if piece: - char = PIECES.get(piece.symbol(), '?') - # White pieces bright, black pieces yellow - if piece.color == chess.WHITE: - row += f" \033[1;37m{char}\033[0m │" + # Piece row + piece_row = f" \033[1;36m{rank + 1}\033[0m ║" + for file in range(8): + square = chess.square(file, rank) + piece = board.piece_at(square) + is_light = (rank + file) % 2 == 1 + bg = "" if is_light else "\033[48;5;236m" + bg_end = "" if is_light else "\033[0m" + + if piece: + char = PIECES.get(piece.symbol(), '?') + if piece.color == chess.WHITE: + piece_row += f"{bg} \033[1;97m{char}\033[0m{bg} {bg_end}" + else: + piece_row += f"{bg} \033[1;33m{char}\033[0m{bg} {bg_end}" else: - row += f" \033[1;33m{char}\033[0m │" - else: - # Dot for dark squares, space for light - if is_light: - row += " │" + piece_row += f"{bg} {bg_end}" + + if file < 7: + piece_row += "│" + piece_row += f"║ \033[1;36m{rank + 1}\033[0m" + lines.append(piece_row) + + # Bottom padding row + pad_row = " ║" + for file in range(8): + is_light = (rank + file) % 2 == 1 + fill = " " if is_light else "\033[48;5;236m \033[0m" + pad_row += fill + if file < 7: + pad_row += "│" + pad_row += "║" + lines.append(pad_row) + + # Row separator + if rank > 0: + lines.append(" ╟───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────╢") + + lines.append(" ╚═══════╧═══════╧═══════╧═══════╧═══════╧═══════╧═══════╧═══════╝") + lines.append(" A B C D E F G H") + board_width = 73 + else: + # COMPACT BOARD - 5 char cells, 2 rows tall + lines.append(" A B C D E F G H") + lines.append(" ╔═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╗") + + for rank in range(7, -1, -1): + # Piece row + piece_row = f" \033[1;36m{rank + 1}\033[0m ║" + for file in range(8): + square = chess.square(file, rank) + piece = board.piece_at(square) + is_light = (rank + file) % 2 == 1 + bg = "" if is_light else "\033[48;5;236m" + bg_end = "" if is_light else "\033[0m" + + if piece: + char = PIECES.get(piece.symbol(), '?') + if piece.color == chess.WHITE: + piece_row += f"{bg} \033[1;97m{char}\033[0m{bg} {bg_end}" + else: + piece_row += f"{bg} \033[1;33m{char}\033[0m{bg} {bg_end}" else: - row += " \033[90m·\033[0m │" + piece_row += f"{bg} {bg_end}" + + if file < 7: + piece_row += "│" + piece_row += f"║ \033[1;36m{rank + 1}\033[0m" + lines.append(piece_row) + + # Padding row + pad_row = " ║" + for file in range(8): + is_light = (rank + file) % 2 == 1 + fill = " " if is_light else "\033[48;5;236m \033[0m" + pad_row += fill + if file < 7: + pad_row += "│" + pad_row += "║" + lines.append(pad_row) + + if rank > 0: + lines.append(" ╟─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────╢") - row += f" \033[36m{rank + 1}\033[0m" - lines.append(row) - - # Row separator - if rank > 0: - lines.append(" ├───┼───┼───┼───┼───┼───┼───┼───┤") + lines.append(" ╚═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╝") + lines.append(" A B C D E F G H") + board_width = 57 - # Bottom border - lines.append(" └───┴───┴───┴───┴───┴───┴───┴───┘") - - # Column labels again - lines.append(" A B C D E F G H") lines.append("") # Status if board.turn == chess.WHITE: - lines.append(" \033[1;37mWhite ♔\033[0m to move") + lines.append(" \033[1;97mWhite ♔\033[0m to move") else: - lines.append(" \033[1;33mBlack ♚\033[0m to move") + lines.append(" \033[1;33mBlack ♚\033[0m to move") if board.is_check(): - lines.append(" \033[1;31m⚠ CHECK! ⚠\033[0m") + lines.append(" \033[1;31m⚠ CHECK! ⚠\033[0m") if move_history: last_moves = move_history[-5:] - lines.append(f" \033[90mRecent: {' '.join(last_moves)}\033[0m") + lines.append(f" \033[90mRecent: {' '.join(last_moves)}\033[0m") if status_msg: - lines.append(f" \033[33m{status_msg}\033[0m") + lines.append(f" \033[33m{status_msg}\033[0m") status_msg = "" lines.append("") - lines.append(" \033[32mMove:\033[0m e2e4 \033[90m│\033[0m \033[31mq\033[0muit \033[90m│\033[0m \033[31mr\033[0mesign") + lines.append(" \033[32mType move\033[0m (e.g. e2e4) │ \033[31mq\033[0m = quit │ \033[31mr\033[0m = resign") lines.append("") # Calculate centering - board_width = 37 # Width of the board total_height = len(lines) # Horizontal padding @@ -376,7 +449,7 @@ async def run_chess_game(process, session: TerminalSession, username: str, oppon session.write(pad + line + "\r\n") # Move prompt - session.write(pad + " \033[36mMove: \033[0m") + session.write(pad + " \033[36m> \033[0m") session.show_cursor() render_board()