Skip to content

Commit c3d74e9

Browse files
committed
Serialize snake positions more efficiently
1 parent 0c459f9 commit c3d74e9

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

snakeng/__main__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
def keypress_event(e):
2424
newdir = dirmap.get(e.name, None)
25-
if newdir is not None:
25+
if newdir is None:
26+
if e.name == 'p':
27+
runtime_data['paused'] = not runtime_data['paused']
28+
else:
2629
runtime_data['last_direction'] = newdir
2730

2831

snakeng/snake.py

+100-5
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,110 @@ def __eq__(self, other):
6363
def __neq__(self, other):
6464
return not self.__eq__(other)
6565

66+
def __add__(self, other):
67+
return Position(x=self.x + other.x, y=self.y + other.y)
68+
69+
def __sub__(self, other):
70+
return Position(x=self.x - other.x, y=self.y - other.y)
71+
6672
def serialize(self):
67-
return {'x': self.x, 'y': self.y}
73+
return [self.x, self.y]
6874

6975
def deserialize(self, attrs):
70-
self.x = attrs['x']
71-
self.y = attrs['y']
76+
self.x = attrs[0]
77+
self.y = attrs[1]
7278
return self
7379

7480

81+
def _direction_between_points(startpos, endpos, allow_jumps=False):
82+
ret = None
83+
diff = endpos - startpos
84+
85+
if (not allow_jumps) and ((abs(diff.x) > 1) or (abs(diff.y) > 1)):
86+
return None
87+
88+
if diff.x < 0:
89+
ret = Direction.LEFT
90+
elif diff.x > 0:
91+
ret = Direction.RIGHT
92+
elif diff.y < 0:
93+
ret = Direction.DOWN
94+
elif diff.y > 0:
95+
ret = Direction.UP
96+
97+
return ret
98+
99+
def _serialize_snake_positions(positions):
100+
"""
101+
Compress all snake segment positions to a list of only the head, tail and corner positions
102+
"""
103+
ret = []
104+
chunk = []
105+
last_pos = None
106+
last_direction = None
107+
108+
for pos in positions:
109+
if last_pos is None:
110+
chunk.append([pos.x, pos.y])
111+
else:
112+
direction = _direction_between_points(last_pos, pos)
113+
if direction is None:
114+
# Wall wrap
115+
if not ((chunk[-1][0] == last_pos.x) and (chunk[-1][1] == last_pos.y)):
116+
chunk.append((last_pos.x, last_pos.y))
117+
118+
ret.append(chunk)
119+
chunk = [(pos.x, pos.y)]
120+
last_pos = pos
121+
else:
122+
if last_direction is not None:
123+
if last_direction != direction:
124+
chunk.append((last_pos.x, last_pos.y))
125+
126+
last_direction = direction
127+
128+
last_pos = pos
129+
130+
chunk.append((positions[-1].x, positions[-1].y))
131+
ret.append(chunk)
132+
133+
return ret
134+
135+
def _draw_line(startpos, endpos):
136+
diff = endpos - startpos
137+
direction = _direction_between_points(startpos, endpos, allow_jumps=True)
138+
move = _MOVEMAP[direction]
139+
140+
ret = [startpos]
141+
newpos = startpos
142+
while newpos != endpos:
143+
newpos = Position(x=newpos.x + move[0], y=newpos.y + move[1])
144+
ret.append(newpos)
145+
146+
return ret
147+
148+
def _deserialize_snake_positions(attrs):
149+
"""
150+
Convert a serialized list of snake head, tail and corner positions, to a full
151+
list of snake segment positions
152+
"""
153+
ret = []
154+
for chunk in attrs:
155+
for i in range(len(chunk[:-1])):
156+
x = chunk[i][0]
157+
y = chunk[i][1]
158+
nextx = chunk[i + 1][0]
159+
nexty = chunk[i + 1][1]
160+
161+
newps = _draw_line(Position(x=x, y=y), Position(x=nextx, y=nexty))
162+
if ret and (newps[0] == ret[-1]) and (len(newps) > 2):
163+
newps.pop(0)
164+
165+
ret.extend(newps)
166+
167+
return ret
168+
169+
75170
@dataclass
76171
class SnakeGameState(object):
77172
"""
@@ -112,7 +207,7 @@ def serialize(self):
112207
return {
113208
'area_width': self.area_width,
114209
'area_height': self.area_height,
115-
'snake_segments': [x.serialize() for x in self.snake_segments],
210+
'snake_segments': _serialize_snake_positions(self.snake_segments),
116211
'snake_direction': self.snake_direction,
117212
'score': self.score,
118213
'apple_position': self.apple_position.serialize(),
@@ -128,7 +223,7 @@ def deserialize(self, attrs):
128223
"""
129224
self.area_width = attrs['area_width']
130225
self.area_height = attrs['area_height']
131-
self.snake_segments = [Position().deserialize(x) for x in attrs['snake_segments']]
226+
self.snake_segments = _deserialize_snake_positions(attrs['snake_segments'])
132227
self.snake_direction = attrs['snake_direction']
133228
self.score = attrs['score']
134229
self.apple_position = Position().deserialize(attrs['apple_position'])

0 commit comments

Comments
 (0)