Skip to content

Instantly share code, notes, and snippets.

@samneggs
Last active May 17, 2025 15:40
Show Gist options
  • Save samneggs/4c93714c2be20085751c95cd5b615437 to your computer and use it in GitHub Desktop.
Save samneggs/4c93714c2be20085751c95cd5b615437 to your computer and use it in GitHub Desktop.
Boulder Dash on Pi Pico 2 in MicroPython
# Boulder Dash Pico2
from st7796 import LCD_3inch5
from gfx import Gfx
from machine import Pin, PWM, SPI
from uctypes import addressof
from time import sleep, sleep_ms, ticks_us, ticks_diff, ticks_ms
import gc, array, re, _thread
from random import randint, random
MAXSCREEN_X = const(240)
MAXSCREEN_Y = const(160)
SCALE = const(13)
SHOWING = const(0)
SHUTDOWN = const(1)
PLAYER_PARAMS = const(10)
POS = const(0)
DIR = const(1)
EXTRA_L = const(2)
GAME_PARAMS = const(10)
FPS = const(0)
LIVES = const(1)
ANIMATE = const(2)
TOT_DIAM = const(3)
TIME = const(4)
SCORE = const(5)
STATE = const(6)
LEV_DIAM = const(7)
#game states
PLAYING = const(0)
TIME_UP = const(1)
EXPLODE = const(2)
LEVDONE = const(3)
BOARD_WIDTH = const(40)
BOARD_HEIGHT = const(22)
NUM_BOULDERS = const(30)
NUM_DIAMONDS = const(15)
NUM_FIREFLIES = const(3) # 3
NUM_BUTTERFLIES = const(2) # 2
NUM_SPRITES = const(14)
EXTRA_SCORE = const(500)
# Constants for tile types
EMPTY = const(0)
WALL = const(1)
DIRT = const(2)
BOULDER = const(3)
DIAMOND = const(4)
PLAYER = const(5)
MAGIC_WALL = const(6)
AMOEBA = const(7)
FIREFLY = const(8)
BUTTERFLY = const(9)
EXIT = const(10)
STANDING = const(11)
EXPLOSION = const(12)
LIFE_UP = const(13)
TILE_SIZE = const(16) # Size of each tile in pixels
SCREEN_TILES_X = const(MAXSCREEN_X // TILE_SIZE)
SCREEN_TILES_Y = const(MAXSCREEN_Y // TILE_SIZE)
SCROLL_MARGIN = const(2) # Number of tiles from edge before scrolling
# Configuration for sprites: (sprite_number, frame_count)
SPRITE_CONFIG = {
EMPTY: (0, 1),
DIRT: (1, 1),
MAGIC_WALL: (2, 3),
BOULDER: (5, 1),
WALL: (9, 1),
EXPLOSION: (15,3),
STANDING: (18, 1),
BUTTERFLY: (20, 8),
DIAMOND: (28, 8),
FIREFLY: (36, 8),
AMOEBA: (44, 4),
PLAYER: (60, 8),
EXIT: (116, 6),
LIFE_UP: (132, 8)
}
char_map=array.array('b',(
0x00, 0x3e, 0x67, 0x67, 0x67, 0x67, 0x7f, 0x3e, # U+0030 (0)
0x00, 0x18, 0x1c, 0x1c, 0x18, 0x18, 0x7e, 0x7e, # U+0031 (1)
0x00, 0x3e, 0x73, 0x38, 0x1c, 0x0e, 0x7f, 0x7f, # U+0032 (2)
0x00, 0x7e, 0x30, 0x18, 0x30, 0x67, 0x7f, 0x3e, # U+0033 (3)
0x00, 0x03, 0x03, 0x3b, 0x7f, 0x38, 0x38, 0x38, # U+0034 (4)
0x00, 0x7f, 0x07, 0x3f, 0x60, 0x67, 0x7f, 0x3e, # U+0035 (5)
0x00, 0x3e, 0x07, 0x3f, 0x67, 0x67, 0x7f, 0x3e, # U+0036 (6)
0x00, 0x7f, 0x71, 0x38, 0x1c, 0x0e, 0x07, 0x07, # U+0037 (7)
0x00, 0x3e, 0x67, 0x3e, 0x67, 0x67, 0x7f, 0x3e, # U+0038 (8)
0x00, 0x3e, 0x67, 0x67, 0x7e, 0x38, 0x1c, 0x0e, # U+0039 (9)
))
@micropython.viper
def show_num_viper(num:int,x_offset:int,y_offset:int,color:int):
if num < 0: return
char_ptr = ptr8(char_map)
screen_ptr = ptr16(LCD.fbdraw)
size = 1 # 1,2,3
char = 0
offset = MAXSCREEN_X*y_offset+x_offset
first = 1
while num > 0 or first:
first = 0
total = num//10
digit = num - (total * 10)
num = total
for y in range(8):
row_data = char_ptr[digit*8+y]
for x in range(8):
if row_data & (1<<x) > 0:
addr = size*y*MAXSCREEN_X+x-(char*8)+offset
screen_ptr[addr] = color
if size>1:
screen_ptr[MAXSCREEN_X+addr] = color
if size>2:
screen_ptr[2*MAXSCREEN_X+addr] = color
char += 1
def calculate_total_frames():
return sum(frame_count for _, frame_count in SPRITE_CONFIG.values())
def init_sprites(filename, tile_size):
global SPRITES, SPRITE_START, FRAMES
total_frames = calculate_total_frames()
SPRITES = bytearray()
SPRITE_START = bytearray(NUM_SPRITES) # number sprite types
FRAMES = bytearray(NUM_SPRITES)
with open(filename, "rb") as file:
file.read(4) # Read and skip the header
sprite_index = 0
for sprite_type in range(NUM_SPRITES): # 11 sprite types
sprite_number, frame_count = SPRITE_CONFIG.get(sprite_type, (0, 0))
SPRITE_START[sprite_type] = sprite_index
FRAMES[sprite_type] = frame_count
for frame in range(frame_count):
# Calculate the position of the sprite in the file
file_position = 4 + (sprite_number + frame) * (tile_size * tile_size * 2)
file.seek(file_position)
# Read the sprite data and extend SPRITES
sprite_data = file.read(tile_size * tile_size * 2)
SPRITES.extend(sprite_data)
sprite_index += 1
def init_pot():
global POT_X,POT_Y,POT_X_ZERO,POT_Y_ZERO
POT_X = machine.ADC(26)
POT_Y = machine.ADC(27)
POT_X_ZERO = 0
POT_Y_ZERO = 0
for i in range(1000):
POT_X_ZERO += POT_X.read_u16()
POT_Y_ZERO += POT_Y.read_u16()
POT_X_ZERO = POT_X_ZERO//1000
POT_Y_ZERO = POT_Y_ZERO//1000
pot_scale = 12
@micropython.viper
def read_pot():
g = ptr32(GAME)
global PLAYER_ARRY, BOARD
pot_scale = 12
x_inc = int(POT_X.read_u16() - POT_X_ZERO) >> pot_scale
y_inc = int(POT_Y.read_u16() - POT_Y_ZERO) >> pot_scale
if -2 < x_inc < 2:
x_inc = 0
if -5 < y_inc < 5:
y_inc = 0
player = ptr32(PLAYER_ARRY)
board = ptr8(BOARD)
width = int(BOARD_WIDTH)
current_pos = int(player[POS])
new_pos = current_pos
if x_inc < 0:
new_pos -= 1
player[DIR] = 1
elif x_inc > 0:
new_pos += 1
player[DIR] = 0
elif y_inc < 0:
new_pos += width
elif y_inc > 0:
new_pos -= width
length = int(len(BOARD))
# Check if the new position is valid and update if possible
if new_pos == current_pos: board[new_pos] = STANDING
if 0 <= new_pos < length:
if board[new_pos] == BUTTERFLY or board[new_pos] == FIREFLY:
player_hit()
return
elif board[new_pos] == EMPTY or board[new_pos] == DIRT or board[new_pos] == DIAMOND:
if board[new_pos] == DIAMOND:
g[TOT_DIAM] += 1
g[LEV_DIAM] += 1
g[SCORE] += 10
if not (g[SCORE] % EXTRA_SCORE):
player[EXTRA_L] = 1
board[current_pos] = EMPTY
board[new_pos] = PLAYER
player[POS] = new_pos
elif board[new_pos] == BOULDER and (x_inc != 0): # Check if trying to push boulder horizontally
push_pos = new_pos + (1 if x_inc < 0 else -1) # Position to push boulder to
if 0 <= push_pos < length: # and board[push_pos] == EMPTY or board[push_pos] == BUTTERFLY:
# Push the boulder
if board[push_pos] == EMPTY:
board[push_pos] = BOULDER
board[new_pos] = PLAYER
board[current_pos] = EMPTY
player[POS] = new_pos
elif board[push_pos] == BUTTERFLY:
board[current_pos] = EMPTY
fill_box(push_pos, DIAMOND)
player[POS] = new_pos
elif board[push_pos] == FIREFLY:
board[current_pos] = EMPTY
fill_box(push_pos, EMPTY)
player[POS] = new_pos
elif board[push_pos] == MAGIC_WALL:
board[current_pos] = EMPTY
player[POS] = new_pos
board[push_pos] = DIAMOND
elif board[new_pos] == EXIT and g[LEV_DIAM] > 8:
g[STATE] = LEVDONE
def init_game():
global GAME, FPS_ARRY, TO_UPDATE, ENEMY_DIRS, MOVED, DX, DY, BOARD, FALLING, PLAYER_ARRY
size = int(BOARD_WIDTH * BOARD_HEIGHT)
BOARD = bytearray(size)
PLAYER_ARRY = array.array('i',0 for _ in range(PLAYER_PARAMS))
GAME = array.array('i',0 for _ in range(GAME_PARAMS))
ENEMY_DIRS = bytearray(BOARD_WIDTH * BOARD_HEIGHT)
FPS_ARRY = bytearray(35)
TO_UPDATE = bytearray(BOARD_WIDTH * BOARD_HEIGHT)
FALLING = bytearray(BOARD_WIDTH * BOARD_HEIGHT)
MOVED = bytearray(BOARD_WIDTH * BOARD_HEIGHT)
# Directions: 0 = Left, 1 = Up, 2 = Right, 3 = Down
DX = array.array('i',[int(-1), int(0), int(1), int(0)])
DY = array.array('i',[int(0), int(-1), int(0), int(1)])
GAME[FPS] = 0
GAME[LIVES] = 3
GAME[SCORE] = 0
def parse_level_file(filename, board, width, height):
# Dictionary to map object names to sprite numbers
object_map = {
'SPACE': EMPTY, # SPACE is now correctly mapped to EMPTY
'DIRT': DIRT,
'WALL': WALL,
'BOULDER': BOULDER,
'DIAMOND': DIAMOND,
'INBOX': PLAYER,
'OUTBOX': EXIT
}
# Initialize the entire board with DIRT
#board = [DIRT for _ in range(width * height)]
with open(filename, 'r') as file:
content = file.readlines()
# Parse RandomFill
for line in content:
if line.startswith('RandomFill='):
fill_items = line.strip().split('=')[1].split()
total_weight = sum(int(fill_items[i+1]) for i in range(0, len(fill_items), 2))
for pos in range(width * height):
rand_val = random() * 256 #total_weight
cumulative_weight = 0
if board[pos] == DIRT:
for i in range(0, len(fill_items), 2):
item_name = fill_items[i]
weight = int(fill_items[i+1])
cumulative_weight += weight
#print(rand_val,cumulative_weight)
#if rand_val <= cumulative_weight:
if rand_val <= weight:
board[pos] = object_map.get(item_name, EMPTY)
#break
break # Stop after processing RandomFill
# Parse objects
in_objects_section = False
for line in content:
if line.strip() == '[objects]':
in_objects_section = True
continue
if line.strip() == '[/objects]':
break
if in_objects_section and '=' in line:
obj_type, obj_data = line.strip().split('=')
obj_data = obj_data.split()
if obj_type == 'Line':
x1, y1, x2, y2 = map(int, obj_data[:4])
item = object_map.get(obj_data[4], DIRT)
for x in range(x1, x2+1):
for y in range(y1, y2+1):
board[y * width + x] = item
elif obj_type == 'Point':
x, y = map(int, obj_data[:2])
item = object_map.get(obj_data[2], DIRT)
board[y * width + x] = item
if obj_data[2] == 'INBOX':
PLAYER_ARRY[POS] = y * width + x
return board
def init_game_board(width: int, height: int):
size = int(width * height)
# Fill the board with dirt
for i in range(size):
BOARD[i] = DIRT
# Add walls around the edges
for i in range(width):
BOARD[i] = WALL # Top wall
BOARD[size - width + i] = WALL # Bottom wall
for i in range(height):
BOARD[i * width] = WALL # Left wall
BOARD[(i + 1) * width - 1] = WALL # Right wall
@micropython.viper
def init_enemy_dirs(width: int, height: int):
board = ptr8(BOARD)
enemy_dirs = ptr8(ENEMY_DIRS)
size = int(width * height)
for pos in range(size):
if board[pos] == FIREFLY or board[pos] == BUTTERFLY:
enemy_dirs[pos] = int(randint(0, 3))
else:
enemy_dirs[pos] = 0 # Default direction for non-enemy tiles
def add_items(width: int, height: int, num_boulders: int, num_diamonds: int,
num_fireflies: int, num_butterflies: int):
board = BOARD
playable_width = width - 2 # Exclude left and right walls
playable_height = height - 2 # Exclude top and bottom walls
playable_size = playable_width * playable_height
items_to_add = [(BOULDER, num_boulders), (DIAMOND, num_diamonds),
(FIREFLY, num_fireflies), (BUTTERFLY, num_butterflies)]
def get_playable_position():
while True:
pos = randint(0, playable_size - 1)
x = pos % playable_width + 1 # Add 1 to avoid left wall
y = pos // playable_width + 1 # Add 1 to avoid top wall
board_pos = y * width + x
if board[board_pos] == DIRT:
return board_pos
for item, count in items_to_add:
for _ in range(count):
pos = get_playable_position()
board[pos] = item
# Add player and exit
player_pos = get_playable_position()
board[player_pos] = PLAYER
PLAYER_ARRY[POS] = player_pos
exit_pos = get_playable_position()
board[exit_pos] = EXIT
# Add some magic walls (just a few for now)
for _ in range(3):
magic_wall_pos = get_playable_position()
board[magic_wall_pos] = MAGIC_WALL
# Add a small amoeba
amoeba_pos = get_playable_position()
board[amoeba_pos] = AMOEBA
@micropython.viper
def fill_box(pos: int, sprite: int):
board = ptr8(BOARD)
width = int(BOARD_WIDTH)
height = int(BOARD_HEIGHT)
x = pos % width
y = pos // width
for dy in range(-1, 2):
for dx in range(-1, 2):
new_x = x + dx
new_y = y + dy
# Check if the new position is within the board boundaries
if 0 <= new_x < width and 0 <= new_y < height:
pos = new_y * width + new_x
board[pos] = sprite
@micropython.viper
def apply_boulder_physics(width: int, height: int):
board = ptr8(BOARD)
g = ptr32(GAME)
size = int(width * height)
to_update = ptr8(TO_UPDATE)
falling = ptr8(FALLING) # Persistent array to track falling objects
for i in range(size):
to_update[i] = 0
# First pass: mark objects that need to be updated
for y in range(height - 2, 0, -1):
for x in range(1, width - 1):
pos = int(y * width + x)
below_pos = int(pos + width)
if board[pos] == BOULDER or board[pos] == DIAMOND:
if board[below_pos] == EMPTY:
to_update[pos] = 1 # Fall straight down
elif board[below_pos] == BOULDER or board[below_pos] == DIAMOND:
# Check if it can roll
left_pos = int(pos - 1)
right_pos = int(pos + 1)
if board[left_pos] == EMPTY and board[left_pos + width] == EMPTY:
to_update[pos] = 2 # Roll left
elif board[right_pos] == EMPTY and board[right_pos + width] == EMPTY:
to_update[pos] = 3 # Roll right
else:
falling[pos] = 0 # Reset falling state if it can't move
elif board[below_pos] == MAGIC_WALL:
to_update[pos] = 4 # Transform through magic wall
elif (board[below_pos] == PLAYER or board[below_pos] == STANDING) and falling[pos]:
to_update[pos] = 5 # Falling object hits player
elif board[below_pos] == DIRT:
falling[pos] = 0 # Reset falling state if it can't move
elif board[below_pos] == BUTTERFLY or board[below_pos] == FIREFLY:
to_update[pos] = 6
# Second pass: apply updates
for pos in range(size):
update = to_update[pos]
if update == 1: # Fall straight down
board[pos + width] = board[pos]
board[pos] = EMPTY
falling[pos + width] = 1 # Transfer falling state
falling[pos] = 0 # Reset old position
elif update == 2: # Roll left
board[pos - 1] = board[pos]
board[pos] = EMPTY
falling[pos - 1] = falling[pos] # Transfer falling state
falling[pos] = 0 # Reset old position
elif update == 3: # Roll right
board[pos + 1] = board[pos]
board[pos] = EMPTY
falling[pos + 1] = falling[pos] # Transfer falling state
falling[pos] = 0 # Reset old position
elif update == 4: # Transform through magic wall
if board[pos] == BOULDER:
board[pos + width] = DIAMOND
else:
board[pos + width] = BOULDER
board[pos] = EMPTY
falling[pos + width] = 1 # Mark as falling after transformation
falling[pos] = 0 # Reset old position
elif update == 5: # Falling object hits player
player_hit()
elif update == 6:
fill_box(pos, DIAMOND)
@micropython.viper
def update_amoeba(width: int, height: int):
board = ptr8(BOARD)
size = int(width * height)
to_update = ptr8(TO_UPDATE)
for i in range(int(len(TO_UPDATE))):
to_update[i] = 0
# Mark cells where amoeba can grow
for y in range(1, height - 1):
for x in range(1, width - 1):
pos = int(y * width + x)
if board[pos] == AMOEBA:
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
new_pos = int(pos + int(dy) * width + int(dx))
if board[new_pos] == EMPTY or board[new_pos]== DIRT:
to_update[new_pos] = 1
# Grow amoeba
for i in range(size):
if to_update[i] == 1 and (board[i] == EMPTY or board[i] == DIRT):
board[i] = AMOEBA
@micropython.viper
def move_enemies(width: int, height: int):
board = ptr8(BOARD)
enemy_dirs = ptr8(ENEMY_DIRS)
dx = ptr32(DX)
dy = ptr32(DY)
moved = ptr8(MOVED)
size = int(width * height)
for i in range(size):
moved[i] = 0
for pos in range(size):
if (board[pos] == FIREFLY or board[pos] == BUTTERFLY) and not moved[pos]:
current_dir = enemy_dirs[pos]
x = pos % width
y = pos // width
# Try to move in the current direction
new_x = x + dx[current_dir]
new_y = y + dy[current_dir]
new_pos = new_y * width + new_x
if 0 < new_x < width - 1 and 0 < new_y < height - 1:
if board[new_pos] == EMPTY:
# Move in the current direction
board[new_pos] = board[pos]
board[pos] = EMPTY
enemy_dirs[new_pos] = current_dir
moved[new_pos] = 1
elif board[new_pos] == PLAYER or board[new_pos] == STANDING:
player_hit()
return
else:
# Turn clockwise
new_dir = (current_dir + 1) % 4
enemy_dirs[pos] = new_dir
moved[pos] = 1
else:
# If the enemy is at the edge, turn clockwise
new_dir = (current_dir + 1) % 4
enemy_dirs[pos] = new_dir
moved[pos] = 1
def player_hit():
GAME[LIVES]-= 1
GAME[STATE] = EXPLODE
def reset_level():
init_game_board(BOARD_WIDTH, BOARD_HEIGHT)
#add_items(BOARD_WIDTH, BOARD_HEIGHT, NUM_BOULDERS, NUM_DIAMONDS, NUM_FIREFLIES, NUM_BUTTERFLIES)
#init_enemy_dirs(BOARD_WIDTH, BOARD_HEIGHT)
parse_level_file('level1.bd', BOARD, BOARD_WIDTH, BOARD_HEIGHT)
GAME[TIME] = 150
GAME[LEV_DIAM] = 0
GAME[STATE] = PLAYING
def level_done():
pass
@micropython.viper
def draw_board():
g = ptr32(GAME)
sprite = ptr16(SPRITES)
frames = ptr8(FRAMES)
start_pos = ptr8(SPRITE_START)
board = ptr8(BOARD)
buffer = ptr16(LCD.fbdraw)
width = int(BOARD_WIDTH)
height = int(BOARD_HEIGHT)
max_x = int(MAXSCREEN_X)
max_y = int(MAXSCREEN_Y)
player = ptr32(PLAYER_ARRY)
player_x = int(player[POS] % width)
player_y = int(player[POS] // width)
player_dir = int(player[DIR])
# Calculate the top-left corner of the viewable area
view_x = player_x - SCREEN_TILES_X // 2
view_x = 0 if view_x < 0 else (width - SCREEN_TILES_X if view_x > width - SCREEN_TILES_X else view_x)
view_y = player_y - SCREEN_TILES_Y // 2
view_y = 0 if view_y < 0 else (height - SCREEN_TILES_Y if view_y > height - SCREEN_TILES_Y else view_y)
for y in range(SCREEN_TILES_Y):
for x in range(SCREEN_TILES_X):
board_x = view_x + x
board_y = view_y + y
tile = board[board_y * width + board_x]
if tile == EMPTY and player[EXTRA_L]:
tile = LIFE_UP
animate = g[ANIMATE] % frames[tile]
offset = (start_pos[tile] + animate) * TILE_SIZE * TILE_SIZE
# Draw a 16x16 square for each tile
x2 = x * TILE_SIZE
y2 = y * TILE_SIZE
for dy in range(TILE_SIZE):
y3 = dy * TILE_SIZE
y4 = (y2 + dy) * max_x + x2
if tile == PLAYER and player_dir == 0:
# Reverse player sprite horizontally
for dx in range(TILE_SIZE):
color = sprite[y3 + (TILE_SIZE - 1 - dx) + offset]
buffer[y4 + dx] = color
else:
# Draw other sprites normally
for dx in range(TILE_SIZE):
color = sprite[y3 + dx + offset]
if color:
buffer[y4 + dx] = color
@micropython.viper
def test_draw():
g = ptr32(GAME)
sprite = ptr16(SPRITES)
frames = ptr8(FRAMES)
start_pos = ptr8(SPRITE_START)
board = ptr8(BOARD)
buffer = ptr16(LCD.fbdraw)
width = int(BOARD_WIDTH)
height = int(BOARD_HEIGHT)
max_x = int(MAXSCREEN_X)
max_y = int(MAXSCREEN_Y)
player = ptr32(PLAYER_ARRY)
player_x = int(player[POS] % width)
player_y = int(player[POS] // width)
tile = PLAYER
y = 5
g[ANIMATE] = 1
for x in range(8):
for dy in range(TILE_SIZE):
for dx in range(TILE_SIZE):
animate = g[ANIMATE] % frames[tile]
animate = x
offset = start_pos[tile] + animate
color = sprite[(dy * TILE_SIZE + dx) + offset * TILE_SIZE * TILE_SIZE]
buffer[(y * TILE_SIZE + dy) * max_x + (x * TILE_SIZE + dx)] = color
@micropython.viper
def draw():
status = ptr8(LCD.aux)
while not status[SHOWING]: sleep_ms(1)
LCD.fbdraw.rect(0,0,MAXSCREEN_X,MAXSCREEN_Y,0,1)
draw_board()
game = ptr32(GAME)
LCD.fbdraw.rect(0,8,8*2,9,0,1) # FPS background
show_num_viper(game[FPS],8,8,0xff) # FPS
LCD.fbdraw.rect(24,0,8*2,9,0,1) # diamonds background
show_num_viper(game[TOT_DIAM],32,0,0xff) # diamonds
LCD.fbdraw.rect(0,0,8*2,9,0,1)
show_num_viper(game[LIVES],8,0,0xff)
LCD.fbdraw.rect(80-(8*2),0,8*3,9,0,1)
show_num_viper(game[TIME],80,0,0xff)
LCD.fbdraw.rect(160-(8*4),0,8*4,9,0,1)
show_num_viper(game[SCORE],150,0,0xff)
status[SHOWING] = 0
@micropython.asm_thumb
def avg_fps_asm(r0,r1): # r0 = fps[] , r1 = current_fps
ldrb(r2,[r0,0]) # r2 = fps[0]
add(r2,r2,1) # fps[0] += 1
cmp(r2,33)
blt(LT_32) # if fps[0] > 32:
mov(r2,1)
label(LT_32)
strb(r2,[r0,0]) # fps[0] = new index
add(r2,r2,r0)
strb(r1,[r2,0]) # fps[fps[0]] = current_fps
mov(r2,1) # r2 = i
mov(r3,0) # r3 = tot
label(LOOP)
add(r0,r0,1)
ldrb(r4,[r0,0]) # r4 = fps[i]
add(r3,r3,r4) # tot += fps[i]
add(r2,r2,1)
cmp(r2,33) #33
blt(LOOP)
asr(r0,r3,5)
@micropython.viper
def main():
init_sprites('Enhanced2.bin', TILE_SIZE)
init_pot()
init_game()
player = ptr32(PLAYER_ARRY)
reset_level()
g = ptr32(GAME)
pot_ticks = 0
enemy_ticks = 0
amoeba_ticks = 0
boulder_ticks = 0
animate_ticks = 0
second_ticks = 0
explode_ticks = 0
extra_ticks = 0
ms250_ticks = 0
while 1:#not RESET_PB():
gticks = int(ticks_ms())
sleep(0.001) # allows ctrl-c
if g[STATE] == PLAYING:
if gticks - second_ticks > 1000:
second_ticks = gticks
g[TIME] -= 1
if g[TIME] < 0 :
g[STATE] = EXPLODE
if gticks - pot_ticks > 150:
pot_ticks = gticks
read_pot()
if gticks - boulder_ticks > 300:
boulder_ticks = gticks
apply_boulder_physics(BOARD_WIDTH, BOARD_HEIGHT)
if gticks - enemy_ticks > 500:
enemy_ticks = gticks
move_enemies(BOARD_WIDTH, BOARD_HEIGHT)
if gticks - amoeba_ticks > 100_000:
amoeba_ticks = gticks
update_amoeba(BOARD_WIDTH, BOARD_HEIGHT)
if player[EXTRA_L]:
if extra_ticks == 0:
extra_ticks = gticks
if gticks - extra_ticks > 2000:
extra_ticks = 0
player[EXTRA_L] = 0
if g[STATE] == EXPLODE:
if explode_ticks == 0:
explode_ticks = gticks
pos = PLAYER_ARRY[POS]
fill_box(pos,EXPLOSION)
elif gticks - explode_ticks > 2000:
explode_ticks = 0
reset_level()
elif gticks - explode_ticks > 250:
pos = PLAYER_ARRY[POS]
fill_box(pos,EMPTY)
if gticks - animate_ticks > 100:
animate_ticks = gticks
g[ANIMATE] += 1
if int(g[ANIMATE]) > 7: g[ANIMATE] = 0
if g[STATE] == LEVDONE and gticks - ms250_ticks > 250 :
g[SCORE] += 1
g[TIME] -= 1
if g[TIME] == 0:
reset_level()
draw()
g[FPS] = int(avg_fps_asm(FPS_ARRY,1_000//int(ticks_diff(ticks_ms(),gticks))))
def shutdown():
LCD.aux[SHUTDOWN] = 1
sleep_ms(200)
LCD.writecommand(0x28) #LCD off
sleep_ms(200)
machine.freq(150_000_000)
print('core0 done')
@micropython.viper
def core1():
status = ptr8(LCD.aux)
sleep_ms(200)
while not status[SHUTDOWN]:
gticks=int(ticks_ms())
status[SHOWING] = 1
LCD.show_all()
#sleep_ms(3)
LCD.flip()
LCD.fps = (1_000//int(ticks_diff(int(ticks_ms()),gticks)))
print('core1 done')
if __name__=='__main__':
FIRE_BUTTON = Pin(14, Pin.IN, Pin.PULL_UP)
machine.freq(220_000_000) #220
machine.mem32[0x40010048] = 1<<11 # enable peri_ctrl clock
LCD = LCD_3inch5()
gfx = Gfx(LCD.fbdraw,240,160)
_thread.start_new_thread(core1, ())
sleep_ms(200)
try:
main()
except KeyboardInterrupt :
print('CTRL-C!')
shutdown()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment