Liza Shulyayeva

Snaily Updates: BrainHub + Runners, SnailLife Logo

I finally have two BrainRunners working on a DigitalOcean droplet, and one BrainHub on another droplet queueing and assigning tasks to the runners.

It’s still rough, but let’s start with the BrainHub’s scheduled artisan commands (Artisan is the CLI that comes with Laravel):

QueueIdleBrainChecks runs every minute.

    public function handle()
    {
        // Find snail due for brain check.
        $snailController = new SnailController();
        $allIdleSnails = $snailController->getAllIdleSnails();

        foreach ($allIdleSnails as $snail) {
            // Seconds since last brain check
            $diff = Carbon::now()->diffInSeconds (Carbon::parse($snail->brainCheckedAt));
            if ($diff >= 60) {
                $existingQueuedBrainCheck = QueuedBrain::where('snailID', '=', $snail->snailID)->first();
                // If brain check is not already queued, queue a new check
                if ($existingQueuedBrainCheck === null) {
                    $queuedBrain = new QueuedBrain();
                    $queuedBrain->snailID = $snail->snailID;
                    $queuedBrain->save();
                }
            }
        }
    }

This basically just gets all living idle snails from the snail_life db and creates a new brain check entry in the brain_hub db.

Also every minute we run the AssignBrainsToRunners artisan command:

    public function handle()
    {
        $brainRunnerRetriever = new BrainRunnerRetriever();
        $allIdleBrainRunners = $brainRunnerRetriever->getAllIdleBrainRunners();
        $taskRetriever = new TaskRetriever();
        foreach ($allIdleBrainRunners as $brainRunner) {
            $task = $taskRetriever->GetNextQueuedTask();
            if ($task !== null) {
                $brainRunner->assignQueuedTask ($task);
            }
        }
    }

This finds any available (idle) brain runners and assigns the next queued tasks to them.

In the BrainRunner model:

    public function assignQueuedTask($task) {
        // Change status of BrainRunner to 1 - Busy
        $this->updateStatusCode(1);
        $url = $this->url() . DIRECTORY_SEPARATOR . 'api/runTask';
        Log::info('assignQueuedTask url: ' . $url);
        $postfields = 'taskID=' . $task->id . '&snailID=' . $task->snailID . '&runnerID=' . $this->id . '&hubURL=' . env('APP_URL');

        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_URL => $url,
            CURLOPT_POST => 1,
            CURLOPT_POSTFIELDS => $postfields,
            CURLOPT_TIMEOUT_MS => 2500
        ));
        $resp = curl_exec($curl);
        curl_close($curl);

        // Delete task from queue
        $task->delete();
    }

The brain runner then runs the task:

    public function RunTask($task) {
        try{
            $this->taskID = $task['taskID'];
            $this->hubURL = $task['hubURL'];
            $this->runnerID = $task['runnerID'];

            // Get runner specific logger
            $utility = new Utility($this->taskID);
            $logger = $utility->logger;
            $snailController = new SnailController();
            $logger->addInfo('INFO: RunTask: Initialized SnailActionController');

            // The recurring event is actually not required anymore,
            // but we kind of hack it together because before the BrainHub
            // the snail brains relied on it and still do temporarily.
            $event = new RecurringEvent();
            $event->name = 'IdleSnailAction';
            $logger->addInfo('INFO: RunTask: Initialized IdleSnailAction Event');

            // Find the snail
            $snail = $snailController->findSnail($task['snailID']);
            if ($snail === null) {
                $logger->addError('ERROR: Snail ID ' . $task['snailID'] . ' NOT FOUND.');
            }
            else {
                $logger->addInfo ('INFO: RunTask: Found Snail ID: ' . $snail->snailID);
                $snailActionCoordinator = new SnailActionCoordinator();
                $snailActionCoordinator->checkAction([$snail], 60, $event);
                $logger->addInfo ('INFO: RunTask: Action Checked');
            }

            $logger->addInfo ('INFO: RunTask: Reported task as finished');

            // Save log to S3 (might rip this out later as we don't need to keep that many logs anyway)
            $utility->saveLog($this->runnerID);
        }
        catch(\Exception $e){
            $logger->addError('ERROR: RunTask: Exception Caught, cancelling task');
            $logger->addError($e);
        }
        $this->reportTaskFinish();
    }

The BrainHub connects to both the main SnailLife database and the BrainHub database. BrainRunners can only connect to the SnailLife database. The only thing that ever reads from or modifies the BrainHub DB is the BrainHub itself.

SnailLife logo and website

I have been getting really sick of looking at the ugly black and white SnailLife website. So I decided to try and make it a little more exciting. It’s still ugly, but at least it’s colorful-ugly now! I stumbled across a bunch of open source logos over at Logodust. I felt like taking a break from the BrainHub for a while and messed around with combining two of the logos and adding an eye stalk to make something vaguely resembling a snail. The logo is also quite versatile in case I ever decide to ditch the snail idea, since I’ve been told it looks like a chinchilla or one of those deep water fish with the light in front of its face as well…

SnailLife logo

So now the site is, though by no means great, just a little less bland:

SnailLife website

BrainHub and BrainRunner - Finally Over the Hump

I’ve started this post maybe twenty times now, since before Christmas, and each time I keep putting it off, thinking “I’ll just blog when I have this next bit done.” But each “next bit” is followed by something else, and then something else, into infinity.

So I’ll just write an update.

Since before the holidays I’ve been working on BrainHub and BrainRunner, which I’ve already written about. Basically - checking each brain every minute as part of the main SnailLife brain was becoming unmanageable. All background processes will be moved to complementary apps away from the user-facing SnailLife application. The idea is to have a BrainHub controlling tasks sent to individual BrainRunners (which are hosted on other DigitalOcean or EC2 droplets).

So, here is the first iteration of the BrainHub admin site. The BrainHub has its own database of queued tasks and BrainRunners but also connects to the main SnailLife database and imports a package called SnailLifeCore to be able to get information about brains due for a check and allow admin SnailLife users to log in/control the hub.

BrainHub admin site

It is sort of functional in that BrainHub runs 2 scheduled tasks:

  • QueueIdleBrainChecks
  • AssignBrainsToBrainRunners

Both of these run every minute. The first gets a list of brains that need to be processed and puts them into the queued_brains table in the brainhub db.

The second, AssignBrainsTobrainRunners, looks for any idle brain runners (brain runners with status code 0) and assigns the next brain in the queue to them. Then, the brain runner checks the brain and reports back to BrainHub with the result, which releases the runner to process the next brain in the queue.

Right now there are some issues - runners don’t get consistently released, for example. Should be easy enough to fix, but for now I’ve added an emergency release button to the admin site (you can see it above).

But right right now I am working on logging. The brain runner creates a new log for each brain task it runs. These then need to be backed up to AWS S3 (as opposed to being stored on the Droplet itself), and then the admin site will display the logs by-task for each runner.

There is a mountain of work to do on this but it feels like I’m sort of over the main hump of setting up the core package and the BrainHub and BrainRunner apps to sit alongside the main SnailLife app.

The Brain Scope Is Growing: BrainHub, BrainRunners, Brains

I’ve been MIA for a while, I know. I haven’t stopped working on SnailLife - I just wanted to get to some sort of completion with the brain refactoring phase before talking about it. Now, however, I realize there won’t be any completion for a long while and I may as well post an update.

I found a memory leak in the brain. Multiple, actually. And while going through and fixing those I realized the current way of running snail brain checks was completely unsustainable - made clear by the same incident that exposed the leak(s): 1500 snail eggs hatching on the same day.

Right now every minute all living snails run an action check - where we use the snail brain to see what objects the snail detects in its surroundings, how it feels about them/if it remembers any previous encounters with them, and how it will react to them. It loops through these snails one by one - with 1500 snails this takes quite a while. Aside from optimizing the inner workings of the brain itself I realized doing one giant for loop just wasn’t going to be sustainable as the count of snails in the world grows.

@drparham in the Laravel Slack channel gave me an idea and based on that I have come up with a hopefully more scalable plan:

  • Move all core classes into a private package (SnailLifeCore)
  • Have Laravel handle only the user-facing parts of the app. No background tasks or recurring events.
  • Move all background and scheduled tasks to a Lumen app (a microframework based on Laravel, much more bare-bones and faster)
  • Two new Lumen apps to be constructed: BrainRunner and BrainHub
  • BrainRunners can run brain check events for smaller groups of snail brains - eg 10 vs 1500. Multiple instances of BrainRunners can be spun up on an as-needed basis. Eventually they can even be made to autoscale via AWS E3 instances.
  • The BrainHub queues and distributes tasks among BrainRunners and processes the results (probably to have some sort of admin UI). It finds free BrainRunners to run brain action checks for certain snails and distributes them as efficiently as it can.

I need to fit the rest of the events somewhere. The brain checks are by far the most complicated, but we also have breeding events, stable temperature check events, item rotting, market delivery, auction timing events, etc etc. I suspect just one app can handle these remaining events easily. Something like an “EventRunner” based on the “BrainRunner”.

So far the following parts are done:

  • Main classes are in a separate private package called SnailLifeCore
  • BrainHub and BrainRunner apps are created with SnailLifeCore imported

It’s safe to say the complexity of the brain and related management systems is greater than the complexity of the rest of the simulation combined at this point. But it’s ok - I’m soon going on my 3rd year working on SnailLife and haven’t even begun to lose interest yet. This is the longest hobby project I’ve ever stuck with! I’m glad the thing I’ve been dreaming about building for the last 10 years is really as interesting to work on as I imagined.

Snaily Updates, Nov 10 - Family Trees and Inbreeding Coefficient

  • I did some research on snail ailments. This particular page was especially useful. Though SnailLife snails are not aquatic, some of these still seem very relevant: http://applesnail.net/content/various/snail_disease.php
  • Also did research on inbreeding in snails and complications it could cause. Apparently one commonly observed side effect is shell thinning. I then did some reading on finding the inbreeding coefficent and how to calculate it.
  • Looked into Google Visualization API to display the family trees and started a test implementation.
  • Started actually using Taiga.io, I’ve been wanting to try it out for a while.

Snaily Updates - Bug Fixing and Cleaning Out Old Stuff

Snaily Updates are going to be really brief posts with some short “daily” updates on SnailLife. I haven’t updated in a while because I’ve mostly just been refactoring and bug fixing. But it’s important, and even though I’m not working on new features right now I still want to document some of my progress and what I do each day.

  • Fixed an issue with migration to dead snail table trying to set snail position in a non-existent jar.
  • Added message template for egg hatching
  • Renamed UserMessageManager to UserMessageSender
  • Renamed SnailHistoricalEventManaer to SnailHistoricalEventGenerator
  • Get rid of BreedingController as everything it was used for is now in more specific classes
  • Get rid of SnailOrganController
  • Get rid of TrainingController since there is now no unique training functionality - all snail conditioning is done via interaction in jars and pre/post race action assignments.

My Livecoding.tv Account Deletion Saga

Update 3, Nov 2 2015 (PM): Well, it’s been…strange. First, it seems my account was finally deleted by Livecoding.tv (thank you!) After being up all night with libel posted all over it, hours after Jamie Green (the other Livecoding co-founder) and Sam Altman (Y Combinator president) replied to the Hacker News thread claiming they were trying to do something, something finally happened. I have heard nothing from either of them since their original comments and am not sure exactly what spurred the removal of slander from the profile and its eventual deletion. I know some other Livecoding users quit and had a discussion with the CEO in live chat also.

The account was then recreated, seemingly by a third party under the same username. They originally replaced a modified version of the libel on the profile and eventually changed the description. It seems to be someone else who has a problem with Livecoding.tv and maybe wanted to use this whole saga to their advantage.

First, the CEO of Livecoding has again emailed me this morning and, almost unbelievably, tried to lie yet again. This was before the deletion was done. Our email conversations consisted of this (messages go from bottom up):

He then went to the Hacker News thread about this and started posting defensive copy-pasted comments telling people they were cowards for not listing their names. After this there was more of pretty much the same on Twitter - copy-pasted replies to tweets about Livecoding.

Some responses to this weird and confused behaviour and this unbelievably bad response to the situation on HN have been borderline, and in some cases over-the-line, cruel. As I mentioned in a comment on the thread, I did not write this post to make a joke out of anyone or be cruel to a person who now seems like they might be unwell. I am the last person to defend how Livecoding.tv has handled this and don’t plan on censoring any accounts of my experiences with them, but in my opinion it is important to stay mature in these interactions and not become a complete asshole.

Anyway, at this point with my account deleted (though username now taken by someone else, but they have now removed my name from the new profile) I think I’ve gotten the best possible outcome. It’s obvious that Livecoding still has no interest in treating this in a professional manner and that they have a lot of growing to do in terms of their approach to communicating with their users, but as long as my details are off of that site that’s all I can realistically expect at this point.

I really hope this post will not need any more updates.


Update 2, Nov 2 2015: The text on my profile has just been changed to “Marked for removal from database”. Perhaps they fixed their “cron job”.

@elgruntox mentioned on Twitter that that he just got banned also and actually managed to have an exchange with the CEO:

I’m not sure where the first line is coming from about any kind of information that they were “doing migrations” (I have had no such communication). I did have communication that my account was deleted as mentioned in the original post below, but that turned out to be false. My videos were already deleted because I deleted them myself one by one before they changed my password, put in a redirect back to home on my account, and updated my profile for me.

Anyway, it seems they removed the libel and replaced it with a more generic message. I guess we’re just back at square one now.


Update 1, Nov 1 2015: It looks like Livecoding have now taken the liberty of updating my profile for me, claiming I am “spamming them” after this review got some attention: Archive.is link.

I guess writing an account of your experience with their site is somehow considered spam to Livecoding. How professional of them.

The original post follows below:


A few months ago I figured I’d try streaming my development of SnailLife (or parts thereof). I considered Twitch, but ended up going for a site called Livecoding.tv because it seemed smaller and more obscure. My main goal for streaming would be to keep myself focused knowing someone may be watching - I didn’t actually want to build any great audience for myself as I didn’t want to build up any kind of expectation of streaming, plus I knew that my streams would probably just be really boring. It was more a motivator for me to not procrastinate and browse Reddit or HN and focus on my work.

So I set up an account on Livecoding and did my first stream. A few people actually came in and asked questions about the project, which was cool. I got a message and/or email from someone from Livecoding asking if or when I was able to stream, or what my streaming schedule might look like. I said I would try to stream whenever I could, but with a busy job, a sick cat, and life in general streaming was simply not on my list of priorities so I could not commit to a schedule.

I streamed a few more times and then life got in the way as expected (ie my cat almost died) and I stopped. Once every few weeks I would get another message from Livecoding (outside of their usual automated newsletter) asking what my streaming schedule might be as well as saying that they want to get more female streamers on the platform. Basically emails like this:

“Where have you been? ;) We plan to heavily increase the number of female Livecoders so if you have ideas on how we could achieve this, let us know. Could you invite other female coders you know to Livecoding.tv?”

And this:

“Hey Yeliz - This is Michael, founder of Livecoding.tv

I like your snail game. I want to use you to inspire other women and young girls to get excited about software development.

How often are you on Livecoding.tv?”

Each time one of these came in I kept explaining that with everything else going on streaming was not my highest priority and I would not commit to a schedule - so in reply to “how often?” the answer was basically “not often”. At some point it got pretty frustrating to keep receiving these emails, but whatever.

A couple of weeks ago I got another email from Livecoding. “Viewer Feedback” - with “tips” on how to get more people to view my stream. Including handy items like:

Yeah thanks, Livecoding

This looked like an at least partially automated email, but I am not sure to what extent because in the few streams I did do I chose to not talk and just code, and maybe interact with people in the text chat when they had question. This was sort of the thing that led to my not feeling like being on Livecoding anymore. I just felt there was too much recurring pressure to stream when I clearly communicated that I will not be streaming regularly. Not only that, but I never wanted to stream to get a big viewership or cater to viewers - I wanted to stream for my benefit, to keep myself focused. These unsolicted “tips” indicated to me that Livecoding clearly did not seem like the right channel for this anymore.

I decided to delete my account until I can work with Livecoding’s expectations and actually stream on a regular basis as they kept asking me to do.

I quickly found out that you can’t just delete your account. You have to put in a support request asking them to delete your account. So that is what I did and here is the exchange that followed:

Asking Livecoding to remove my account Livecoding asks why I want my account deleted I explain my reasons for wanting to delete my account Livecoding promises to delete my account soon

So far so good. They asked why I wanted to delete the account, I explained, they said they’d do it soon. The next day I asked for some clarification on when the deletion would actually take place:

Asking Livecoding for clarification on when my account will be deleted

This was met with silence.

7 days later I sent another email, now feeling a little bit frustrated. It has now been over a week. Here is the exchange that followed:

Reminded by another email, I follow up with Livecoding on my account deletion Livecoding stating that their email newsletters are separate from their account deletion My explaining that despite the newsletter being separate, I checked and my account is still not deleted Livecoding states my account will be deleted 'on our next cron job. Nothing complex.' I ask when the next cron job is scheduled to run

My last question was, again, met with silence.

At this point I spoke out publicly on Twitter and got the following reply from Livecoding’s Twitter account - of course, my account would be deleted “soon”. I let them know that their last “soon” was over a week ago with no clarification of when their “soon” is. Guess what answer I got. That’s right. None, as usual.

Livecoding replies to my tweet: We'll process your request soon I let them know that their last 'soon' was over a week ago and that they have not yet clarified when 'soon' will be

So I started posting daily updates about the status of my account on Twitter. The situation at this point was frustrating and the incompetence was laughable. I updated my Livecoding profile to highlight the problem. I mused if posting some inappropriate content in my profile would encourage them to finally delete my account.

I didn’t post anything bad in my profile yet, but just the mention of it seems to have kicked some gears into motion, because I received this email this morning, from Michael the great Livecoding CEO.

Livecoding replies: sorry for the delay. We had a lot going on. Done now.

I guess that cron job had a lot going on. But hey, excellent! Something happened! All is right with the world! We’re all good!

However after checking the site I saw that my profile was still there…I was no longer able to log in at least. But judging by past experience and the fact that my profile was still visible I was suspicious and tried to change my password, just in case. Well what do you know - it worked. A password reset form was sent to me. Now I was getting angry. This seems to not just be a case of delay or plain incompetence - the Livecoding CEO just lied about deleting my account. I wonder in what world changing someone’s password amounts to an account deletion - Livecoding’s world, apparently!

Now angry at the blatant lie, I posted on Twitter about this and replied to Michael’s last email:

I reply letting them know that my account has NOT been deleted

He (or someone from Livecoding, there is no name signed anymore) quickly responded with the following:

Livecoding: Do you and your friends want to disturb us with this and spamming? We will just ignore you at this point

I’m not sure who “me and my friends” are. I think he was talking about my discussions on Twitter. We certainly haven’t been spamming, a couple of people have simply been discussing my issues with getting my account deleted. My response was:

I reply explaining why I spoke out publicly, asking if they ever plan to delete my account as requested.

And that was it. Again, the expected silence took over and there was no further reply. My profile still sits on Livecoding, undeleted. But they DID put in a redirect back to home on login attempt, presumably to make sure I didn’t post those porn links I joked about.

The seeming incompetence Livecoding management is just plain laughable at this point and I am still not sure what exactly their problem is with deleting an account. Are they just unable to do this? Is their system this crappy? Judging by their lie claiming my account was deleted when it was not it seems they actually want to keep my data and profile on their site for some weird reason. But why?! Why list instructions on how to delete your account on your website and then not honor them?

Of course there are sites that outright refuse to delete all of your information. Livecoding was not one of those sites - Livecoding repeatedly promised to delete my accout “soon”, then went on to claim it deleted my account when my account was still there, visible, and with a resettable password. The way they have handled this in such an unprofessional, misleading manner makes them seem totally untrustworthy and extremely shady in my mind.

I remember Michael’s request in one of his spammy manual emails from earlier: “Could you invite other female coders you know to Livecoding.tv?”

Dear Michael: sure I can! But I won’t. I will instead warn other coders (not just female, since you may not know this but we don’t gather in gender specific female herds), but anyone interested in streaming their projects. Livecoding’s unprofessionalism, continued pressure to stream, unwillingness to let go of your data despite really loving the word “soon” is simply not worth the hassle.

So, Livecoding/Michael, here are some unsolicited “tips” from me to you this time:

  • Get a proper support person. Who is not you, since you are not good at interacting with your users.
  • If you must make people put in a manual ticket to delete their account, actually do what you say you will and delete their account
  • Do not lie to your users and claim you deleted their account when it is blatantly obvious that their account is still there. Out of the dilly-dallying, the seemingly non-existent cron jobs, etc, this is the thing that left the worst impression of Livecoding to me. When you start blatantly lying to people you know you just turned a very bad corner.

To use your preferred phrasing from before, processing a support ticket to delete a user account should have been “nothing complex”. I still do not know what your problem is with actually doing this.

Snail Organs, Immunity, and Ageing

Organs! Immune systems! Old age! Dying from old age!

I’m super excited. I was thinking: “How do I implement old age and death, and health in general? Is health just energy? When the snail is out of energy, it dies? But that seems too simplistic.”

At this point I should have realized that ‘simplistic’ is a good thing. Instead I decided to try to mimic a scenario that is a little closer to real life. Again, I am no snail expert or biologist and mostly I just…guessed. I want to tweak the system when I move more into the research phase of the project vs the implementing random features and fixing bugs phase. I have a feeling this should have been done the other way around.

Anyway - why does any living organism die? It seems that we die either because of some trauma or injury to our internal organs or…old age. But what is old age, really? How is it different from trauma or injury to our internal organs?

From the very brief amount of reading I’d done it’s partly about telomeres and how efficiently our cells can keep dividing as we get older. Telomeres protect our chromosomes. Each time a cell divides, our telomeres shorten until eventually our cells can’t divide anymore. And then what?

I think that’s when we become more prone to age-related diseases. The immune system is apparently highly related to the length of the telomeres. And when your immune system gets worse our bodies can no longer handle various illnesses as well as they used to, which eventually results in that first thing - trauma or injury to our internal organs that we can no longer recover from.

My cat Rigel has a damaged bladder. A vet “broke” him and he has been handicapped ever since. The bad news is that he may now need to be on medication for the rest of his life. The good news is that there is a chance, especially since he is so young, that his bladder will regenerate over time. The cells and nerves of his bladder may still kick themselves into shape over months or years and he may be able to take a little less medicine or maybe, if a miracle happens, eventually none at all!

This is how I want the snails to work. Organ damage can be sustained, but with a high enough immune system the snail may be able to recover and at least partially heal/regenerate itself. As the immune system weakens organ damage becomes permanent and the snail is not as able to recover. Eventually the snail is just too weak, its organs start failing, and it dies.

The details

I’ve kind of done it in a really hacky way…like all the experimental features so far…which is pretty much all the features.

Organs

So a snail has a base organ efficiency score and an organ efficiency percentage. Eg:

  • baseHeartEffic: 60
  • heartEfficPerc: 100%
  • baseLungEffic: 45
  • lungEfficPerc: 90%

The organs so far are:

Yes, I know a snail has more organs than this. But I have sort of stayed away from organs that already are represented by sensors in the brain. These include eyes (vision is accounted for), mouth (taste is accounted for), tentacles (touch is accounted for). They will be added eventually, but I wanted to focus on the others for now.

All snails start with 100% efficiency for each organ. At first I was thinking of not having baesXEffic at all and just have a 0-100 percentage for each snail, but I realized this would be boring. All snails are not created equal. Just as Secretariat had a very large heart, a snail can have a much stronger or weaker organ than average. A simple percentage standard across all snails would not allow for the amazing outliers of nature that we see in real life.

Ageing

Snails already have a maturity rate (the rate at which they grow after birth). I decided to reuse this for ageing. Snails age every hour. Until a snail is mature it grows each hour based on its maturity rate. Once a snail is mature it begins to age and deteriorate every hour. The amounts may be small - something like 0.05% decrease in immunity per hour. You will be able to boost immunity with supplements, medicines, etc, but for now we are just focusing on the snail’s natural rate of ageing here.

Each minute we check for idle snail actions. Each minute we will also check for any organ damage as a result of those actions. For example, if a snail is operating at 95-100% physical effort it may have a higher chance of heart, lung, or foot damage.

Recovery

I have not built recovery in yet. I want to see how long a snail might live without any recovery of the organs. Recovery is to follow!

Wild Snails Around the World

Did you know that there is an entirely separate formula to calculate the distance between two points on the surface of a sphere? I mean…it kind of makes sense when you think about it. The shortest distance between two points without accounting for the curvature of the sphere would go right through the sphere itself, making it pretty useless when calculating physical distance on, for example, the surface of the Earth.

I’ve been working on creating some variation in the attributes of wild snails you might catch in different parts of the world. It’s not perfect, but the snails you find in Mobile, Alabama now look generally different from the snails you might find in Stockholm, Sweden.

So far the general testing rules are thus:

  • The snail shells get less red and more blue in them as you travel away from the equator
  • Snail shells get more green in them as you travel East
  • Snail pattern colors are the opposite of the above
  • Snail eye colors are always randomized
  • Snail pattern shapes and sizes also remin random for now, but will definitely change with the region at some later stage

I have added an idealTemp column to the snail table. The ideal temperature of snails you find in the wild in various regions tends to change (warmer toward equator and colder toward the poles). This means you will need to be quite careful when trading with others or buying other people’s snails (or even sending your snail overseas for a race on a foreign track). You’d need to closely control temperature and evaluate how your snail might perform on a track where it is currently 40C when it is used to a temperature of 20C. Temperature conditioning will need to be a thing.

Admittedly with these rules the snails you tend to find end up being a little more boring. Instead of a totally random mishmash of colors you end up with something more constrained. But I think this is ok. Wild snails are not meant to be beautiful or exciting - users are supposed to breed for those traits. If you regularly find unique, amazing snails in the wild there won’t be as much incentive or excitement in learning about your snail’s genetic traits and breeding for a desired result.

As I mentioned before, it takes a couple of seconds for HTML5 geolocation to get your coordinates (and then a few more seconds to get city/country name via the Geoname API). If you for some reason click to search for a wild snail before your location is loaded the coordinates of the search default to the coordinates of your stable.

In addition users will eventually be able to release their snails into the wild. At first it will just mean that someone else can find them. Down the line maybe the wild snails will be able to breed amongst themselves and change the attributes of wild snails in that region.

I have taken some screenshots of wild snails you may find in different places. Here they are:

Mobile, Alabama, US

Setauket, New York, US

San Francisco, California, US

Perth, Western Australia

Stockholm, Sweden

Kyoto, Japan

The North Pole

More Location and Temperature Stuff

I realized I didn’t actually include any implementation details when writing my sleepy post about stable locations and temperature the other night. I figured I’d elaborate on that now.

Having never used HTML5 geolocation features before, I kind of winged it and hacked together something that works for now. Currently I get the user’s location in two cases - on registration, and when searching for wild snails.

Here is how it happens on the registration page:

    <script>
        getLocation();
        function getLocation() {
            console.log('getLocation here');
            if (Modernizr.geolocation) {
                console.log('geolocation in');
                navigator.geolocation.getCurrentPosition(createHiddenInput, geolocationError);
            } else {
                console.log('no native support');
            }
        }

        function createHiddenInput(position) {
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;
            console.log('latitude: ' + latitude);
            console.log('longitude: ' + longitude);

            jQuery(document).ready(function() {
                $('#registrationForm').append('<input type="hidden" name="stableLatitude" value=' + latitude + '/>');
                $('#registrationForm').append('<input type="hidden" name="stableLongitude" value=' + longitude +'/>');
                $('#registrationForm').append('Stable Location: ' + getCityName(latitude, longitude) + ',' + getCountryName(latitude, longitude));

            });
        }
    </script>

Problem - retrieving the location takes a few seconds. If the user happens to register too quickly their location will not be retrieved. As a potential solution I am considering disabling the Submit button until the location is ready…however, what if they don’t want to allow me to retrieve their location at all?

If the user does not let me retrieve their location, it will be set by default to some place in Louisiana. Maybe then I can just display a “Don’t want to be in Louisiana? Wait while we get your location!” message…

So the latitude and longitude is stored for the user’s new account. Then to get the country and city name when displaying where the user is we use the Geonames API. Example:

public static function GetCityName($latitude, $longitude) {
    $cityName = null;
    if (Utility::InternetOn()) {
        $url = "http://api.geonames.org/findNearbyPlaceName?lat=" . $latitude . "&lng=" . $longitude . "&username=myusername";
        Log::info ('connection');
        $xmlDoc = new \DOMDocument();
        $xmlDoc->load($url);
        $cityNameNode = $xmlDoc->getElementsByTagName("name");
        $cityName = null;
        if ($cityNameNode->length > 0) {
            $cityName = $cityNameNode->item(0)->nodeValue;
        }
    }

    return $cityName;
}

To get temperature I originally tried OpenWeatherMap but that seems very slow and unreliable. So I am trying out The Dark Sky:

public static function GetCurrentTemperature($latitude, $longitude) {
    $url = "https://api.forecast.io/forecast/MYAPIKEY/" . $latitude . "," . $longitude;

    $JSON = file_get_contents($url);
    $data = json_decode($JSON);

    $f = $data->currently->temperature;
    $c = Utility::FahrenheitToCelsius($f);

    return $c;
}

The Dark Sky gives me 1000 free API calls per day. I blew through that in under an hour because I was stupid and calling the API each time I needed to get the temperature. Which is a lot…passive events alone blow through about 200 calls in 5 minutes.

So I added a currentTemp field in the User table and a temp_updated_at field. I added a recurring event to update the temperature once per hour.

Now it is time to start varying wild snail traits based on location you are looking in. I might even embed a Google Map on the wild-snail-collection page so that instead of just clicking “Look under a rock” or whatever you can click on a spot in your general vicinity on a map and see what snail pops up there!

Bringing Snail Stables Down to Earth

SnailLife snail stables have always lived in the ether - in some virtual universe with no physical location. Since I’m trying to make SnailLife based in reality, users’ snail stables should also be based in real locations. This is why now, upon registration, the user’s physical location is used as the location of their snail stable.

Right now in my snail stable it is 15.5 degrees Celsius. I don’t think I’m going to simulate building insulation, so without temperature control it would be about the same temperature in each jar (I may take humidity from the jar’s substrate into account, and have other items adding warmth or cold aside from temperature control gadgets). The user won’t see their exact jar temperature until they install a thermometer in the jar, and they won’t be able to regulate the temperature without installing a temperature controller.

Temperature

Snail jars have had temperature for a long time, but now that temperature is influenced by real life weather at the stable’s location. This will in turn influence the snails within the jar: their health, mood, etc (this last part is not yet implemented…well, it is only implemented very very roughly).

Different snails will adapt to live better in certain climates, and you will find different kinds of snails in different places. This way if you happen to physically be traveling the world you could hunt for wild snails away from your stable and find totally different patterns, colors, and traits.

Of course, this can only happen if you allow SnailLife to get your coordinates by using HTML5 Geolocation feature. Right now there is no fallback…I was thinking of falling back on location of IP, but that might make it easy for people to cheat and place their stables in rare locations. For now I think I might put you in a really boring part of the world by default when geolocation is not permitted…

Aside from affecting snail health and such in the future, temperature in a jar already affects how quickly consumable items rot. Things rot faster at a higher temperature.

Up next I want to think of a good way to vary snail types based on coordinates. There will be some manually set snail types that are available only in certain areas of the world if I want to make some super cool rare location (eg - get a special snail if you’re at the NASA headquarters!), but for the most part I want to create that variation automatically. I just have to think of a good way to approach this.