feat: Add rat AI, 4-channel tracker, interactive cursor, and test suite updates
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
|
||||
def decode_gb_tile(data):
|
||||
pixels = [[0]*8 for _ in range(8)]
|
||||
for y in range(8):
|
||||
lsb = data[y*2]
|
||||
msb = data[y*2+1]
|
||||
for x in range(8):
|
||||
bit_lsb = (lsb >> (7 - x)) & 1
|
||||
bit_msb = (msb >> (7 - x)) & 1
|
||||
pixels[y][x] = (bit_msb << 1) | bit_lsb
|
||||
return pixels
|
||||
|
||||
RatSpriteData = [
|
||||
0x00, 0x00, 0x07, 0x07, 0x3F, 0x38, 0x5E, 0x71, 0x5B, 0x74, 0x3F, 0x38, 0x07, 0x07, 0x00, 0x00,
|
||||
0x00, 0x00, 0xF0, 0xF0, 0xC8, 0x08, 0xD4, 0x1C, 0xD4, 0x1C, 0xC8, 0x08, 0xF0, 0xF0, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x18, 0x18, 0x24, 0x3C, 0x3C, 0x3C, 0x3C, 0x24, 0x6E, 0x52, 0x7E, 0x42, 0x76, 0x4A,
|
||||
0x7E, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x5A, 0x5A, 0x24, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00
|
||||
]
|
||||
|
||||
tiles = [decode_gb_tile(RatSpriteData[i*16:(i+1)*16]) for i in range(4)]
|
||||
|
||||
colors = {0: ' ', 1: '░░', 2: '▓▓', 3: '██'}
|
||||
rgb_colors = {0: (255,255,255), 1: (170,170,170), 2: (85,85,85), 3: (0,0,0)}
|
||||
|
||||
def print_and_save(name, pixels_2d, width, height):
|
||||
print(f"--- {name} ---")
|
||||
for y in range(height):
|
||||
row_str = ""
|
||||
for x in range(width):
|
||||
row_str += colors[pixels_2d[y][x]]
|
||||
print(row_str)
|
||||
|
||||
scale = 20
|
||||
with open(f"/tmp/{name}.ppm", "w") as f:
|
||||
f.write(f"P3\n{width*scale} {height*scale}\n255\n")
|
||||
for y in range(height):
|
||||
for sy in range(scale):
|
||||
for x in range(width):
|
||||
c = pixels_2d[y][x]
|
||||
rgb = rgb_colors[c]
|
||||
for sx in range(scale):
|
||||
f.write(f"{rgb[0]} {rgb[1]} {rgb[2]} ")
|
||||
f.write("\n")
|
||||
|
||||
right_pixels = [[0]*16 for _ in range(8)]
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
right_pixels[y][x] = tiles[0][y][x]
|
||||
right_pixels[y][x+8] = tiles[1][y][x]
|
||||
|
||||
left_pixels = [[0]*16 for _ in range(8)]
|
||||
for y in range(8):
|
||||
for x in range(16):
|
||||
left_pixels[y][x] = right_pixels[y][15-x]
|
||||
|
||||
down_pixels = [[0]*8 for _ in range(16)]
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
down_pixels[y][x] = tiles[2][y][x]
|
||||
down_pixels[y+8][x] = tiles[3][y][x]
|
||||
|
||||
up_pixels = [[0]*8 for _ in range(16)]
|
||||
for y in range(16):
|
||||
for x in range(8):
|
||||
up_pixels[y][x] = down_pixels[15-y][x]
|
||||
|
||||
print_and_save("sprite_right", right_pixels, 16, 8)
|
||||
print_and_save("sprite_left", left_pixels, 16, 8)
|
||||
print_and_save("sprite_down", down_pixels, 8, 16)
|
||||
print_and_save("sprite_up", up_pixels, 8, 16)
|
||||
@@ -0,0 +1,74 @@
|
||||
def encode_gb_tile(pixels):
|
||||
data = []
|
||||
for y in range(8):
|
||||
lsb = 0
|
||||
msb = 0
|
||||
for x in range(8):
|
||||
c = pixels[y][x]
|
||||
if c & 1: lsb |= (1 << (7 - x))
|
||||
if c & 2: msb |= (1 << (7 - x))
|
||||
data.extend([lsb, msb])
|
||||
return data
|
||||
|
||||
def print_hex(data):
|
||||
print(", ".join(f"0x{b:02X}" for b in data))
|
||||
|
||||
# Down Top Half
|
||||
down_top = [
|
||||
[0,0,0,0,0,0,0,0],
|
||||
[0,0,0,3,3,0,0,0],
|
||||
[0,0,3,2,2,3,0,0],
|
||||
[0,0,3,3,3,3,0,0],
|
||||
[0,0,3,1,1,3,0,0],
|
||||
[0,3,1,2,1,1,3,0],
|
||||
[0,3,1,1,1,1,3,0],
|
||||
[0,3,1,1,2,1,3,0]
|
||||
]
|
||||
|
||||
# Down Bottom Half
|
||||
down_bot = [
|
||||
[0,3,1,1,1,1,3,0],
|
||||
[0,3,1,1,1,1,3,0],
|
||||
[0,3,0,0,0,0,3,0],
|
||||
[0,3,0,3,3,0,3,0],
|
||||
[0,0,3,2,2,3,0,0],
|
||||
[0,0,0,3,3,0,0,0],
|
||||
[0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,0]
|
||||
]
|
||||
|
||||
# Combine Down
|
||||
down_full = down_top + down_bot
|
||||
|
||||
# Rotate CCW for Right (Wait: earlier we established CCW rotation maps nose to Right side)
|
||||
# If Nose is at y=12, CCW rotation: new_x = y, new_y = 7 - x?
|
||||
# For 16x8 from 8x16:
|
||||
# new_width = 16, new_height = 8
|
||||
# old_x in 0..7, old_y in 0..15
|
||||
# Rotation CCW:
|
||||
# X_new = old_y
|
||||
# Y_new = 7 - old_x
|
||||
right_full = [[0]*16 for _ in range(8)]
|
||||
for oy in range(16):
|
||||
for ox in range(8):
|
||||
nx = oy
|
||||
ny = 7 - ox
|
||||
right_full[ny][nx] = down_full[oy][ox]
|
||||
|
||||
# Split into two 8x8 tiles
|
||||
right_left = [row[:8] for row in right_full]
|
||||
right_right = [row[8:] for row in right_full]
|
||||
|
||||
data0 = encode_gb_tile(right_left)
|
||||
data1 = encode_gb_tile(right_right)
|
||||
data2 = encode_gb_tile(down_top)
|
||||
data3 = encode_gb_tile(down_bot)
|
||||
|
||||
print("Tile 0 (Right Left):")
|
||||
print_hex(data0)
|
||||
print("Tile 1 (Right Right):")
|
||||
print_hex(data1)
|
||||
print("Tile 2 (Down Top):")
|
||||
print_hex(data2)
|
||||
print("Tile 3 (Down Bot):")
|
||||
print_hex(data3)
|
||||
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
Script per validare e testare la ROM su emulatore in modalità headless.
|
||||
Questo file è utile per l'automazione dei test o come integrazione CI.
|
||||
Utilizza PyBoy per eseguire 5 secondi di emulazione ed esportare uno screenshot.
|
||||
"""
|
||||
from pyboy import PyBoy
|
||||
import sys
|
||||
|
||||
def main():
|
||||
try:
|
||||
# Avvia l'emulatore PyBoy disattivando la finestra nativa ("null")
|
||||
pyboy = PyBoy('maze.gb', window='null')
|
||||
|
||||
# Effettua l'avanzamento esatto di 5 secondi (300 frame a 60 fps logici)
|
||||
for _ in range(60 * 5):
|
||||
pyboy.tick()
|
||||
|
||||
# Salva un fotogramma esatto dell'esito della generazione su file
|
||||
pyboy.screen.image.save('/tmp/maze_gb.png')
|
||||
pyboy.stop()
|
||||
|
||||
print("Test passed: Screenshot saved to /tmp/maze_gb.png")
|
||||
except Exception as e:
|
||||
print(f"Test failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user