.. note::
Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.
**Why Join?**
- **Expert Support**: Solve post-sale issues and technical challenges with help from our community and team.
- **Learn & Share**: Exchange tips and tutorials to enhance your skills.
- **Exclusive Previews**: Get early access to new product announcements and sneak peeks.
- **Special Discounts**: Enjoy exclusive discounts on our newest products.
- **Festive Promotions and Giveaways**: Take part in giveaways and holiday promotions.
👉 Ready to explore and create with us? Click [|link_sf_facebook|] and join today!
.. _3.1.14_py_pi5:
3.1.14 GAME– Not Not
=======================
Introduction
--------------------
In this lesson, we will make an interesting game device, and we call it \"Not Not\".
During the game, the dot matrix will refresh an arrow randomly. What you need to do is to press the button in the opposite direction of the arrow within a limited time. If the time is up, or if the button in the same direction as the arrow is pressed, you are out.
This game can really practice your reverse thinking, and now shall we have a try?
Required Components
------------------------------
In this project, we need the following components.
.. image:: ../python_pi5/img/3.1.14_game_not_not_list.png
:width: 800
:align: center
Schematic Diagram
-----------------------
============ ======== ======== ===
T-Board Name physical wiringPi BCM
GPIO17 Pin 11 0 17
GPIO18 Pin 12 1 18
GPIO27 Pin 13 2 27
GPIO20 Pin 38 28 20
GPIO26 Pin 37 25 26
============ ======== ======== ===
.. image:: ../python_pi5/img/3.1.14_game_not_not_schematic.png
:align: center
Experimental Procedures
-----------------------
**Step 1:** Build the circuit.
.. image:: ../python_pi5/img/3.1.14_game_not_not_circuit.png
**Step 2:** Open the code file.
.. raw:: html
.. code-block::
cd ~/davinci-kit-for-raspberry-pi/python-pi5
**Step 3:** Run.
.. raw:: html
.. code-block::
sudo python3 3.1.14_MotionControl.py
After starting the program, on the dot matrix appears an arrow pointing
to the right or the left. What you need to do is to press the button in
the opposite direction of the arrow within a limited time. Then
\"**√**\" appears on the dot matrix. If the time is up, or if the
button in the same direction as the arrow is pressed, you are out and
the dot matrix displays \"x\". You can also add 2 new buttons or replace
them with Joystick keys for up, down, left and right— 4 directions to
increase the difficulty of the game.
.. warning::
If there is an error prompt ``RuntimeError: Cannot determine SOC peripheral base address``, please refer to :ref:`faq_soc`
**Code**
.. note::
You can **Modify/Reset/Copy/Run/Stop** the code below. But before that, you need to go to source code path like ``davinci-kit-for-raspberry-pi/python-pi5``. After modifying the code, you can run it directly to see the effect.
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
from gpiozero import OutputDevice, Button
import time
import threading
import random
# GPIO pins for the 74HC595 shift register
SDI = OutputDevice(17) # Serial Data Input
RCLK = OutputDevice(18) # Register Clock
SRCLK = OutputDevice(27) # Shift Register Clock
# GPIO pins for buttons
AButtonPin = Button(20) # Button A
BButtonPin = Button(26) # Button B
# Game variables initialization
timerPlay = 0
timerCheck = 0
waypoint = "NULL"
stage = "NULL"
# Arrow glyphs for LED matrix display
arrow = {
"right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
"left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
}
# Feedback glyphs for correct/wrong answers
check = {
"wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
"right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
}
def hc595_shift(dat):
""" Shift data to the 74HC595 shift register. """
for i in range(8):
SDI.value = 0x80 & (dat << i)
SRCLK.on()
SRCLK.off()
def display(glyphCode):
""" Display a glyph on the LED matrix. """
for i in range(0, 8):
hc595_shift(glyphCode[i])
hc595_shift(0x80 >> i)
RCLK.on()
RCLK.off()
def creatGlyph():
""" Create a new glyph for the game and start the play timer. """
global waypoint, stage, timerPlay
waypoint = random.choice(list(arrow.keys()))
stage = "PLAY"
timerPlay = threading.Timer(2.0, timeOut)
timerPlay.start()
def checkPoint(inputKey):
""" Check player's input and update game state. """
global waypoint, stage, timerCheck
if inputKey == "empty" or inputKey == waypoint:
waypoint = "wrong"
else:
waypoint = "right"
timerPlay.cancel()
stage = "CHECK"
timerCheck = threading.Timer(1.0, creatGlyph)
timerCheck.start()
def timeOut():
""" Handle game timeout scenario. """
checkPoint("empty")
def getKey():
""" Detect button press and trigger checkpoint. """
if AButtonPin.is_pressed and not BButtonPin.is_pressed:
checkPoint("right")
elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
checkPoint("left")
def main():
""" Main game loop. """
creatGlyph()
while True:
if stage == "PLAY":
display(arrow[waypoint])
getKey()
elif stage == "CHECK":
display(check[waypoint])
def destroy():
""" Clean up resources on program exit. """
global timerPlay, timerCheck
timerPlay.cancel() # Cancel the play timer
timerCheck.cancel() # Cancel the checkpoint timer
# Run the game, handle KeyboardInterrupt for clean exit
try:
main()
except KeyboardInterrupt:
destroy()
**Code Explanation**
Based on :ref:`1.1.6_py_pi5`, this lesson adds **2** buttons to make an amusing game device. So, if you are not very familiar with the dot matrix, please refer to :ref:`1.1.6_py_pi5`.
#. The code begins by importing necessary libraries. ``gpiozero`` is used for interacting with GPIO pins like buttons and output devices. ``time`` allows for adding delays, ``threading`` enables running multiple tasks concurrently, and ``random`` is useful for introducing randomness in the project.
.. code-block:: python
#!/usr/bin/env python3
from gpiozero import OutputDevice, Button
import time
import threading
import random
#. Initializes GPIO pins for the shift register (``SDI``, ``RCLK``, ``SRCLK``) and buttons (``AButtonPin``, ``BButtonPin``). The shift register is used to control multiple LEDs with fewer GPIO pins, which is crucial for the LED matrix display.
.. code-block:: python
# GPIO pins for the 74HC595 shift register
SDI = OutputDevice(17) # Serial Data Input
RCLK = OutputDevice(18) # Register Clock
SRCLK = OutputDevice(27) # Shift Register Clock
# GPIO pins for buttons
AButtonPin = Button(20) # Button A
BButtonPin = Button(26) # Button B
#. Initializes variables used in the game logic, such as timers and game state indicators.
.. code-block:: python
# Game variables initialization
timerPlay = 0
timerCheck = 0
waypoint = "NULL"
stage = "NULL"
#. Defines binary patterns for displaying arrows and feedback (right/wrong) on the LED matrix. Each array element represents a row of the LED matrix, where ``1`` and ``0`` correspond to an LED being on or off, respectively.
.. code-block:: python
# Arrow glyphs for LED matrix display
arrow = {
"right": [0xFF, 0xEF, 0xDF, 0x81, 0xDF, 0xEF, 0xFF, 0xFF],
"left": [0xFF, 0xF7, 0xFB, 0x81, 0xFB, 0xF7, 0xFF, 0xFF]
}
# Feedback glyphs for correct/wrong answers
check = {
"wrong": [0xFF, 0xBB, 0xD7, 0xEF, 0xD7, 0xBB, 0xFF, 0xFF],
"right": [0xFF, 0xFF, 0xF7, 0xEB, 0xDF, 0xBF, 0xFF, 0xFF]
}
#. This function shifts a byte of data to the 74HC595 shift register. It iterates over each bit of the ``dat`` byte, setting the ``SDI`` pin high or low accordingly, and toggles the ``SRCLK`` pin to shift the bit into the register.
.. code-block:: python
def hc595_shift(dat):
""" Shift data to the 74HC595 shift register. """
for i in range(8):
SDI.value = 0x80 & (dat << i)
SRCLK.on()
SRCLK.off()
#. This function displays a glyph on the LED matrix. It sends each row of the glyph (represented by ``glyphCode``) and the row's address to the shift register using ``hc595_shift``, then toggles the ``RCLK`` pin to update the display.
.. code-block:: python
def display(glyphCode):
""" Display a glyph on the LED matrix. """
for i in range(0, 8):
hc595_shift(glyphCode[i])
hc595_shift(0x80 >> i)
RCLK.on()
RCLK.off()
#. This function randomly selects a glyph from the ``arrow`` dictionary, starts the play timer, and sets the game stage to "PLAY". The ``threading.Timer`` is used for timing control in the game.
.. code-block:: python
def creatGlyph():
""" Create a new glyph for the game and start the play timer. """
global waypoint, stage, timerPlay
waypoint = random.choice(list(arrow.keys()))
stage = "PLAY"
timerPlay = threading.Timer(2.0, timeOut)
timerPlay.start()
#. This function checks the player's input against the current glyph. If the input is correct, it sets the waypoint to "right", otherwise to "wrong". It then cancels the current play timer and starts a new timer for the next glyph.
.. code-block:: python
def checkPoint(inputKey):
""" Check player's input and update game state. """
global waypoint, stage, timerCheck
if inputKey == "empty" or inputKey == waypoint:
waypoint = "wrong"
else:
waypoint = "right"
timerPlay.cancel()
stage = "CHECK"
timerCheck = threading.Timer(1.0, creatGlyph)
timerCheck.start()
#. This function is called when the game times out. It invokes ``checkPoint`` with "empty" to indicate no button was pressed in time.
.. code-block:: python
def timeOut():
""" Handle game timeout scenario. """
checkPoint("empty")
#. This function checks the state of the buttons. If ``AButtonPin`` is pressed (and ``BButtonPin`` is not), it calls ``checkPoint`` with "right". If ``BButtonPin`` is pressed (and ``AButtonPin`` is not), it calls ``checkPoint`` with "left".
.. code-block:: python
def getKey():
""" Detect button press and trigger checkpoint. """
if AButtonPin.is_pressed and not BButtonPin.is_pressed:
checkPoint("right")
elif not AButtonPin.is_pressed and BButtonPin.is_pressed:
checkPoint("left")
#. The ``main`` function controls the game flow. It starts by creating a glyph, then continuously checks the game stage. If in "PLAY" stage, it displays the current glyph and checks for button presses. In "CHECK" stage, it displays the feedback based on the player's action.
.. code-block:: python
def main():
""" Main game loop. """
creatGlyph()
while True:
if stage == "PLAY":
display(arrow[waypoint])
getKey()
elif stage == "CHECK":
display(check[waypoint])
#. This function cancels any running timers when the program exits, ensuring a clean shutdown.
.. code-block:: python
def destroy():
""" Clean up resources on program exit. """
global timerPlay, timerCheck
timerPlay.cancel() # Cancel the play timer
timerCheck.cancel() # Cancel the checkpoint timer
#. The game is run in a ``try`` block. If a ``KeyboardInterrupt`` (like pressing Ctrl+C) occurs, it catches the exception and calls ``destroy`` to clean up before exiting.
.. code-block:: python
# Run the game, handle KeyboardInterrupt for clean exit
try:
main()
except KeyboardInterrupt:
destroy()