Simplify chess board rendering - plain text that works over SSH

- Remove complex Rich styling from board (caused rendering issues)
- Use simple ASCII box drawing (+---+) instead of Unicode box chars
- Use dots for dark squares, spaces for light
- Plain text status display
- Much more reliable over SSH terminals
This commit is contained in:
Greg Hendrickson
2026-01-27 18:53:54 +00:00
parent e3915f1b33
commit ed484e27a2
2 changed files with 52 additions and 76 deletions

View File

@@ -272,88 +272,66 @@ async def run_chess_game(process, session: TerminalSession, username: str, oppon
def render_board(): def render_board():
nonlocal status_msg nonlocal status_msg
writer = ProcessWriter(session)
console = Console(file=writer, width=session.width, height=session.height, force_terminal=True)
session.clear() session.clear()
# Calculate centering # Simple text-based board that works reliably over SSH
board_width = 42 lines = []
left_pad = max(0, (session.width - board_width) // 2) lines.append("")
pad = " " * left_pad lines.append(" ♔ SHELLMATE CHESS ♔")
lines.append("")
# Title lines.append(" a b c d e f g h")
console.print() lines.append(" +---+---+---+---+---+---+---+---+")
console.print(Align.center(Text("♔ SHELLMATE CHESS ♔", style="bold cyan")))
console.print()
# Board with better colors
console.print(f"{pad} a b c d e f g h")
console.print(f"{pad} ┌───┬───┬───┬───┬───┬───┬───┬───┐")
for rank in range(7, -1, -1): for rank in range(7, -1, -1):
row = f"{pad}{rank + 1} " row = f" {rank + 1} |"
for file in range(8): for file in range(8):
square = chess.square(file, rank) square = chess.square(file, rank)
piece = board.piece_at(square) piece = board.piece_at(square)
is_light = (rank + file) % 2 == 1
# Green theme for board
if is_light:
bg = "on #769656" # Light green
else:
bg = "on #4a7c3f" # Dark green
if piece: if piece:
char = PIECES.get(piece.symbol(), '?') char = PIECES.get(piece.symbol(), '?')
# White pieces in white/cream, black pieces in dark row += f" {char} |"
if piece.color == chess.WHITE:
fg = "#ffffff bold"
else:
fg = "#1a1a1a bold"
row += f"[{fg} {bg}] {char} [/]│"
else: else:
row += f"[{bg}] [/]│" # Use dots for dark squares, spaces for light
is_light = (rank + file) % 2 == 1
row += " . |" if not is_light else " |"
row += f" {rank + 1}" row += f" {rank + 1}"
console.print(row) lines.append(row)
lines.append(" +---+---+---+---+---+---+---+---+")
if rank > 0:
console.print(f"{pad} ├───┼───┼───┼───┼───┼───┼───┼───┤")
console.print(f"{pad} └───┴───┴───┴───┴───┴───┴───┴───┘") lines.append(" a b c d e f g h")
console.print(f"{pad} a b c d e f g h") lines.append("")
console.print()
# Status panel # Center the board
board_width = 42
left_pad = max(0, (session.width - board_width) // 2)
pad = " " * left_pad
for line in lines:
session.write(pad + line + "\r\n")
# Use Rich only for the status panel (simpler)
# Simple status display
turn = "White ♔" if board.turn == chess.WHITE else "Black ♚" turn = "White ♔" if board.turn == chess.WHITE else "Black ♚"
turn_style = "bold white" if board.turn == chess.WHITE else "bold" session.write(pad + f" {turn} to move\r\n")
status_lines = []
status_lines.append(f"[{turn_style}]{turn} to move[/]")
if board.is_check(): if board.is_check():
status_lines.append("[red bold]⚠️ CHECK![/red bold]") session.write(pad + " *** CHECK! ***\r\n")
if move_history: if move_history:
last_moves = move_history[-3:] last_moves = move_history[-3:]
status_lines.append(f"[dim]Moves: {' '.join(last_moves)}[/dim]") session.write(pad + f" Moves: {' '.join(last_moves)}\r\n")
if status_msg: if status_msg:
status_lines.append(f"[yellow]{status_msg}[/yellow]") session.write(pad + f" {status_msg}\r\n")
status_msg = "" status_msg = ""
status_lines.append("") session.write("\r\n")
status_lines.append("[dim]Enter move (e.g. e2e4) [q]uit [r]esign[/dim]") session.write(pad + " Enter move (e.g. e2e4) | [q]uit | [r]esign\r\n")
session.write(pad + " Move: ")
status_panel = Panel( session.show_cursor()
"\n".join(status_lines),
box=ROUNDED,
border_style="blue",
width=min(50, session.width - 4),
title="[bold]Game Status[/bold]"
)
console.print(Align.center(status_panel))
# Show cursor for input # Show cursor for input
session.show_cursor() session.show_cursor()

View File

@@ -92,36 +92,34 @@ def test_game_status_render():
def test_chess_board_render(): def test_chess_board_render():
"""Test that chess board renders without errors.""" """Test that chess board renders without errors."""
output = StringIO() output = StringIO()
console = Console(file=output, width=80, height=24, force_terminal=True, color_system="truecolor")
PIECES = { PIECES = {
'K': '', 'Q': '', 'R': '', 'B': '', 'N': '', 'P': '', 'K': '', 'Q': '', 'R': '', 'B': '', 'N': '', 'P': '',
'k': '', 'q': '', 'r': '', 'b': '', 'n': '', 'p': '', 'k': '', 'q': '', 'r': '', 'b': '', 'n': '', 'p': '',
} }
# Render a simple board section # Simplified board rendering (plain text)
console.print(" a b c d e f g h") lines = []
console.print(" ┌───┬───┬───┬───┬───┬───┬───┬───┐") lines.append(" a b c d e f g h")
lines.append(" +---+---+---+---+---+---+---+---+")
# Render one rank with colored squares
row = "8 │"
for file in range(8):
is_light = (7 + file) % 2 == 1
if is_light:
bg = "on #769656"
else:
bg = "on #4a7c3f"
# Starting position pieces
pieces_row = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r']
char = PIECES.get(pieces_row[file], ' ')
row += f"[#1a1a1a bold {bg}] {char} [/]│"
# Render rank 8 with pieces
pieces_row = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r']
row = " 8 |"
for file, piece_char in enumerate(pieces_row):
char = PIECES.get(piece_char, '?')
row += f" {char} |"
row += " 8" row += " 8"
console.print(row) lines.append(row)
lines.append(" +---+---+---+---+---+---+---+---+")
for line in lines:
output.write(line + "\n")
rendered = output.getvalue() rendered = output.getvalue()
assert "a b c" in rendered assert "a b c" in rendered
assert "" in rendered # Black rook
assert "+---+" in rendered
def test_narrow_terminal_render(): def test_narrow_terminal_render():