mirror of
https://github.com/ghndrx/shellmate.git
synced 2026-02-10 06:45:02 +00:00
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:
@@ -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:
|
lines.append(" a b c d e f g h")
|
||||||
console.print(f"{pad} ├───┼───┼───┼───┼───┼───┼───┼───┤")
|
lines.append("")
|
||||||
|
|
||||||
console.print(f"{pad} └───┴───┴───┴───┴───┴───┴───┴───┘")
|
# Center the board
|
||||||
console.print(f"{pad} a b c d e f g h")
|
board_width = 42
|
||||||
console.print()
|
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 = "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()
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
Reference in New Issue
Block a user