arcade kit/Build it/09. A coin-operated game
Build it · 09 of 11

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

SocketPico pinJob
KEY1GP16Start
KEY2GP17Score
COINGP28Credit

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

A coin buys one round
step 1 / 9
Credits
0
Score
0
The game has three pieces of state: credits, score, and whether a round is running. Press play and watch which inputs change which.

The program keeps three things: how many credits there are, the score, and whether a round is running. Four rules change them:

  1. A pulse group listed in CREDITS_BY_PULSES adds credit. Anything else is ignored.
  2. Start with no credit prints a reminder and does nothing else.
  3. Start with credit spends one and starts the clock in the same step.
  4. 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:

credit_game.py
CREDITS_BY_PULSES = {1: 1}  # pulse group -> credits. {3: 1}: three pulses buy one credit.
ROUND_MS = 10_000
USE_DISPLAY = False
View on GitHub · examples/pico/credit_game.py @ v1.0

CREDITS_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

credit_game.py

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.0

When it does not work

Start says Insert a coin after I dropped one

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}.

Holding KEY2 only scores once

That is on purpose. The game counts presses, not time held, so each point is one release and one press.

Credits are gone after I unplug it

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.

The coin counts twice while a round is running

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.

Where this goes next

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

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

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.

Browse Modules and blocks on the forum