Multi Agent Snek

December 2019 ยท Last updated 2023-01-27 ยท 758 words

I am really glad of the revival of the functional programming paradigm. Before, programming in an Object Oriented style was kind of the thing you just did if you didn’t want people to think you were from the past. It’s the right tool for serious projects people would proclaim, and that Java is a blue collar language that gets the job done.

The tide has long turned; now you find a lot of articles like Object-Oriented Programming โ€” The Trillion Dollar Disaster, stating OO was all a big mistake and we should move on. Rich Hickey, author of Clojure, delivered a great keynote speech at Railsconf 2012, in which he made the point that object orientation complects things by (i.a.) mixing code and data, and that mutable state by nature is much more complex than simple functions.

And I agree. But that doesn’t mean OO isn’t good for something :)

The old schoolers will remember how Object Orientation was all the rage because it was supposed to facilitate Artificial Intelligence. Of course, just like today, everybody knew that AI was just the next big thing. Use Smalltalk! they would say. “Passing messages between Objects is such a great way of modelling the reality.”

Well, what can I say. It indeed is. Object orientation:

Born of simulation, now used for everything.

The first object oriented programming language, which later influenced both C++ and Java, was Simula, and it didn’t only introduce OO terms like Objects and Classes, but also came with a package for discrete event simulation built right in.

As it happens, simulation is what I do during the day. And pointing the Instruction Pointer at some wild mix of data, code, and whatever comes along sometimes is just right thing to do.

So, let’s simulate some!

At CCCamp2019 wanda_fish and I wrote axelerometersnek, a small (~ 30 SLOC) Python script for the smart watch themed card10 badge. It already is a lot of what you’d need for the classic game Snake, but it isn’t really a game yet.

Let’s make a game of it!

I want to keep the code base as simple as possible, but I know introducing a lot of different things to make this code a game will riddle that awesome two dozen line of bliss with lots of cruft, yielding a bad kind of snake spaghetti. OO to the rescue!

We will have different things in our game, depending on what kind of game we want to write. A labyrinth would need a labyrinth, a space ship would need some obstacles, our snake first needs something to eat. And of course we need a player. All these things are agents in our multi-agent simulation:

UML diagram of our multi agent snek

Agent is an abstract class that only implements a run() method, which in turn calls move(), collide() and paint(). To create a new agent, one only has to extend the Agent class and implement (and/or add, overwrite etc.) the required methods. This makes the main program even shorter than axelerometersnek’s:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import display
import utime
from apps.multiagentsnek.snek import Snek
from apps.multiagentsnek.cookie import Cookie
from apps.multiagentsnek.referee import Referee

disp = display.open()
agents = [Snek(), Cookie(), Referee()]

while True:
    utime.sleep(0.03) # ~30 fps
    disp.clear()
    for agent in agents:
        agent.run()
    disp.update()

All the complexity (and most bugs ๐Ÿ˜‰) can now be organized in separate files. Neat!

To give something to hold on to, the Agent base class gives a rough and optional outline: Override any of the methods to state your intent to the rest of the team, or just override run() and do whatever you want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Agent:
    """ Abstract base class for all entities in our `simulation'.
        Implement move(), collide() and paint() as you please. """

    def move(self):
        """ Calculate new coordinates """
        pass

    def collide(self):
        """ Collision detection """
        pass

    def paint(self):
        """ Render """
        pass

    def run(self):
        """ Hello instruction pointer """
        self.move()
        self.collide()
        self.paint()

The Referee is only displaying points (the Snek’s length) and stopping the game when the player has eaten too much of itself. So it doesn’t need collision detection, for example.

Here’s a blurry video of me playing Multi Agent Snek on my card10:

Get the game (and the rest of the source code) at the Hatchery (or at GitHub, should that fail).

Have fun!

Portrait

Greetings! You are reading the personal web page of Florian Sesser.

I always like to hear from people. Please have a look at the imprint for ways to get in touch.