2 - Snake

Video

Code

from machine import Pin, PWM
import time
from random import randint
import _thread

class LED_Matrix():
    def __init__(self):
        
        # Create a list of pin numbers
        self.row_pins = [6,4,7,5,3,8,2,9]
        self.row_pins.reverse()
        self.col_pins = [10,11,12,13,18,19,20,21]
        self.col_pins.reverse()
        
        # Create empty lists to contain the Pin objects
        self.rows = []
        self.cols = []
        
        # Use the list of pin numbers to create
        # Pin objects and add them to the lists
        # created above.
        for i in self.row_pins:
            self.rows.append(Pin(i, Pin.OUT))
            
        for i in self.col_pins:
            self.cols.append(Pin(i, Pin.OUT))
        
        # Create empty 8x8 array
        self.matrix = [
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0]
            ]
        
        # Create a counter for current column
        self.cur_col = 0
        
    def clear(self):
        self.matrix = [
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0]
            ]
      
    def next_row(self):
        # Deactivate current row
        self.cols[self.cur_col].value(0)
        
        # Increment current row
        self.cur_col = (self.cur_col + 1) % 8
        
        # Load next row's columns
        for i in range(8):
            self.rows[i].value(self.matrix[self.cur_col][i])
        
        # Activate next row
        self.cols[self.cur_col].value(1)
        
    def draw(self, tup_list):
        for row , col in tup_list:
            self.matrix[col][row] = 1


class Snake():
    def __init__(self):
        self.pos = [(4,4)] # A list of tuples that contains coordinates for each segment
        self.len = 2       # Current length of the snake
        self.dx = 1        # Change in X position per frame
        self.dy = 0        # Change in Y position per frame
        
        self.notes = [262,294,330,349,392,440,495,523,587,659,698,784,880,988,1047,1175,1319,1397,1568,1760,1976,2093]
        self.cur_note = 0
        self.spkr = PWM(Pin(22)) # Create PWM object for speaker
        self.sprk_vol = 2**10
        
        
        self.spawn_apple() # Call a method to spawn an apple
        
    def spawn_apple(self):
        self.apple = [(randint(0,7),randint(0,7))] # Spawn Apple at Random Location
        # Retry if it landed on the snake
        for tup in self.pos:
            if tup == self.apple[0]:
                self.spawn_apple()
    
    def ate_apple(self):
        print(f'Ate an apple!')
        self.len += 1
        self.cur_note += 1
        
        self.spkr.freq(self.notes[self.cur_note]) # Set frequency in Hz
        self.spkr.duty_u16(self.sprk_vol)
        time.sleep_ms(100)
        self.spkr.duty_u16(0) # Set duty to 0
        self.spawn_apple()
    
    def reset(self):
        print('Reset')
        for self.i in range (self.cur_note, -1, -1):
            print(f'Note{self.i}')
            self.spkr.freq(self.notes[self.i])
            self.spkr.duty_u16(self.sprk_vol)
            time.sleep_ms(200)
            self.spkr.duty_u16(0) # Set duty to 0
            time.sleep_ms(50)
        
        self.len = 2
        self.cur_note = 0
        self.spawn_apple()  
        
    def up(self):
        print('up')
        # Prevent Snake from flipping back on itself
        if self.dy == -1:
            return
        # Otherwise set direction to up
        else:
            self.dx = 0
            self.dy = 1
    
    def down(self):
        print('down')
        # Prevent Snake from flipping back on itself
        if self.dy == 1:
            return
        # Otherwise set direction to down
        else:
            self.dx = 0
            self.dy = -1
    
    def left(self):
        print('left')
        # Prevent Snake from flipping back on itself
        if self.dx == 1:
            return
        # Otherwise set direction to left
        else:
            self.dx = -1
            self.dy = 0
    
    def right(self):
        print('right')
        # Prevent Snake from flipping back on itself
        if self.dx == -1:
            return
        # Otherwise set direction to right
        else:
            self.dx = 1
            self.dy = 0
    
    def update(self):
        # Get current position of snake head
        self.x, self.y = self.pos[0]
        
        # Move the head to the new position
        self.x += self.dx
        self.y += self.dy
        
        # Ensure the new position stays within bounds
        self.x = self.x % 8
        self.y = self.y % 8
        
        # Add new position to front of position list
        self.pos.insert(0,(self.x,self.y))
        
        # Detect Apple Collision
        if self.pos[0] == self.apple[0]:
            self.ate_apple()
        
        # Detect Self Collision
        for self.i in range(1,len(self.pos)):
            if self.pos[0] == self.pos[self.i]:
                self.reset()
        
        # Ensure snake only grows when eating apples
        while len(self.pos) > self.len:
            self.pos.pop()


def update_display():
    while True:
        global d
        d.next_row()
        time.sleep_ms(2)

def game_loop():
    # Connect to globals
    global d
    global s
    
    # Create Button Objects
    up = Pin(14, Pin.IN, Pin.PULL_UP)
    down = Pin(17, Pin.IN, Pin.PULL_UP)
    left = Pin(16, Pin.IN, Pin.PULL_UP)
    right = Pin(1, Pin.IN, Pin.PULL_UP)
    
    # Main Loop for game, runs at 10 Hz
    while True:
        # Get button inputs
        if not up.value():
            s.up()
        if not down.value():
            s.down()
        if not left.value():
            s.left()
        if not right.value():
            s.right()
            
        # Update Snake
        s.update()
        
        # Clear Display
        d.clear()
        
        # Draw Snake on Display
        print(s.pos)
        try:
            d.draw(s.pos)
        except:
            print('Could not draw snake, initiating reset.')
            s.reset()
        
        # Draw Apple on Display
        print(s.apple)
        try:
            d.draw(s.apple)
        except:
            print('Could not draw apple, initiating reset.')
            s.reset()
        
        # Do nothing for 100ms, (10Hz Frame-Rate)
        time.sleep_ms(100)


# Create LED Matrix object        
d = LED_Matrix()

# Create Snake Object
s = Snake()

# Use second core to constantly run the display
_thread.start_new_thread(update_display,())

# Use main core to run game loop at 10Hz
game_loop()
    

    

Last updated