from machine import Pin, I2C
import framebuf, time, urandom
from ssd1306 import SSD1306_I2C
# === Display Setup ===
i2c = I2C(1, scl=Pin(11), sda=Pin(10), freq=400_000)
oled = SSD1306_I2C(128, 64, i2c)
# === Tile Definitions ===
def tile(b): return framebuf.FrameBuffer(bytearray(b), 8, 8, framebuf.MONO_HLSB)
TILES = {
'☺': tile([0x3C,0x42,0xA5,0x81,0xA5,0x99,0x42,0x3C]), # Hero
'W': tile([0xFF,0x81,0xBD,0xA5,0xBD,0x81,0xFF,0x00]), # Wall
'F': tile([0]*8), # Floor
'C': tile([0x18,0x3C,0x7E,0xDB,0xFF,0x7E,0x3C,0x18]), # Coin
'T': tile([0x3C,0x66,0x5A,0xA5,0xA5,0x5A,0x66,0x3C]), # Treasure
'H': tile([0x00,0x6C,0xFE,0xFE,0x7C,0x38,0x10,0x00]), # Heart
'E': tile([0x18,0x3C,0x7E,0xDB,0xFF,0x7E,0x24,0x42]), # Enemy
'S': tile([0x00,0x3C,0x42,0x99,0xA5,0x42,0x3C,0x00]) # Stairs
}
# === World Setup ===
WIDTH, HEIGHT = 32, 16
CAM_W, CAM_H = 16, 8
player = {'x': 2, 'y': 2, 'hp': 10, 'coins': 0, 'deaths': 0}
# Create map
world = [['F' for _ in range(WIDTH)] for _ in range(HEIGHT)]
for x in range(WIDTH): world[0][x] = world[HEIGHT-1][x] = 'W'
for y in range(HEIGHT): world[y][0] = world[y][WIDTH-1] = 'W'
# Add inner structure
for y in range(4,12):
world[y][10] = 'W'
world[y][15] = 'W'
world[8][y-2] = 'W'
# Sprinkle objects
world[3][5] = 'C'
world[4][7] = 'H'
world[5][13] = 'T'
world[7][6] = 'E'
world[9][9] = 'E'
world[10][14] = 'S'
# === Draw World ===
def draw_world(cam_x, cam_y):
for row in range(CAM_H):
for col in range(CAM_W):
wx, wy = cam_x + col, cam_y + row
tile_id = world[wy][wx] if 0 <= wx < WIDTH and 0 <= wy < HEIGHT else 'W'
oled.blit(TILES.get(tile_id, TILES['F']), col*8, row*8)
# Draw hero
px = (player['x'] - cam_x) * 8
py = (player['y'] - cam_y) * 8
oled.blit(TILES['☺'], px, py)
# HUD
oled.text("HP:%d" % player['hp'], 0, 56)
oled.text("$:%d" % player['coins'], 60, 56)
# === Roll Dice ===
def roll_die():
return urandom.getrandbits(3) % 6 + 1 # 1–6
# === Get directions ===
DIRS_ORTHO = [(0,-1),(0,1),(-1,0),(1,0)]
DIRS_DIAG = [(-1,-1),(1,-1),(-1,1),(1,1)]
# === Movement ===
def move_player():
roll = roll_die()
dirs = DIRS_DIAG if roll % 2 == 1 else DIRS_ORTHO
dir_idx = urandom.getrandbits(2) % len(dirs)
dx, dy = dirs[dir_idx]
steps = roll
for _ in range(steps):
nx, ny = player['x'] + dx, player['y'] + dy
if world[ny][nx] == 'W':
# Bounce: pick a new direction
for try_dir in dirs:
tx, ty = player['x'] + try_dir[0], player['y'] + try_dir[1]
if world[ty][tx] != 'W':
dx, dy = try_dir
break
nx, ny = player['x'] + dx, player['y'] + dy
# Move
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
player['x'], player['y'] = nx, ny
interact(world[ny][nx])
world[ny][nx] = 'F'
# === Interact ===
def interact(tile):
if tile == 'C':
player['coins'] += 1
elif tile == 'T':
player['coins'] += roll_die()
elif tile == 'H':
player['hp'] = min(10, player['hp'] + 2)
elif tile == 'E':
player['hp'] -= 3
if player['hp'] <= 0:
player['deaths'] += 1
player['hp'] = 10
player['x'], player['y'] = 2, 2
elif tile == 'S':
# Reached stairs – reset world (demo)
oled.fill(0)
oled.text("Next Floor!", 20, 30)
oled.show()
time.sleep(1)
generate_new_floor()
# === New Floor ===
def generate_new_floor():
global world
world = [['F' for _ in range(WIDTH)] for _ in range(HEIGHT)]
for x in range(WIDTH): world[0][x] = world[HEIGHT-1][x] = 'W'
for y in range(HEIGHT): world[y][0] = world[y][WIDTH-1] = 'W'
# Random decorations
for _ in range(15):
x, y = urandom.getrandbits(5) % WIDTH, urandom.getrandbits(4) % HEIGHT
world[y][x] = urandom.choice(['W','C','T','H','E'])
world[urandom.getrandbits(4)%HEIGHT][urandom.getrandbits(5)%WIDTH] = 'S'
player['x'], player['y'] = 2, 2
# === Main Game Loop ===
while True:
move_player()
cam_x = max(0, min(WIDTH - CAM_W, player['x'] - CAM_W//2))
cam_y = max(0, min(HEIGHT - CAM_H, player['y'] - CAM_H//2))
oled.fill(0)
draw_world(cam_x, cam_y)
oled.show()
time.sleep(1.0) # 1 turn per second