-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmove.py
More file actions
executable file
·337 lines (288 loc) · 11.3 KB
/
move.py
File metadata and controls
executable file
·337 lines (288 loc) · 11.3 KB
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# License, blah.
import curses
from curses import wrapper
import random
import time
import argparse
import numpy as np
class ddd(object):
""" The ddd object (Needs a better name).
"""
def __init__(self):
self.screen = curses.initscr()
curses.noecho()
curses.cbreak()
self.width = self.screen.getmaxyx()[1]
self.height = self.screen.getmaxyx()[0]
self.encrypt = False
# The msg_list is a list of all the characters that make up what we
# are displaying.
# Each member of the list has:
# cur_x, cur_y The characters current x,y position.
# home_x, home_y The "home" position for that character.
# direction If falling, which way we are going.
# speed If falling (or rising) how fast
# msg, orig_msg The current and original value for the location
#
# We only support the ASCII character set, and of that, only the
# printable range (32-127).
self.msg_list = []
self.screen_mat = np.full((self.width, self.height), 0)
curses.curs_set(0)
curses.start_color()
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_YELLOW, -1)
curses.init_pair(2, curses.COLOR_MAGENTA, -1)
curses.init_pair(3, curses.COLOR_RED, -1)
curses.init_pair(4, curses.COLOR_BLUE, -1)
self.screen.clear()
def set_encrypt(self):
self.encrypt = True
def fill(self, filename):
""" Read in a file and build the mat matrix based on the file contents
This generates a new matrix to display after the import
"""
with open(filename) as f:
# We convert tab to eight spaces, and strip off newline and CR
content = f.readlines()
content = [x.replace('\t', ' ') for x in content]
content = [x.strip('\r\n') for x in content]
y = 0
for line in content:
x_max = min(self.width, len(line))
for x in range(x_max):
my_ch = ord(line[x])
if my_ch > 31 and my_ch < 127:
self.screen_mat[x][y] = my_ch
self.msg_list.append({'home_x': x, 'home_y': y,
'cur_x': x, 'cur_y': y,
'direction': 'down',
'speed': 0,
'msg': ord(line[x]),
'orig_msg': ord(line[x])})
y += 1
if y >= self.height:
break
def debug_show(self):
""" Dump half the current screen matrix to the screen, print the number
value for the character instead of the character itself
"""
for x in range(0, int(((self.width - 1) / 2) - 1)):
for y in range(0, self.height - 1):
if self.empty_space(x, y):
color = 0
else:
color = 1
self.screen.addstr(y, x * 2, str(self.screen_mat[x][y]),
curses.color_pair(color) | curses.A_BOLD)
def show(self):
""" Dump the current screen matrix to the screen
"""
for x in range(0, self.width - 1):
for y in range(0, self.height - 1):
# If desired, change empty_space color to something different
# to indicate this space can move. Useful for debugging
if self.empty_space(x, y):
color = 0
else:
color = 0
if self.screen_mat[x][y] >= 32:
my_chr = chr(self.screen_mat[x][y])
self.screen.addstr(y, x, my_chr,
curses.color_pair(color) |
curses.A_BOLD)
else:
self.screen.addstr(y, x, ' ',
curses.color_pair(color) |
curses.A_BOLD)
self.screen.refresh()
self.screen.timeout(30)
def key_press(self):
""" Check to see if a curses keypress was detected """
self.screen.nodelay(True)
return self.screen.getch()
def empty_space(self, cur_x, cur_y):
""" Check the 3x3 square around us to see if there is a space that is
not populated by anything
"""
min_x = max(0, cur_x - 1)
min_y = max(0, cur_y - 1)
max_x = min(self.width, cur_x + 2)
max_y = min(self.height, cur_y + 2)
for x in range(min_x, max_x):
for y in range(min_y, max_y):
# Just in case this location is a space, ignore it for
# consideration of a free space to move
if x == cur_x and y == cur_y:
continue
if self.screen_mat[x][y] <= 32:
return True
return False
def place_to_move(self, msg):
cur_x = msg['cur_x']
cur_y = msg['cur_y']
return self.empty_space(cur_x, cur_y)
def do_fall(self):
""" Fall characters in our message list.
We re-use the existing screen matrix, but we have to clear out a
space when we leave it, so someone else can move into it.
"""
for msg in self.msg_list:
cur_x = msg['cur_x']
cur_y = msg['cur_y']
direction = msg['direction']
if direction == 'down':
if cur_y + 1 < self.height - 1 \
and self.screen_mat[cur_x][cur_y + 1] <= 32:
new_y = cur_y + 1
msg['cur_y'] = new_y
msg['speed'] = msg['speed'] + 1
self.screen_mat[cur_x][new_y] = msg['msg']
self.screen_mat[cur_x][cur_y] = 0
elif cur_y == self.height - 1:
msg['directon'] = None
def move_uniq(self):
""" Move characters in our message list, but only if there is a nearby
unoccupied location.
We can re-use the existing matrix, but we have to clear out a
space when we leave it, so someone else can move into it.
"""
for msg in self.msg_list:
if self.encrypt and msg['msg'] != 32:
move_step = random.randrange(5)
msg['msg'] = max((msg['msg'] + move_step) % 127, 33)
# Move only so often, otherwise, just stay in place.
if self.place_to_move(msg) and random.randrange(2) == 0:
cur_x = msg['cur_x']
cur_y = msg['cur_y']
new_x = msg['cur_x'] + (random.randrange(-1, 2))
new_y = msg['cur_y'] + (random.randrange(-1, 2))
if new_x >= self.width:
new_x = self.width - 1
if new_x < 0:
new_x = 1
if new_y >= self.height:
new_y = self.height - 1
if new_y < 0:
new_y = 1
if self.screen_mat[new_x][new_y] <= 32:
msg['cur_x'] = new_x
msg['cur_y'] = new_y
self.screen_mat[new_x][new_y] = msg['msg']
self.screen_mat[cur_x][cur_y] = 0
else:
new_x = msg['cur_x']
new_y = msg['cur_y']
self.screen_mat[new_x][new_y] = msg['msg']
def move(self):
""" Move all characters in our message list
For both X and Y, we randomly add -1, 0, or 1 to each
This generates a new matrix to display after the move
"""
self.screen_mat = np.full((self.width, self.height), 0)
for msg in self.msg_list:
if self.encrypt:
msg['msg'] = max(msg['msg'] + 1 % 127, 32)
if random.randrange(5) == 0:
new_x = msg['cur_x'] + (random.randrange(-1, 2))
new_y = msg['cur_y'] + (random.randrange(-1, 2))
if new_x >= self.width:
new_x = self.width - 1
if new_x < 0:
new_x = 1
if new_y >= self.height:
new_y = self.height - 1
if new_y < 0:
new_y = 1
msg['cur_x'] = new_x
msg['cur_y'] = new_y
self.screen_mat[new_x][new_y] = msg['msg']
else:
new_x = msg['cur_x']
new_y = msg['cur_y']
self.screen_mat[new_x][new_y] = msg['msg']
def go_home(self):
""" Move everything on msg_list toward home.
Return the number of things moved, or the number of
things not yet home
This generates a new matrix to display after the move
"""
moved = 0
self.screen_mat = np.full((self.width, self.height), 0)
for msg in self.msg_list:
# Move the X home
if msg['cur_x'] < msg['home_x']:
msg['cur_x'] += 1
moved += 1
elif msg['cur_x'] > msg['home_x']:
msg['cur_x'] -= 1
moved += 1
# Move the Y home
if msg['cur_y'] < msg['home_y']:
msg['cur_y'] += 1
moved += 1
elif msg['cur_y'] > msg['home_y']:
msg['cur_y'] -= 1
moved += 1
new_x = msg['cur_x']
new_y = msg['cur_y']
self.screen_mat[new_x][new_y] = msg['msg']
# If we are home, and encrypt is on, decrypt
if moved == 0 and self.encrypt:
for msg in self.msg_list:
if msg['msg'] != msg['orig_msg']:
if msg['msg'] < msg['orig_msg']:
msg['msg'] = msg['msg'] + 1
else:
msg['msg'] = msg['msg'] - 1
return moved
def start_movement(stdscr, filename):
""" Main movement loop.
Here we decide what to do next, based on new or previous
key presses.
"""
my_dis = ddd()
my_dis.fill(filename)
my_dis.show()
st = 0.25
action = 0
while True:
command = 0
command = my_dis.key_press()
if command == ord('e'):
my_dis.set_encrypt()
elif command == ord('m'):
# Start moving
action = 1
elif command == ord('r'):
# Return home
action = 2
elif command == ord('p'):
# Pause where we are
action = 0
elif command == ord('f'):
# Fall to the bottom
action = 3
elif command == ord('s'):
# Single step (if paused)
my_dis.move_uniq()
elif command == ord('q'):
# quit
break
if action == 1:
my_dis.move_uniq()
elif action == 2:
my_dis.go_home()
elif action == 3:
my_dis.do_fall()
my_dis.show()
time.sleep(st)
if st > 0.0225:
st = st / 2
return
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('file', help='Filename to \
display and decay')
args = parser.parse_args()
wrapper(start_movement, args.file)