Etter å ha sett Rick and Morty episoden med snake Jazz i, fikk jeg en ide om at jeg ville lage en slangeanimasjon på min Rasberry PI Sense Hat. Animasjonen skulle være random og holde på så lenge jeg ønsket. Displayet består av et 8×8 led display og etter en del tankevirksomhet kom jeg på denne visuelle representasjonen med noen logiske regler for animasjonen. Koden begynte jeg på, men stagnerte da jeg innså at det var den siste kvelden i den offisielle påskeferien og tiden burde kanskje bli brukt annerledes…

Logiske notater for en randomisert slangeanimasjon på 8X8

Etter en del om og men ble denne koden til. Jeg måtte ha litt hjelp for å unngå at den stanget i seg selv når lengden ble større enn 4. Jeg fikk det ikke 100% til grunnet at min dybdeforståelse rundt rekursivitet ikke var optimal.

from enum import Enum
import random
import argparse


class Direction(Enum):
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

    @classmethod
    def directions(cls):
        return (cls.UP, cls.DOWN, cls.LEFT, cls.RIGHT)


def _get_new_pos(position: list, direction: Direction):
    x, y = position[-1]
    if direction == Direction.UP:
        new_pos = (x, y - 1)
    elif direction == Direction.DOWN:
        new_pos = (x, y + 1)
    elif direction == Direction.LEFT:
        new_pos = (x - 1, y)
    elif direction == Direction.RIGHT:
        new_pos = (x + 1, y)
    else:
        raise RuntimeError("Invalid direction")
    return new_pos


class Snake:
    def __init__(self, length: int, x_min: int, y_min: int, x_max: int, y_max: int):
        self.length = length
        self.x_min = x_min
        self.y_min = y_min
        self.x_max = x_max
        self.y_max = y_max
        self.pos = [(x, 0) for x in range(length)]

    def coordinates(self):
        return self.pos

    def check_move(self, position: list, direction: Direction):
        new_pos = _get_new_pos(position, direction)
        return (
            (new_pos not in position)
            and (self.x_min <= new_pos[0] < self.x_max)
            and (self.y_min <= new_pos[1] < self.y_max)
        )

    def check_move_lookahead(self, position: list, direction: Direction, lookahead=1):
        if self.check_move(position, direction):
            future = position[1:] + [_get_new_pos(position, direction)]
            if lookahead > 1:
                possible_directions = [
                    direction
                    for direction in Direction.directions()
                    if self.check_move_lookahead(future, direction, lookahead - 1)
                ]
            else:
                possible_directions = [
                    direction
                    for direction in Direction.directions()
                    if self.check_move(future, direction)
                ]
            return len(possible_directions) > 0
        return False

    def move(self, direction: Direction):
        new_head = _get_new_pos(self.pos, direction)
        x, y = new_head
        assert new_head not in self.pos, "moving into itself"
        assert self.x_min <= x < self.x_max, "x out of bounds!"
        assert self.y_min <= y < self.y_max, "y out of bounds!"
        self.pos = self.pos[1:] + [new_head]

    def random_move(self):
        self.move(
            random.choice(
                [
                    direction
                    for direction in Direction.directions()
                    if self.check_move_lookahead(self.pos, direction, lookahead=3)
                ]
            )
        )

Last ned: Snake.zip her og kjør app.py hvis du vet hva du gjør.
Du kan installere pyhton på windows fra Store!