forked from asweigart/the-big-book-of-small-python-projects
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathducklings.py
210 lines (174 loc) · 6.52 KB
/
ducklings.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
"""Duckling Screensaver, by Al Sweigart [email protected]
A screensaver of many many ducklings.
>" ) =^^) (``= ("= >") ("=
( >) ( ^) (v ) (^ ) ( >) (v )
^ ^ ^ ^ ^ ^ ^^ ^^ ^^
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: large, artistic, object-oriented, scrolling"""
import random, shutil, sys, time
# Set up the constants:
PAUSE = 0.2 # (!) Try changing this to 1.0 or 0.0.
DENSITY = 0.10 # (!) Try changing this to anything from 0.0 to 1.0.
DUCKLING_WIDTH = 5
LEFT = 'left'
RIGHT = 'right'
BEADY = 'beady'
WIDE = 'wide'
HAPPY = 'happy'
ALOOF = 'aloof'
CHUBBY = 'chubby'
VERY_CHUBBY = 'very chubby'
OPEN = 'open'
CLOSED = 'closed'
OUT = 'out'
DOWN = 'down'
UP = 'up'
HEAD = 'head'
BODY = 'body'
FEET = 'feet'
# Get the size of the terminal window:
WIDTH = shutil.get_terminal_size()[0]
# We can't print to the last column on Windows without it adding a
# newline automatically, so reduce the width by one:
WIDTH -= 1
def main():
print('Duckling Screensaver, by Al Sweigart')
print('Press Ctrl-C to quit...')
time.sleep(2)
ducklingLanes = [None] * (WIDTH // DUCKLING_WIDTH)
while True: # Main program loop.
for laneNum, ducklingObj in enumerate(ducklingLanes):
# See if we should create a duckling in this lane:
if (ducklingObj == None and random.random() <= DENSITY):
# Place a duckling in this lane:
ducklingObj = Duckling()
ducklingLanes[laneNum] = ducklingObj
if ducklingObj != None:
# Draw a duckling if there is one in this lane:
print(ducklingObj.getNextBodyPart(), end='')
# Delete the duckling if we've finished drawing it:
if ducklingObj.partToDisplayNext == None:
ducklingLanes[laneNum] = None
else:
# Draw five spaces since there is no duckling here.
print(' ' * DUCKLING_WIDTH, end='')
print() # Print a newline.
sys.stdout.flush() # Make sure text appears on the screen.
time.sleep(PAUSE)
class Duckling:
def __init__(self):
"""Create a new duckling with random body features."""
self.direction = random.choice([LEFT, RIGHT])
self.body = random.choice([CHUBBY, VERY_CHUBBY])
self.mouth = random.choice([OPEN, CLOSED])
self.wing = random.choice([OUT, UP, DOWN])
if self.body == CHUBBY:
# Chubby ducklings can only have beady eyes.
self.eyes = BEADY
else:
self.eyes = random.choice([BEADY, WIDE, HAPPY, ALOOF])
self.partToDisplayNext = HEAD
def getHeadStr(self):
"""Returns the string of the duckling's head."""
headStr = ''
if self.direction == LEFT:
# Get the mouth:
if self.mouth == OPEN:
headStr += '>'
elif self.mouth == CLOSED:
headStr += '='
# Get the eyes:
if self.eyes == BEADY and self.body == CHUBBY:
headStr += '"'
elif self.eyes == BEADY and self.body == VERY_CHUBBY:
headStr += '" '
elif self.eyes == WIDE:
headStr += "''"
elif self.eyes == HAPPY:
headStr += '^^'
elif self.eyes == ALOOF:
headStr += '``'
headStr += ') ' # Get the back of the head.
if self.direction == RIGHT:
headStr += ' (' # Get the back of the head.
# Get the eyes:
if self.eyes == BEADY and self.body == CHUBBY:
headStr += '"'
elif self.eyes == BEADY and self.body == VERY_CHUBBY:
headStr += ' "'
elif self.eyes == WIDE:
headStr += "''"
elif self.eyes == HAPPY:
headStr += '^^'
elif self.eyes == ALOOF:
headStr += '``'
# Get the mouth:
if self.mouth == OPEN:
headStr += '<'
elif self.mouth == CLOSED:
headStr += '='
if self.body == CHUBBY:
# Get an extra space so chubby ducklings are the same
# width as very chubby ducklings.
headStr += ' '
return headStr
def getBodyStr(self):
"""Returns the string of the duckling's body."""
bodyStr = '(' # Get the left side of the body.
if self.direction == LEFT:
# Get the interior body space:
if self.body == CHUBBY:
bodyStr += ' '
elif self.body == VERY_CHUBBY:
bodyStr += ' '
# Get the wing:
if self.wing == OUT:
bodyStr += '>'
elif self.wing == UP:
bodyStr += '^'
elif self.wing == DOWN:
bodyStr += 'v'
if self.direction == RIGHT:
# Get the wing:
if self.wing == OUT:
bodyStr += '<'
elif self.wing == UP:
bodyStr += '^'
elif self.wing == DOWN:
bodyStr += 'v'
# Get the interior body space:
if self.body == CHUBBY:
bodyStr += ' '
elif self.body == VERY_CHUBBY:
bodyStr += ' '
bodyStr += ')' # Get the right side of the body.
if self.body == CHUBBY:
# Get an extra space so chubby ducklings are the same
# width as very chubby ducklings.
bodyStr += ' '
return bodyStr
def getFeetStr(self):
"""Returns the string of the duckling's feet."""
if self.body == CHUBBY:
return ' ^^ '
elif self.body == VERY_CHUBBY:
return ' ^ ^ '
def getNextBodyPart(self):
"""Calls the appropriate display method for the next body
part that needs to be displayed. Sets partToDisplayNext to
None when finished."""
if self.partToDisplayNext == HEAD:
self.partToDisplayNext = BODY
return self.getHeadStr()
elif self.partToDisplayNext == BODY:
self.partToDisplayNext = FEET
return self.getBodyStr()
elif self.partToDisplayNext == FEET:
self.partToDisplayNext = None
return self.getFeetStr()
# If this program was run (instead of imported), run the game:
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit() # When Ctrl-C is pressed, end the program.