๐ชด๐ณ๐๐ฒ๐ดโธ๐๐ฅฌ๐ฅ๐ฅฆ๐๐๐๐๐๐๐๐ต๐ฟ๐ฅ๐ฅ๐ฅ๐๐๐๐๐ชต๐ฅ๐ซ๐ซ๐ ๐๐ฅฅ๐๐๐ฅ๐ชท๐๐๐๐ฅ๐พ๐ฝ๐นโ๐ฎ๐๐๐ฅ๐ถ๐ต๐ฑ๐ป๐ท๐ซ๐๐ชฑ๐ฆ๐ชฒ๐๐ชฐ๐๐๐ฆ๐ท๐ฆ๐ฆ๐ชณ๐๐๐ฆ๐ธ๐ฆ๐ง๐ฅ๐ฆ๐ฆ ๐๐๐ฆ๐ฆข๐ค๐ฃ๐ฆ๐๐ง๐ฆ๐ฆซ๐ฆฉ๐ฆ๐ฆค
Alright, now getting to video visualization of our Plant Insect Bird Braitenbergian simulation game. When freshly starting emacs, I visited the last article 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)
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
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-tableput 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.
random-new-organism
makerput 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.
. (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.
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>
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.
(defun eepitch-send
(buffername line)
(setq eepitch-buffer-name buffername)
(setq line (eepitch-preprocess-line line))
(eepitch-prepare)
(eepitch-line line))
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.
(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.
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).