Ducklings
This program creates a scrolling field of ducklings. Each duckling has slight variations: they can face left or right and have two different body sizes, four types of eyes, two types of mouths, and three positions for their wings. This gives us 96 different possible variations, which the Ducklings program produces endlessly.kk
ducklings.py
1"""Duckling Screensaver, by Al Sweigart al@inventwithpython.com
2A screensaver of many many ducklings.
3
4>" ) =^^) (``= ("= >") ("=
5( >) ( ^) (v ) (^ ) ( >) (v )
6 ^ ^ ^ ^ ^ ^ ^^ ^^ ^^
7
8This code is available at https://nostarch.com/big-book-small-python-programming
9Tags: large, artistic, object-oriented, scrolling"""
10
11import random, shutil, sys, time
12
13# Set up the constants:
14PAUSE = 0.2 # (!) Try changing this to 1.0 or 0.0.
15DENSITY = 0.10 # (!) Try changing this to anything from 0.0 to 1.0.
16
17DUCKLING_WIDTH = 5
18LEFT = 'left'
19RIGHT = 'right'
20BEADY = 'beady'
21WIDE = 'wide'
22HAPPY = 'happy'
23ALOOF = 'aloof'
24CHUBBY = 'chubby'
25VERY_CHUBBY = 'very chubby'
26OPEN = 'open'
27CLOSED = 'closed'
28OUT = 'out'
29DOWN = 'down'
30UP = 'up'
31HEAD = 'head'
32BODY = 'body'
33FEET = 'feet'
34
35# Get the size of the terminal window:
36WIDTH = shutil.get_terminal_size()[0]
37# We can't print to the last column on Windows without it adding a
38# newline automatically, so reduce the width by one:
39WIDTH -= 1
40
41
42def main():
43 print('Duckling Screensaver, by Al Sweigart')
44 print('Press Ctrl-C to quit...')
45 time.sleep(2)
46
47 ducklingLanes = [None] * (WIDTH // DUCKLING_WIDTH)
48
49 while True: # Main program loop.
50 for laneNum, ducklingObj in enumerate(ducklingLanes):
51 # See if we should create a duckling in this lane:
52 if (ducklingObj == None and random.random() <= DENSITY):
53 # Place a duckling in this lane:
54 ducklingObj = Duckling()
55 ducklingLanes[laneNum] = ducklingObj
56
57 if ducklingObj != None:
58 # Draw a duckling if there is one in this lane:
59 print(ducklingObj.getNextBodyPart(), end='')
60 # Delete the duckling if we've finished drawing it:
61 if ducklingObj.partToDisplayNext == None:
62 ducklingLanes[laneNum] = None
63 else:
64 # Draw five spaces since there is no duckling here.
65 print(' ' * DUCKLING_WIDTH, end='')
66
67 print() # Print a newline.
68 sys.stdout.flush() # Make sure text appears on the screen.
69 time.sleep(PAUSE)
70
71
72class Duckling:
73 def __init__(self):
74 """Create a new duckling with random body features."""
75 self.direction = random.choice([LEFT, RIGHT])
76 self.body = random.choice([CHUBBY, VERY_CHUBBY])
77 self.mouth = random.choice([OPEN, CLOSED])
78 self.wing = random.choice([OUT, UP, DOWN])
79
80 if self.body == CHUBBY:
81 # Chubby ducklings can only have beady eyes.
82 self.eyes = BEADY
83 else:
84 self.eyes = random.choice([BEADY, WIDE, HAPPY, ALOOF])
85
86 self.partToDisplayNext = HEAD
87
88 def getHeadStr(self):
89 """Returns the string of the duckling's head."""
90 headStr = ''
91 if self.direction == LEFT:
92 # Get the mouth:
93 if self.mouth == OPEN:
94 headStr += '>'
95 elif self.mouth == CLOSED:
96 headStr += '='
97
98 # Get the eyes:
99 if self.eyes == BEADY and self.body == CHUBBY:
100 headStr += '"'
101 elif self.eyes == BEADY and self.body == VERY_CHUBBY:
102 headStr += '" '
103 elif self.eyes == WIDE:
104 headStr += "''"
105 elif self.eyes == HAPPY:
106 headStr += '^^'
107 elif self.eyes == ALOOF:
108 headStr += '``'
109
110 headStr += ') ' # Get the back of the head.
111
112 if self.direction == RIGHT:
113 headStr += ' (' # Get the back of the head.
114
115 # Get the eyes:
116 if self.eyes == BEADY and self.body == CHUBBY:
117 headStr += '"'
118 elif self.eyes == BEADY and self.body == VERY_CHUBBY:
119 headStr += ' "'
120 elif self.eyes == WIDE:
121 headStr += "''"
122 elif self.eyes == HAPPY:
123 headStr += '^^'
124 elif self.eyes == ALOOF:
125 headStr += '``'
126
127 # Get the mouth:
128 if self.mouth == OPEN:
129 headStr += '<'
130 elif self.mouth == CLOSED:
131 headStr += '='
132
133 if self.body == CHUBBY:
134 # Get an extra space so chubby ducklings are the same
135 # width as very chubby ducklings.
136 headStr += ' '
137
138 return headStr
139
140 def getBodyStr(self):
141 """Returns the string of the duckling's body."""
142 bodyStr = '(' # Get the left side of the body.
143 if self.direction == LEFT:
144 # Get the interior body space:
145 if self.body == CHUBBY:
146 bodyStr += ' '
147 elif self.body == VERY_CHUBBY:
148 bodyStr += ' '
149
150 # Get the wing:
151 if self.wing == OUT:
152 bodyStr += '>'
153 elif self.wing == UP:
154 bodyStr += '^'
155 elif self.wing == DOWN:
156 bodyStr += 'v'
157
158 if self.direction == RIGHT:
159 # Get the wing:
160 if self.wing == OUT:
161 bodyStr += '<'
162 elif self.wing == UP:
163 bodyStr += '^'
164 elif self.wing == DOWN:
165 bodyStr += 'v'
166
167 # Get the interior body space:
168 if self.body == CHUBBY:
169 bodyStr += ' '
170 elif self.body == VERY_CHUBBY:
171 bodyStr += ' '
172
173 bodyStr += ')' # Get the right side of the body.
174
175 if self.body == CHUBBY:
176 # Get an extra space so chubby ducklings are the same
177 # width as very chubby ducklings.
178 bodyStr += ' '
179
180 return bodyStr
181
182 def getFeetStr(self):
183 """Returns the string of the duckling's feet."""
184 if self.body == CHUBBY:
185 return ' ^^ '
186 elif self.body == VERY_CHUBBY:
187 return ' ^ ^ '
188
189 def getNextBodyPart(self):
190 """Calls the appropriate display method for the next body
191 part that needs to be displayed. Sets partToDisplayNext to
192 None when finished."""
193 if self.partToDisplayNext == HEAD:
194 self.partToDisplayNext = BODY
195 return self.getHeadStr()
196 elif self.partToDisplayNext == BODY:
197 self.partToDisplayNext = FEET
198 return self.getBodyStr()
199 elif self.partToDisplayNext == FEET:
200 self.partToDisplayNext = None
201 return self.getFeetStr()
202
203
204
205# If this program was run (instead of imported), run the game:
206if __name__ == '__main__':
207 try:
208 main()
209 except KeyboardInterrupt:
210 sys.exit() # When Ctrl-C is pressed, end the program.