-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathCharGrid.py
executable file
·296 lines (252 loc) · 9.05 KB
/
CharGrid.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
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
#!/usr/bin/env python3
# Copyright (c) 2008-11 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. It is provided for educational
# purposes and is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
"""
This module provides functionality for writing characters on a grid.
The module provides functions for adding horizontal and vertical lines,
and for (optionally filled) rectangles, and for (optionally boxed) text.
All char arguments must be strings of length 1; these are guarded by
assertions. If out of range row or column values are given, the
appropriate exception, RowRangeError or ColumnRangeError will
be raised---use the RangeError exception if you want to catch either.
>>> resize(14, 50)
>>> add_rectangle(0, 0, *get_size())
>>> add_vertical_line(5, 10, 13)
>>> add_vertical_line(2, 9, 12, "!")
>>> add_horizontal_line(3, 10, 20, "+")
>>> add_rectangle(0, 0, 5, 5, "%")
>>> add_rectangle(5, 7, 12, 40, "#", True)
>>> add_rectangle(7, 9, 10, 38, " ")
>>> add_text(8, 10, "This is the CharGrid module")
>>> add_text(1, 32, "Pleasantville", "@")
>>> add_rectangle(6, 42, 11, 46, fill=True)
>>> render(False)
%%%%%*********************************************
% % @@@@@@@@@@@@@@@ *
% % @Pleasantville@ *
% % ++++++++++ @@@@@@@@@@@@@@@ *
%%%%% *
* ################################# *
* ################################# **** *
* ## ## **** *
* ## This is the CharGrid module ## **** *
* ! ## ## **** *
* ! | ################################# **** *
* ! | ################################# *
* | *
**************************************************
"""
import subprocess
import sys
class RangeError(Exception): pass
class RowRangeError(RangeError): pass
class ColumnRangeError(RangeError): pass
_CHAR_ASSERT_TEMPLATE = ("char must be a single character: '{0}' "
"is too long")
_max_rows = 25
_max_columns = 80
_grid = []
_background_char = " "
if sys.platform.startswith("win"):
def clear_screen():
subprocess.call(["cmd.exe", "/C", "cls"])
else:
def clear_screen():
subprocess.call(["clear"])
clear_screen.__doc__ = """Clears the screen using the underlying \
window system's clear screen command"""
def char_at(row, column):
"""Returns the character at the given position
This is really just for debugging.
>>> char_at(0, 0)
'%'
>>> char_at(4, 11)
' '
>>> char_at(32, 24)
Traceback (most recent call last):
...
RowRangeError
"""
try:
return _grid[row][column]
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
def set_background(char=" "):
"""Sets the background character
>>> set_background("$")
>>> char_at(0, 0)
'%'
>>> char_at(4, 24)
'$'
>>> set_background("<>")
Traceback (most recent call last):
...
AssertionError: char must be a single character: '<>' is too long
>>> set_background(" ")
"""
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
global _background_char
old_background_char = _background_char
_background_char = char
for row in range(_max_rows):
for column in range(_max_columns):
if _grid[row][column] == old_background_char:
_grid[row][column] = _background_char
def add_vertical_line(column, row0, row1, char="|"):
"""Adds a vertical line to the grid using the given char
>>> add_vertical_line(5, 2, 10, "&")
>>> char_at(2, 5) == char_at(3, 5) == "&"
True
>>> add_vertical_line(85, 1, 2)
Traceback (most recent call last):
...
ColumnRangeError
"""
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
try:
for row in range(row0, row1):
_grid[row][column] = char
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
def add_horizontal_line(row, column0, column1, char="-"):
"""Adds a horizontal line to the grid using the given char
>>> add_horizontal_line(8, 20, 25, "=")
>>> char_at(8, 20) == char_at(8, 24) == "="
True
>>> add_horizontal_line(31, 11, 12)
Traceback (most recent call last):
...
RowRangeError
"""
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
try:
for column in range(column0, column1):
_grid[row][column] = char
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
def add_rectangle(row0, column0, row1, column1, char="*", fill=False):
"""Adds a rectangle to the grid using the given char for the
outline
If filled is True, fills the rectangle with the given char.
>>> add_rectangle(10, 30, 14, 35, "^", True)
>>> char_at(10, 30) == char_at(12, 32) == "^"
True
>>> add_rectangle(10, 30, 14, 35, "!")
>>> char_at(10, 30) == char_at(13, 34) == "!"
True
>>> add_rectangle(10, 30, 14, 95, "x")
Traceback (most recent call last):
...
ColumnRangeError
>>> add_rectangle(10, 30, 14, 95)
Traceback (most recent call last):
...
ColumnRangeError
"""
if not fill:
add_vertical_line(column0, row0, row1, char)
add_vertical_line(column1 - 1, row0, row1, char)
add_horizontal_line(row0, column0, column1, char)
add_horizontal_line(row1 - 1, column0, column1, char)
else:
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
try:
for row in range(row0, row1):
for column in range(column0, column1):
_grid[row][column] = char
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
def add_text(row, column, text, char=None):
"""Adds a string of text to the grid
If char is not None, draws a box around the text with
the given char. The box's top-left corner is one row above
and one column left of the given row and column and extends
to encompass the text.
>>> add_text(6, 15, "Alpha Beta")
>>> char_at(6, 15) == "A"
True
>>> char_at(6, 19) == char_at(6, 24) == "a"
True
>>> add_text(11, 22, "Gamma", ":")
>>> char_at(12, 23) == "G"
True
>>> char_at(12, 24) == char_at(12, 27) == "a"
True
>>> char_at(11, 24) == char_at(13, 27) == ":"
True
>>> add_text(10, 89, "Delta")
Traceback (most recent call last):
...
ColumnRangeError
>>> add_text(110, 8, "Epsison", "O")
Traceback (most recent call last):
...
RowRangeError
"""
try:
if char is None:
for i, column in enumerate(range(column,
column + len(text))):
_grid[row][column] = text[i]
else:
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
row0 = row
row1 = row0 + 3
column0 = column
column1 = column0 + len(text) + 2
add_rectangle(row0, column0, row1, column1, char)
row = row0 + 1
for i, column in enumerate(range(column0 + 1,
column1 - 1)):
_grid[row][column] = text[i]
except IndexError:
if not 0 <= row <= _max_rows:
raise RowRangeError()
raise ColumnRangeError()
def render(clear=True):
"""Renders the grid onto the console and clears the grid
"""
if clear:
clear_screen()
for row in range(_max_rows):
print("".join(_grid[row]))
for column in range(_max_columns):
_grid[row][column] == _background_char
def get_size():
"""Returns the size of the grid
>>> get_size()
(14, 50)
"""
return _max_rows, _max_columns
def resize(max_rows, max_columns, char=None):
"""Changes the size of the grid, wiping out the contents and
changing the background if the background char is not None
"""
assert max_rows > 0 and max_columns > 0, "too small"
global _grid, _max_rows, _max_columns, _background_char
if char is not None:
assert len(char) == 1, _CHAR_ASSERT_TEMPLATE.format(char)
_background_char = char
_max_rows = max_rows
_max_columns = max_columns
_grid = [[_background_char for column in range(_max_columns)]
for row in range(_max_rows)]
resize(_max_rows, _max_columns)
if __name__ == "__main__":
import doctest
doctest.testmod()