1515 clock_pin = board .D13 , latch_pin = board .D0 , output_enable_pin = board .D1 )
1616display = framebufferio .FramebufferDisplay (matrix , auto_refresh = False )
1717
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.
1821bitmap_file = open ("emoji.bmp" , 'rb' )
1922bitmap = displayio .OnDiskBitmap (bitmap_file )
2023
24+ # Each wheel can be in one of three states:
2125STOPPED , RUNNING , BRAKING = range (3 )
2226
27+ # Return a duplicate of the input list in a random (shuffled) order.
2328def shuffled (seq ):
2429 return sorted (seq , key = lambda _ : random .random ())
2530
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 ):
2736 def __init__ (self ):
37+ # Portions of up to 3 tiles are visible.
2838 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 )
3040 self .order = shuffled (range (20 ))
3141 self .state = STOPPED
3242 self .pos = 0
@@ -36,62 +46,91 @@ def __init__(self):
3646 self .stop_time = time .monotonic_ns ()
3747
3848 def step (self ):
49+ # Update each wheel for one time step
3950 if self .state == RUNNING :
51+ # Slowly lose speed when running, but go at least speed 64
4052 self .vel = max (self .vel * 9 // 10 , 64 )
4153 if time .monotonic_ns () > self .stop_time :
4254 self .state = BRAKING
4355 elif self .state == BRAKING :
56+ # More quickly lose speed when baking, down to speed 7
4457 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
4561 self .pos = (self .pos + self .vel ) % 7680
62+
63+ # Compute the rounded Y coordinate
4664 yy = round (self .pos / 16 )
65+ # Compute the offset of the tile (tiles are 24 pixels tall)
4766 yyy = yy % 24
67+ # Find out which tile is the top tile
4868 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
4972 if self .state == BRAKING and self .vel == 7 and yyy < 4 :
5073 self .pos = off * 24 * 16
5174 self .vel = 0
5275 yy = 0
5376 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 ):
5682 self [i ] = self .order [(19 - i + off ) % 20 ]
5783
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.
5887 def kick (self , i ):
5988 self .state = RUNNING
6089 self .vel = random .randint (256 , 320 )
6190 self .stop_time = time .monotonic_ns () + 3000000000 + i * 350000000
6291
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.
6694g = displayio .Group (max_size = 3 )
67- strips = []
95+ wheels = []
6896for 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 )
74102display .show (g )
75103
104+ # Make a unique order of the emoji on each wheel
76105orders = [shuffled (range (20 )), shuffled (range (20 )), shuffled (range (20 ))]
77106
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 ):
80110 si [idx ] = oi [idx ]
81111
112+ # We want a way to check if all the wheels are stopped
82113def all_stopped ():
83- return all (si .state == STOPPED for si in strips )
114+ return all (si .state == STOPPED for si in wheels )
84115
85- for idx , si in enumerate (strips ):
116+ # To start with, though, they're all in motion
117+ for idx , si in enumerate (wheels ):
86118 si .kick (idx )
87119
120+ # Here's the main loop
88121while True :
122+ # Refresh the dislpay (doing this manually ensures the wheels move
123+ # together, not at different times)
89124 display .refresh (minimum_frames_per_second = 0 )
90125 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.
91129 for idx in range (100 ):
92130 display .refresh (minimum_frames_per_second = 0 )
93- for idx , si in enumerate (strips ):
131+ for idx , si in enumerate (wheels ):
94132 si .kick (idx )
95133
96- for idx , si in enumerate (strips ):
134+ # Otherwise, let the wheels keep spinning...
135+ for idx , si in enumerate (wheels ):
97136 si .step ()
0 commit comments