15
15
clock_pin = board .D13 , latch_pin = board .D0 , output_enable_pin = board .D1 )
16
16
display = framebufferio .FramebufferDisplay (matrix , auto_refresh = False )
17
17
18
+ # This bitmap contains the emoji we're going to use. It is assumed
19
+ # to contain 20 icons, each 20x24 pixels. This fits nicely on the 64x32
20
+ # RGB matrix display.
18
21
bitmap_file = open ("emoji.bmp" , 'rb' )
19
22
bitmap = displayio .OnDiskBitmap (bitmap_file )
20
23
24
+ # Each wheel can be in one of three states:
21
25
STOPPED , RUNNING , BRAKING = range (3 )
22
26
27
+ # Return a duplicate of the input list in a random (shuffled) order.
23
28
def shuffled (seq ):
24
29
return sorted (seq , key = lambda _ : random .random ())
25
30
26
- class Strip (displayio .TileGrid ):
31
+ # The Wheel class manages the state of one wheel. "pos" is a position in
32
+ # scaled integer coordinates, with one revolution being 7680 positions
33
+ # and 1 pixel being 16 positions. The wheel also has a velocity (in positions
34
+ # per tick) and a state (one of the above constants)
35
+ class Wheel (displayio .TileGrid ):
27
36
def __init__ (self ):
37
+ # Portions of up to 3 tiles are visible.
28
38
super ().__init__ (bitmap = bitmap , pixel_shader = displayio .ColorConverter (),
29
- width = 1 , height = 4 , tile_width = 20 , tile_height = 24 )
39
+ width = 1 , height = 3 , tile_width = 20 )
30
40
self .order = shuffled (range (20 ))
31
41
self .state = STOPPED
32
42
self .pos = 0
@@ -36,62 +46,91 @@ def __init__(self):
36
46
self .stop_time = time .monotonic_ns ()
37
47
38
48
def step (self ):
49
+ # Update each wheel for one time step
39
50
if self .state == RUNNING :
51
+ # Slowly lose speed when running, but go at least speed 64
40
52
self .vel = max (self .vel * 9 // 10 , 64 )
41
53
if time .monotonic_ns () > self .stop_time :
42
54
self .state = BRAKING
43
55
elif self .state == BRAKING :
56
+ # More quickly lose speed when baking, down to speed 7
44
57
self .vel = max (self .vel * 85 // 100 , 7 )
58
+
59
+ # Advance the wheel according to the velocity, and wrap it around
60
+ # after 7680 positions
45
61
self .pos = (self .pos + self .vel ) % 7680
62
+
63
+ # Compute the rounded Y coordinate
46
64
yy = round (self .pos / 16 )
65
+ # Compute the offset of the tile (tiles are 24 pixels tall)
47
66
yyy = yy % 24
67
+ # Find out which tile is the top tile
48
68
off = yy // 24
69
+
70
+ # If we're braking and a tile is close to midscreen,
71
+ # then stop and make sure that tile is exactly centered
49
72
if self .state == BRAKING and self .vel == 7 and yyy < 4 :
50
73
self .pos = off * 24 * 16
51
74
self .vel = 0
52
75
yy = 0
53
76
self .state = STOPPED
54
- self .y = yy % 24 - 20
55
- for i in range (4 ):
77
+
78
+ # Move the displayed tiles to the correct height and make sure the
79
+ # correct tiles are displayed.
80
+ self .y = yyy - 20
81
+ for i in range (3 ):
56
82
self [i ] = self .order [(19 - i + off ) % 20 ]
57
83
84
+ # Set the wheel running again, using a slight bit of randomness.
85
+ # The 'i' value makes sure the first wheel brakes first, the second
86
+ # brakes second, and the third brakes third.
58
87
def kick (self , i ):
59
88
self .state = RUNNING
60
89
self .vel = random .randint (256 , 320 )
61
90
self .stop_time = time .monotonic_ns () + 3000000000 + i * 350000000
62
91
63
- def brake (self ):
64
- self .state = BRAKING
65
-
92
+ # Our fruit machine has 3 wheels, let's create them with a correct horizontal
93
+ # (x) offset and arbitrary vertical (y) offset.
66
94
g = displayio .Group (max_size = 3 )
67
- strips = []
95
+ wheels = []
68
96
for idx in range (3 ):
69
- strip = Strip ()
70
- strip .x = idx * 22
71
- strip .y = - 20
72
- g .append (strip )
73
- strips .append (strip )
97
+ wheel = Wheel ()
98
+ wheel .x = idx * 22
99
+ wheel .y = - 20
100
+ g .append (wheel )
101
+ wheels .append (wheel )
74
102
display .show (g )
75
103
104
+ # Make a unique order of the emoji on each wheel
76
105
orders = [shuffled (range (20 )), shuffled (range (20 )), shuffled (range (20 ))]
77
106
78
- for si , oi in zip (strips , orders ):
79
- for idx in range (4 ):
107
+ # And put up some images to start with
108
+ for si , oi in zip (wheels , orders ):
109
+ for idx in range (3 ):
80
110
si [idx ] = oi [idx ]
81
111
112
+ # We want a way to check if all the wheels are stopped
82
113
def all_stopped ():
83
- return all (si .state == STOPPED for si in strips )
114
+ return all (si .state == STOPPED for si in wheels )
84
115
85
- for idx , si in enumerate (strips ):
116
+ # To start with, though, they're all in motion
117
+ for idx , si in enumerate (wheels ):
86
118
si .kick (idx )
87
119
120
+ # Here's the main loop
88
121
while True :
122
+ # Refresh the dislpay (doing this manually ensures the wheels move
123
+ # together, not at different times)
89
124
display .refresh (minimum_frames_per_second = 0 )
90
125
if all_stopped ():
126
+ # Once everything comes to a stop, wait a little bit and then
127
+ # start everything over again. Maybe you want to check if the
128
+ # combination is a "winner" and add a light show or something.
91
129
for idx in range (100 ):
92
130
display .refresh (minimum_frames_per_second = 0 )
93
- for idx , si in enumerate (strips ):
131
+ for idx , si in enumerate (wheels ):
94
132
si .kick (idx )
95
133
96
- for idx , si in enumerate (strips ):
134
+ # Otherwise, let the wheels keep spinning...
135
+ for idx , si in enumerate (wheels ):
97
136
si .step ()
0 commit comments