-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathentry.py
107 lines (83 loc) · 3.43 KB
/
entry.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""Class representing a crossword clue."""
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional, Tuple, List, Dict
from word_filler import WordFiller
from square import Square
class Direction(Enum):
ACROSS = 'A'
DOWN = 'D'
@dataclass
class Entry:
index: int
direction: Direction
row_in_grid: int
col_in_grid: int
answer_length: int
clue: Optional[str] = None
squares: List[Square] = None
cached_num_matches: Dict[str, int] = field(default_factory=dict)
# returns True if the current entry shares any squares
# with the passed-in Entry, false otherwise.
def intersects_with(self, entry: 'Entry') -> bool:
common_squares = set(self._get_square_indices()).\
intersection(set(entry._get_square_indices()))
if len(common_squares) == 0:
print(f"{entry.index_str()} does not intersect with {self.index_str()}")
return len(common_squares) > 0
def get_current_hint(self) -> str:
current_hint = ""
for sq in self.squares:
current_hint += sq.letter if sq.letter is not None else "."
return current_hint
def is_complete(self) -> bool:
for sq in self.squares:
if sq.letter is None:
return False
return True
def get_possible_matches(self, word_filler: WordFiller) -> List[str]:
current_hint = self.get_current_hint()
return word_filler.get_possible_words_for_hint(current_hint)
def get_num_possible_matches(self, word_filler: WordFiller):
current_hint = self.get_current_hint()
if current_hint not in self.cached_num_matches:
self.cached_num_matches[current_hint] = len(self.get_possible_matches(word_filler))
return self.cached_num_matches[current_hint]
# Uses a heuristic to determine the priority of the given entry during
# puzzle filling.
def get_fill_priority(self, word_filler: WordFiller) -> int:
# no need to fill if the entry is already filled
if self.is_complete():
return 0
num_possible_matches = self.get_num_possible_matches(word_filler)
# no need to fill if the entry cannot be filled
if num_possible_matches == 0:
return 0
return max(0, 300 - num_possible_matches*10) + self.answer_length
def _get_square_indices(self) -> List[Tuple[int, int]]:
r = self.row_in_grid
c = self.col_in_grid
square_indices = []
if self.direction == Direction.ACROSS:
for i in range(self.answer_length):
square_indices.append((r, c + i))
else:
for i in range(self.answer_length):
square_indices.append((r + i, c))
return square_indices
def index_str(self) -> str:
return f"{self.index}{self.direction.value}"
def clue_str(self) -> str:
return f"{self.index_str()}: {self.clue} ({self.get_current_hint()})"
def unmarshal_clue_str(self, clue_str: str) -> None:
components1 = clue_str.split(":", 1)
clue_str_without_index = components1[1]
components2 = clue_str_without_index.split("(")
clue_only = "(".join(components2[:-1]).strip()
self.clue = clue_only
@staticmethod
def index_from_clue_str(clue_str: str) -> str:
components = clue_str.split(":", 1)
return components[0]
def __hash__(self) -> int:
return hash(self.index_str())