MemoryError¶
Published on 2018-01-25 in Micro:Boy.
So I got that Boulder Dash game as far as I can, working around the ridiculously low memory of Micro:bit, and I think I won’t be able to go further. This platform is simply too puny.
I have an animated player character digging tunnels, and I have boulders that block his way and fall into empty space you dig under them. And I can’t add even a single line of code without getting a MemoryError.
———- more ———-What I would need to make it a playable game at a minimum? Well, rocks need to slip sideways when on top of other rocks, and there have to be gems to collect — then I could design some logic levels, at least. If I managed to also add monsters, it could be a fun arcade game. This is the code I have so far:
from microbit import i2c, running_time, sleep, pin1, pin2
class Boy:
def __init__(self, delay=100):
b = bytearray
self.buffer = b(1024 + 1)
self.dmin = b([0] * 8)
self.dmax = b([128] * 8)
i2c.init(freq=400000, scl=pin2, sda=pin1)
try:
i2c.write(0x3c, b'')
except:
pass
i2c.write(0x3c,
b'\x00\xae\xd5\x80\xa8\x3f\xd3\x00\x40\x80\x14'
b'\x20\x00\xa1\xc8\xa1\xda\x12\x81\xcf\xd9\xf1'
b'\xdb\x40\xa4\xa6\xaf')
self.next = running_time() + delay
self.delay = delay
@staticmethod
def buttons():
return i2c.read(0x10, 1)[0]
def tick(self):
sleep(self.next - running_time())
self.next += self.delay
def update(self):
c = bytearray(4)
b = self.buffer
i = 0
for p in range(8):
dmin = self.dmin[p]
dmax = self.dmax[p]
if dmax:
c[1] = 0xb0 | p
c[2] = 0x00 | ((2 + dmin) & 0x0f)
c[3] = 0x10 | ((2 + dmin) >> 4) & 0x0f
i2c.write(0x3c, c)
l = b[i + dmin:i + dmax + 1]
l[0] = 0x40
i2c.write(0x3c, l)
i += 128
self.dmax[p] = 0
self.dmin[p] = 127
def blit(self, x, y, data, mask=b''):
if not 0 <= x < 120:
return
b = self.buffer
p = y // 8
s = y % 8
if 0 <= p <= 7:
i = x + 128 * p + 1
for a in mask:
b[i] &= ~(a << s)
i += 1
i = x + 128 * p + 1
for a in data:
b[i] ^= a << s
i += 1
self.dmin[p] = min(self.dmin[p], x)
self.dmax[p] = max(self.dmax[p], x + 8)
p += 1
if 0 <= p <= 7 and s:
s = 8 - s
i = x + 128 * p + 1
for a in mask:
b[i] &= ~(a >> s)
i += 1
i = x + 128 * p + 1
for a in data:
b[i] ^= a >> s
i += 1
self.dmin[p] = min(self.dmin[p], x)
self.dmax[p] = max(self.dmax[p], x + 8)
boy = Boy()
MAN_MASK = b'0\xfe\xff\xff\xff\xff\xfe0'
DIRT = b'\x8a \x05P\x05 \x8a '
DARK = b'\x00\x00\x00\x00\x00\x00\x00\x00'
ROCK_MASK = b'<~\xff\xff\xff\xff~<'
ROCK = b'\x004Z>V*\x14\x00'
dirt = bytearray(16)
for y in range(8):
for x in range(16):
boy.blit(x * 8, y * 8, DIRT)
rocks = {(0, 0), (3, 3), (3, 4), (5, 2), (10, 1)}
for x, y in rocks:
boy.blit(x * 8, y * 8, ROCK, ROCK_MASK)
x, y = 0, 8
dx = dy = steps = 0
while True:
key = boy.buttons()
if steps == 0:
dirt[x // 8] |= 1 << (y // 8)
dx = dy = 0
if key & 4 and x < 120:
dx = 1
elif key & 16 and x > 0:
dx = -1
elif key & 32 and y < 56:
dy = 1
elif key & 8 and y > 0:
dy = -1
if (x // 8 + dx, y // 8 + dy) in rocks:
dx = dy = 0
if dx or dy:
steps = 7
else:
steps -= 1
x += dx
y += dy
man = (b'\x00\x10z>:~ \x00', b'\x00 \x1a~z>\x10\x00')[(steps // 2) % 2]
if dx + dy > 0:
man = bytes(reversed(man))
for rx, ry in list(rocks):
if dirt[rx] & (1 << (ry + 1)) and not (
(y // 8 == ry + 1) and
(rx * 8 - 6 <= x <= rx * 8 + 6)
) and (rx, ry + 1) not in rocks:
dirt[rx] |= 1 << ry
boy.blit(rx * 8, ry * 8, DARK, ROCK_MASK)
boy.blit(rx * 8, ry * 8 + 8, ROCK, ROCK_MASK)
rocks.remove((rx, ry))
rocks.add((rx, ry + 1))
boy.blit(x, y, man, MAN_MASK)
boy.update()
boy.blit(x, y, DARK, MAN_MASK)
boy.tick()
It’s not the most readable code possible, you have to admit, but it works. I guess if I completely skipped animations, and made things jump by full tiles, then I could save some memory…