Add timer to legacy version

This commit is contained in:
markmental 2026-03-22 22:42:37 -04:00
commit 6ca7417011
2 changed files with 188 additions and 1 deletions

View file

@ -25,6 +25,7 @@ q : Quit
"""
import curses
import time
try:
import random
@ -168,6 +169,14 @@ def pad_center(text, width):
return (" " * left) + text + (" " * right)
def format_elapsed_time(seconds):
if seconds < 0:
seconds = 0
minutes = int(seconds / 60)
remaining = seconds % 60
return "%02d:%02d" % (minutes, remaining)
def rounded_position(start_x, span, index, steps):
if steps <= 0:
return start_x
@ -273,10 +282,30 @@ class FreeCellGame:
self.cursor_zone = "bottom"
self.cursor_index = 0
self.status = "Arrows move, v selects, ? hints."
self.start_time = time.time()
self.finished_time_seconds = None
self.needs_full_repaint = 0
self._deal_new_game()
def request_full_repaint(self):
self.needs_full_repaint = 1
def elapsed_time_seconds(self):
if self.finished_time_seconds is not None:
return self.finished_time_seconds
elapsed = int(time.time() - self.start_time)
if elapsed < 0:
elapsed = 0
if self.is_won():
self.finished_time_seconds = elapsed
return self.finished_time_seconds
return elapsed
def clear_hint(self):
had_hint = self.hint_move is not None
self.hint_move = None
if had_hint:
self.request_full_repaint()
def _deal_new_game(self):
deck = []
@ -612,9 +641,11 @@ class FreeCellGame:
if not moves:
self.hint_move = None
self.status = "Hint: no obvious legal move found."
self.request_full_repaint()
return
self.hint_move = moves[0]
self.status = "Hint: %s." % self.describe_hint_move(self.hint_move)
self.request_full_repaint()
def hinted_freecell(self, index):
if self.hint_move is None:
@ -734,6 +765,7 @@ class FreeCellGame:
moving_count,
self.cursor_index + 1,
)
self.request_full_repaint()
self.exit_visual_mode(None)
return
if self.cursor_index < 4:
@ -748,6 +780,7 @@ class FreeCellGame:
card.short_name(),
self.cursor_index + 1,
)
self.request_full_repaint()
return
self.status = "Cannot pick up directly from foundations."
@ -761,6 +794,7 @@ class FreeCellGame:
self.freecells[self.held.source_index] = cards[0]
self.held = None
self.clear_hint()
self.request_full_repaint()
def drop(self):
if self.held is None:
@ -784,6 +818,7 @@ class FreeCellGame:
self.cursor_index + 1,
)
self.held = None
self.request_full_repaint()
else:
self.status = "Illegal move for tableau column."
return
@ -799,6 +834,7 @@ class FreeCellGame:
self.cursor_index + 1,
)
self.held = None
self.request_full_repaint()
else:
self.status = "That free cell is occupied."
return
@ -811,6 +847,7 @@ class FreeCellGame:
foundation.append(moving_top)
self.status = "Moved %s to foundation." % moving_top.short_name()
self.held = None
self.request_full_repaint()
else:
self.status = "Illegal move for foundation."
@ -833,6 +870,7 @@ class FreeCellGame:
self.clear_hint()
self.freecells[free_idx] = col.pop()
self.status = "Moved card to free cell %d." % (free_idx + 1)
self.request_full_repaint()
return
if self.cursor_zone == "top" and self.cursor_index < 4:
self.status = "Already on a free cell."
@ -870,6 +908,7 @@ class FreeCellGame:
else:
self.freecells[source[1]] = None
self.status = "Moved %s to foundation." % card.short_name()
self.request_full_repaint()
def is_won(self):
total = 0
@ -991,6 +1030,8 @@ def draw_top_row(stdscr, game, layout):
index = 0
while index < 4:
x = layout.freecell_xs[index]
safe_addstr(stdscr, TOP_Y + 1, x - 1, " ", pair_attr(1))
safe_addnstr(stdscr, TOP_Y + 1, x, " " * CARD_W, CARD_W, pair_attr(1))
selected = game.cursor_zone == "top" and game.cursor_index == index
hinted = game.hinted_freecell(index)
marker = " "
@ -1019,6 +1060,8 @@ def draw_top_row(stdscr, game, layout):
index = 0
while index < 4:
x = layout.foundation_xs[index]
safe_addstr(stdscr, TOP_Y + 1, x - 1, " ", pair_attr(1))
safe_addnstr(stdscr, TOP_Y + 1, x, " " * CARD_W, CARD_W, pair_attr(1))
selected = game.cursor_zone == "top" and game.cursor_index == index + 4
hinted = game.hinted_foundation(index)
marker = " "
@ -1080,6 +1123,8 @@ def draw_columns(stdscr, game, layout):
col_idx = 0
while col_idx < 8:
x = layout.tableau_xs[col_idx]
safe_addstr(stdscr, COL_Y, x - 1, " ", pair_attr(1))
safe_addnstr(stdscr, COL_Y, x, " " * CARD_W, CARD_W, pair_attr(1))
selected = game.cursor_zone == "bottom" and game.cursor_index == col_idx
hinted_dest = game.hinted_column_destination(col_idx)
header_label = str(col_idx + 1)
@ -1093,6 +1138,15 @@ def draw_columns(stdscr, game, layout):
header_attr = pair_attr(1)
draw_box(stdscr, COL_Y, x, CARD_W, header_label, header_attr)
clear_y = COL_Y + 1
clear_limit = COL_Y + 8 + max_height
while clear_y < clear_limit:
safe_addstr(stdscr, clear_y, x - 1, " ", pair_attr(1))
safe_addnstr(
stdscr, clear_y, x + 1, " " * (CARD_W - 2), CARD_W - 2, pair_attr(1)
)
clear_y = clear_y + 1
column = game.columns[col_idx]
if not column:
safe_addstr(stdscr, COL_Y + 1, x + 2, ".", pair_attr(1))
@ -1215,6 +1269,7 @@ def render(stdscr, game):
stdscr.erase()
max_y, max_x = stdscr.getmaxyx()
layout = compute_board_layout(max_x)
elapsed_text = "Time: %s" % format_elapsed_time(game.elapsed_time_seconds())
title = "mcfreecell 1.5.2"
if len(title) > max_x - 4:
title = "mcfreecell"
@ -1229,6 +1284,18 @@ def render(stdscr, game):
max_x - title_x - 1,
pair_attr(1) | text_attr("A_BOLD"),
)
timer_x = max_x - len(elapsed_text) - 2
if timer_x < 2:
timer_x = 2
if timer_x > title_x + len(title):
safe_addnstr(
stdscr,
0,
timer_x,
elapsed_text,
max_x - timer_x - 1,
pair_attr(1) | text_attr("A_BOLD"),
)
if game.visual_mode:
visual_text = "-- VISUAL --"
visual_x = title_x + len(title) + 3
@ -1242,7 +1309,10 @@ def render(stdscr, game):
draw_status(stdscr, game, max_y, max_x)
draw_help(stdscr, max_y, max_x)
if game.is_won():
safe_addstr(stdscr, 3, 2, "You won.", pair_attr(5) | text_attr("A_BOLD"))
win_text = "You won in %s." % format_elapsed_time(game.elapsed_time_seconds())
safe_addnstr(
stdscr, 3, 2, win_text, max_x - 4, pair_attr(5) | text_attr("A_BOLD")
)
stdscr.refresh()
@ -1332,6 +1402,8 @@ def handle_key(game, ch):
def curses_main(stdscr):
idle_ticks = 0
poll_delay = 0
if hasattr(curses, "curs_set"):
try:
curses.curs_set(0)
@ -1342,11 +1414,65 @@ def curses_main(stdscr):
stdscr.keypad(1)
except:
pass
if hasattr(stdscr, "timeout"):
try:
stdscr.timeout(100)
except:
pass
elif hasattr(stdscr, "nodelay"):
try:
stdscr.nodelay(1)
poll_delay = 1
except:
poll_delay = 0
init_colors()
game = FreeCellGame()
while 1:
if game.needs_full_repaint:
game.needs_full_repaint = 0
if hasattr(stdscr, "redrawwin"):
try:
stdscr.redrawwin()
except:
pass
elif hasattr(stdscr, "touchwin"):
try:
stdscr.touchwin()
except:
pass
elif hasattr(stdscr, "clearok"):
try:
stdscr.clearok(1)
except:
pass
render(stdscr, game)
ch = stdscr.getch()
if ch == -1:
idle_ticks = idle_ticks + 1
if idle_ticks >= 5:
idle_ticks = 0
if hasattr(stdscr, "redrawwin"):
try:
stdscr.redrawwin()
except:
pass
elif hasattr(stdscr, "touchwin"):
try:
stdscr.touchwin()
except:
pass
elif hasattr(stdscr, "clearok"):
try:
stdscr.clearok(1)
except:
pass
if poll_delay:
try:
time.sleep(0.1)
except:
pass
continue
idle_ticks = 0
if not handle_key(game, ch):
break