From 5191405e04a0c98298b5ae6cb626e0a8448442e8 Mon Sep 17 00:00:00 2001 From: Alex DeJarnatt Date: Fri, 20 Sep 2024 12:55:40 +0200 Subject: [PATCH] add a Grid helper class to assist with looking up clues --- puz.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++----- tests.py | 28 +++++++++++++++++++++++ tox.ini | 4 ++++ 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/puz.py b/puz.py index e77d176..15017a0 100644 --- a/puz.py +++ b/puz.py @@ -305,17 +305,22 @@ def has_rebus(self): return self.rebus().has_rebus() def rebus(self): - return self.helpers.setdefault('rebus', Rebus(self)) + if 'rebus' not in self.helpers: + self.helpers['rebus'] = Rebus(self) + return self.helpers['rebus'] def has_markup(self): return self.markup().has_markup() def markup(self): - return self.helpers.setdefault('markup', Markup(self)) + if 'markup' not in self.helpers: + self.helpers['markup'] = Markup(self) + return self.helpers['markup'] def clue_numbering(self): - numbering = DefaultClueNumbering(self.fill, self.clues, self.width, self.height) - return self.helpers.setdefault('clues', numbering) + if 'clues' not in self.helpers: + self.helpers['clues'] = DefaultClueNumbering(self.fill, self.clues, self.width, self.height) + return self.helpers['clues'] def blacksquare(self): return BLACKSQUARE2 if self.puzzletype == PuzzleType.Diagramless else BLACKSQUARE @@ -506,7 +511,10 @@ def __init__(self, grid, clues, width, height): 'clue': clues[c], 'clue_index': c, 'cell': i, - 'len': self.len_across(i) + 'row': self.row(i), + 'col': self.col(i), + 'len': self.len_across(i), + 'dir': 'across', }) c += 1 is_down = self.row(i) == 0 or is_blacksquare(grid[i - width]) @@ -516,7 +524,10 @@ def __init__(self, grid, clues, width, height): 'clue': clues[c], 'clue_index': c, 'cell': i, - 'len': self.len_down(i) + 'row': self.row(i), + 'col': self.col(i), + 'len': self.len_down(i), + 'dir': 'down' }) c += 1 if c > lastc: @@ -544,6 +555,50 @@ def len_down(self, index): return c + 1 +class Grid: + def __init__(self, grid, width, height): + self.grid = grid + self.width = width + self.height = height + assert len(self.grid) == self.width * self.height + + def get_cell(self, row, col): + return self.grid[self.get_cell_index(row, col)] + + def get_cell_index(self, row, col): + return row * self.width + col + + def get_range(self, row, col, length, dir='across'): + if dir == 'across': + return self.get_range_across(row, col, length) + elif dir == 'down': + return self.get_range_down(row, col, length) + else: + assert False, "dir not one of 'across' or 'down'" + + def get_range_across(self, row, col, length): + start = self.get_cell_index(row, col) + return self.grid[start:start+length] + + def get_range_down(self, row, col, length): + return [self.grid[self.get_cell_index(row + i, col)] for i in range(length)] + + def get_range_for_clue(self, clue): + return self.get_range(clue['row'], clue['col'], clue['len'], clue['dir']) + + def get_string(self, row, col, length, dir='across'): + return ''.join(self.get_range(row, col, length, dir)) + + def get_string_across(self, row, col, length): + return ''.join(self.get_range_across(row, col, length)) + + def get_string_down(self, row, col, length): + return ''.join(self.get_range_down(row, col, length)) + + def get_string_for_clue(self, clue): + return ''.join(self.get_range_for_clue(clue)) + + class Rebus: def __init__(self, puzzle): self.puzzle = puzzle diff --git a/tests.py b/tests.py index c525bb9..69ca47f 100755 --- a/tests.py +++ b/tests.py @@ -23,6 +23,34 @@ def test_clue_numbering(self): self.assertEqual(len(p.clues), len(clues.across) + len(clues.down)) self.assertTrue(len(p.clues) > 0) + a1 = clues.across[0] + self.assertEqual(a1['num'], 1) + self.assertEqual(a1['dir'], 'across') + self.assertEqual(a1['clue'], "Mary's pet") + self.assertEqual(a1['clue_index'], 0) + self.assertEqual(a1['cell'], 0) + self.assertEqual(a1['row'], 0) + self.assertEqual(a1['col'], 0) + self.assertEqual(a1['len'], 4) + + self.assertEqual(a1['clue'], p.clues[a1['clue_index']]) + + d1 = clues.down[0] + self.assertEqual(d1['num'], 1) + self.assertEqual(d1['dir'], 'down') + self.assertEqual(d1['clue'], "Hit high in the air") + self.assertEqual(d1['clue_index'], 1) + self.assertEqual(d1['cell'], 0) + self.assertEqual(d1['row'], 0) + self.assertEqual(d1['col'], 0) + self.assertEqual(d1['len'], 4) + + self.assertEqual(d1['clue'], p.clues[d1['clue_index']]) + + soln = puz.Grid(p.solution, p.width, p.height) + self.assertEqual('LAMB', soln.get_string_for_clue(a1)) + self.assertEqual('LOFT', soln.get_string_for_clue(d1)) + def test_diagramless_clue_numbering(self): p = puz.read('testfiles/nyt_diagramless.puz') clues = p.clue_numbering() diff --git a/tox.ini b/tox.ini index a85250b..6086093 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,10 @@ description = run unit tests commands = python tests.py +[testenv:unittest] +description = quick run unit tests without installing package +skip_install = true + [testenv:lint] description = run flake8 on source code commands =