The Repo on GitHub

We simplify two classes by adding just a bit of capability to another. Still wondering how to write articles after the fact.

Last night, in Pyto, the iPad Python, I experimented with Enum and then with a class representing directions on a plane. The flat kind, not the flying kind. This morning around 0645, while waiting for pairs to show up, I implemented the core of a new Direction class, which was formerly an Enum.

Bryan and GeePaw arrived after a while, and Bryan took on extending the Bot to be able to move in a few directions. He also rigged the PyGame demo to run over the block and visibly take it. the movie below shows the robot moving in from the left, running over the Block, and picking it up. Not shown is that it now has the block in its inventory.

So that was a Woot! moment. There’s real joy in seeing the computer actually do what you set out to do. Far more fun than the unit test working, which, of course, it does.

Then Bryan wandered off to watch F1 or something, and GeePaw and I went a bit further, plugging in my new Direction class, which is like this:

class Direction:
    NORTH = None
    EAST = None
    SOUTH = None
    WEST = None
    ALL = None

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return f'({self.x}, {self.y})'

    def left(self):
        x, y = self.x, self.y
        new_x, new_y = -y, x
        return Direction(new_x, new_y)


Direction.NORTH = Direction(0, 1)
Direction.EAST = Direction(1, 0)
Direction.SOUTH = Direction(0, -1)
Direction.WEST = Direction(-1, 0)
Direction.ALL = [Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST]

There are just a couple of tests so far:

class TestDirection:
    def test_hookup(self):
        assert 2 + 2 == 4

    def test_direction_EAST(self):
        dir = Direction.EAST

    def test_direction_left(self):
        east = Direction.EAST
        assert east.left() == Direction.NORTH

GeePaw mentioned that his inclination would have been to go with a Direction like this rather than the simple Enum that we had. It does turn out that with the step values in x and y like that you get free rotations, since sine and cosine of 90 degrees work as they do.

In the world, our motion is much simplified, and the Bot is certainly no more complicated than it was.

class Bot:
    def step(self):
        self.world.step(self, self.direction)

    def change_direction(self):
        direction = self.direction
        while direction == self.direction:
            direction = random.choice(Direction.ALL)
        self.direction = direction

And then in World:

class World:
    def step(self, bot, direction):
        self._move(bot, direction.x, direction.y)

    def _move(self, entity, dx, dy):
        entity = self.map.contents[entity.id]
        location = entity.location
        new_x = self.clip(location.x + dx, self.width)
        new_y = self.clip(location.y + dy, self.height)
        entity.location = Point(new_x, new_y)

I think there are still some vestiges of the old scheme around, in tests, but the new scheme has less code in Bot, less in World, and just the smallest bit in Direction. It seems to have been a nice move.

And that’s about all the news that’s fit to print. I’m still trying to figure out how to write articles post hoc. I can’t write, program, and chat all at the same time. I’ll try to improve and am certainly open to suggestions.

See you next time!