After
organisms-kb
knowledgebase of our plant/insect/bird universethingtype
s for organism
, plant
, insect
, bird
,{plant, insect, bird}
are subsumed-by
organism
organism
s from our simulation universelispdef
entity
, sense2
We got pretty close to having a working simulation. I think the jump is similar in size and character to writing that sense2
lispdef
, so we should be able to mostly bridge this gap in one article here.
Plantworld
software-individual in demus
agentLet’s run through it explicitly here. I am going to mostly ignore how the code gets sent and the initial minutae are implicit, though I am using my eepitch-send
extension.
(setq inferior-lisp-program "clisp -E ISO-8859-1 -modern")
(slime)
(setq eepitch-buffer-name "*slime-repl clisp*")
(require "asdf")
(merge-pathnames #P"demus/Process/main/" #P"~/leocommunity/Plantworld/")
(uiop:chdir *) ; Remember * means 'last result' here.
(merge-pathnames #P"../../../remus/Startup/cl/acleo.leos" **) ; ** meaning second-last-result
(load *)
(cle)
We got here:
cs-user> (cle)
Starting or resuming interaction using the CLE command-loop
****************************************************************************
ses.001)
organisms-kb
We can actually set up organisms-kb
’s mustload
attribute, but that’s a story for another day. Manual control, then.
loadk organisms-kb
setk organisms-kb
. (symbol-plist 'organisms-kb)
checking what locations there were because its mustload
is not set up yet
(has-phrases nil exit-proc #<function :lambda nil nil> end-startup-proc
#<function :lambda nil nil> init-startup-proc #<function :lambda nil nil>
latest-rearchived nil attrib-converted nil archivepoint-sequence nil
latest-archived-entity nil local-ents nil sections nil dont-display nil
leos-use nil onto-amend nil indivinfo nil hostinfo nil overlay-own nil
overlay-types nil overlay-on nil profile nil codefiles nil uses-hostcommands
nil removed-entities nil mustload nil requires nil namephrase nil purpose nil
preferred-directory "Organisms/" contents
(seq&
(organisms-kb organisms-kb-properties |(location: organisms)|
|(location: plants)| |(location: insects)| |(location: birds)|
|(location: sensors)| |(location: world)|))
latest-written "2025-07-13/03:04.+12" type kb-index textprops nil read-in-file
organisms-kb)
loadk organisms
loadk plants
loadk insects
loadk birds
. (require :series)
loadk sensors
loadk world
eat
, propagate
and senesce
lispdef
s to organism
thingtype
put eat type lispdef
put propagate type lispdef
put senesce type lispdef
put organisms contents (union (get organisms contents) { eat, propagate, senesce })
writefil organisms
leodef
thoseRemembering the path to edit lisp in is #P"~/leocommunity/Plantworld/demus/Organisms/organisms.leo"
.
eat
def---------------------------------------------------------
-- eat
[: type lispdef]
[: latest-rearchived nil]
(leodef nil eat (the-organism)
(let* ((prey (cadr (get the-organism 'prey-species)))
(range (get the-organism 'eating-distance))
(lethality-rate
(get the-organism 'lethality-rate))
(starvation-rate
(get the-organism 'starvation-rate))
(world (cdadr (get 'world 'contents)))
(victim
(loop
:with ox := (get the-organism 'x-position)
:with oy := (get the-organism 'y-position)
:for thing :in world
:for type := (get thing 'type)
:for x := (get thing 'x-position)
:for y := (get thing 'y-position)
:do
(print
`(,thing ,type in ,prey
& ,(abs (- x ox)) < ,range
& ,(abs (- y oy)) < ,range))
:when (and
(member type prey)
(> range (abs (- x ox)))
(> range (abs (- y oy))))
:return thing)))
(print 'eat_) (print victim) (princ '?)
(cond
(victim
(when
(< (random 100) lethality-rate)
(setf (get victim 'type) nil)))
(:otherwise
(when
(< (random 100) starvation-rate)
(setf (get the-organism 'type) nil))))))
---------------------------------------------------------
propagate
def-- propagate
[: type lispdef]
[: latest-rearchived nil]
(leodef nil propagate (the-organism)
(let ((ox (get the-organism 'x-position))
(oy (get the-organism 'y-position))
(nat (get the-organism 'natality-rate))
(dist
(get the-organism 'propagation-distance))
(dir (get the-organism 'current-direction)))
(when (< (random 100) nat)
(let* ((theta
(loop :for compass :in
'(e ne n nw w sw s se)
:for ang :from 0 :by (/ pi 4)
:when (equal
compass
dir)
:return ang))
(new-sym
(intern
(symbol-name
(gensym
(symbol-name
(get the-organism 'type))))))
(delta-x (* dist (cos theta)))
(delta-y (* dist (sin theta)))
(new-x (+ ox delta-x))
(new-y (+ oy delta-y)))
(setf (symbol-plist new-sym)
(copy-list (symbol-plist the-organism))
(get new-sym 'x-position)
new-x
(get new-sym 'y-position)
new-y)
(nconc
(cadr
(get 'world 'contents))
(list new-sym))))))
---------------------------------------------------------
senesce
-- senesce
[: type lispdef]
[: latest-rearchived nil]
(leodef nil senesce (the-organism)
(let ((mort (get the-organism 'mortality-rate)))
(when (< (random 100) mort)
(setf (get the-organism 'type) nil))))
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
And I guess introducing a bunch of new things: ssv
, en
, random,
-,
length,
soact` …
.target
ssv .target (en (random (- (length (get world contents)) 1)) (get world contents))
This line says to choose a target other than the first and set-session-variable .target
to it.
soact
In the special case of non-branching trees, we can use soact
for composite actions:
soact [propagate .target] [eat .target] [senesce .target]
writefil
and loadk
the world to clean upwritefil world
loadk world
Remembering that it’s actually loadk
that cleans up entities with nil
led type.
sense2
smoke: a plant that turns away from other plantsput lonely-plant type plant
put lonely-plant x-position 0
put lonely-plant y-position 0
put lonely-plant current-direction e
put lonely-plant opening-angle 23
put lonely-plant sensor-weights <<plant -1>>
put lonely-plant sensor-scale 1
put plant-a type plant
put plant-a x-position 2
put plant-a y-position 1
addmember (get world contents) plant-a
sense2 lonely-plant
After some surgeory to sense2
- I made it an action as well as a lisp helper action, and I had accidentally written values
instead of list
somewhere - we got (with some verbose print
s):
ses.101) sense2 lonely-plant
plant-a
(ene 0 2 <= 2 < 3 & 1 <= 1 < 2)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw 0 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw 0 -2 -1 -1 0))
ses.102) (get lonely-plant current-direction)
=> n
What happened, I think is-
plant-a
is in the sensor area.plant-a
is found to be ene
plant
has a weight of -1
, e
and ne
are weighted -1
e
, n
is the first 8-compass direction with highest-equal sensor score.Hypothetically, if we add a plant-b
at nnw
<-1, 2>
put plant-b type plant
put plant-b x-position -1
put plant-b y-position 2
addmember (get world contents) plant-b
and make lonely-plant
aware of both plants with 360° vision,
put lonely-plant opening-angle 180
I believe we will turn w instead.
sense2 lonely-plant
(get lonely-plant current-direction)
ses.114) sense2 lonely-plant
plant-a
(ene 0 2 <= 2 < 3 & 1 <= 1 < 2)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw 0 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw 0 -2 -1 -1 0))
plant-b
(ene -1 2 <= -1 < 3 & 1 <= 2 < 2)
(nne 0 1 <= -1 < 2 & 2 <= 2 < 3)
(nnw 0 -1 <= -1 < 0 & 2 <= 2 < 3)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw -1 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw 0 -2 -1 -1 0))
ses.115) (get lonely-plant current-direction)
=> w
Do the southern directions work at all?
put plant-c type plant
put plant-c x-position -2
put plant-c y-position -1
addmember (get world contents) plant-c
(get lonely-plant current-direction)
sense2 lonely-plant
ses.120) (get lonely-plant current-direction)
=> w
ses.121) sense2 lonely-plant
plant-a
(ene 0 2 <= 2 < 3 & 1 <= 1 < 2)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw 0 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw 0 -2 -1 -1 0))
plant-b
(ene -1 2 <= -1 < 3 & 1 <= 2 < 2)
(nne 0 1 <= -1 < 2 & 2 <= 2 < 3)
(nnw 0 -1 <= -1 < 0 & 2 <= 2 < 3)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw -1 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw 0 -2 -1 -1 0))
plant-c
(ene -1 2 <= -2 < 3 & 1 <= -1 < 2)
(nne 0 1 <= -2 < 2 & 2 <= -1 < 3)
(nnw -1 -1 <= -2 < 0 & 2 <= -1 < 3)
(sse 0 1 <= -2 < 2 & -2 <= -1 < -1)
(ssw 0 -1 <= -2 < 0 & -2 <= -1 < -1)
(ene 0 2 <= -2 < 3 & 1 <= -1 < 2)
(ese 0 2 <= -2 < 3 & -1 <= -1 < 0)
(wnw 0 -2 <= -2 < -1 & 1 <= -1 < 2)
(wsw 0 -2 <= -2 < -1 & -1 <= -1 < 0)
"success"
(seq& (plant -1))
((ene -1 2 3 1 2) (nne 0 1 2 2 3) (nnw -1 -1 0 2 3) (sse 0 1 2 -2 -1)
(ssw 0 -1 0 -2 -1) (ene 0 2 3 1 2) (ese 0 2 3 -1 0) (wnw 0 -2 -1 1 2)
(wsw -1 -2 -1 -1 0))
ses.122) (get lonely-plant current-direction)
=> s
Let’s try and turn se
just for fun by adding a plant ssw
.
put plant-d type plant
put plant-d x-position -1
put plant-d y-position -2
addmember (get world contents) plant-d
sense2 lonely-plant
(get lonely-plant current-direction)
ses.130) (get lonely-plant current-direction)
=> se
This makes me somewhat confident in sense2
.
lonely-plant
propagate
?put lonely-plant propagation-distance 4
put lonely-plant natality-rate 100
propagate lonely-plant
(get world contents)
the answer was yes:
ses.189) (get world contents)
=> <world plant-a plant-b plant-c plant-d PLANT40431 PLANT40547 PLANT40589 PLANT40636>
hungry-bug
eat
a plant
?put hungry-bug type insect
put hungry-bug prey-species {plant}
put hungry-bug eating-distance 5
put hungry-bug lethality-rate 100
put hungry-bug starvation-rate 100
put hungry-bug natality-rate 100
put hungry-bug mortality-rate 0
put hungry-bug x-position -6
put hungry-bug y-position -1
put hungry-bug sensor-weights <<insect -1> <plant 1> <bird -2>>
put hungry-bug opening-angle 90
put hungry-bug current-direction e
put hungry-bug propagation-distance 3
put hungry-bug sensor-scale 2
addmember (get world contents) hungry-bug
(get world contents)
sense2 hungry-bug
anyway, it was working.
senesce
put parrot type bird
put parrot mortality-rate 100
senesce parrot
(get parrot type)
ses.272) put parrot type bird
put: parrot type bird
ses.273) put parrot mortality-rate 100
put: parrot mortality-rate 100
ses.274) senesce parrot
ses.275) (get parrot type)
=> nil
ses.276)
Well, that was a bit exhausting, but everything got working eventually, and I fixed the values
-list
mistake in sense2
from before.
So we achieved a slightly-too-verbose simulation. I dunno about you but I am too exhausted right now to conclude anything else.
I’m leaving exploring the implications of the simulation for the future.