Liza Shulyayeva

Deploying Gastropoda to Amazon Web Services - Never Forget

After my last blog post I decided that it was time to deploy Gastropoda to Amazon Web Services - namely Elastic Beanstalk, which utilizes an EC2 instance (reserved) and an RDS DB instance.

I started last Saturday. I’ve just now gotten something running that seems on par with what I have on my vagrant box. I have decided to put together a post of random tips for myself so that I never have to go through this nightmare again the next time I have to set up a new Elastic Beanstalk app or environment.

I started off mostly following these very helpful Laravel deployment instructions. These set me off on the right track, but there were so many obstacles that I ran into that just following this wasn’t really enough.

So…notes.

Differences from instructions above

  • The environment config file in the above ended up not working for me. The environment that was created with eb start was a t1.micro instance instead of the specified t2.micro. This led me to think that other settings probably weren’t taking either. I ended up creating an environment manually through the Elastic Beanstalk dashboard.
  • The environmentVariables config was also for some reason not being applied, specifically - not finding the DB_HOST environment variable. I ended up creating a new set of configs for a development environment in app/config alongside the local config and set my db login information there. Then bootstrap/start.php I added the development environment to detect:
$env = $app->detectEnvironment(array(

    'local' => array('homestead'),
    'development' => array('hostnamehere'),

));
  • To get the hostname, remote into the EC2 instance and run hostname
  • However, this wasn’t working consistently, so to be safe I also forced a specific environment in .ebextensions/02artisan.config:
container_commands:
   01migrateSeed:
      command: "php artisan migrate --env=development --force"
   02seed:
      command: "php artisan db:seed --force"

Migrating and Seeding Database

  • I used Xethron’s Migrations Generator to generate a migration from my existing DB schema on the vagrant box. This was quick and painless.
  • What wasn’t so painless was the migration’s inability to run foreign key commands. There were all kinds of foreign key dependency errors, so I removed all foreign key migrations because by this point I wanted to rip my hair out.
  • I then created seeders for all of my template tables, which are:
    • race_terrains
    • snail_history_templates
    • item_types
    • item_templates
    • item_nutrition
    • recurring_events
  • Seeder example:
class RaceTerrainTableSeeder extends Seeder {

    public function run()
    {
        DB::table('race_terrains')->delete();

        $statement = "
                        ALTER TABLE race_terrains AUTO_INCREMENT = 1;
                    ";
        DB::unprepared($statement);

        DB::table('race_terrains')->insert(array(
                array(
                    'name' => 'Plastic Tabletop',
                    'description' => 'A smooth, plastic tabletop - lightly misted with water'              
                )
            )                 
        );
    }
}

The $statement above is very important. Before I added this, the auto-incremeneted primary key would keep counting up from the previous batch each time I deployed a new version of the app. This broke everything, since I had code and other tables referring specifically to various IDs. This makes sure the IDs increment back from 1 each time the table is seeded.

  • I started off using MySQLWorkbench, but am now using a trial version of Navicat MySQL to connect via SSH - much better

Cron job

  • I had to set up a cron job for Dispatcher to run various events (like recurring store restocking and deliveries, race events, breeding events, etc). I ended up doing this in .ebextensions/01composer.config:
container_commands:
   01optimize:
      command: "/usr/bin/composer.phar dump-autoload --optimize"
   02dispatcher:
      command: "cat .ebextensions/dispatcherJob.txt > /etc/cron.d/dispatcherJob && chmod 644 /etc/cron.d/dispatcherJob"
      leader_only: true
  • dispatcherJob.txt looks like this. Remember that it requires exact path to php and artisan and must have a newline at the end:
* * * * * root /usr/bin/php /var/www/html/artisan scheduled:run > /dev/null

Logs and Storage

  • To get logs from EC2 instance, use this example: scp -i path/to/.pem ec2-user@public-ec2-instance-ip:/var/www/html/app/storage/logs/error.log .
  • The chmod to the storage directory keeps getting reset with each deployment. Currently adding this command to .ebextensions/01composer.config under container_commands seems to have worked. I know, I shouldn’t use 777…will try to lower this as much as possible and see what happens.
   03chmodStorage:
      command: sudo chmod -R 777 /var/www/html/app/storage

More most likely to come…

Claustrophobia and Eggicide

Yesterday I put in claustrophobia for the snails. Jars have a capacity attribute - a maximum number of snails that they should hold. But just because a jar with a capacity of 20 may not be able to support 25 snails very well doesn’t mean the snails are going to conveniently stop reproducing or hatching (if it is a breeding jar). Instead, they’re just going to get uncomfortable enough to eat their babies.

It’s all kind of thrown together right now with arbitrary values, but first we get each snail’s claustrophobiaTrigger, which tells us how many other snails a snail has to have around it in order to start feeling claustrophobic. Right now this is the same for all snails, but eventually I can store it in the db and make it a more interesting attribute.

    protected function getClaustrophobiaTrigger() {
        return $this->jar->capacity / 4;
    }

Scheduled Food Delivery, Arousal Suppressant, and Brain Woes

I haven’t updated in a long time and it’s because the battle with snail brains has been raging on. Over the past few weeks I have run into several weird behavioural problems, including but not limited to:

  • Snail over-eagerly cannibalizing each other
  • Immature snails not only mating but getting pregnant
  • Snails having no motivation to go out and search for food

Maybe it’s time to run this remotely

Part of the problem, I’ve realized, is that my server uptime is not consistent. I am running everything on my MacBook Air - as soon as I close the lid everything stops. However, my recurring events (like energy depletion) are not based on counting uptime - they are based on hard timestamps. If I close my laptop at 7am, reopen it at 10pm, and there was an energy depletion event scheduled at 9pm for all snails, the event runs immediately. That’s hours of eating that the snails have missed out on. In some cases they just don’t have time to eat before they’re drained of energy.

This has been leading me to think that maybe I should deploy this in a remote environment - namely AWS, which already has the bare bones setup for the website (but nothing actually deployed). Problem is I have no idea what kinds of costs this is going to rake up on AWS. Am I going to end up with a giant bill? I know there’s a way to put a cost limit to the service, so maybe I’m just scared of knowing. In addition everything is just so much more hassle when working remotely.

Scheduled food delivery

To make it easier for myself and any other potential user, I added a scheduled delivery service to the world store. You can register to receive regular food boxes for your snails, to be placed into either a specific jar, your closet, or distributed evently amongst all of your jars. Available frequency are once a day, twice a day, or once a week.

Right now you can only have one delivery scheduled at a time, but I actually want to change this to let people schedule as many as they want.

Food delivery scheduler

This has already helped a bit since I don’t have to keep running to the store every day to dump more food into the jar.

Arousal suppressant

You may remember (probably not) that specific jars are “breeding” jars. Those are the only jars that allow snails to mate within them - the rest suppress the mating instinct by default. Another feature I wanted myself is being able to stop snails from mating in the breeding jars on a temporary basis. So I’ve implemented an arousal suppressant option (that is currently not very user friendly due to the uninutitive naming, but it works):

Arousal suppressant

I’m also in the middle of an accidental wolfling run of sorts. Two snails bred into a bunch of snails. The parent snails have long since starved due to some of the bugs I mention above. I guess we’ll see how long their baby snails last! There have been a couple of generations so far.

Wolfling run

No More Starving Snails

Dear jar capacity validator,

I’m sorry I ever doubted you. I thought you were killing my snails, but really it’s my lack of you that caused the problem. I am the one guilty of virtual snailslaughter.

Before you refuse to ever speak to me again, let me explain.

(Ok I’m stopping this now)

For the past couple of weeks my snails have been dying of starvation. I didn’t really notice that there was a problem straight away. After some digging I realized that the most delicious item in the jar I was using was at y pos 1180, but for some reason when the snails tried to approach it they never got there. This resulted with them trying to get to the item each minute, but never making it, and starving as a result.

There are two problems here. The most immediate problem is why aren’t the snail’s reaching the item? I quickly realized that this was because I run a jar position validation check before setting target positions for the snails. The max y and x positions are calculated based on the jar’s capacity. In this case, the jar’s capacity was 20. The max y pos of a snail within the jar was 20 * 50, so 1000. So during validation the snail’s targetPosY kept getting set to 1000 because that is the farthest the snail could go.

The problem, it turned out, was that I was doing zero jar posiion validation on the item itself. Something must have at some point set the y position for the item to 1180 (either the autogenerated random position when the item is dropped or maybe I dragged it weirdly on canvas). I manually moved that one item and added a check, so this should never happen again.

The second problem

The second problem became apparent due to this bug. If a snail can’t physically reach a consumable item to feed itself, it needs to switch attention to another item and try to reach it instead. In real life a snail wouldn’t keep beating its head against the glass trying to get the one piece of lettuce on the other side when there is lots of other lettuce sitting around in the jar itself, within easy reach! I will tackle this next.

Detangling Snail Actions. Also a Cat

I started writing this post on the 8th of November and have just now gotten around to finishing it.

Figuring out what my snails are doing and why is getting close to impossible. I’ve got snails eating each other, or refusing to eat proper food, or for some reason not mating even though their sex drive should be pretty high. I used to print stuff out to one big default log file (laravel.log), but this soon became unmanageable because I was printing logs associated with multiple actions for multiple snails at the same time.

I ended up creating a logger for each snail:

    public function getLoggerAttribute() {
        $path = storage_path() . '/logs/' . $this->snailID . '.log';
        if (!File::exists($path)) {
            File::put($path, "");
        };
        $snailLog = new Logger('Snail Logs');
        $snailLog->pushHandler(new StreamHandler($path, Logger::INFO));
        return $snailLog;
    }

So now I can log relevant information to each snail’s individual log file. Example:

$this->snail->logger->addInfo("Override escape for hunger");

I think I need a couple of weeks of just bug fixing. I’m noticing a crapton of little issues. Some are easy things, others are weird behavioural things that I’ve been too lazy to try to debug.

And the reason I’ve been so lazy is because of this new distraction. Meet Rigel the Maine Coon kitten!

Rigel the Maine Coon

Hiking, Beyond Earth, and Gastropoda Update

One cool thing about Stockholm is how easy it is to get to both a central location and a green/forested area.

Even when you’re in a busy cafe in Södermalm or surrounded by shopping centers in T-Centralen water, trees, green walking paths are never far away.

Last weekend we went hiking at the nearby nature preserve. We called it foraging, but didn’t really forage much. We did, however, build a fire and toast walnuts!

Walnut roasting

Beyond Earth

I’ve been playing Beyond Earth this weekend. It has been glorious. Of course the first people I go to war with is the Slavs. They’re such hypocrites - telling me that my expansion has greatly worried them, but when I ask them not to settle near my cities they’re like “We’ll settle wherever we damn well please, it’s our right, yada yada”. Then I rename my city closest to them to Kyivolaev and they declare war on the next turn. They took the city away from me first, but I got it back and then some with my xeno swarms.

I haven’t played any of the Civilization games. The reason I was so excited about Beyond Earth is because I loved Sid Meier’s Alpha Centauri. And I really like Beyond Earth, but…it just doesn’t feel like a true spiritual successor for me. Alpha Centauri was awe-inspiring. I’m sure that was at least in part due to my having never played anything like that before. Even now, when I play SMAC I get that same feeling I did years ago. Beyond Earth just isn’t that for me. I’m going to keep playing it, and I’m going to enjoy it, but at the same time I’m going to try to dig up some of the other recommendations of SMAC-like games that people have mentioned on my Twitter feed.

Gastropoda

Training

Now that I have the general snail approach/run away/bite/swallow/mate decisions working I thought I’d start on training. However, I quickly realized that it’s too early for that. Because snails already learn from every day experiences in the jar, I should start by perfecting those experiences. Because really, if I get this right in theory I don’t even need any other explicit “training” functionality. And if I do want to build it (which I probably will), I can base it entirely on the existing behavior/memory/learning implementation.

So I’m going to go back to expanding the range of interactions a snail can have in its jar and refining its decisions and memories.

Memories

Memories currently work like this:

  • A snail can store up to 5 sensory memories
  • If 3 or more sensory memories are stored about the same object, they get converted into short term memories
  • 5 short term memories of the same neuron being fired in relation to the same object turn into a long term memory. Long term memories start off with a strength of 100. Every 24 hours 10 points is taken off of the strength (and more points are added if the snail gets another 5 short term memories to add to the same long term memory). Long term memories are never deleted, but can be weakened over time.

Biting vs swallowing

I have also implemented a the swallowing motor neuron. Previously, the snail could bite an item to swallow it. It could also bite another snail. However, biting can also be more of an aggressive action - just because a snail is biting something doesn’t mean it wants to eat it. So when a snail does want to try to actually EAT something, it will swallow. Otherwise, it will simply bite.

Survivability

Snails are also tough to keep alive at the moment - they run out of 50% of their energy each day, and I am having to constantly throw food into the jar to keep them well fed. I have tweaked this slightly by calculating how many bites a snail should be able to take between idle event checks (similar to movement). A snail takes a bite every 10 seconds. An event checking all snails’ idle actions runs every 60 seconds, so every SWALLOW neuron firing will actually fire 6 times each minute (of course there is a way to specify a different interval if we are calculating for more or less time between checks).

Documentation

The codebase is getting large and I feel I need to start documenting the project better in terms of both design and technical documentation. I made a doc site on http://gastropoda.readme.io/ though it has no proper content there yet, just the very bare bones beginnings.

Decision-making

Making decisions has been tough. The decision making code is a huge mess right now.

I’ve decided to implement something along the lines of Maslow’s hierarchy of needs. So far there are only two needs - food and sex. I made a separate “Prioritisation” neuron that handles both weight setting in the inputs and later setting “motivations” for the snails when it comes to decision time (eg foodMotivation, sexMotivation).

It’s working a bit better than the previous system (in that snails that refused to eat before are now eating), but is all still very experimental and needs to be observed and tweaked as I go.

The Ongoing Saga of Snail Mating Decisions

I’ve written about snail breeding here before, at length. However, since I now want snail behavior to go through the brain I had to rework the trigger that led to two snails mating and producing offspring. A few different things had to be prototyped to make this work, especially because in a snail’s mind there is no real way of distinguishing food from another snail without learning from experience and some basic instincts to guide the way.

When I started, snails tried to mate with lettuce leaves. Technically possible, but they should learn that this is a little…ineffective.

Then, the snails just kept overshooting each other when trying to approach. Approaching inanimate objects is easy - they don’t move. However, one snail kept targeting the other snail’s current position, and vice versa, and they ended up in the wrong spot. The solution (for now) was to have each snail target the other snail’s target position. This is kind of dumb, because of course the Snail A can’t read minds and see where Snail B is going, so I’ll have to find a non-hacky better way of doing it by basically seeing if the two snails will cross paths on their way to one another and meet each other in the middle instead.

Anyway, then the snails did reach each other but started biting each other as if the other snail was a food item. This resulted in one snail becoming deathly afraid of the other one and trying to run away (and sometimes biting back). Basically the two snails grew to despise each other and want nothing more than to go in opposite directions.

Then they stopped eating.

Then I created scent signatures for other snails and items. Right now this basically just shows an object’s highest macro or fertility rate. This is placeholder, but now snails can “smell” other fertile objects nearby. In the future they’ll also be able to get hungry for certain macros and favor food items which smell more like the thing they’re hungry for.

I also made a sex drive and food drive, so a snail might try to bite an object instead of mating with it if it smells like carbs and its food drive is higher than its sex drive. This can also be used for the snail’s decision about which object it wants to approach (so, we can use this to apply higher weights to certain inputs).

Then I realized there was no good way to test this efficiently without making it easy to move snails around within the jar (without having to go and change positions in the db each time). This way I could place snails next to the objects I wanted them to interact with or detect easier.

And then…then…well, this happened:

(Obviously I need to have them get tired and/or have their sex drive go down after they mate)

The eggs don’t hatch until tomorrow…and I’m both curious and terrified of what will happen then, because I do not yet have a minimum maturity rate for snail breeding.

Went to My First Laravel Meetup

Last Friday night I attended what was apparently the first ever Laravel Meetup in Sweden. I’m glad I actually made myself go, because I’m not really good with social engagements. I don’t tend to go out much and sometimes end up just not bothering and staying home at the last minute.

Anyway, I’m really glad I went. The meetup was hosted by Snowfire and the main talks were about an introduction to Laravel 5.0 and the Laravel IoC container. Both were super interesting and gave me a lot to think about (and a taste of how much I still have to learn about Laravel and PHP in general).

I don’t think I’m going to update to 5.0 until November at the earliest. I have to mentally prepare myself - I’ve already done so many port experiments, and then the actual full port from vanilla PHP to Laravel 4.2, that I’m not looking forward to another large and complicated migration.

Some crooked pictures of the event:

Laravel 5.0 walkthrough

Laravel 5.0 walkthrough 2

Laravel IoC Container

We finished off the night by looking at some Laravel resources and browsing the codebase.

Laravel GitHub

Event Queues

The brain thing is really getting out of control.

Yesterday I ran into this problem:

Fatal error: Maximum function nesting level of '100' reached, aborting!

What caused it was my dumbness. Basically, I was firing the Bite neuron which called the Bite method in the snail class. Then I was checking if the object the snail was biting was an item or another snail. If it was another snail, it called BiteSnail() in the snail class. Here is where it got screwy. I wanted to have the bite do the following:

  • Send pain to the snail being bitten via a manually inserted input to the other snail’s Tactile sensor. Esssentially I was manually pushing a detected object (the biter) to the other snail’s tactile sensor’s detectedArr. To reflect the bite for now I’m using the input’s “roughness” attribute (though this will change) and setting roughness to 55 (the normal snail’s roughness pain threshold is 50)
  • Have the snail being bitten immediately start checking inputs, since you want the snail to react to the bite. The other reason for this is that the recurring action/input checking event creates a new brain each time it runs for each snail, so if we just pushed that input to the other snail’s brain and then forgot about it expecting it to have a reaction the next minute, that input would be totally wiped the next time the event ran.

The problem though was that I was creating a new snail brain, calling all the same functions that were already running on the current snail. The input checks for the two snails were overlapping and either I had too many recursive function calls or I had an infinite loop somewhere (infinite snail-biting? A snail ouroboros?) because the normal solution - increasing xdebug.max_nesting_level was not working. I decided to implement a queue system instead of having a snail check all inputs from another snail that’s checking all inputs.

So now I’ve got this possibly even dodgier thing going where I have a queue in the event model. The event, in this case the CheckIdleSnailActions event that checks inputs for the snails and makes them do stuff, finishes running. If a snail bites another snail during this time we add another checkAllInputs action to the event’s queue. When the event does the thing it’s supposed to do the first time around and gets to the end, it loops through its queue and runs everything in there as well.

Here’s what we push to the queue (we use the brain of the snail being bitten, with the extra input already saved to that brain’s tactile sensor)

$toPush = [
            'object' => $object->brain,
            'function' => 'checkAllInputs'
        ];
array_push($event->queue, $toPush);

And then, at the end of the event’s runEvent method we do this:

    if ($eventFinished) {
        Log::info ('event finished');
        $this->resetRunTime();
        if (count($this->queue) > 0) {
            foreach ($this->queue as $event) {
                Log::info('about to call ' . $event['function']);
                call_user_func_array(array($event['object'],$event['function']), func_get_args());
            }
        }
    }

One crappy thing (of many) is that currently I don’t have a good place to save the event, so I end up passing it from runEvent to the snail controller that loops through the snails and checks their inputs, to the snail brains that get created. It’s kind of a mess.

I think this weekend I’m going to stop working on new brain stuff and just focus on refactoring all of this.

The Snails Are Eating Again

Back to the brain we go.

I already had snails eating food in jars before, but it was kind of a hack. Each snail would just eat a bite of whatever consumable item was in the jar. When I stupidly decided to try to make a simple snail brain, I knew I’d have to make things like eating fit into it. Today I made a very rough version of something like that.

Each snail has a number of motor neurons in its brain. Out of all the motor neurons, there is one for biting and one for swallowing. So far I am bypassing the swallowing completely and just firing the bite neuron and having the snail bite food and swallow it at the same time. Basically…here’s how it works. This is kind of a brain dump so may not actually make any sense.

Items and snails are now given some properties: luminance, temperature, hardness, roughness. There may be others down the line, but for now that’s it.

I’m also setting some general pain thresholds for the snails (so far all the snails have the same thresholds), as well as totally random default properties (as per above):

    public $luminance = 25;
    public $temperature = 20;
    public $roughness = 5;
    public $hardness = 15;
    public $luminancePainThreshold = 100;
    public $temperaturePainThreshold = 50;

Items are a little more tailored - their individual luminance, temp, etc properties can be set by item template in the db (though right now I’m just using some default values there too).

So basically if a snail sees something with a luminance that’s too high it will not be having such a good time.

Oh, and snails now have a currentMood property. In the memory tables (which are already created but not yet in use) there is a consequenceRating property, which reflects an action’s impact on the snail’s currentMood.

So the main thing you need to bite an item is to actually be close enough to it to touch it. I made a small change to checkSensor() to allow for a maxDistance to be passed:

    public function checkSensor($sensorQual, $maxDistance = 999999) {
    // ...blah blah blah

Also, we set luminance, temp, roughness, and hardness to null by default in the array of detected objects and then specify which attributes to grab for each sensor (because we don’t care about roughness if the snail isn’t touching the thing, for example).

                    $toPush = [
                        'id' => $object->$idString,
                        'idString' => $idString,
                        'inputWeight' => $inputWeight,
                        'distance' => $distance,
                        'luminance' => null,
                        'temperature' => null,
                        'roughness' => null,
                        'hardness' => null
                    ];

So our TactileSensor ends up looking like this:

class TactileSensor extends Neuron
{
    
    public function checkForInput() {
        $maxDistance = 5;
        $detectedArr = $this->checkSensor($this->snail->currentTactileReceptorQual, $maxDistance);
        foreach ($detectedArr as &$detectedObject) {
         //   $controller = null;
            if ($detectedObject['idString'] === 'snailID') {
            //    $controller = $this->snailController;
                $object = $this->snailController->findSnail($detectedObject['id']);
            }
            else if ($detectedObject['idString'] === 'itemID') {
            //    $controller = $this->itemController;
                $object = $this->itemController->findItem($detectedObject['id']);

            }
            $detectedObject['hardness'] = $object->hardness;
            $detectedObject['temperature'] = $object->temperature;
            $detectedObject['roughness'] = $object->roughness;
        }
        $this->detectedArr = $detectedArr;

    }
}

Another change is in setWeights(). Whereas before we’d just add something like 0.05 to the inputWeight if an object had been detected by more than one sensor, now we actually add all the inputs together. If a specific sensor had a maxDistance specified we give it a higher inputWeight by default (2 instead of 1) because it had a tougher criteria to meet (not sure I’ll keep this, since the closest object already gets a boost anyway down the line).

Anyway, then in the decision neuron we decide what to do…right now none of the memory stuff is implemented, so we go straight to a random “curious or not” check:

    public function checkDecision($object, $memories = null) {
        Log::info('checkDecision object: ' , $object);
        if (count($memories) === 0) {
            $rand = Utility::RandomInteger(0,1);
            if ($rand === 1) {
                // Curious. Approach if far away
                if ($object['distance'] >= 5) {
                    Log::info('approaching object1: ' . $object['id'] . ' distance: ' . $object['distance']);
                    $this->approachObject($object);
                }
                else {
                    Log::info('TOUCHING. TRY TO BITE ' . $object['id']);
                    $this->biteObject($object);

                }

            }
            else {
                // Indifferent. Ignore;
            }
        }
    }

approachObject() and biteObject() then fire the biting and moveleft/moveright/moveup/movedown motor neurons.

As you can see it’s still not really that decision-makey. Snails will eventually have other criteria involved in whether they try to bite something or not (that is, they won’t necessarily try to bite an object just because they’re touching it).

That’s it for now…at least they’re eating again.