Skip to main content

Eater Experiment

Each creature is driven by a random neural architecture.

The more food eaten, the higher the chance to mate.

After 500 ticks, a new population is generated by genetically crossbreeding the neural architectures of the most successful.

import React, { useRef } from 'react';
import _ from 'lodash';
import { Layers, Vector3 } from 'three';
import { NodeGene, NodeType } from '@alenaksu/neatjs';
import {
Box,
OrbitControls,
PerspectiveCamera,
Sphere,
} from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { Teleporter } from '@react-ecs/boids';
import {
Entity,
rad,
useAnimationFrame,
useECS,
} from '@react-ecs/core';
import {
CollisionSystem,
Eater,
Evolver,
EvolverSystem,
FollowingSystem,
RaycastingSystem,
Rewarding,
RewardSystem,
SensingSystem,
ThrustingSystem,
TurningSystem,
} from '@react-ecs/neat'
import { ThreeView } from '@react-ecs/three';
import { Indexed, Quadtree } from '../hooks/useQuadtree';
import { MeshResetSystem } from '../systems/MeshResetSystem';
import { SeenSystem } from '../systems/SeenSystem';
const Scenery = ({ size }) => {
const lightRef = useRef()
// useHelper(lightRef, DirectionalLightHelper, 'royalblue', 1)
// useHelper(lightRef, DirectionalLightShadow,)
return (
<group>
<directionalLight
ref={lightRef}
position={[10, 10, 10]}
castShadow
shadow-mapSize-width={2048}
shadow-mapSize-height={2048}
shadow-camera-far={100}
shadow-camera-left={-100}
shadow-camera-right={100}
shadow-camera-top={100}
shadow-camera-bottom={-100}
/>
<OrbitControls
minDistance={30}
maxPolarAngle={rad(60)}
minPolarAngle={rad(20)} />
<Box scale={[size * 2, 1, size * 2]} position={[0, -1, 0]} receiveShadow>
<meshPhongMaterial color='white' />
</Box>
</group>
);
}
export const BasicEaterScene = () => {
const ECS = useECS()
useAnimationFrame(ECS.update)
const size = 100;
const randomPosition = () => {
return new Vector3(Math.random() * size * 2 - size, 0, Math.random() * size * 2 - size);
}
// enable camera for sim and debug layers
const layers = new Layers();
layers.enable(0)
layers.enable(1)
return (
<Canvas shadows>
<PerspectiveCamera makeDefault layers={layers} />
<Scenery size={size} />
<ECS.Provider>
<Quadtree size={size}>
{/* generate food on the board */}
{_.times(600, (i) => (
<Entity key={i}>
<Indexed />
<Rewarding />
<ThreeView>
<Sphere name="food" position={randomPosition()}>
<meshPhongMaterial color='blue' />
</Sphere>
</ThreeView>
</Entity>
))}
{/* systems to run before network activation */}
<MeshResetSystem />
<RaycastingSystem />
{/* <SmellingSystem /> */}
<SensingSystem />
{/* evolver for the creatures */}
<Evolver
populationSize={30}
feedForwardOnly={false}
fitnessThreshold={45}
survivalThreshold={0.95}
ageSignificance={0}
dropoffAge={999}
addConnectionTries={20}
adjustCompatibilityThreshold={false}
compatibilityModifier={0.3}
compatibilityModifierTarget={10}
compatibilityThreshold={3.0}
interspeciesMateRate={0.001}
disjointCoefficient={1.0}
excessCoefficient={1.0}
weightDifferenceCoefficient={1}
genomeWeightPerturbated={0.9}
mutateAddConnectionProbability={0.05}
mutateAddNodeProbability={0.03}
mutateConnectionWeightsProbability={1}
mutateOnlyProbability={.5}
mutateToggleEnableProbability={0}
mutationPower={.05}
reEnableGeneProbability={0.05}
inputs={[
new NodeGene(NodeType.Input, 'hitDistance0'),
new NodeGene(NodeType.Input, 'hitGood0'),
new NodeGene(NodeType.Input, 'hitBad0'),
new NodeGene(NodeType.Input, 'hitDistance1'),
new NodeGene(NodeType.Input, 'hitGood1'),
new NodeGene(NodeType.Input, 'hitBad1'),
new NodeGene(NodeType.Input, 'hitDistance2'),
new NodeGene(NodeType.Input, 'hitGood2'),
new NodeGene(NodeType.Input, 'hitBad2'),
new NodeGene(NodeType.Input, 'hitDistance3'),
new NodeGene(NodeType.Input, 'hitGood3'),
new NodeGene(NodeType.Input, 'hitBad3'),
new NodeGene(NodeType.Input, 'hitDistance4'),
new NodeGene(NodeType.Input, 'hitGood4'),
new NodeGene(NodeType.Input, 'hitBad4'),
new NodeGene(NodeType.Input, 'hitDistance5'),
new NodeGene(NodeType.Input, 'hitGood5'),
new NodeGene(NodeType.Input, 'hitBad5'),
new NodeGene(NodeType.Input, 'hitDistance6'),
new NodeGene(NodeType.Input, 'hitGood6'),
new NodeGene(NodeType.Input, 'hitBad6'),
]}
outputs={[
new NodeGene(NodeType.Output, 'turnAmount'),
new NodeGene(NodeType.Output, 'thrustAmount'),
]}>
<EvolverSystem ticks={250} />
{(o, i) => {
return <Eater
key={Math.random()}
organism={o}
rotation={[0, Math.random() * Math.PI, 0]}
position={randomPosition()} />
}}
</Evolver>
{/* systems to run after network activation */}
<TurningSystem multiplier={3} />
<ThrustingSystem multiplier={60} />
<CollisionSystem />
<RewardSystem />
<Teleporter size={size} />
<FollowingSystem />
<SeenSystem />
</Quadtree>
</ECS.Provider>
</Canvas>
)
}