# 2 - Snake

### Video

{% embed url="<https://youtu.be/BaRkOadMJEc>" %}

### Code

```python
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()
    

    

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learn.breadstick.ca/breadstick/pico-slices/slice-3-8x8-dot-matrix/micropython-code/2-snake.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
