Skip to content

Instantly share code, notes, and snippets.

@samneggs
Last active February 27, 2023 07:08
Show Gist options
  • Save samneggs/7ad05d05927a655e66bc6714da1dab78 to your computer and use it in GitHub Desktop.
Save samneggs/7ad05d05927a655e66bc6714da1dab78 to your computer and use it in GitHub Desktop.
Missile Command game in MicroPython on Pi Pico
# Missile Command game
from machine import Pin,SPI,PWM,ADC, Timer, reset, soft_reset
import framebuf, gc
import time, array, _thread
from time import sleep, ticks_us, ticks_diff, ticks_ms
from lcd_1_8 import LCD_1inch8
from random import randint
from sys import exit
MAXSCREEN_X = const(160)
MAXSCREEN_Y = const(128)
NUM_ENEMY = const(10)
NUM_ASSET = const(50)
EXIT = False
LIVE_ASSETS = 0
PLAYER_BLINK = const(30)
ENEMY_BLINK = const(200)
ASSET_BLINK = const(30)
BOTTOM_SCREEN = const(160*119)
LAUNCH_LEFT = const(160*120)
LAUNCH_CENTER = const(160*120+75)
LAUNCH_RIGHT = const(160*120+150)
X_POS = const(0)
Y_POS = const(1)
SPRITE = const(2)
BUTTON = const(3)
LIFE = const(3)
TARGET = const(4)
TARGETED = const(5)
CROSS_X = const(6)
CROSS_Y = const(7)
PARAMS = const(6)
X_INC = const(2)
Y_INC = const(3)
DURATION = const(4)
EXP_DIR = const(5)
EXP_PARAMS = const(6)
ENEMY_PARAMS = const(6)
ASSET_PARAMS = const(8)
EXP_SIZE = const(15)
cursor = array.array('B',(
1,0,0,1,
0,0,0,0,
0,0,0,0,
1,0,0,1))
enemy_sprite = array.array('B',(
1,1,1,1,
1,0,0,1,
1,0,0,1,
1,1,1,1))
asset_sprite = array.array('B',(
0,0,0,0,
0,1,1,0,
0,1,1,0,
0,0,0,0,
1,0,0,1,
0,1,1,0,
0,1,1,0,
1,0,0,1))
fire_rgb = array.array('H',(8192 ,8216 ,24616 ,24640 ,41040 ,57440 ,57456 ,8329 ,24729 ,57513 ,8378 ,8386 ,24794 ,
41178 ,41178 ,57554 ,57554 ,8659 ,25035 ,41419 ,57803 ,8908 ,8900 ,25284 ,41924 ,58300 ,
58300 ,9405 ,9405 ,26045 ,26037 ,42421 ,42677 ,28110 ,62430 ,30959 ,65535))
city_sprite = array.array('H',(0,0,0,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,7936,0,0,0,0,0,7936,0,
7936,0,0,0,0,0,0,7936,7936,0,7936,0,0,65287,7936,7936,7936,7936,0,7936,0,0,7936,7936,7936,7936,7936,7936,
0,65287,65287,7936,7936,7936,7936,7936,0,0,7936,65287,7936,7936,65287,7936,65287,65287,65287,7936,7936,
65287,7936,7936,0,7936,7936,65287,65287,7936,65287,65287,65287,7936,65287,65287,7936,65287,65287,7936,
7936,57599,65287,65287,65287,65287,65287,7936,7936,7936,7936,65287,65287,65287,65287,65287,57599,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,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 init_player():
global player
player = array.array('i',(X_POS,Y_POS,SPRITE,BUTTON))
player[X_POS] = MAXSCREEN_X>>1
player[Y_POS] = MAXSCREEN_Y>>1
def init_enemy():
global enemy
enemy = array.array('i',())
j=0
for i in range(NUM_ENEMY):
enemy.append(randint(5,MAXSCREEN_X-5)) # XPOS
enemy.append(0) # YPOS
enemy.append(enemy[X_POS+j*ENEMY_PARAMS]) # SPRITE
enemy.append(100) # LIFE
enemy.append(TARGET)
enemy.append(99) # TARGETED
j+=1
def init_asset():
global asset, trail
asset = array.array('i', 0 for _ in range(NUM_ASSET*ASSET_PARAMS))
trail = array.array('B', 0 for _ in range(NUM_ASSET*210*2))
def init_joystick():
global POT_X,POT_Y,POT_X_ZERO,POT_Y_ZERO
POT_X = machine.ADC(27)
POT_Y = machine.ADC(26)
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
def init_field():
global SOUND
LCD.fill(0)
LCD.show()
SOUND = 1
def init_explode():
global explosion
explosion = array.array('i',())
for i in range(0,1000,EXP_PARAMS):
explosion.append(X_POS) # X_POS
explosion.append(Y_POS) # Y_POS
explosion.append(randint(0-(1<<16),1<<16)) # x inc
explosion.append(randint(0-(1<<16),1<<16)) # y inc
explosion.append(EXP_SIZE+1) # DURATION
explosion.append(-1) # EXP_DIR
@micropython.viper
def start_explode(x:int,y:int):
exp = ptr32(explosion)
for j in range(1): # num particles, 1 for circle
for i in range(0,EXP_PARAMS*20,EXP_PARAMS):
if exp[i+DURATION] < EXP_SIZE+1: continue
explosion[i+X_POS] = x<<16
explosion[i+Y_POS] = y<<16
explosion[i+DURATION] = EXP_SIZE
explosion[i+EXP_DIR] = -1
break
@micropython.viper
def draw_explode(explode_inc:int): # circle explosion
global ENEMY_REMAIN
screen_ptr = ptr16(LCD.buffer)
exp = ptr32(explosion)
enemy_ptr = ptr32(enemy)
fire_ptr = ptr16(fire_rgb)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
finish_exp = 1
for i in range(0,1000,EXP_PARAMS):
if exp[i+DURATION] > EXP_SIZE: continue
if exp[i+DURATION] == 0 and exp[i+EXP_DIR] == -1:
exp[i+EXP_DIR] = 1
exp[i+DURATION] = 1
x0 = exp[i+X_POS]>>16
y0 = exp[i+Y_POS]>>16
radius = 1+EXP_SIZE - (exp[i+DURATION])
if explode_inc == 1 :
exp[i+DURATION] += exp[i+EXP_DIR]
fill_circle(x0,y0, radius, fire_ptr[37-(radius<<1)])
finish_exp = 0
for j in range(NUM_ENEMY):
index = ENEMY_PARAMS*j
if enemy_ptr[index+LIFE] == 0 : continue
enemy_x = enemy_ptr[index+X_POS]
enemy_y = enemy_ptr[index+Y_POS]
dx = enemy_x - x0
dy = enemy_y - y0
dist_squared = dx*dx + dy*dy
radius_squared = radius*radius
if dist_squared < radius_squared:
ENEMY_REMAIN = int(ENEMY_REMAIN)-1
enemy_ptr[index+LIFE] = 0
start_explode(enemy_x,enemy_y)
if int(ENEMY_REMAIN) == 0 and finish_exp == 1:
init_enemy()
@micropython.viper
def draw_explode2(game_counter:int): # particle explosion
screen_ptr = ptr16(LCD.buffer)
exp = ptr32(explosion)
fire_ptr = ptr16(fire_rgb)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
for i in range(0,1000,EXP_PARAMS):
if exp[i+DURATION] > EXP_SIZE or exp[i+DURATION] == 0 :
exp[i+DURATION] = EXP_SIZE + 1
continue
if not (game_counter % 10):
exp[i+DURATION] -= 1
exp[i+X_POS] += exp[i+X_INC]
exp[i+Y_POS] += exp[i+Y_INC]
addr = (exp[i+Y_POS]>>16)*MAXSCREEN_X + (exp[i+X_POS]>>16)
if addr>=0 and addr < max_addr:
screen_ptr[addr] = fire_ptr[7+(exp[i+DURATION]<<1)]
@micropython.viper
def fill_circle(x0:int, y0:int, radius:int, color:int):
screen_ptr = ptr16(LCD.buffer)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
# From Adafruit GFX Arduino library
y_start = y0 - radius
for y2 in range(y_start, y_start+2*radius + 1):
addr = y2 * MAXSCREEN_X + x0
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
f = 1 - radius
ddF_x = 1
ddF_y = -2 * radius
x = 0
y = radius
while x < y:
if f >= 0:
y -= 1
ddF_y += 2
f += ddF_y
x += 1
ddF_x += 2
f += ddF_x
x2 = x0 + x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 + y
y_start = y0 - x
for y2 in range(y_start,y_start+ 2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - y
y_start = y0 - x
for y2 in range(y_start, y_start+2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
@micropython.viper
def draw(fps:int):
screen_ptr = ptr16(LCD.buffer)
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
cursor_ptr = ptr8(cursor)
e_sprite_ptr = ptr8(enemy_sprite)
a_sprite_ptr = ptr8(asset_sprite)
trail_ptr = ptr8(trail)
size = 4
offset = 0
# ggg_bbbbbrrrrr_ggg
red = 0b111_0011111111_001
player_ptr[SPRITE]+=1
if player_ptr[SPRITE] > PLAYER_BLINK:
player_ptr[SPRITE] = 0
for y in range(size):
for x in range(size):
color = cursor_ptr[y*4+x]
if (color == 1 and player_ptr[SPRITE]<PLAYER_BLINK>>1) or (color == 0 and player_ptr[SPRITE]>=PLAYER_BLINK>>1):
screen_ptr[(y+player_ptr[Y_POS])*MAXSCREEN_X + player_ptr[X_POS]+x] = 0xffff
for i in range(NUM_ENEMY): #NUM_ENEMY
if enemy_ptr[i*ENEMY_PARAMS+LIFE] == 0: continue
sprite_index = SPRITE+ENEMY_PARAMS*i
enemy_ptr[sprite_index]+=1
if enemy_ptr[sprite_index] > ENEMY_BLINK:
enemy_ptr[sprite_index] = 0
for y in range(size):
for x in range(size):
x1 = X_POS + ENEMY_PARAMS * i
y1 = Y_POS + ENEMY_PARAMS * i
color = e_sprite_ptr[y*4+x]
if (color == 1 and enemy_ptr[sprite_index]<ENEMY_BLINK>>1) or (color == 0 and enemy_ptr[sprite_index]>=ENEMY_BLINK>>1):
screen_ptr[(y+enemy_ptr[y1])*MAXSCREEN_X + enemy_ptr[x1]+x] = red
for i in range(NUM_ASSET):
index = ASSET_PARAMS*i
index2 = 200*(i)*2
pos = asset_ptr[index+LIFE] # 400 -> 0
if pos == 0: continue
#print(pos)
for j in range(400,pos,-2):
x = trail_ptr[index2+j]
y = trail_ptr[index2+j+1]
addr = y * MAXSCREEN_X + x
screen_ptr[addr] = 0xffff
LCD.text(str(fps),0,0,0xff)
LCD.text(str(ENEMY_REMAIN),0,10,0xff)
LCD.show()
LCD.fill(0)
@micropython.viper
def draw_citys():
screen_ptr = ptr16(LCD.buffer)
bitmap_ptr = ptr16(city_sprite)
for n in range(3):
for y in range(9):
for x in range(16):
addr = y * 160 + x + 15 + BOTTOM_SCREEN
screen_ptr[addr+n*20] = bitmap_ptr[y*16+x]
screen_ptr[addr+n*20+72] = bitmap_ptr[y*16+x]
for y in range(6):
for x in range(14):
screen_ptr[y*160+n*72+x+(160*2)+BOTTOM_SCREEN] = 0xff
def read_joystick():
global player
pot_scale = 13
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 int(abs(x_inc))<2:
x_inc=0
if int(abs(y_inc))<2:
y_inc=0
if x_inc==0 and y_inc==0: return
player[X_POS] -= x_inc
player[Y_POS] += y_inc
if player[X_POS] < 0:
player[X_POS] = 0
if player[X_POS] > MAXSCREEN_X-1:
player[X_POS] = MAXSCREEN_X-1
if player[Y_POS] < 0:
player[Y_POS] = 0
if player[Y_POS] > MAXSCREEN_Y-1:
player[Y_POS] = MAXSCREEN_Y-1
@micropython.viper
def button():
global LIVE_ASSETS, SOUND
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
pushed = not FIRE.value()
asset_ptr = ptr32(asset)
if not pushed: return
x = player_ptr[X_POS]
y = player_ptr[Y_POS]
for i in range(NUM_ASSET): # search through assets to find unused
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0:
asset_ptr[index+X_POS] = (LAUNCH_CENTER % 160)<<16
asset_ptr[index+Y_POS] = (LAUNCH_CENTER // 160)<<16
asset_ptr[index+CROSS_X] = x<<16
asset_ptr[index+CROSS_Y] = y<<16
# min_distance = MAXSCREEN_X + MAXSCREEN_Y
# for j in range(NUM_ENEMY): # search through enemies to target
# enemy_index = ASSET_PARAMS*j
# if enemy_ptr[enemy_index+LIFE] == 0 or enemy_ptr[enemy_index+TARGETED]<99:
# continue
# #distance = int(abs(x-enemy_ptr[enemy_index+X_POS])+abs(y-enemy_ptr[enemy_index+Y_POS]))
# distance = int(distance_asm(x,y,enemy_ptr[enemy_index+X_POS],enemy_ptr[enemy_index+Y_POS]))
# if distance<min_distance:
# min_distance = distance
# closest_enemy = j
# if closest_enemy>NUM_ENEMY: return # no enemies left
asset_ptr[index+LIFE] = 200*2
init_trail((LAUNCH_CENTER % 160)<<16,(LAUNCH_CENTER // 160)<<16,x<<16,y<<16,i)
SOUND = 2
return
@micropython.viper
def move_asset(t):
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
trail_ptr = ptr8(trail)
for i in range(NUM_ASSET):
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0 : continue
asset_ptr[index+LIFE] -= 2
pos = asset_ptr[index+LIFE]
trail_x = trail_ptr[i*200+pos]
trail_y = trail_ptr[i*200+pos+1]
cross_x = asset_ptr[index+CROSS_X]>>16
cross_y = asset_ptr[index+CROSS_Y]>>16
print(pos,cross_x,trail_x,cross_y,trail_y)
if cross_x == trail_x and (cross_y == trail_y or cross_y == trail_y-1): # trail hits cross
asset_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
@micropython.viper
def move_enemy():
global ENEMY_REMAIN
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
ENEMY_REMAIN = 0
for i in range(NUM_ENEMY):
index = ENEMY_PARAMS*i
if enemy_ptr[index+LIFE] == 0 : continue
ENEMY_REMAIN = int(ENEMY_REMAIN)+1
enemy_x = enemy_ptr[index+X_POS]
enemy_y = enemy_ptr[index+Y_POS]
#target = ENEMY_PARAMS*enemy_ptr[index+TARGET]
target_x = player_ptr[X_POS]
target_y = player_ptr[Y_POS]
dx = enemy_x - target_x
dy = enemy_y - target_y
if dx<0 : enemy_ptr[index+X_POS] += 1
elif dx>0: enemy_ptr[index+X_POS] -= 1
if dy>0 : enemy_ptr[index+Y_POS] -= 1
elif dy<0: enemy_ptr[index+Y_POS] += 1
if dy == 0 and dx==0:
enemy_ptr[index+LIFE] = 0
enemy_ptr[index+TARGETED] = 99
if enemy_ptr[index+LIFE]<=0:
enemy_ptr[index+LIFE] = 0
start_explode(enemy_x,enemy_y)
@micropython.viper
def init_trail(x1:int,y1:int,x2:int,y2:int,index:int):
trail_ptr = ptr8(trail)
x = x1
y = y1
dx = (x2-x1)
dy = (y2-y1)
dx_a = int(abs(dx))
dy_a = int(abs(dy))
#if (dx>>16)*(dy>>16)==0: return x,y
index = index * 200 * 2+400
if dy_a >= dx_a:
if y2 > y1:
y_inc = 1<<16
x_inc = dx//(dy>>16) * -1
for y in range(y1,y1-dy_a,1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
#x -= x_inc
#index -= 2
else:
y_inc = -1<<16
x_inc = dx//(dy>>16)
for y in range(y1,y1-dy_a,-1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
#x -= x_inc
#index -= 2
else:
y_inc = dy//(dx>>16)
if x2 > x1:
x_inc = 1<<16
for x in range(x1,x2,1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
#index -= 2
#y += y_inc
else:
x_inc = -1<<16
y_inc = y_inc * -1
for x in range(x1,x2,-1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
#index -= 2
#y += y_inc
def core1():
global SOUND,WATCHDOG
sleep(1)
while(1):
WATCHDOG += 1
if WATCHDOG == 100_000:
print('core 0 crash')
reset()
if SOUND==1:
sound_alarm()
if SOUND==1:
SOUND = 0
if SOUND==2:
sound_missile()
if SOUND==2:
SOUND = 0
@micropython.asm_thumb
def distance_asm(r0,r1,r2,r3): # x1,y1,x2,y2
sub(r4,r2,r0) # x2-x1
sub(r5,r3,r1) # y2-y1
mul(r4,r4) # (x2-x1)^2
mul(r5,r5) # (y2-y1)^2
add(r0,r4,r5) # (x2-x1)^2 + (y2-y1)^2
mov(r2,0) # r2 = res
cmp(r0,0) # r0 = num
bpl(MAIN)
mov(r0,0)
b(EXIT)
label(MAIN)
mov(r1,1) # r1 = bit
lsl(r1,r1,30)
label(FOUR_POWER)
cmp(r1,r0) # bit <= num
ble(LOOP)
asr(r1,r1,2) # bit = bit >> 2
b(FOUR_POWER)
label(LOOP)
cmp(r1,0)
beq(EXIT) # bit == 0
add(r3,r1,r2) # res + bit
cmp(r0,r3) # if num >= res + bit
blt(ELSE)
sub(r0,r0,r3) # num = num - (res + bit)
asr(r4,r2,1) # res >> 1
add(r2,r4,r1) # res = (res >> 1) + bit
b(IF_DONE)
label(ELSE)
asr(r2,r2,1) # res = res>>1
label(IF_DONE)
asr(r1,r1,2) # bit = bit >> 2
b(LOOP)
label(EXIT)
cmp(r0,r2) # if num <= res:
ble(NO_ROUND)
add(r2,r2,1) # res += 1
label(NO_ROUND)
mov(r0,r2)
def is_collision(x, y, x0, y0, r):
dx = x - x0
dy = y - y0
dist_squared = dx**2 + dy**2
radius_squared = r**2
return dist_squared <= radius_squared
def sound_alarm():
volume = 1000
POWER.on()
BUZZ.duty_u16(volume)
start =1250
end = 1050
for repeats in range(6):
for i in range(start,end,-10):
BUZZ.duty_u16(volume)
BUZZ.freq(i)
sleep(.014)
POWER.off()
def sound_missile():
global SOUND
SOUND = 0
volume = 2000
POWER.on()
BUZZ.duty_u16(volume)
start =1500
end = 500
for i in range(start,end,-1):
BUZZ.duty_u16(volume)
volume -= 2
BUZZ.freq(i)
if SOUND == 2 :
POWER.off()
return
for delay in range(150):
pass
POWER.off()
if __name__=='__main__':
FIRE = Pin(22, Pin.IN, Pin.PULL_UP)
POWER = Pin(16, machine.Pin.OUT)
BUZZ = machine.PWM(Pin(17, machine.Pin.OUT))
pwm = PWM(Pin(13))
pwm.freq(1000)
pwm.duty_u16(0x7fff)#max 65535
#machine.freq(200_000_000)
LCD = LCD_1inch8()
#machine.mem32[0x40008048] = 1<<11 # enable peri_ctrl clock
WATCHDOG = 0
SOUND = 0
_thread.start_new_thread(core1, ())
init_player()
init_enemy()
init_asset()
init_joystick()
init_field()
init_explode()
gc.collect()
print(gc.mem_free())
fps = 0
game_counter = 0
ENEMY_REMAIN = 0
move_enemy_ticks = ticks_ms()
explode_ticks = ticks_ms()
explode_inc = 0
move_asset_ticks = ticks_ms()
button_ticks = ticks_ms()
joystick_ticks = ticks_ms()
while not EXIT:
gticks = ticks_us()
WATCHDOG = 0
game_counter +=1
if game_counter > 50: game_counter = 0
if ticks_diff(ticks_ms(),move_enemy_ticks) > 1000:
move_enemy()
move_enemy_ticks = ticks_ms()
if ticks_diff(ticks_ms(),move_asset_ticks) > 50:
move_asset(0)
move_asset_ticks = ticks_ms()
if ticks_diff(ticks_ms(),joystick_ticks) > 50:
read_joystick()
joystick_ticks = ticks_ms()
if ticks_diff(ticks_ms(),button_ticks) > 200:
button()
button_ticks = ticks_ms()
if ticks_diff(ticks_ms(),explode_ticks) > 100:
explode_ticks = ticks_ms()
explode_inc = 1
else:
explode_inc = 0
draw_explode(explode_inc)
draw_citys()
draw(fps)
fps = 1_000_000//int(ticks_diff(ticks_us(),gticks))
# Missile Command game
from machine import Pin,SPI,PWM,ADC, Timer, reset, soft_reset
import framebuf, gc
import time, array, _thread
from time import sleep, ticks_us, ticks_diff, ticks_ms
from lcd_1_8 import LCD_1inch8
from random import randint
from sys import exit
MAXSCREEN_X = const(160)
MAXSCREEN_Y = const(128)
NUM_ENEMY = const(10) # 10
NUM_ASSET = const(10)
EXIT = False
LIVE_ASSETS = 0
PLAYER_BLINK = const(30)
ENEMY_BLINK = const(200)
ASSET_BLINK = const(30)
BOTTOM_SCREEN = const(160*119)
LAUNCH_LEFT = const(160*120)
LAUNCH_CENTER = const(160*120+75)
LAUNCH_RIGHT = const(160*120+150)
X_POS = const(0)
Y_POS = const(1)
SPRITE = const(2)
BUTTON = const(3)
LIFE = const(3)
TARGET_X = const(4)
TARGET_Y = const(5)
CROSS_X = const(6)
CROSS_Y = const(7)
PARAMS = const(6)
X_INC = const(2)
Y_INC = const(3)
DURATION = const(4)
EXP_DIR = const(5)
EXP_PARAMS = const(6)
ENEMY_PARAMS = const(6)
ASSET_PARAMS = const(8)
EXP_SIZE = const(15)
cursor = array.array('B',(
1,0,0,1,
0,0,0,0,
0,0,0,0,
1,0,0,1))
enemy_sprite = array.array('B',(
1,1,1,1,
1,0,0,1,
1,0,0,1,
1,1,1,1))
asset_sprite = array.array('B',(
0,0,0,0,
0,1,1,0,
0,1,1,0,
0,0,0,0,
1,0,0,1,
0,1,1,0,
0,1,1,0,
1,0,0,1))
fire_rgb = array.array('H',(8192 ,8216 ,24616 ,24640 ,41040 ,57440 ,57456 ,8329 ,24729 ,57513 ,8378 ,8386 ,24794 ,
41178 ,41178 ,57554 ,57554 ,8659 ,25035 ,41419 ,57803 ,8908 ,8900 ,25284 ,41924 ,58300 ,
58300 ,9405 ,9405 ,26045 ,26037 ,42421 ,42677 ,28110 ,62430 ,30959 ,65535))
city_sprite = array.array('H',(0,0,0,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,7936,0,0,0,0,0,7936,0,
7936,0,0,0,0,0,0,7936,7936,0,7936,0,0,65287,7936,7936,7936,7936,0,7936,0,0,7936,7936,7936,7936,7936,7936,
0,65287,65287,7936,7936,7936,7936,7936,0,0,7936,65287,7936,7936,65287,7936,65287,65287,65287,7936,7936,
65287,7936,7936,0,7936,7936,65287,65287,7936,65287,65287,65287,7936,65287,65287,7936,65287,65287,7936,
7936,57599,65287,65287,65287,65287,65287,7936,7936,7936,7936,65287,65287,65287,65287,65287,57599,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,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 init_player():
global player
player = array.array('i',(X_POS,Y_POS,SPRITE,BUTTON))
player[X_POS] = MAXSCREEN_X>>1
player[Y_POS] = MAXSCREEN_Y>>1
def init_enemy():
global enemy, trail2, ENEMY_REMAIN
enemy = array.array('i',())
for i in range(NUM_ENEMY):
x1 = randint(5,MAXSCREEN_X-5)
y1 = randint(0,10)
enemy.append(x1) # XPOS
enemy.append(y1) # YPOS
enemy.append(enemy[X_POS+i*ENEMY_PARAMS]) # SPRITE
enemy.append(400) # LIFE
x2 = (randint(1,9)*16)
y2 = 120
enemy.append(x2) # TARGET_X
enemy.append(y2) # TARGET_Y
init_trail(trail2,x1<<16,y1<<16,x2<<16,y2<<16,i)
ENEMY_REMAIN += 1
def init_asset():
global asset, trail, trail2
asset = array.array('i', 0 for _ in range(NUM_ASSET*ASSET_PARAMS))
trail = array.array('B', 0 for _ in range(NUM_ASSET*402))
trail2 = array.array('B', 0 for _ in range(NUM_ENEMY*402))
def init_joystick():
global POT_X,POT_Y,POT_X_ZERO,POT_Y_ZERO
POT_X = machine.ADC(27)
POT_Y = machine.ADC(26)
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
def init_field():
global SOUND
LCD.fill(0)
LCD.show()
SOUND = 1
def init_explode():
global explosion
explosion = array.array('i',())
for i in range(0,1000,EXP_PARAMS):
explosion.append(X_POS) # X_POS
explosion.append(Y_POS) # Y_POS
explosion.append(randint(0-(1<<16),1<<16)) # x inc
explosion.append(randint(0-(1<<16),1<<16)) # y inc
explosion.append(EXP_SIZE+1) # DURATION
explosion.append(-1) # EXP_DIR
@micropython.viper
def start_explode(x:int,y:int):
exp = ptr32(explosion)
for j in range(1): # num particles, 1 for circle
for i in range(0,EXP_PARAMS*20,EXP_PARAMS):
if exp[i+DURATION] < EXP_SIZE+1: continue
explosion[i+X_POS] = x<<16
explosion[i+Y_POS] = y<<16
explosion[i+DURATION] = EXP_SIZE
explosion[i+EXP_DIR] = -1
break
@micropython.viper
def draw_explode(explode_inc:int): # circle explosion
global ENEMY_REMAIN
screen_ptr = ptr16(LCD.buffer)
exp = ptr32(explosion)
enemy_ptr = ptr32(enemy)
trail_ptr = ptr8(trail2)
fire_ptr = ptr16(fire_rgb)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
finish_exp = 1
for i in range(0,1000,EXP_PARAMS):
if exp[i+DURATION] > EXP_SIZE: continue
if exp[i+DURATION] == 0 and exp[i+EXP_DIR] == -1:
exp[i+EXP_DIR] = 1
exp[i+DURATION] = 1
x0 = exp[i+X_POS]>>16
y0 = exp[i+Y_POS]>>16
radius = 1+EXP_SIZE - (exp[i+DURATION])
if explode_inc == 1 :
exp[i+DURATION] += exp[i+EXP_DIR]
fill_circle(x0,y0, radius, fire_ptr[37-(radius<<1)])
finish_exp = 0
for j in range(NUM_ENEMY):
index = ENEMY_PARAMS*j
if enemy_ptr[index+LIFE] == 0 : continue
pos = enemy_ptr[index+LIFE]
trail_x = trail_ptr[j*400+pos]
trail_y = trail_ptr[j*400+pos+1]
dx = trail_x - x0
dy = trail_y - y0
dist_squared = dx*dx + dy*dy
radius_squared = radius*radius
if dist_squared < radius_squared:
ENEMY_REMAIN = int(ENEMY_REMAIN)-1
enemy_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
if int(ENEMY_REMAIN) == 0 and finish_exp == 1:
init_enemy()
@micropython.viper
def fill_circle(x0:int, y0:int, radius:int, color:int):
screen_ptr = ptr16(LCD.buffer)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
# From Adafruit GFX Arduino library
y_start = y0 - radius
for y2 in range(y_start, y_start+2*radius + 1):
addr = y2 * MAXSCREEN_X + x0
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
f = 1 - radius
ddF_x = 1
ddF_y = -2 * radius
x = 0
y = radius
while x < y:
if f >= 0:
y -= 1
ddF_y += 2
f += ddF_y
x += 1
ddF_x += 2
f += ddF_x
x2 = x0 + x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 + y
y_start = y0 - x
for y2 in range(y_start,y_start+ 2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - y
y_start = y0 - x
for y2 in range(y_start, y_start+2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
@micropython.viper
def draw(fps:int):
screen_ptr = ptr16(LCD.buffer)
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
cursor_ptr = ptr8(cursor)
e_sprite_ptr = ptr8(enemy_sprite)
a_sprite_ptr = ptr8(asset_sprite)
trail_ptr = ptr8(trail)
trail2_ptr = ptr8(trail2)
size = 4
offset = 0
# ggg_bbbbbrrrrr_ggg
red = 0b111_0011111111_001
blue= 0b000_1111100000_000
player_ptr[SPRITE]+=1
if player_ptr[SPRITE] > PLAYER_BLINK:
player_ptr[SPRITE] = 0
for y in range(size):
for x in range(size):
color = cursor_ptr[y*4+x]
if (color == 1 and player_ptr[SPRITE]<PLAYER_BLINK>>1) or (color == 0 and player_ptr[SPRITE]>=PLAYER_BLINK>>1):
screen_ptr[(y+player_ptr[Y_POS])*MAXSCREEN_X + player_ptr[X_POS]+x] = 0xffff
# for i in range(NUM_ENEMY): #NUM_ENEMY
# if enemy_ptr[i*ENEMY_PARAMS+LIFE] == 0: continue
# sprite_index = SPRITE+ENEMY_PARAMS*i
# enemy_ptr[sprite_index]+=1
# if enemy_ptr[sprite_index] > ENEMY_BLINK:
# enemy_ptr[sprite_index] = 0
# for y in range(size):
# for x in range(size):
# x1 = X_POS + ENEMY_PARAMS * i
# y1 = Y_POS + ENEMY_PARAMS * i
# color = e_sprite_ptr[y*4+x]
# if (color == 1 and enemy_ptr[sprite_index]<ENEMY_BLINK>>1) or (color == 0 and enemy_ptr[sprite_index]>=ENEMY_BLINK>>1):
# screen_ptr[(y+enemy_ptr[y1])*MAXSCREEN_X + enemy_ptr[x1]+x] = red
for i in range(NUM_ASSET): # draw player missiles
index = ASSET_PARAMS*i
index2 = 200*(i)*2
pos = asset_ptr[index+LIFE] # 400 -> 0
if pos == 0: continue
for j in range(400,pos-2,-2):
x = trail_ptr[index2+j]
y = trail_ptr[index2+j+1]
addr = y * MAXSCREEN_X + x
screen_ptr[addr] = blue
screen_ptr[addr] = 0xffff
for i in range(NUM_ENEMY): # draw enemy missiles
index = ENEMY_PARAMS*i
index2 = 200*(i)*2
pos = enemy_ptr[index+LIFE] # 400 -> 0
if pos == 0: continue
for j in range(400,pos-2,-2):
x = trail2_ptr[index2+j]
y = trail2_ptr[index2+j+1]
addr = y * MAXSCREEN_X + x
screen_ptr[addr] = red
screen_ptr[addr] = 0xffff
LCD.text(str(fps),0,0,0xff)
LCD.text(str(ENEMY_REMAIN),0,10,0xff)
LCD.show()
LCD.fill(0)
@micropython.viper
def draw_citys():
screen_ptr = ptr16(LCD.buffer)
bitmap_ptr = ptr16(city_sprite)
for n in range(3):
for y in range(9):
for x in range(16):
addr = y * 160 + x + 15 + BOTTOM_SCREEN
screen_ptr[addr+n*20] = bitmap_ptr[y*16+x]
screen_ptr[addr+n*20+72] = bitmap_ptr[y*16+x]
for y in range(6):
for x in range(14):
screen_ptr[y*160+n*72+x+(160*2)+BOTTOM_SCREEN] = 0xff
def read_joystick():
global player
pot_scale = 13
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 int(abs(x_inc))<2:
x_inc=0
if int(abs(y_inc))<2:
y_inc=0
if x_inc==0 and y_inc==0: return
player[X_POS] -= x_inc
player[Y_POS] += y_inc
if player[X_POS] < 15:
player[X_POS] = 15
if player[X_POS] > MAXSCREEN_X-15:
player[X_POS] = MAXSCREEN_X-15
if player[Y_POS] < 15:
player[Y_POS] = 15
if player[Y_POS] > MAXSCREEN_Y-15:
player[Y_POS] = MAXSCREEN_Y-15
@micropython.viper
def button():
global LIVE_ASSETS, SOUND
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
pushed = not FIRE.value()
asset_ptr = ptr32(asset)
if not pushed: return
x = player_ptr[X_POS]
y = player_ptr[Y_POS]
for i in range(NUM_ASSET): # search through assets to find unused
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0:
asset_ptr[index+X_POS] = (LAUNCH_CENTER % 160)<<16
asset_ptr[index+Y_POS] = (LAUNCH_CENTER // 160)<<16
asset_ptr[index+CROSS_X] = x<<16
asset_ptr[index+CROSS_Y] = y<<16
# min_distance = MAXSCREEN_X + MAXSCREEN_Y
# for j in range(NUM_ENEMY): # search through enemies to
# enemy_index = ASSET_PARAMS*j
# if enemy_ptr[enemy_index+LIFE] == 0 or enemy_ptr[enemy_index+TARGETED]<99:
# continue
# #distance = int(abs(x-enemy_ptr[enemy_index+X_POS])+abs(y-enemy_ptr[enemy_index+Y_POS]))
# distance = int(distance_asm(x,y,enemy_ptr[enemy_index+X_POS],enemy_ptr[enemy_index+Y_POS]))
# if distance<min_distance:
# min_distance = distance
# closest_enemy = j
# if closest_enemy>NUM_ENEMY: return # no enemies left
asset_ptr[index+LIFE] = 200*2
init_trail(trail,(LAUNCH_CENTER % 160)<<16,(LAUNCH_CENTER // 160)<<16,x<<16,y<<16,i)
SOUND = 2
return
@micropython.viper
def move_asset():
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
trail_ptr = ptr8(trail)
for i in range(NUM_ASSET):
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0 : continue
asset_ptr[index+LIFE] -= 2
pos = asset_ptr[index+LIFE]
trail_x = trail_ptr[i*400+pos]
trail_y = trail_ptr[i*400+pos+1]
cross_x = asset_ptr[index+CROSS_X]>>16
cross_y = asset_ptr[index+CROSS_Y]>>16
#print(i,pos,cross_x,trail_x,cross_y,trail_y)
if (cross_x == trail_x or cross_x == trail_x-1 or cross_x == trail_x+1) and (cross_y == trail_y or cross_y == trail_y-1 or cross_y == trail_y+1): # trail hits cross
asset_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
@micropython.viper
def move_enemy():
global ENEMY_REMAIN
enemy_ptr = ptr32(enemy)
trail_ptr = ptr8(trail2)
for i in range(NUM_ENEMY):
index = ENEMY_PARAMS*i
if enemy_ptr[index+LIFE] == 0 : continue
enemy_ptr[index+LIFE] -= 2
pos = enemy_ptr[index+LIFE]
trail_x = trail_ptr[i*400+pos]
trail_y = trail_ptr[i*400+pos+1]
target_x = enemy_ptr[index+TARGET_X]
target_y = enemy_ptr[index+TARGET_Y]
if (target_x == trail_x or target_x == trail_x-1 or target_x == trail_x+1) and (target_y == trail_y or target_y == trail_y+1): # trail hits target
enemy_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
ENEMY_REMAIN = int(ENEMY_REMAIN)-1
#print(i,pos,target_x,trail_x,target_y,trail_y)
@micropython.viper
def init_trail(t,x1:int,y1:int,x2:int,y2:int,index:int):
trail_ptr = ptr8(t)
x = x1
y = y1
dx = (x2-x1)
dy = (y2-y1)
dx_a = int(abs(dx))
dy_a = int(abs(dy))
#if (dx>>16)*(dy>>16)==0: return x,y
index = (index * 400)+400
if dy_a >= dx_a:
if y2 > y1:
y_inc = 1<<16
x_inc = dx//(dy>>16) * -1
for y in range(y1,y1+dy_a+(1<<16),1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
y_inc = -1<<16
x_inc = dx//(dy>>16)
for y in range(y1,y1-dy_a-(0<<16),-1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
y_inc = dy//(dx>>16)
if x2 > x1:
x_inc = 1<<16
for x in range(x1,x2+(0<<16),1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
x_inc = -1<<16
y_inc = y_inc * -1
for x in range(x1,x2-(0<<16),-1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
def core1():
global SOUND,WATCHDOG
sleep(.2)
while(1):
WATCHDOG += 1
if WATCHDOG == 100_000:
print('core 0 crash')
reset()
if SOUND==1:
sound_alarm()
if SOUND==1:
SOUND = 0
if SOUND==2:
sound_missile()
if SOUND==2:
SOUND = 0
@micropython.asm_thumb
def distance_asm(r0,r1,r2,r3): # x1,y1,x2,y2
sub(r4,r2,r0) # x2-x1
sub(r5,r3,r1) # y2-y1
mul(r4,r4) # (x2-x1)^2
mul(r5,r5) # (y2-y1)^2
add(r0,r4,r5) # (x2-x1)^2 + (y2-y1)^2
mov(r2,0) # r2 = res
cmp(r0,0) # r0 = num
bpl(MAIN)
mov(r0,0)
b(EXIT)
label(MAIN)
mov(r1,1) # r1 = bit
lsl(r1,r1,30)
label(FOUR_POWER)
cmp(r1,r0) # bit <= num
ble(LOOP)
asr(r1,r1,2) # bit = bit >> 2
b(FOUR_POWER)
label(LOOP)
cmp(r1,0)
beq(EXIT) # bit == 0
add(r3,r1,r2) # res + bit
cmp(r0,r3) # if num >= res + bit
blt(ELSE)
sub(r0,r0,r3) # num = num - (res + bit)
asr(r4,r2,1) # res >> 1
add(r2,r4,r1) # res = (res >> 1) + bit
b(IF_DONE)
label(ELSE)
asr(r2,r2,1) # res = res>>1
label(IF_DONE)
asr(r1,r1,2) # bit = bit >> 2
b(LOOP)
label(EXIT)
cmp(r0,r2) # if num <= res:
ble(NO_ROUND)
add(r2,r2,1) # res += 1
label(NO_ROUND)
mov(r0,r2)
def sound_alarm():
volume = 1000
POWER.on()
BUZZ.duty_u16(volume)
start =1250
end = 1050
for repeats in range(6):
for i in range(start,end,-10):
BUZZ.duty_u16(volume)
BUZZ.freq(i)
sleep(.014)
POWER.off()
def sound_missile():
global SOUND
SOUND = 0
volume = 2000
POWER.on()
BUZZ.duty_u16(volume)
start =1500
end = 500
for i in range(start,end,-1):
BUZZ.duty_u16(volume)
volume -= 2
BUZZ.freq(i)
if SOUND == 2 :
POWER.off()
return
for delay in range(150):
pass
POWER.off()
if __name__=='__main__':
FIRE = Pin(22, Pin.IN, Pin.PULL_UP)
POWER = Pin(16, machine.Pin.OUT)
BUZZ = machine.PWM(Pin(17, machine.Pin.OUT))
pwm = PWM(Pin(13))
pwm.freq(1000)
pwm.duty_u16(0x7fff)#max 65535
machine.freq(200_000_000)
LCD = LCD_1inch8()
machine.mem32[0x40008048] = 1<<11 # enable peri_ctrl clock
WATCHDOG = 0
SOUND = 0
ENEMY_REMAIN = 0
_thread.start_new_thread(core1, ())
init_player()
init_asset()
init_enemy()
init_joystick()
init_field()
init_explode()
gc.collect()
print(gc.mem_free())
fps = 0
game_counter = 0
move_enemy_ticks = ticks_ms()
explode_ticks = ticks_ms()
explode_inc = 0
move_asset_ticks = ticks_ms()
button_ticks = ticks_ms()
joystick_ticks = ticks_ms()
while not EXIT:
gticks = ticks_us()
WATCHDOG = 0
game_counter +=1
if game_counter > 50: game_counter = 0
if ticks_diff(ticks_ms(),move_enemy_ticks) > 100: #100
move_enemy_ticks = ticks_ms()
move_enemy()
if ticks_diff(ticks_ms(),move_asset_ticks) > 10:
move_asset_ticks = ticks_ms()
move_asset()
if ticks_diff(ticks_ms(),joystick_ticks) > 30:
joystick_ticks = ticks_ms()
read_joystick()
if ticks_diff(ticks_ms(),button_ticks) > 200:
button_ticks = ticks_ms()
button()
if ticks_diff(ticks_ms(),explode_ticks) > 100:
explode_ticks = ticks_ms()
explode_inc = 1
else:
explode_inc = 0
draw_explode(explode_inc)
draw_citys()
draw(fps)
fps = 1_000_000//int(ticks_diff(ticks_us(),gticks))
# Missile Command game
from machine import Pin,SPI,PWM,ADC, Timer, reset, soft_reset
import framebuf, gc
import time, array, _thread
from time import sleep, ticks_us, ticks_diff, ticks_ms
from lcd_1_8 import LCD_1inch8
from random import randint
from sys import exit
MAXSCREEN_X = const(160)
MAXSCREEN_Y = const(128)
NUM_ENEMY = const(10) # 10
NUM_ASSET = const(10)
EXIT = False
LIVE_ASSETS = 0
PLAYER_BLINK = const(30)
ENEMY_BLINK = const(200)
ASSET_BLINK = const(30)
BOTTOM_SCREEN = const(160*119)
LAUNCH_LEFT = const(160*120+8)
LAUNCH_CENTER = const(160*120+80)
LAUNCH_RIGHT = const(160*120+150)
X_POS = const(0)
Y_POS = const(1)
SPRITE = const(2)
BUTTON = const(3)
LIFE = const(3)
TARGET_X = const(4)
TARGET_Y = const(5)
CROSS_X = const(6)
CROSS_Y = const(7)
TARGET = const(6)
X_INC = const(2)
Y_INC = const(3)
DURATION = const(4)
EXP_DIR = const(5)
EXP_PARAMS = const(6)
ENEMY_PARAMS = const(7)
ASSET_PARAMS = const(8)
EXP_SIZE = const(15)
cursor = array.array('B',(
1,0,0,1,
0,0,0,0,
0,0,0,0,
1,0,0,1))
enemy_sprite = array.array('B',(
1,1,1,1,
1,0,0,1,
1,0,0,1,
1,1,1,1))
asset_sprite = array.array('B',(
0,0,0,0,
0,1,1,0,
0,1,1,0,
0,0,0,0,
1,0,0,1,
0,1,1,0,
0,1,1,0,
1,0,0,1))
fire_rgb = array.array('H',(8192 ,8216 ,24616 ,24640 ,41040 ,57440 ,57456 ,8329 ,24729 ,57513 ,8378 ,8386 ,24794 ,
41178 ,41178 ,57554 ,57554 ,8659 ,25035 ,41419 ,57803 ,8908 ,8900 ,25284 ,41924 ,58300 ,
58300 ,9405 ,9405 ,26045 ,26037 ,42421 ,42677 ,28110 ,62430 ,30959 ,65535))
city_sprite = array.array('H',(0,0,0,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,0,0,0,0,0,0,0,0,7936,0,0,0,0,0,0,7936,7936,0,0,0,0,0,7936,0,
7936,0,0,0,0,0,0,7936,7936,0,7936,0,0,65287,7936,7936,7936,7936,0,7936,0,0,7936,7936,7936,7936,7936,7936,
0,65287,65287,7936,7936,7936,7936,7936,0,0,7936,65287,7936,7936,65287,7936,65287,65287,65287,7936,7936,
65287,7936,7936,0,7936,7936,65287,65287,7936,65287,65287,65287,7936,65287,65287,7936,65287,65287,7936,
7936,57599,65287,65287,65287,65287,65287,7936,7936,7936,7936,65287,65287,65287,65287,65287,57599,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,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 init_player():
global player, city
player = array.array('i',(X_POS,Y_POS,SPRITE,BUTTON))
city = array.array('B',(0,1,1,1,0,1,1,1,0))
player[X_POS] = MAXSCREEN_X>>1
player[Y_POS] = MAXSCREEN_Y>>1
def init_enemy():
global enemy, trail2, ENEMY_REMAIN
enemy = array.array('i',())
ENEMY_REMAIN = 0
for i in range(NUM_ENEMY):
x1 = randint(5,MAXSCREEN_X-5)
y1 = randint(0,10)
enemy.append(x1) # XPOS
enemy.append(y1) # YPOS
enemy.append(enemy[X_POS+i*ENEMY_PARAMS]) # SPRITE
enemy.append(400) # LIFE
target = randint(0,8) # target city
#target = 5
x2 = (target*16)+8
if target > 4:
x2 += 12
y2 = 120
enemy.append(x2) # TARGET_X
enemy.append(y2) # TARGET_Y
enemy.append(target) # TARGET
init_trail(trail2,x1<<16,y1<<16,x2<<16,y2<<16,i)
ENEMY_REMAIN += 1
def init_asset():
global asset, trail, trail2
asset = array.array('i', 0 for _ in range(NUM_ASSET*ASSET_PARAMS))
trail = array.array('B', 0 for _ in range(NUM_ASSET*402))
trail2 = array.array('B', 0 for _ in range(NUM_ENEMY*402))
def init_joystick():
global POT_X,POT_Y,POT_X_ZERO,POT_Y_ZERO
POT_X = machine.ADC(27)
POT_Y = machine.ADC(26)
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
def init_field():
global SOUND
LCD.fill(0)
LCD.show()
SOUND = 1
def init_explode():
global explosion
explosion = array.array('i',())
for i in range(0,1000,EXP_PARAMS):
explosion.append(X_POS) # X_POS
explosion.append(Y_POS) # Y_POS
explosion.append(randint(0-(1<<16),1<<16)) # x inc
explosion.append(randint(0-(1<<16),1<<16)) # y inc
explosion.append(EXP_SIZE+1) # DURATION
explosion.append(-1) # EXP_DIR
@micropython.viper
def start_explode(x:int,y:int):
global SOUND
exp = ptr32(explosion)
for i in range(0,EXP_PARAMS*20,EXP_PARAMS):
if exp[i+DURATION] < EXP_SIZE+1: continue
explosion[i+X_POS] = x<<16
explosion[i+Y_POS] = y<<16
explosion[i+DURATION] = EXP_SIZE
explosion[i+EXP_DIR] = -1
SOUND = 3
return
@micropython.viper
def draw_explode(explode_inc:int): # circle explosion
global ENEMY_REMAIN, SOUND, EXIT
screen_ptr = ptr16(LCD.buffer)
exp = ptr32(explosion)
enemy_ptr = ptr32(enemy)
trail_ptr = ptr8(trail2)
fire_ptr = ptr16(fire_rgb)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
finish_exp = 1
for i in range(0,1000,EXP_PARAMS):
if exp[i+DURATION] > EXP_SIZE: continue
if exp[i+DURATION] == 0 and exp[i+EXP_DIR] == -1:
exp[i+EXP_DIR] = 1
exp[i+DURATION] = 1
x0 = exp[i+X_POS]>>16
y0 = exp[i+Y_POS]>>16
radius = 1+EXP_SIZE - (exp[i+DURATION])
if explode_inc == 1 :
exp[i+DURATION] += exp[i+EXP_DIR]
fill_circle(x0,y0, radius, fire_ptr[37-(radius<<1)])
finish_exp = 0
for j in range(NUM_ENEMY):
index = ENEMY_PARAMS*j
if enemy_ptr[index+LIFE] == 0 : continue
pos = enemy_ptr[index+LIFE]
trail_x = trail_ptr[j*400+pos]
trail_y = trail_ptr[j*400+pos+1]
dx = trail_x - x0
dy = trail_y - y0
dist_squared = dx*dx + dy*dy
radius_squared = radius*radius
if dist_squared < radius_squared:
ENEMY_REMAIN = int(ENEMY_REMAIN)-1
enemy_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
if int(ENEMY_REMAIN) == 0 and finish_exp == 1:
if int(OVER) == 1:
EXIT = 1
sleep(1)
exit()
SOUND = 1
init_enemy()
@micropython.viper
def fill_circle(x0:int, y0:int, radius:int, color:int):
screen_ptr = ptr16(LCD.buffer)
max_addr = MAXSCREEN_X * MAXSCREEN_Y
# From Adafruit GFX Arduino library
y_start = y0 - radius
for y2 in range(y_start, y_start+2*radius + 1):
addr = y2 * MAXSCREEN_X + x0
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
f = 1 - radius
ddF_x = 1
ddF_y = -2 * radius
x = 0
y = radius
while x < y:
if f >= 0:
y -= 1
ddF_y += 2
f += ddF_y
x += 1
ddF_x += 2
f += ddF_x
x2 = x0 + x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 + y
y_start = y0 - x
for y2 in range(y_start,y_start+ 2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - x
y_start = y0 - y
for y2 in range(y_start, y_start+2*y + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
x2 = x0 - y
y_start = y0 - x
for y2 in range(y_start, y_start+2*x + 1):
addr = y2 * MAXSCREEN_X + x2
if addr > 0 and addr < max_addr:
screen_ptr[addr] = color
@micropython.viper
def draw(fps:int):
screen_ptr = ptr16(LCD.buffer)
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
cursor_ptr = ptr8(cursor)
e_sprite_ptr = ptr8(enemy_sprite)
a_sprite_ptr = ptr8(asset_sprite)
trail_ptr = ptr8(trail)
trail2_ptr = ptr8(trail2)
size = 4
offset = 0
# ggg_bbbbbrrrrr_ggg
red = 0b111_0011111111_001
blue= 0b000_1111100000_000
player_ptr[SPRITE]+=1
if player_ptr[SPRITE] > PLAYER_BLINK:
player_ptr[SPRITE] = 0
for y in range(size):
for x in range(size):
color = cursor_ptr[y*4+x]
if (color == 1 and player_ptr[SPRITE]<PLAYER_BLINK>>1) or (color == 0 and player_ptr[SPRITE]>=PLAYER_BLINK>>1):
screen_ptr[(y+player_ptr[Y_POS])*MAXSCREEN_X + player_ptr[X_POS]+x] = 0xffff
for i in range(NUM_ASSET): # draw player missiles
index = ASSET_PARAMS*i
index2 = 200*(i)*2
pos = asset_ptr[index+LIFE] # 400 -> 0
if pos == 0: continue
for y in range(size): # draw cross
for x in range(size):
x1 = CROSS_X + ASSET_PARAMS * i
y1 = CROSS_Y + ASSET_PARAMS * i
color = a_sprite_ptr[y*4+x+16]
if color:
screen_ptr[(y+(asset_ptr[y1]>>16))*MAXSCREEN_X + (asset_ptr[x1]>>16)+x] = 0xffff
for j in range(400,pos-2,-2):
x = trail_ptr[index2+j]
y = trail_ptr[index2+j+1]
addr = y * MAXSCREEN_X + x
screen_ptr[addr] = blue
screen_ptr[addr] = 0xffff
for i in range(NUM_ENEMY): # draw enemy missiles
index = ENEMY_PARAMS*i
index2 = 200*(i)*2
pos = enemy_ptr[index+LIFE] # 400 -> 0
if pos == 0: continue
for j in range(400,pos-2,-2):
x = trail2_ptr[index2+j]
y = trail2_ptr[index2+j+1]
addr = y * MAXSCREEN_X + x
screen_ptr[addr] = red
screen_ptr[addr] = 0xffff
LCD.text(str(fps),0,0,0xff)
LCD.text(str(CITYS_REMAIN),0,10,0xff)
LCD.show()
LCD.fill(0)
@micropython.viper
def draw_citys():
screen_ptr = ptr16(LCD.buffer)
bitmap_ptr = ptr16(city_sprite)
city_ptr = ptr8(city)
for n in range(3):
if city_ptr[n+1] == 0 : continue
for y in range(9):
for x in range(16):
addr = y * 160 + x + 15 + BOTTOM_SCREEN
screen_ptr[addr+n*20] = bitmap_ptr[y*16+x]
#screen_ptr[addr+n*20+72] = bitmap_ptr[y*16+x]
for n in range(3):
if city_ptr[n+5] == 0 : continue
for y in range(9):
for x in range(16):
addr = y * 160 + x + 15 + BOTTOM_SCREEN
#screen_ptr[addr+n*20] = bitmap_ptr[y*16+x]
screen_ptr[addr+n*20+72] = bitmap_ptr[y*16+x]
for n in range(3):
for y in range(6):
for x in range(14):
screen_ptr[y*160+n*72+x+(160*2)+BOTTOM_SCREEN] = 0xff
def read_joystick():
global player
pot_scale = 13
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 int(abs(x_inc))<2:
x_inc=0
if int(abs(y_inc))<2:
y_inc=0
if x_inc==0 and y_inc==0: return
player[X_POS] -= x_inc
player[Y_POS] += y_inc
if player[X_POS] < 15:
player[X_POS] = 15
if player[X_POS] > MAXSCREEN_X-15:
player[X_POS] = MAXSCREEN_X-15
if player[Y_POS] < 15:
player[Y_POS] = 15
if player[Y_POS] > MAXSCREEN_Y-15:
player[Y_POS] = MAXSCREEN_Y-15
@micropython.viper
def button():
global LIVE_ASSETS, SOUND
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
pushed = not FIRE.value()
asset_ptr = ptr32(asset)
if not pushed: return
x = player_ptr[X_POS]
y = player_ptr[Y_POS]
for i in range(NUM_ASSET): # search through assets to find unused
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0:
dist_left = int(distance_asm(x,y,(LAUNCH_LEFT % 160),(LAUNCH_LEFT // 160)))
dist_center = int(distance_asm(x,y,(LAUNCH_CENTER % 160),(LAUNCH_CENTER // 160)))
dist_right = int(distance_asm(x,y,(LAUNCH_RIGHT % 160),(LAUNCH_RIGHT // 160)))
if dist_left < dist_center and dist_left < dist_right:
launch_x = (LAUNCH_LEFT % 160)<<16
launch_y = (LAUNCH_LEFT // 160)<<16
if dist_center <= dist_left and dist_center <= dist_right:
launch_x = (LAUNCH_CENTER % 160)<<16
launch_y = (LAUNCH_CENTER // 160)<<16
if dist_right < dist_center and dist_right < dist_left:
launch_x = (LAUNCH_RIGHT % 160)<<16
launch_y = (LAUNCH_RIGHT // 160)<<16
asset_ptr[index+X_POS] = launch_x
asset_ptr[index+Y_POS] = launch_y
asset_ptr[index+CROSS_X] = x<<16
asset_ptr[index+CROSS_Y] = y<<16
asset_ptr[index+LIFE] = 200*2
init_trail(trail,launch_x,launch_y,x<<16,y<<16,i)
SOUND = 2
return
@micropython.viper
def move_asset():
player_ptr = ptr32(player)
enemy_ptr = ptr32(enemy)
asset_ptr = ptr32(asset)
trail_ptr = ptr8(trail)
for i in range(NUM_ASSET):
index = ASSET_PARAMS*i
if asset_ptr[index+LIFE] == 0 : continue
asset_ptr[index+LIFE] -= 2
pos = asset_ptr[index+LIFE]
trail_x = trail_ptr[i*400+pos]
trail_y = trail_ptr[i*400+pos+1]
cross_x = asset_ptr[index+CROSS_X]>>16
cross_y = asset_ptr[index+CROSS_Y]>>16
if (cross_x == trail_x or cross_x == trail_x-1 or cross_x == trail_x+1) and (cross_y == trail_y or cross_y == trail_y-1 or cross_y == trail_y+1): # trail hits cross
asset_ptr[index+LIFE] = 0
start_explode(trail_x,trail_y)
@micropython.viper
def move_enemy():
global ENEMY_REMAIN, CITYS_REMAIN, OVER
enemy_ptr = ptr32(enemy)
trail_ptr = ptr8(trail2)
city_ptr = ptr8(city)
for i in range(NUM_ENEMY):
index = ENEMY_PARAMS*i
if enemy_ptr[index+LIFE] == 0 : continue
enemy_ptr[index+LIFE] -= 2
pos = enemy_ptr[index+LIFE]
trail_x = trail_ptr[i*400+pos]
trail_y = trail_ptr[i*400+pos+1]
target_x = enemy_ptr[index+TARGET_X]
target_y = enemy_ptr[index+TARGET_Y]
if (target_x == trail_x or target_x == trail_x-1 or target_x == trail_x+1) and (target_y == trail_y or target_y == trail_y+1): # trail hits target
enemy_ptr[index+LIFE] = 0
target_hit = enemy_ptr[index+TARGET]
if city_ptr[target_hit] == 1 and ((target_hit > 0 and target_hit < 4) or (target_hit > 4 and target_hit < 8)):
CITYS_REMAIN = int(CITYS_REMAIN)-1
city_ptr[target_hit] = 0
start_explode(trail_x,trail_y)
ENEMY_REMAIN = int(ENEMY_REMAIN)-1
if int(CITYS_REMAIN) == 0:
print('GAME OVER')
OVER = 1
@micropython.viper
def init_trail(t,x1:int,y1:int,x2:int,y2:int,index:int):
trail_ptr = ptr8(t)
x = x1
y = y1
dx = (x2-x1)
dy = (y2-y1)
dx_a = int(abs(dx))
dy_a = int(abs(dy))
#if (dx>>16)*(dy>>16)==0: return x,y
index = (index * 400)+400
if dy_a >= dx_a:
if y2 > y1:
y_inc = 1<<16
x_inc = dx//(dy>>16) * -1
for y in range(y1,y1+dy_a+(1<<16),1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
y_inc = -1<<16
x_inc = dx//(dy>>16)
for y in range(y1,y1-dy_a-(0<<16),-1<<16):
x -= x_inc
index -= 2
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
y_inc = dy//(dx>>16)
if x2 > x1:
x_inc = 1<<16
for x in range(x1,x2+(0<<16),1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
else:
x_inc = -1<<16
y_inc = y_inc * -1
for x in range(x1,x2-(0<<16),-1<<16):
index -= 2
y += y_inc
trail_ptr[index ] = x>>16
trail_ptr[index+1] = y>>16
def core1():
global SOUND,WATCHDOG, EXIT
sleep(.2)
while not EXIT:
WATCHDOG += 1
if WATCHDOG == 100_000:
print('core 0 crash')
reset()
if SOUND==1:
sound_alarm()
if SOUND==1:
SOUND = 0
if SOUND==2:
sound_missile()
if SOUND==2:
SOUND = 0
if SOUND==3:
sound_explode()
if SOUND==3:
SOUND = 0
@micropython.asm_thumb
def distance_asm(r0,r1,r2,r3): # x1,y1,x2,y2
sub(r4,r2,r0) # x2-x1
sub(r5,r3,r1) # y2-y1
mul(r4,r4) # (x2-x1)^2
mul(r5,r5) # (y2-y1)^2
add(r0,r4,r5) # (x2-x1)^2 + (y2-y1)^2
mov(r2,0) # r2 = res
cmp(r0,0) # r0 = num
bpl(MAIN)
mov(r0,0)
b(EXIT)
label(MAIN)
mov(r1,1) # r1 = bit
lsl(r1,r1,30)
label(FOUR_POWER)
cmp(r1,r0) # bit <= num
ble(LOOP)
asr(r1,r1,2) # bit = bit >> 2
b(FOUR_POWER)
label(LOOP)
cmp(r1,0)
beq(EXIT) # bit == 0
add(r3,r1,r2) # res + bit
cmp(r0,r3) # if num >= res + bit
blt(ELSE)
sub(r0,r0,r3) # num = num - (res + bit)
asr(r4,r2,1) # res >> 1
add(r2,r4,r1) # res = (res >> 1) + bit
b(IF_DONE)
label(ELSE)
asr(r2,r2,1) # res = res>>1
label(IF_DONE)
asr(r1,r1,2) # bit = bit >> 2
b(LOOP)
label(EXIT)
cmp(r0,r2) # if num <= res:
ble(NO_ROUND)
add(r2,r2,1) # res += 1
label(NO_ROUND)
mov(r0,r2)
def sound_alarm():
volume = 1000
POWER.on()
BUZZ.duty_u16(volume)
start =1250
end = 1050
for repeats in range(6):
for i in range(start,end,-10):
BUZZ.duty_u16(volume)
BUZZ.freq(i)
sleep(.014)
POWER.off()
def sound_missile():
global SOUND
SOUND = 0
volume = 2000
POWER.on()
BUZZ.duty_u16(volume)
start =1500
end = 500
for i in range(start,end,-1):
BUZZ.duty_u16(volume)
volume -= 2
BUZZ.freq(i)
if SOUND == 2 :
POWER.off()
return
for delay in range(150):
pass
POWER.off()
def sound_explode():
POWER.on()
start =1000
end = 0
BUZZ.freq(50)
for i in range(start,end,-10):
BUZZ.duty_u16(i)
sleep(.001)
POWER.off()
if __name__=='__main__':
FIRE = Pin(22, Pin.IN, Pin.PULL_UP)
POWER = Pin(16, machine.Pin.OUT)
BUZZ = machine.PWM(Pin(17, machine.Pin.OUT))
pwm = PWM(Pin(13))
pwm.freq(1000)
pwm.duty_u16(0x7fff)#max 65535
machine.freq(200_000_000)
LCD = LCD_1inch8()
machine.mem32[0x40008048] = 1<<11 # enable peri_ctrl clock
WATCHDOG = 0
SOUND = 0
ENEMY_REMAIN = 0
CITYS_REMAIN = 6
EXIT = 0
OVER = 0
_thread.start_new_thread(core1, ())
init_player()
init_asset()
init_enemy()
init_joystick()
init_field()
init_explode()
gc.collect()
print(gc.mem_free())
fps = 0
move_enemy_ticks = ticks_ms()
explode_ticks = ticks_ms()
explode_inc = 0
move_asset_ticks = ticks_ms()
button_ticks = ticks_ms()
joystick_ticks = ticks_ms()
while not EXIT:
gticks = ticks_us()
WATCHDOG = 0
if ticks_diff(ticks_ms(),move_enemy_ticks) > 100:
move_enemy_ticks = ticks_ms()
move_enemy()
if ticks_diff(ticks_ms(),move_asset_ticks) > 5:
move_asset_ticks = ticks_ms()
move_asset()
if ticks_diff(ticks_ms(),joystick_ticks) > 30:
joystick_ticks = ticks_ms()
read_joystick()
if ticks_diff(ticks_ms(),button_ticks) > 200:
button_ticks = ticks_ms()
button()
if ticks_diff(ticks_ms(),explode_ticks) > 100:
explode_ticks = ticks_ms()
explode_inc = 1
else:
explode_inc = 0
draw_citys()
draw_explode(explode_inc)
draw(fps)
fps = 1_000_000//int(ticks_diff(ticks_us(),gticks))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment