Description

The initial concept for this game is a back-and-forth match between the player and computer. It starts with dropping a marble into the playfield, and each side earns points by hitting the marble into the opposite end zone. The computer constantly moves its paddle on the far side pseudo-randomly to defend its goal, while the player manually controls the paddle on their end via photosensor. The closer one’s finger gets to the sensor, the further clockwise the paddle spins. Conversely, the farther one’s finger moves to the sensor, the further counterclockwise it spins. By randomizing the way the opposition spins its paddle, the game comes down to reaction time and getting used to controlling the sensor and paddle. The final outcome is a game where players must hone in on fine motor control and be ready to take advantage of any opportunity to shoot the marble into their opponent’s end zone, creating more of a rally spectacle.

# dual_spin.py
#
# Raspberry Pi Pico - DC motor motion demo
#
# Demonstrates operating two DC motors driven by a DRV8833.
#
# This assumes a Pololu DRV8833 dual motor driver has been wired up to the Pico as follows:
#   Pico pin 24, GPIO18   -> AIN1
#   Pico pin 25, GPIO19   -> AIN2
#   Pico pin 26, GPIO20   -> BIN2
#   Pico pin 27, GPIO21   -> BIN1
#   any Pico GND          -> GND

# DRV8833 carrier board: https://www.pololu.com/product/2130

################################################################
# CircuitPython module documentation:
# time    https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/index.html
# math    https://circuitpython.readthedocs.io/en/latest/shared-bindings/math/index.html
# board   https://circuitpython.readthedocs.io/en/latest/shared-bindings/board/index.html
# pwmio   https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html

################################################################################
# print a banner as reminder of what code is loaded
print("Starting dual_spin script.")

# load standard Python modules
import math, time

# load the CircuitPython hardware definition module for pin definitions
import board

# load the CircuitPython pulse-width-modulation module for driving hardware
import pwmio
from digitalio import DigitalInOut, Direction, Pull

import analogio
import digitalio
import random

#--------------------------------------------------------------------------------
# Class to represent a single dual H-bridge driver.
class DRV8833():
    def __init__(self, AIN1=board.GP18, AIN2=board.GP19, BIN2=board.GP20, BIN1=board.GP21, pwm_rate=20000):
        # Create a pair of PWMOut objects for each motor channel.
        self.ain1 = pwmio.PWMOut(AIN1, duty_cycle=0, frequency=pwm_rate)
        self.ain2 = pwmio.PWMOut(AIN2, duty_cycle=0, frequency=pwm_rate)

        self.bin1 = pwmio.PWMOut(BIN1, duty_cycle=0, frequency=pwm_rate)
        self.bin2 = pwmio.PWMOut(BIN2, duty_cycle=0, frequency=pwm_rate)

    def write(self, channel, rate):
        """Set the speed and direction on a single motor channel.

        :param channel:  0 for motor A, 1 for motor B
        :param rate: modulation value between -1.0 and 1.0, full reverse to full forward."""

        # convert the rate into a 16-bit fixed point integer
        pwm = min(max(int(2**16 * abs(rate)), 0), 65535)

        if channel == 0:
            if rate < 0:
                self.ain1.duty_cycle = pwm
                self.ain2.duty_cycle = 0
            else:
                self.ain1.duty_cycle = 0
                self.ain2.duty_cycle = pwm
        else:
            if rate < 0:
                self.bin1.duty_cycle = pwm
                self.bin2.duty_cycle = 0
            else:
                self.bin1.duty_cycle = 0
                self.bin2.duty_cycle = pwm


#--------------------------------------------------------------------------------
# Create an object to represent a dual motor driver.
print("Creating driver object.")
driver = DRV8833()

#--------------------------------------------------------------------------------
# Begin the main processing loop.  This is structured as a looping script, since
# each movement primitive 'blocks', i.e. doesn't return until the action is
# finished.

# Set up built-in green LED for output.
led = DigitalInOut(board.LED)  # GP25
led.direction = Direction.OUTPUT

# Set up a digital input on GP15, which is physically pin 20 at one corner of the board.
# E.g., this may be attached to a switch or photointerrupter with associated pullup resistor.
switch = DigitalInOut(board.GP15)
switch.direction = Direction.INPUT


# Set up an analog input on ADC0 (GP26), which is physically pin 31.
# E.g., this may be attached to photocell or photointerrupter with associated pullup resistor.
sensor = analogio.AnalogIn(board.A0)

# These may be need to be adjusted for your particular hardware.  The Pico has
# 12-bit analog-to-digital conversion so the actual conversion has 4096 possible
# values, but the results are scaled to a 16-bit unsigned integer with range
# from 0 to 65535.
lower_threshold =  8000
upper_threshold = 45000

#---------------------------------------------------------------
# Run the main loop: script equivalent to Arduino loop()

# The following logic detects input transitions using a state machine with two
# possible states.  The state index reflects the current estimated state of the
# input.  The transition rules apply hysteresis in the form of assymmetric
# thresholds to reduce switching noise: the sensor value must rise higher than
# the upper threshold or lower than the lower threshold to trigger a state
# change.
state_index = False

#---------------------------------------------------------------
# Run the main loop: script equivalent to Arduino loop()

# Keep track of the previous switch state in order to detect input changes.
last_change_time = time.time()

start_time = time.time()
print("Starting main script.")
change_action_time = 0
while True:
    # Computer's paddle
    # Check if curr time &gt; change time
    # If so, create a new random movement.
    # print("Time", time.time() - start_time, "change action time", change_action_time)
    if time.time()-start_time &gt;= change_action_time:
        action_length = 0.7 * random.random()
        change_action_time = time.time() - start_time + action_length
        action_power = random.uniform(0.6, 1.0)

        if random.random() &gt; 0.5:
            action_power = action_power * -1.0

        driver.write(0, action_power)
        print("Power:", action_power, "time", action_length)

    # Read the sensor once per cycle.
    sensor_level = sensor.value

    # Player's paddle
    player_power = 2.0*(float(sensor_level)/float(65535))- 1.0
    
    driver.write(1, player_power)



    # uncomment the following to print tuples to plot with the mu editor
    # print((sensor_level,))
    # time.sleep(0.02)  # slow sampling to avoid flooding