-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcrossmath.py
173 lines (154 loc) · 5.81 KB
/
crossmath.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import random
from typing import Tuple
from expression import Expression
from expression_map import (
ExpressionMap,
ExpressionItem,
Direction,
)
from operator_factory import OperatorFactory
from resolver.expression_resolver import (
ExpressionResolver,
)
from number_factory import NumberFactory
from resolver.resolver_exceptions import ExpressionResolverException
class DeadPoints:
"""
Dead points storage
"""
def __init__(self):
self._set: set[Tuple] = set()
def append(self, *args):
self._set.add(tuple(args))
def exists(self, *args) -> bool:
return tuple(args) in self._set
def clear(self):
self._set.clear()
class CrossMath:
def __init__(
self,
exp_map: ExpressionMap,
number_factory: NumberFactory,
operator_factory: OperatorFactory,
expression_resolver: ExpressionResolver,
):
self._map = exp_map
self._number_factory = number_factory
self._expression_resolver = expression_resolver
self._dead_points = DeadPoints()
self._operator_factory = operator_factory
def _find_potential_positions(self) -> list[Tuple[int, int]]:
for point in self._map.get_all_operand_points():
yield point
def _check_expression_frame(
self, x: int, y: int, direction: Direction, length: int
) -> bool:
if direction.is_horizontal():
if x > 0 and self._map.get(x - 1, y) is not None:
return False
if (
x + length + 1 < self._map.width()
and self._map.get(x + length + 1, y) is not None
):
return False
elif direction.is_vertical():
if y > 0 and self._map.get(x, y - 1) is not None:
return False
if (
y + length + 1 < self._map.height()
and self._map.get(x, y + length + 1) is not None
):
return False
else:
raise ValueError(f"Not supported direction: {direction}")
return True
def _check_x_y_overflow(self, x: int, y: int, length: int) -> bool:
if x < 0:
return False
elif x + length >= self._map.width():
return False
elif y < 0:
return False
elif y + length >= self._map.height():
return False
return True
def _find_potential_values(
self,
potential_positions: list[Tuple[int, int]],
) -> list[Tuple[Direction, int, int, list]]:
max_expression_length = max(Expression.SUPPORTED_LENGTHS)
for next_position in potential_positions:
x, y = next_position
for direction in Direction.all():
for expression_offset in range(0, -max_expression_length - 1, -2):
values_x_offset = (
0 if direction.is_vertical() else expression_offset
)
values_y_offset = (
0 if direction.is_horizontal() else expression_offset
)
values_x = x + values_x_offset
values_y = y + values_y_offset
for expression_length in Expression.SUPPORTED_LENGTHS:
if self._dead_points.exists(
values_x, values_y, direction, expression_length
):
continue
if not self._check_x_y_overflow(
values_x, values_y, expression_length
):
continue
if not self._check_expression_frame(
values_x, values_y, direction, expression_length
):
continue
values = self._map.get_values(
values_x, values_y, direction, expression_length
)
if all([value is not None for value in values]):
# already filled
continue
yield (
direction,
values_x,
values_y,
values,
)
def _init_generate(self):
expression = self._expression_resolver.resolve(Expression())
if expression is None:
raise Exception("No expression found")
direction = random.choice([Direction.HORIZONTAL, Direction.VERTICAL])
item = ExpressionItem(
2,
2,
direction,
expression,
)
self._map.put(item)
def generate(self):
self._dead_points.clear()
self._init_generate()
latest_version = None
while latest_version != self._map.get_version():
latest_version = self._map.get_version()
potential_values = self._find_potential_values(
potential_positions=self._find_potential_positions(),
)
pvs = list(potential_values)
random.shuffle(pvs)
for desc in pvs:
direction, _x, _y, values = desc
try:
expression = self._expression_resolver.resolve(
Expression.from_list(values)
)
except ExpressionResolverException as e:
print(e)
self._dead_points.append(_x, _y, direction, len(values))
continue
expression_item = ExpressionItem(_x, _y, direction, expression)
self._map.put(expression_item)
break
def print(self):
self._map.print(number_factory=self._number_factory)