A coin-operated game
Drop a coin, press Start, then hit the score button as often as you can for ten seconds. The whole game is three pieces of state and four rules, on a Pico, with no screen needed: the Thonny shell shows credits, score and the result.
Wire two buttons
| Socket | Pico pin | Job |
|---|---|---|
| KEY1 | GP16 | Start |
| KEY2 | GP17 | Score |
| COIN | GP28 | Credit |
Pass the input monitor and the coin probe first. Then save arcade.py and
credit_game.py onto the Pico and run the second.
One session
The program keeps three things: how many credits there are, the score, and whether a round is running. Four rules change them:
- A pulse group listed in
CREDITS_BY_PULSESadds credit. Anything else is ignored. - Start with no credit prints a reminder and does nothing else.
- Start with credit spends one and starts the clock in the same step.
- When the clock passes ten seconds the round ends, whatever is being pressed. The program checks the clock before it checks for a score, so a press on the last millisecond does not count.
The loop never sleeps for more than a millisecond. A long sleep() in it
would not lose coins, which are counted in an interrupt, but it would lose
button presses, which are not.
Make it yours
The settings are three lines at the top:
CREDITS_BY_PULSES = {1: 1} # pulse group -> credits. {3: 1}: three pulses buy one credit.
ROUND_MS = 10_000
USE_DISPLAY = FalseCREDITS_BY_PULSES maps a pulse group to credits. {1: 1} means a coin
that sends one pulse buys one credit. For a coin taught to three pulses, use
{3: 1}: one credit per group of three, not three credits. Several coins can
share the table, {1: 1, 5: 5}.
USE_DISPLAY = True puts credits, score and the last message on an ILI9488
screen, once the screen test works.
The code
For the Pico and Pico 2 adapter. Save arcade.py to the board, then this file, and run it from Thonny with both USB cables in: the acceptor needs the mainboard's 12 V.
"""Insert a coin, press KEY1, then score with KEY2 for ten seconds."""
from time import ticks_ms, ticks_diff, sleep_ms
from arcade import Button, CoinInput
CREDITS_BY_PULSES = {1: 1} # pulse group -> credits. {3: 1}: three pulses buy one credit.
ROUND_MS = 10_000
USE_DISPLAY = False
class Game:
def __init__(self):
self.credits = 0
self.score = 0
self.playing = False
self.started = 0
def update(self, now, pulses=0, start=False, hit=False):
messages = []
if pulses:
credit = CREDITS_BY_PULSES.get(pulses, 0)
if credit:
self.credits += credit
messages.append("Credits: %d" % self.credits)
else:
messages.append("Unknown pulse group ignored: %d" % pulses)
# Expiry comes before scoring: a press at the deadline is too late.
if self.playing and ticks_diff(now, self.started) >= ROUND_MS:
self.playing = False
messages.append("Time up! Score: %d" % self.score)
return messages
if start and not self.playing:
if not self.credits:
messages.append("Insert a coin")
else:
self.credits -= 1
self.score = 0
self.started = now
self.playing = True
messages.append("GO! Press KEY2 for %d seconds" % (ROUND_MS // 1000))
return messages
if hit and self.playing:
self.score += 1
messages.append("Score: %d" % self.score)
return messages
def main():
display = None
if USE_DISPLAY:
from arcade import make_display
display = make_display()
display.draw_text8("HotSwap Arcade", 8, 8, 0x07E0)
start, hit = Button(16), Button(17)
coin = CoinInput(28)
game = Game()
previous_rows = (None, None, None)
status = "Insert a coin. KEY1 starts; KEY2 scores."
print(status)
try:
while True:
pulses = coin.poll()
messages = game.update(ticks_ms(), pulses,
start.poll() == 1, hit.poll() == 1)
for message in messages:
print(message)
status = message
if display:
rows = ("Credits: %d" % game.credits,
"Score: %d" % game.score, status)
for row, text in enumerate(rows):
if text != previous_rows[row]:
# Fixed-width padding clears the previous row in one draw.
display.draw_text8(text[:56].ljust(56), 8, 32 + row * 20)
previous_rows = rows
sleep_ms(1)
finally:
coin.close()
if __name__ == "__main__":
main()Save it on the Pico as main.py, beside arcade.py, and it starts by itself at power-up. Stop it with Ctrl+C in Thonny when you want to edit it.
View on GitHub · examples/pico/credit_game.py @ v1.0When it does not work
The coin's pulse count is not in CREDITS_BY_PULSES. Run the coin probe, read the count your coin sends, and put that number in the table: a coin taught to three pulses needs {3: 1}.
That is on purpose. The game counts presses, not time held, so each point is one release and one press.
They live in the Pico's RAM, which empties when the power goes. It is a game, and that is fine for a game. Anything that takes real money needs its count kept somewhere that survives a power cut, and a lot more care than this example takes.
Check the coin probe gives one group per coin first. The game reads coins through the same CoinInput, so a timing problem there shows up here as extra credit.
The ribbon socket, which adapters reach it, and which screens the kit's code drives.
Add a screen →Edit this page — content/books/arcade/a-coin-operated-game.mdx
Questions about this product
See what other owners have asked, and read their solutions.
HotSwap Arcade Kit: Buttons, Joystick and Coin Acceptor for ESP32-S3, Nano, Pico and Zero 2 W
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.