1+ # Magic Eight Ball for Thumby
2+ # A fortune-telling game with immersive animations
3+
4+ import thumby
5+ import time
6+ import random
7+ import math
8+
9+ # Initialize the display
10+ thumby .display .setFPS (30 )
11+ thumby .display .fill (0 )
12+
13+ # Game states
14+ STATE_START = 0
15+ STATE_SHAKE = 1
16+ STATE_FORTUNE = 2
17+
18+ # Current game state
19+ current_state = STATE_START
20+
21+ # List of possible fortunes
22+ fortunes = [
23+ "IT IS\n CERTAIN" ,
24+ "IT IS\n DECIDEDLY\n SO" ,
25+ "WITHOUT A\n DOUBT" ,
26+ "YES\n DEFINITELY" ,
27+ "YOU MAY\n RELY ON IT" ,
28+ "AS I SEE\n IT, YES" ,
29+ "MOST\n LIKELY" ,
30+ "OUTLOOK\n GOOD" ,
31+ "YES" ,
32+ "SIGNS POINT\n TO YES" ,
33+ "REPLY HAZY\n TRY AGAIN" ,
34+ "ASK AGAIN\n LATER" ,
35+ "BETTER NOT\n TELL YOU\n NOW" ,
36+ "CANNOT\n PREDICT\n NOW" ,
37+ "CONCENTRATE\n AND ASK\n AGAIN" ,
38+ "DON'T\n COUNT\n ON IT" ,
39+ "MY REPLY\n IS NO" ,
40+ "MY SOURCES\n SAY NO" ,
41+ "OUTLOOK NOT\n SO GOOD" ,
42+ "VERY\n DOUBTFUL"
43+ ]
44+
45+ # Current fortune and previous fortune
46+ current_fortune = ""
47+ previous_fortune = ""
48+
49+ # Animation variables
50+ shake_frames = 0
51+ shake_counter = 0
52+ fade_counter = 0
53+ noise_level = 0
54+ bubble_positions = []
55+
56+ # Draw the start screen
57+ def draw_start_screen ():
58+ # Draw eight ball border to make it look like we're looking through the window
59+ thumby .display .drawRectangle (0 , 0 , 72 , 40 , 1 )
60+ thumby .display .drawRectangle (1 , 1 , 70 , 38 , 1 )
61+
62+ # Draw title text
63+ thumby .display .setFont ("/lib/font5x7.bin" , 5 , 7 , 1 )
64+ thumby .display .drawText ("MAGIC" , 18 , 5 , 1 )
65+ thumby .display .drawText ("EIGHT BALL" , 8 , 15 , 1 )
66+
67+ # Draw the number 8 with a simple background (using lines instead of a triangle)
68+ thumby .display .drawLine (28 , 30 , 50 , 30 , 1 )
69+ thumby .display .drawLine (28 , 30 , 39 , 18 , 1 )
70+ thumby .display .drawLine (50 , 30 , 39 , 18 , 1 )
71+ #thumby.display.setFont("/lib/font3x5.bin", 3, 5, 1)
72+ thumby .display .drawText ("8" , 37 , 22 , 1 )
73+
74+ # Draw instructions
75+ thumby .display .setFont ("/lib/font3x5.bin" , 3 , 5 , 1 )
76+ thumby .display .drawText ("A/B to shake" , 12 , 32 , 1 )
77+
78+ # Draw the fortune with word wrapping
79+ def draw_fortune (text , alpha = 1.0 ):
80+ thumby .display .setFont ("/lib/font5x7.bin" , 5 , 7 , 1 )
81+ lines = text .split ('\n ' )
82+ y = max (0 , (40 - len (lines ) * 10 ) // 2 )
83+
84+ for line in lines :
85+ x = max (0 , (72 - len (line ) * 6 ) // 2 )
86+
87+ # Only draw the text if random value is less than alpha (for fade effect)
88+ if random .random () < alpha :
89+ thumby .display .drawText (line , x , y , 1 )
90+ y += 10
91+
92+ # Create bubble effect
93+ def initialize_bubbles ():
94+ global bubble_positions
95+ bubble_positions = []
96+
97+ # Create random bubbles
98+ for _ in range (10 ):
99+ x = random .randint (5 , 67 )
100+ y = random .randint (5 , 35 )
101+ size = random .randint (1 , 3 )
102+ speed = random .uniform (0.2 , 0.8 )
103+ bubble_positions .append ([x , y , size , speed ])
104+
105+ # Update and draw bubbles
106+ def update_bubbles ():
107+ global bubble_positions
108+
109+ for i , bubble in enumerate (bubble_positions ):
110+ # Move bubble up
111+ bubble [1 ] -= bubble [3 ]
112+
113+ # If bubble goes off screen, reset at bottom
114+ if bubble [1 ] < - 5 :
115+ bubble [0 ] = random .randint (5 , 67 )
116+ bubble [1 ] = 45
117+
118+ # Draw bubble
119+ thumby .display .drawFilledRectangle (int (bubble [0 ]), int (bubble [1 ]), bubble [2 ], bubble [2 ], 1 )
120+
121+ # Update in the list
122+ bubble_positions [i ] = bubble
123+
124+ # Generate random noise pattern for shaking effect
125+ def generate_noise (intensity ):
126+ for y in range (0 , 40 , 2 ):
127+ for x in range (0 , 72 , 2 ):
128+ if random .random () < intensity / 10.0 :
129+ thumby .display .setPixel (x , y , 1 )
130+
131+ # Shake animation - simulates the fluid becoming cloudy and bubbling
132+ def animate_shake ():
133+ global shake_frames , shake_counter , bubble_positions , noise_level
134+
135+ shake_frames += 1
136+
137+ # Draw the eight ball window border
138+ thumby .display .drawRectangle (0 , 0 , 72 , 40 , 1 )
139+
140+ # Calculate noise intensity - increases then decreases
141+ if shake_frames < 30 :
142+ noise_level = min (10 , shake_frames // 3 )
143+ else :
144+ noise_level = max (0 , 10 - (shake_frames - 30 ) // 3 )
145+
146+ # Generate noise pattern with current intensity
147+ generate_noise (noise_level )
148+
149+ # Update and draw bubbles
150+ update_bubbles ()
151+
152+ # Occasionally show the previous fortune fading out
153+ if shake_frames < 30 and previous_fortune and random .random () < 0.3 :
154+ fade_alpha = max (0 , 1.0 - (shake_frames / 30.0 ))
155+ draw_fortune (previous_fortune , fade_alpha )
156+
157+ # Shake the screen slightly
158+ shake_offset = random .randint (- 2 , 2 )
159+ thumby .display .drawLine (0 , 0 + shake_offset , 71 , 0 + shake_offset , 1 )
160+ thumby .display .drawLine (0 , 39 + shake_offset , 71 , 39 + shake_offset , 1 )
161+
162+ # Check if shake animation is complete
163+ if shake_frames >= 60 :
164+ return True
165+ return False
166+
167+ # Animate the fortune appearing from the cloudy fluid
168+ def animate_fortune_reveal ():
169+ global fade_counter
170+
171+ fade_counter += 1
172+
173+ # Draw the eight ball window border
174+ thumby .display .drawRectangle (0 , 0 , 72 , 40 , 1 )
175+
176+ # Calculate fade-in alpha
177+ alpha = min (1.0 , fade_counter / 30.0 )
178+
179+ # Generate decreasing noise as the text becomes clearer
180+ noise_intensity = max (0 , 10 - fade_counter // 3 )
181+ generate_noise (noise_intensity )
182+
183+ # Update and draw bubbles (they continue throughout)
184+ update_bubbles ()
185+
186+ # Draw the fortune with current alpha
187+ draw_fortune (current_fortune , alpha )
188+
189+ # Check if fade animation is complete
190+ if fade_counter >= 30 :
191+ return True
192+ return False
193+
194+ # Initialize the game
195+ initialize_bubbles ()
196+
197+ # Main game loop
198+ while True :
199+ thumby .display .fill (0 )
200+
201+ # Handle button inputs
202+ if thumby .buttonA .justPressed () or thumby .buttonB .justPressed ():
203+ if current_state == STATE_START or current_state == STATE_FORTUNE :
204+ current_state = STATE_SHAKE
205+ shake_frames = 0
206+ shake_counter = 0
207+ # Store previous fortune before selecting a new one
208+ previous_fortune = current_fortune
209+ # Select a random fortune
210+ current_fortune = random .choice (fortunes )
211+ # Reset bubbles for shake animation
212+ initialize_bubbles ()
213+
214+ # Update and render based on current state
215+ if current_state == STATE_START :
216+ draw_start_screen ()
217+
218+ elif current_state == STATE_SHAKE :
219+ # Run shake animation
220+ if animate_shake ():
221+ current_state = STATE_FORTUNE
222+ fade_counter = 0
223+
224+ elif current_state == STATE_FORTUNE :
225+ # Run fortune transition animation or show fortune
226+ if fade_counter < 30 :
227+ animate_fortune_reveal ()
228+ else :
229+ # Draw the eight ball window border
230+ thumby .display .drawRectangle (0 , 0 , 72 , 40 , 1 )
231+ # Draw fully revealed fortune
232+ draw_fortune (current_fortune )
233+ # Still show some occasional bubbles
234+ update_bubbles ()
235+
236+ # Update the display
237+ thumby .display .update ()
0 commit comments