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():
nonlocal status_msg
writer = ProcessWriter(session)
console = Console(file=writer, width=session.width, height=session.height, force_terminal=True)
session.clear()
# Calculate centering
board_width = 42
left_pad = max(0, (session.width - board_width) // 2)
pad = " " * left_pad
# Title
console.print()
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} ┌───┬───┬───┬───┬───┬───┬───┬───┐")
# Simple text-based board that works reliably over SSH
lines = []
lines.append("")
lines.append(" ♔ SHELLMATE CHESS ♔")
lines.append("")
lines.append(" a b c d e f g h")
lines.append(" +---+---+---+---+---+---+---+---+")
for rank in range(7, -1, -1):
row = f"{pad}{rank + 1} "
row = f" {rank + 1} |"
for file in range(8):
square = chess.square(file, rank)
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:
char = PIECES.get(piece.symbol(), '?')
# White pieces in white/cream, black pieces in dark
if piece.color == chess.WHITE:
fg = "#ffffff bold"
row += f" {char} |"
else:
fg = "#1a1a1a bold"
row += f"[{fg} {bg}] {char} [/]│"
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}"
console.print(row)
lines.append(row)
lines.append(" +---+---+---+---+---+---+---+---+")
if rank > 0:
console.print(f"{pad} ├───┼───┼───┼───┼───┼───┼───┼───┤")
lines.append(" a b c d e f g h")
lines.append("")
console.print(f"{pad} └───┴───┴───┴───┴───┴───┴───┴───┘")
console.print(f"{pad} a b c d e f g h")
console.print()
# Center the board
board_width = 42
left_pad = max(0, (session.width - board_width) // 2)
pad = " " * left_pad
# Status panel
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_style = "bold white" if board.turn == chess.WHITE else "bold"
status_lines = []
status_lines.append(f"[{turn_style}]{turn} to move[/]")
session.write(pad + f" {turn} to move\r\n")
if board.is_check():
status_lines.append("[red bold]⚠️ CHECK![/red bold]")
session.write(pad + " *** CHECK! ***\r\n")
if move_history:
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:
status_lines.append(f"[yellow]{status_msg}[/yellow]")
session.write(pad + f" {status_msg}\r\n")
status_msg = ""
status_lines.append("")
status_lines.append("[dim]Enter move (e.g. e2e4) [q]uit [r]esign[/dim]")
status_panel = Panel(
"\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))
session.write("\r\n")
session.write(pad + " Enter move (e.g. e2e4) | [q]uit | [r]esign\r\n")
session.write(pad + " Move: ")
session.show_cursor()
# Show cursor for input
session.show_cursor()

View File

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