BOTS Sunday
It’s 0645 on Sunday. Bryan shows up as I’m summarizing status, and we get a really nice effect with some truly simple code.
I have some ideas, but without at least one working partner, I am reluctant to do anything significant. Yesterday or the day before, whenever it was, I did a little thing in a test file. Maybe I can try that.
Client / Server?
We ran into an interesting situation yesterday that’s worthy of mention and thought. In the Bot code, we wanted to know something, and one of us said we could just ask the World, which has the information. I objected, “because client/server”. We know that we want this game to work with World on a server, serving lots of bots from different sources. We are also mostly trying to ignore that fact, with an eye to seeing what happens. So, possibly, I should have said nothing.1 I don’t recall what the exact topic was now, it was just a moment that went by quickly.
Now, looking ahead as I perhaps shouldn’t to how the client/server thing will be done, I imagine that we’ll have a world proxy on the bot side, with a basic protocol consisting of whatever messages we can send to the server, albeit perhaps with additional methods that the bots find useful. Macros or something, I don’t know.
Anyway, if there is a World-like object on the bots side, its code will belong to the bot makers, and they can enhance it any way they may wish, including caching information and so on. So quite possibly we should feel free to make calls on the existing World class. It feels off to me, and it feels like it is almost guaranteed to give us trouble when we do go client/server, but we’re kind of trying to ignore that problem to see what kind of trouble we get into … and how we get out of it.
Anyway, that’s on my mind. I just thought I’d share it.
Bot Behavior
Bryan brought up the notion of a state machine for the Bots. We agreed that they would probably have something like that and that it was a bit soon to do it. Over the subsequent few days, though, I’ve been thinking about the bots as creatures with very simple motivations and behavior, and I have a bit of a notion as to how it might go, and it goes like this:
- Wandering
- The bot has nothing in scan range, so it just wanders.
- Wondering
- The bot sees something remote in the scan range, but it’s just a “?”, so the bot wonders what it is. It moves toward the thing.
- Found a Block
- The bot has arrived at a Block. If it does not currently have a block, it takes the block. If it currently has a block, it drops the Block it’s carrying.
This, or something very like it, should have the cumulative effect of bots wandering around, picking up blocks and putting them together.
And at this point, Bryan joined me. Here’s what we did.
class Bot:
def do_something(self):
self.gather()
self.move()
def gather(self):
if self.tired > 0:
self.tired -= 1
return
if not self.inventory:
self.take()
if self.inventory:
self.tired = 10
else:
# Check to see if there's a block here
# Drop the block if there's one
self.world.drop(self, self.inventory[0])
self.inventory = []
self.tired = 10
def take(self):
self.world.take(self)
This behavior2 is phenomenally simple. Wherever the Bot is, if it is tired, it ticks down the tired counter and returns. If it’s not tired and has nothing in inventory, it just tries to take
. As we’ll see in a moment, if the Bot happens to be adjacent to something, it takes it. Doesn’t look at it, think about it, just picks it up.
If, on the other hand, the bot does have something in inventory, it drops the thing. We intend to check the area and only do that if there is a block adjacent to us.
We’ll talk further about why this is so dumb but so good, but first, here’s how its supported in World:
And
class World:
def take(self, bot: Bot):
entity = self.find_entity(bot.location)
if entity:
self.map.remove(entity.id)
bot.receive(entity)
def drop(self, bot, entity):
entity.location = bot.location
self.add(entity)
def find_entity(self, bot_location):
directions = Direction.ALL
for direction in directions:
search_location = bot_location + direction
entity = self.map.entity_at(search_location.x, search_location.y)
if entity:
return entity
return None
In take, we search the four squares adjacent to us and if there is anything there, we remove it from the world and give it to the bot.
In drop, we drop the object right where the bot is. (We are relying on the bot to remove the thing from inventory. Should we? I don’t know.)
Why isn’t this just a bad implementation of a bad idea?
Well, on the one hand, maybe it is just that. And we were up against a hard time deadline, so we had to stop a little sooner than we might have. But we actually want something almost this weak. I think we’ll certainly put code where the commented lines are in Bot, so that it only drops the block near other blocks.
But our idea is to see whether truly simple behaviors can produce interesting effects. We do expect that something almost this simple, given a lot of blocks scattered around and a lot of bots running around may result in the bots grabbing blocks and putting them together in clumps.
Now, as things stand, the code above, with more than one bot in the world, would cause one of them to pick up the other if they ever encountered each other. We might let that happen just to see how funny it would be. But what we’re really at is to try to make very simple behaviors that add up to something good.
We’ll see how that goes. Right now what happens is that the bot runs around, and when it comes adjacent to a block, it picks it up and drops it ten moves later, so it just kind of shifts things around.
Summary
Today’s code was experimental: we were trying to figure out a very very simple way to get a bit of conditional behavior. It’s not quite what we want, but it’s working, passing our tests, and generally a step in the right direction.
Oh, and we did write a few more tests for take
, since we changed it from accepting a direction in which to take something, to just taking anything adjacent to the bot. It takes the first thing it notices. Will we provide something more? Almost certainly.
But for now, we’re enjoying seeing how much we can squeeze out of really very ignorant simple behavior. We find it quite fun.
-
This situation arises more often than I would like, where I say something and it would be better had I not. But I digress. ↩
-
The
gather
method above is slightly modified from how we left it. It was passing the tests but the on-screen behavior was not to my liking. I adjusted it for the video above. ↩