Added responsiveness to board layout

This commit is contained in:
markmental 2026-03-18 21:10:30 -04:00
commit 4b4ab92d04

View file

@ -817,6 +817,64 @@ TOP_Y = 1
COL_Y = 6 COL_Y = 6
@dataclass(frozen=True)
class BoardLayout:
"""Computed horizontal positions for the current terminal width."""
freecell_xs: Tuple[int, ...]
foundation_xs: Tuple[int, ...]
tableau_xs: Tuple[int, ...]
freecells_label_x: int
foundations_label_x: int
tableau_label_x: int
def spread_positions(count: int, start_x: int, end_x: int) -> Tuple[int, ...]:
"""Evenly spread fixed-width slots between two inclusive bounds."""
if count <= 1:
return (start_x,)
span = max(0, end_x - start_x)
step = span / (count - 1)
positions = []
for index in range(count):
positions.append(int(round(start_x + step * index)))
return tuple(positions)
def compute_board_layout(max_x: int) -> BoardLayout:
"""Compute a centered, width-aware board layout."""
left_margin = 2
right_margin = max(left_margin, max_x - CARD_W - 2)
if max_x < 72:
top_start = left_margin
top_end = max(top_start, max_x - CARD_W - 2)
top_positions = spread_positions(8, top_start, top_end)
freecell_xs = top_positions[:4]
foundation_xs = top_positions[4:]
else:
center_gap = max(10, min(20, max_x // 6))
left_end = max(left_margin, (max_x // 2) - (center_gap // 2) - CARD_W)
right_start = min(right_margin, (max_x // 2) + (center_gap // 2))
freecell_xs = spread_positions(4, left_margin, left_end)
foundation_xs = spread_positions(4, right_start, right_margin)
tableau_xs = spread_positions(8, left_margin, right_margin)
freecells_label_x = freecell_xs[0]
foundations_label_x = foundation_xs[0]
tableau_label_x = tableau_xs[0]
return BoardLayout(
freecell_xs=freecell_xs,
foundation_xs=foundation_xs,
tableau_xs=tableau_xs,
freecells_label_x=freecells_label_x,
foundations_label_x=foundations_label_x,
tableau_label_x=tableau_label_x,
)
def init_colors() -> None: def init_colors() -> None:
"""Initialize curses color pairs.""" """Initialize curses color pairs."""
curses.start_color() curses.start_color()
@ -859,14 +917,24 @@ def draw_card_label(card: Optional[Card]) -> str:
return card.short_name() return card.short_name()
def draw_top_row(stdscr, game: FreeCellGame) -> None: def draw_top_row(stdscr, game: FreeCellGame, layout: BoardLayout) -> None:
"""Draw freecells and foundations.""" """Draw freecells and foundations."""
stdscr.addstr(TOP_Y, 2, "Free Cells", curses.color_pair(1) | curses.A_BOLD) stdscr.addstr(
stdscr.addstr(TOP_Y, 40, "Foundations", curses.color_pair(1) | curses.A_BOLD) TOP_Y,
layout.freecells_label_x,
"Free Cells",
curses.color_pair(1) | curses.A_BOLD,
)
stdscr.addstr(
TOP_Y,
layout.foundations_label_x,
"Foundations",
curses.color_pair(1) | curses.A_BOLD,
)
# Freecells at indexes 0..3 # Freecells at indexes 0..3
for i in range(4): for i in range(4):
x = 2 + i * (CARD_W + 1) x = layout.freecell_xs[i]
selected = game.cursor_zone == "top" and game.cursor_index == i selected = game.cursor_zone == "top" and game.cursor_index == i
hinted = game.hinted_freecell(i) hinted = game.hinted_freecell(i)
attr = ( attr = (
@ -891,7 +959,7 @@ def draw_top_row(stdscr, game: FreeCellGame) -> None:
# Foundations at indexes 4..7 in cursor space # Foundations at indexes 4..7 in cursor space
for i in range(4): for i in range(4):
x = 40 + i * (CARD_W + 1) x = layout.foundation_xs[i]
selected = game.cursor_zone == "top" and game.cursor_index == i + 4 selected = game.cursor_zone == "top" and game.cursor_index == i + 4
hinted = game.hinted_foundation(i) hinted = game.hinted_foundation(i)
attr = ( attr = (
@ -923,14 +991,19 @@ def draw_top_row(stdscr, game: FreeCellGame) -> None:
) )
def draw_columns(stdscr, game: FreeCellGame) -> None: def draw_columns(stdscr, game: FreeCellGame, layout: BoardLayout) -> None:
"""Draw the tableau columns.""" """Draw the tableau columns."""
stdscr.addstr(COL_Y - 1, 2, "Tableau", curses.color_pair(1) | curses.A_BOLD) stdscr.addstr(
COL_Y - 1,
layout.tableau_label_x,
"Tableau",
curses.color_pair(1) | curses.A_BOLD,
)
max_height = max((len(col) for col in game.columns), default=0) max_height = max((len(col) for col in game.columns), default=0)
for col_idx in range(8): for col_idx in range(8):
x = 2 + col_idx * 9 x = layout.tableau_xs[col_idx]
selected = game.cursor_zone == "bottom" and game.cursor_index == col_idx selected = game.cursor_zone == "bottom" and game.cursor_index == col_idx
hinted_dest = game.hinted_column_destination(col_idx) hinted_dest = game.hinted_column_destination(col_idx)
header_attr = ( header_attr = (
@ -1042,6 +1115,7 @@ def render(stdscr, game: FreeCellGame) -> None:
"""Redraw the full screen.""" """Redraw the full screen."""
stdscr.erase() stdscr.erase()
max_y, max_x = stdscr.getmaxyx() max_y, max_x = stdscr.getmaxyx()
layout = compute_board_layout(max_x)
title = "mcfreecell 0.1" title = "mcfreecell 0.1"
if len(title) > max_x - 4: if len(title) > max_x - 4:
@ -1056,10 +1130,15 @@ def render(stdscr, game: FreeCellGame) -> None:
) )
if game.visual_mode: if game.visual_mode:
stdscr.addstr(0, 30, "-- VISUAL --", curses.color_pair(5) | curses.A_BOLD) visual_text = "-- VISUAL --"
visual_x = min(max_x - len(visual_text) - 2, title_x + len(title) + 3)
if visual_x > title_x + len(title):
stdscr.addstr(
0, visual_x, visual_text, curses.color_pair(5) | curses.A_BOLD
)
draw_top_row(stdscr, game) draw_top_row(stdscr, game, layout)
draw_columns(stdscr, game) draw_columns(stdscr, game, layout)
draw_held(stdscr, game, max_y) draw_held(stdscr, game, max_y)
draw_status(stdscr, game, max_y) draw_status(stdscr, game, max_y)
draw_help(stdscr, max_y) draw_help(stdscr, max_y)