I appear to have gotten myself into a bit of a mess.

“Make a snail brain/nerve-cluster for your snails,” they said. “It’ll be easy”, they said.

No. Nobody said that. Who would say that?! I got into this mess on my own and now I don’t know if I can dig myself out. I’ve already spent so much effort on this brain thing that it’s too late to turn back.

And it’s not all bad - it’s coming along, and it’s fun to think about this stuff. The thing is, I have no idea what I’m doing. I’m just making up how I think a snail brain should work. I’m afraid at some point, probably when I’m very far in, I will realize that I’m doing everything all wrong and have to start from scratch.

Snails have a very primitive brain. They are capable of associative learning. This is what I want to simulate.

A couple of nights ago I talked through the parts of the brain I have so far with David pretending to listen. It helped a lot, since when I got back to snailing after js13kGames I had forgotten where I left off or wtf any of what I’d written meant. Now, as a refresher, I’m going to go over it again here.

Typing this up I can already see a bunch of stuff I need to fix, or scrap completely. I guess doing this does help.

The brain

Each snail has a brain:

protected function getBrainAttribute() { $brain = new Brain($this); return $brain; }

{:lang="php”}

In the brain constructor, we tell the brain which snail it belongs to (since it is not a Model we can’t use the usually handy belongsTo relationship). We also create the olfactory, optical, tactile, and gustatory sensors and what I’m calling the attention and recognition neurons.

function __construct($snail) { $this->snail = $snail; $this->olfactorySensor = new OlfactorySensor($snail); $this->opticalSensor = new OpticalSensor($snail); $this->tactileSensor = new TactileSensor($snail); $this->gustatorySensor = new GustatorySensor($snail); $this->attentionNeuron = new AttentionNeuron($snail); $this->recognitionNeuron = new RecognitionNeuron(); }

{:lang="php”}

Each sensor is a child class of Neuron.

Every minute a job that checks for recurring events runs. One of these recurring events runs checkAllInputs() within the brain.

public function checkAllInputs() { $this->olfactorySensor->checkForInput(); $this->opticalSensor->checkForInput(); $this->tactileSensor->checkForInput(); $this->gustatorySensor->checkForInput(); $this->setWeights(); }

{:lang="php”}

The checkForInput() function then does this. We pass the snail’s current olfactory receptor quality into the checkSensor function.

class OlfactorySensor extends Neuron { public function checkForInput() { $detectedArr = $this->checkSensor($this->snail->currentOlfactoryReceptorQual); $this->detectedArr = $detectedArr; } }

{:lang="php”}

We check the sensor like this in the main Neuron class.

public function checkSensor($sensorQual) { // Create a new jar controller and get all objects inside the jar that the snail is in. $jarController = new JarController(); $allObjectsInJar = $jarController->getAllObjectsInJar($this->snail->jar->jarID); // Create a blank array of detected objects. $detectedArr = []; // Set point1 to the snail's current position from the db. $point1 = [ 'x' => $this->snail->posX, 'y' => $this->snail->posY ]; // Loop through each object in the jar and get its position. foreach ($allObjectsInJar as $object) { // Set point2 to object's current position from db. $point2 = [ 'x' => $object->posX, 'y' => $object->posY ]; // Get the distance between point1 (the snail) and point2 (the object). $distance = Utility::getDistance($point1, $point2); // in cm // If the distance is less than or equal to the sensor quality, object is detected if ($distance <= $sensorQual) { // What KIND of an object is it? Another snail? Item? // Because each of these have their own tables in the DB and their own primary key columns we need to get this information. // We do this by using the name of the object class and appending "ID" to it. // This is the primary key column name. $idString = strtolower(get_class($object)) . 'ID'; // If the ID of the object === ID of the snail that has detected it, // continue through that iteration of the loop and do nothing futher, since we don't need the snail to detect itself. if ($object->$idString === $this->snail->snailID) { continue; } // Otherwise, create an array with the details of the object we're detected. $toPush = [ 'id' => $object->$idString, 'idString' => $idString, 'inputWeight' => 1, 'distance' => $distance ]; array_push($detectedArr, $toPush); } } return $detectedArr; }

{:lang="php”}

The next step in the checkAllInputs() function is setWeights() . This is where all kinds of screwed up stuff happens:

protected function setWeights() { // Create an array of all sensors we care about $allSensors = [ 'olfactory' => $this->snail->currentOlfactoryReceptorQual, 'optical' => $this->snail->currentOpticalReceptorQual, 'tactile' => $this->snail->currentTactileReceptorQual, 'gustatory' => $this->snail->currentGustatoryReceptorQual ]; // Create blank array of detected IDs $allDetectedIDs = []; // Get index of highest value in array and value itself $bestReceptors = Utility::doubleMax($allSensors); // Best receptor name is receptor + Sensor. // Note: why not just name them with Sensor already appended in allSensors array? $bestReceptorName = $bestReceptors['i'] . 'Sensor'; Log::info('best receptor array: ' , $this->$bestReceptorName->detectedArr); // Loop through all objects detected by best receptor by reference and add 0.5 to their input weight foreach ($this->$bestReceptorName->detectedArr as &$object) { $object['inputWeight'] += 0.5; } // Merge objects detected by all sensors into one array $allDetected = array_merge($this->olfactorySensor->detectedArr, $this->opticalSensor->detectedArr, $this->tactileSensor->detectedArr, $this->gustatorySensor->detectedArr); // Create blank array for deduped detected object list $allDetectedDeDuped = []; // Count occurrences of idential ID in array $countedArr = array_count_values(array_map(function($value){return $value['id'];}, $allDetected)); // Loop through array of counted values foreach ($countedArr as $objectCount) { // Loop through all detected objects foreach ($allDetected as &$object) { // Add 0.1 for each occurrence (so if object is detected by multiple sensors, its inputWeight is higher) $object['inputWeight'] += 0.1 * $objectCount; $count = 0; // Loop through all detected deduped array foreach ($allDetectedDeDuped as $dedupedDetectedObject) { // If object id already exists, add to count if ($object['id'] === $dedupedDetectedObject['id']) { $count++; } } // If count is 0/object isn't already in there if ($count === 0) { // Push to array of all detected deduped array_push($allDetectedDeDuped, $object); } } } /**** whaaaaaaaattttttt??! *****/ // Get closest object $closestObject = null; // Loop through all detected deduped array foreach ($allDetectedDeDuped as $object) { // If closest object hasn't yet been set... if ($closestObject === null) { // Set this object as closest object $closestObject = $object; } // If distance to this object is less than distance to current closest object... if ($object['distance'] < $closestObject['distance']) { $closestObject = $object; } } // Loop through deduped...wait again? foreach($allDetectedDeDuped as &$object) { // If this is the closest object, add to inputWeight if ($object['id'] === $closestObject['id']) { $object['inputWeight'] += 0.05; } // Create new synapse for this impulse $synapse = new Synapse($object['inputWeight'], $this->attentionNeuron, $object); } // Pick object to focus on $this->attentionNeuron->chooseFocus(); }

{:lang="php”}

And here is what we have in the Synapse constructor:

function __construct($inputWeight, $destinationNeuron, $object = null ) { $this->inputWeight = $inputWeight; $this->destinationNeuron = $destinationNeuron; $this->targetObjectID = $object['id']; $this->targetObjectClass = $object['idString']; $this->targetObjectInputWeight = $object['inputWeight']; $this->sendImpulse(); }

{:lang="php”}

sendImpulse():

private function sendImpulse() { $this->destinationNeuron->receiveImpulse($this); }

{:lang="php”}

In this case the destination neuron is the attention neuron.

public function receiveImpulse($impulse) { $object = [ 'id' => $impulse->targetObjectID, 'idField' => $impulse->targetObjectClass, 'inputWeight' => $impulse->targetObjectInputWeight ]; array_push($this->allObjects, $object); }

{:lang="php”}

After all this is done, and the attention neuron has a list of objects with their IDs, ID column name, and inputWeight, we try and pick which object the snail will focus on.

function chooseFocus() { $focusedObject = null; foreach ($this->allObjects as $object) { // If there is no focused object, set the first one to focused if ($focusedObject === null) { $focusedObject = $object; } // If the focused object inputWeight < current object inputWeight... if ($focusedObject['inputWeight'] < $object['inputWeight']) { // Set current object as focused $focusedObject = $object; } } // Focus chosen, send to recognition neuron $brain = $this->snail->brain; $brain->recognitionNeuron->checkRecognition($focusedObject); } }

{:lang="php”}

And that’s sort of where I left off on the brain side so far:

class RecognitionNeuron extends Neuron { function checkRecognition($object) { Log::info('checking if object id ' . $object['id'] . ' is recognized'); } }

{:lang="php”}

The memory tables

Right now I am preparing the memory tables. There will be 3:

memories_sensory - for sensory memories, kept for just a few minutes

memories_st - for short term memories. If an object collects n sensory memories it will be stored in short term memory

memores_lt - for long term memories. This never gets deleted, but memories get weaker as they get older (but they can be refreshed)

So far I only have the sensory memory table, which currently consists of the following columns:

memoryID

snailID

objectID

objectIDField

objectProximity

neuronFiredID

consequenceRating

I also have a table of motor neurons, which is what I’ll specify in neuronFiredID . It is basically the action the snail took in relation to the memory. So far it just consists of two columns:

neuronID

name

And the motor neurons in there so far are:

move_left

move_right

move_up

move_down

bite

mate

touch

swallow

The memory models

I’ve also created a memory model class, with child classes for sensory, short term, and long term memories. Each memory has a belongsTo Eloquent relationship with a snail. A snail hasMany memories. And of course there is a MemoryController. This is pretty much where I left off.

Like I said - I have a feeling I’m going to look at this in the morning and decide that it’s all wrong and needs to be scrapped. But I guess even if that’s the case, it will be a lesson learned and the next version will be better.