--- 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) => 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) => 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) => 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) => ``` # 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") # 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).