---
layout: ../Site.layout.js
---
# Leonardo Calculus Knowledge Representation: Running and rendering the Plant Insect Bird simulation once
๐ชด๐ณ๐๐ฒ๐ดโธ๐๐ฅฌ๐ฅ๐ฅฆ๐๐๐๐๐๐๐๐ต๐ฟ๐ฅ๐ฅ๐ฅ๐๐๐๐๐ชต๐ฅ๐ซ๐ซ๐ ๐๐ฅฅ๐๐๐ฅ๐ชท๐๐๐๐ฅ๐พ๐ฝ๐นโ๐ฎ๐๐๐ฅ๐ถ๐ต๐ฑ๐ป๐ท๐ซ
๐๐ชฑ๐ฆ๐ชฒ๐๐ชฐ๐๐๐ฆ๐ท๐ฆ๐ฆ๐ชณ๐๐๐ฆ๐ธ๐ฆ๐ง
๐ฅ๐ฆ๐ฆ
๐๐๐ฆ๐ฆข๐ค๐ฃ๐ฆ๐๐ง๐ฆ๐ฆซ๐ฆฉ๐ฆ๐ฆค
Alright, now getting to video visualization of our Plant Insect Bird [Braitenbergian simulation](/complex/book-review-braitenberg-vehicles) game. When freshly starting emacs, I visited [the last article](/lispgames/LCKR-completing-the-simulation/) in emacs eww in order to just-press-F8-over-and-over in eev style to load everything, though I found that eev ditches the non-printing eev-mode red star character, so I had to make the buffer editable and write that in myself.
I think most of what's remaining to do is add a fall-off-table-edge action, maybe called `deluge`, that `nil`s organisms outside some bounding rectangle to run periodically, so that I don't experience infinite-ish entity growth. Then, since the leonardo system enforces latin1 script, I was thinking about adding a char-code property to organism, actually, let's just do that here and now (setup from the last article I linked)
## Add display char-code attribute to `organism`
`addmember (get organism attributes) char-code`
`writefil organisms`
`loadk organisms`
`(get organism attributes)`
```
ses.016) loadk organisms
Load-ef: organisms at ../../../demus/Organisms/organisms.leo
ses.017) (get organism attributes)
=> {x-position y-position starvation-rate current-direction opening-angle sensor-scale natality-rate mortality-rate propagation-distance eating-distance lethality-rate prey-species sensor-weights char-code}
```
where I guess the video display, whatever, will use [`code-char`](
http://lispm.de/docs/clhs/HyperSpec/Body/f_code_c.htm#code-char) on the organism's `char-code` attribute. So ๐บ is `128570`.
```
CL-USER> (princ (code-char 128570))
๐บ
#\\U01F63A
```
in a unicode-extended lisp. (Our Leonardo system is strictly latin1, but video is going to be external to our individual.).
## `deluge` action for enforcing falling-off-the-table
`put deluge type lispdef`
`addmember (get organisms contents) deluge`
`writefil organisms`
And writing it into `#p"~/leocommunity/Plantworld/demus/Organisms/organisms.leo"`:
```
---------------------------------------------------------
-- deluge
[: type lispdef]
[: latest-rearchived nil]
(leodef deluge deluge (xmin xmax ymin ymax)
(loop
:for organism :in (cdadr (get 'world 'contents))
:unless (and (< xmin (get organism 'x-position) xmax)
(< ymin (get organism 'y-position) ymax))
:do (setf (get organism 'type) nil)))
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
```
`writefil organisms`
`loadk organisms`
hopefully that will do.
```
ses.026) (get world contents)
=> <world>
ses.027) put parrot type bird
put: parrot type bird
ses.028) put parrot x-position -10
put: parrot x-position -10
ses.029) put parrot y-position 0
put: parrot y-position 0
ses.030) addmember (get world contents) parrot
ses.031) deluge -5 5 -5 5
ses.032) (get parrot type)
=> nil
```
seems so.
## A literally so `random-new-organism` maker
`put random-new-organism type lispdef`
`addmember (get organisms contents) random-new-organism`
`writefil organisms`
```
---------------------------------------------------------
-- random-new-organism
[: type lispdef]
[: latest-rearchived nil]
(leodef random-new-organism random-new-organism ()
(let* ((type (nth (random 3)
'(plant insect bird)))
(x-position (random 100))
(y-position (random 100))
(starvation-rate (random 100))
(current-direction
(nth (random 8)
'(e ne n nw w sw s se)))
(opening-angle
(+ 23 (random (- 180 23))))
(sensor-scale
(1+ (random 8)))
(natality-rate
(random 100))
(mortality-rate
(random 100))
(propagation-distance
(1+ (random 8)))
(lethality-rate
(random 100))
(prey-species
`(set&
,(nth
(random 8)
'(()
(plant)
(insect)
(bird)
(plant insect)
(plant bird)
(bird insect)
(plant bird insect)))))
(sensor-weights
`(seq&
(
(seq& (plant ,(- (random 10) 5)))
(seq& (insect ,(- (random 10) 5)))
(seq& (bird ,(- (random 10) 5)))
)))
(char-code
(let ((plants '(129716 127795 127876 127794
127796 11801 127811 129388
129365 129382 127809 127808
127810 127817 127823 127822
127821 127797 127807 129367
129362 129364 127816 127820
127883 127812 129717 129373
129744 129746 127840 127827
129381 127819 127815 129372
129719 127825 127824 127875
129362 127806 127805 127801
9880 128174 128144 127893
129344 127990 127989 127793
127803 127799 129752 129361))
(insects
'(128027 129713 129419 129714 128029
129712 128030 128028 129439 128375
129408 129438 129715 128012 128026
129410 128376 129424 129498))
(birds
'(128037 128038 129413 128020 128019
129414 129442 128036 128035 129411
128330 128039 129417 129451
129449 129434 129444)))
(case type
(plant
(nth (random (length plants)) plants))
(insect
(nth (random (length insects))
insects))
(bird
(nth (random (length birds))
birds)))))
(sym (intern
(symbol-name
(gensym (symbol-name type))))))
(setf (symbol-plist sym)
`(type
,type
x-position ,x-position
y-position ,y-position
starvation-rate ,starvation-rate
current-direction ,current-direction
opening-angle ,opening-angle
sensor-scale ,sensor-scale
natality-rate ,natality-rate
mortality-rate ,mortality-rate
propagation-distance ,propagation-distance
lethality-rate ,lethality-rate
prey-species ,prey-species
sensor-weights ,sensor-weights
char-code ,char-code))
(nconc (cdadr (get 'world 'contents))
(list sym))
(print sym)
(print (symbol-plist sym))
(values sym)))
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
```
Got slightly carried away just brainstorming unicode codes for plants/insects/birds.
## Annoyingly non-portable copy-to-gnu-emacs definition
```
(defun world-to-emacs () (swank:eval-in-emacs `(setq *world* ',(loop :for thing :in (cdadr (get 'world 'contents)) :collect (symbol-plist thing)))))
```
Since cle uses `'read-line`, we need to pitch just one line. Sorry for the overflowing line.
## A bunch of new organisms
First off, I noticed that `mortality-rate` is overwhelmingly high, and secondarily, cannibalistic organisms will eat themselves if they are the oldest available prey-species at hand.
Still, I felt like it was possible that an unattended search would turn up some quasistable alien ecologies.
I might use the `senesce` action to control the total population size- i.e. if there are more than a thousand entities, call senesce until there are below a thousand entities. This means there is differential survival rates between species of culls.
Maybe tomorrow, we can look at some planned dynamics as well as the silliness of these random ones. I'm pretty sure that a mixture of very prolific plants with a high propagation-range + short-sighted/short-propagation herbivorous insects will naturally form quasi-stable wavefronts, that would then be able to host bird species. But it would be fun if something emerges out of randomness.
```
ses.065) (get world contents)
=> <world parrot>
ses.066) random-new-organism
PLANT39744
(type plant x-position 20 y-position 23 starvation-rate 96 current-direction nw
opening-angle 170 sensor-scale 4 natality-rate 57 mortality-rate 74
propagation-distance 1 lethality-rate 54 prey-species (set& (plant bird))
sensor-weights (seq& ((seq& (plant 0)) (seq& (insect -3)) (seq& (bird 3))))
char-code 127820) PLANT39744
ses.067) (get world contents)
=> <world parrot PLANT39744>
ses.068) random-new-organism
PLANT39761
(type plant x-position 96 y-position 43 starvation-rate 75 current-direction e
opening-angle 143 sensor-scale 7 natality-rate 19 mortality-rate 76
propagation-distance 3 lethality-rate 42 prey-species (set& (plant insect))
sensor-weights (seq& ((seq& (plant -2)) (seq& (insect -4)) (seq& (bird 3))))
char-code 129344) PLANT39761
ses.069) random-new-organism
BIRD39770
(type bird x-position 74 y-position 41 starvation-rate 76 current-direction sw
opening-angle 27 sensor-scale 6 natality-rate 17 mortality-rate 13
propagation-distance 4 lethality-rate 83 prey-species (set& (plant bird))
sensor-weights (seq& ((seq& (plant 4)) (seq& (insect 4)) (seq& (bird 3))))
char-code 129436) BIRD39770
ses.070) random-new-organism
INSECT39779
(type insect x-position 71 y-position 60 starvation-rate 26 current-direction
nw opening-angle 50 sensor-scale 8 natality-rate 2 mortality-rate 5
propagation-distance 2 lethality-rate 46 prey-species (set& (bird insect))
sensor-weights (seq& ((seq& (plant 4)) (seq& (insect 2)) (seq& (bird -3))))
char-code 129408) INSECT39779
ses.071) random-new-organism
INSECT39788
(type insect x-position 81 y-position 1 starvation-rate 97 current-direction ne
opening-angle 172 sensor-scale 7 natality-rate 38 mortality-rate 51
propagation-distance 7 lethality-rate 87 prey-species (set& (plant))
sensor-weights (seq& ((seq& (plant 1)) (seq& (insect -4)) (seq& (bird 4))))
char-code 129408) INSECT39788
ses.072) random-new-organism
INSECT39797
(type insect x-position 46 y-position 52 starvation-rate 5 current-direction ne
opening-angle 170 sensor-scale 1 natality-rate 50 mortality-rate 29
propagation-distance 2 lethality-rate 90 prey-species (set& (plant bird))
sensor-weights (seq& ((seq& (plant 0)) (seq& (insect -2)) (seq& (bird -5))))
char-code 128026) INSECT39797
ses.073) (get world contents)
=> <world parrot PLANT39744 PLANT39761 BIRD39770 INSECT39779 INSECT39788 INSECT39797>
```
# Actually driving the simulation
I guess I am going to sit in an embeddable common lisp image, and drive the Plantworld individual via the emacs-server, and print the state of the world.
## eepitch-send (emacs lisp)
[Like before](/complex/eepitch-send/)
```
(defun eepitch-send
(buffername line)
(setq eepitch-buffer-name buffername)
(setq line (eepitch-preprocess-line line))
(eepitch-prepare)
(eepitch-line line))
```
## eepitch (common lisp)
but in this case I need to send strings, since the leonardo calculus often doesn't manifest in normal-looking s-expressions.
` (setq eepitch-buffer-name "*slime-repl ECL*")`
`'a`
```
(defun e-e-str (buffername line)
"'external-eepitch-send'
buffername string (emacs buffer name string)
line string (as eepitch)
"
(require "asdf")
(uiop:launch-program
(let ((*print-pretty* nil))
(format
nil
"emacsclient --eval '(eepitch-send ~s \\"~a\\")'"
buffername line))))
```
```
CL-USER> (e-e-str "*slime-repl ECL*" "`foo")
#<a UIOP/RUN-PROGRAM::PROCESS-INFO 0x7f9b62824a00>
CL-USER> `foo
FOO
CL-USER>
```
everything seems to be going according to plan. However, this article has been a kind of run-on do-everything-that-hasn't-been-done, so let us try and wrap things up for now.
# Print the world
## Copy the world file to emacs
` (setq eepitch-buffer-name "*slime-repl ECL*")`
`(e-e-str "*slime-repl clisp*" ". (world-to-emacs)")`
` (setq eepitch-buffer-name "*slime-repl ECL*")`
`(swank:eval-in-emacs '*world*)`
Well, it's a hassle that those symbols got interned in `SWANK-IO-PACKAGE`.
`*world*`
```
(defun interpret-world (plists)
(loop
:with x-position
:= (intern "X-POSITION" "SWANK-IO-PACKAGE")
:with y-position
:= (intern "Y-POSITION" "SWANK-IO-PACKAGE")
:with char-code
:= (intern "CHAR-CODE" "SWANK-IO-PACKAGE")
:for plist :in plists
:collect
`((,(getf plist x-position) ,(getf plist y-position))
,(code-char (getf plist char-code)))))
```
=>
```
(((20 23) #\\U01F34C) ((96 43) #\\U01F940) ((74 41) #\\U01F99C)
((71 60) #\\U01F980) ((81 1) #\\U01F980) ((46 52) #\\U01F41A))
```
of which, I can just print something like that. I recall the enclosure is 100x100.
```
(defun print-world (alist)
(loop :for y :below 100 :do
(loop
:for x :below 100
:for ch := (cadr (assoc `(,x ,y) alist
:test 'equal))
:if ch :do (princ ch)
:else :do (princ #\\Space)
:finally (terpri))))
```
so.
```
๐ฆ
๐
๐ฆ
๐ฅ
๐
๐ฆ
```
Let's call this here, we implemented rendering states of the world in a separate lisp.
# Conclusion
We saw our way to operating and accessing the Leonardo system simulation from a different, embeddable common lisp image in the same emacs, which is fundamental progress. We were able to render it.
The `(random 100)` mortality rates are very high: But maybe `senesce` can be repurposed as a randomly weighted culling. And there are oddities like cannibalistic species eating themselves.
I think this is the last of the desperate-scramble articles, given that there is a rendered unicode crab visible slightly up from here we got *somewhere*. It seems like all that is left tomorrow is to practice using our simulation, which should be more sane for you to read (sorry about your sanity hitherto).
# Fin.
See you [on the Mastodon thread to talk about it](https:/gamerplus.org/@screwlisp).