(lisp)Games As Knowledge - plant-insect-bird post-mortem

In the end, my lispgamejam25 submission was two hours of implementing a trivial random walk. On the other hand, this was two hours of representing a random walk as a conjunction of disparate pieces of knowledge, some of which were arbitrary lisp programs, and some of which are a sitcalc oriented extension to first order logic. Let us, one more time and in painstaking detail review what this knowledge was and how it was created and accessed.

The knowledgebase to store the knowledge

Code

crek game-kb
loadk game-kb
setk game-kb

Details

Entered interactively. This seems reasonable to create, load, and set as active the knowledge that will constitute a game.

Add a place for types and functions

Code

crefil types-and-fun
loadk types-and-fun

Details

Entered interactively. Creating an entityfile to store entities. The idea is that this entityfile will hold type definitions (thingtypes) and functions relating to those types as a common resource shared by other entityfiles in this knowledgebase.

Defining new type entities

Code

put organism type thingtype
put organism attributes {row col map sensor-range}
addmember (get types-and-fun contents) organism

put plant type thingtype
put plant  attributes {row col map sensor-range}
put plant subsumed-by {organism}
addmember (get types-and-fun contents) plant

put insect type thingtype
put insect attributes {row col map sensor-range}
put insect subsumed-by {organism}
addmember (get types-and-fun contents) insect

put tile type thingtype
put tile attributes {row col contains}
addmember (get types-and-fun contents) tile
writefil types-and-fun
loadk types-and-fun

Details

Of course, the insect type got written out of my gamejam time budget as well. subsumed-by seems not very useful so far, but I understand it to be more a piece of commentary that such that in a different context, where a type organism is being refered to, apparently disjoint to this game, organism might be able to be understood as subsuming the plant and insect types here. My plant definition could have and probably should have been:

put plant type thingtype
put plant subsumed-by {organism}
put plant  attributes (get organism attributes)
addmember (get types-and-fun contents) plant

or more generally

put plant type thingtype
put plant subsumed-by {organism}
put plant  attributes (union (get organism attributes) {extra, plant, attributes})
addmember (get types-and-fun contents) plant

I think that the reason you do not delete attributes as such (our KRF only provides union) is that an unset, i.e. nil attribute is understood to be a lack of that feature. In fact attributes literally means if-these-are-present-their-values-must-be-persisted. A whale’s legs attribute would be nil. It is reasonable that a whale’s known attributes contains legs because whales are closely related to horses, which have legs. One could imagine a whale re-evolving a meaningful value for legs.

entityfile for tracking particular organisms

Code

crefil organisms
loadk organisms

put dandelion-01 type plant
put dandelion-01 row 1
put dandelion-01 col 2
put dandelion-01 map test-map
addmember (get organisms contents) dandelion-01
writefil organisms

Details

A new catch-all location for any entity of type, organism and creating a first particular organism.

Add a tiled ‘test-map’ organisms might be in

Code

crefil test-map
loadk test-map

. (defparameter *tiles-in* (loop :for idx :below 25 :for (row col) := (multiple-value-list (truncate idx 5)) :collect (intern (format nil "tile-~d-~d" row col))))
. ;; cle uses readline. Sorry about the one-liners.
. (loop :for tile :in *tiles-in* :do (setf (get tile 'type) 'tile))

. (setf (get 'test-map 'contents) `(seq& ,(append '(test-map) *tiles-in*)))

. (loop :for idx :from 0 :for (row col) := (multiple-value-list (truncate idx 5)) :for tile :in *tiles-in* :do (setf (get tile 'row) row (get tile 'col) col))

writefil test-map

Details

Here, I have to say I am a little shakier. I respect that (cle) is readline based. I think that similarly to eev, the intent is to stop people from introducing lengthy data via the interactive interface. On the other hand, as turns up in shell useage, I basically want to use loop to auto-populate all the tile entities to make up a rectangular map using the access to ansi common lisp, the extremely high level implementation language of our KRF. So far as shell one-liners are acceptable, I think this is an acceptable useage. Ideally each line would be completely readable as a sentence. I guess it depends how deep into lisp you are for how readable the loop facility is. I could probably have written those neater too.

A plant propagating function

Code

put living-life type lispdef
addmember (get types-and-fun contents) living-life
writefil types-and-fun

doing this interactively stubs this inside Game/types-and-fun.leo:

---------------------------------------------------------
-- living-life

[: type lispdef]
[: latest-rearchived nil]


into which I lisp-mode wrote:

---------------------------------------------------------
-- living-life

[: type lispdef]
[: latest-rearchived nil]

(leodef live-life live-life ()
	(loop :for organism :in (cdadr (get 'organisms 'contents))
	      :do
		 (case (get organism 'type)
		   (plant
		    (let* ((new-name (gensym "dandelion-"))
			   (prev-row (get organism 'row))
			   (prev-col (get organism 'col))
			   (tile (intern
				  (format nil "tile-~d-~d"
					  prev-row
					  prev-col)))
			   (new-row (+ prev-row
				       (1- (random 3))))
			   (new-col (+ prev-col
				       (1- (random 3))))
			   (new-tile
			     (intern
			      (format nil "tile-~d-~d"
				      new-row
				      new-col))))
		      (when (and (get new-tile 'type)
				 (null
				   (get new-tile
					'contains)))
			(setf
			 (get new-tile 'contains)
			 new-name
			 (get new-name 'row) new-row
			 (get new-name 'col) new-col
			 (get new-name 'type) 'plant)
			(nconc
			 (cadr (get 'organisms 'contents))
			 `(,new-name))))))))

Details

This is the flow when not entering interactive one-liners. The flip from writing (get .thing foo) to the lisp exact equivalent (get 'thing 'foo) is a bit jarring but the use of cadr is not. This is because loaded objects can be cdadred to interactively, to check that your guess was right / feel around a little bit for where you are trying to get. This is a somewhat bizarre point of interactive lisp useage that Sandewall leaned in to.

My interpretation of typed programming was to introduce (case (get thing 'type) (plant '(do some plant type things))) reflecting that we cannot (typecase thing (plant ..)) because type is simply a key/value in thing’s symbol-plist in lisp terms, and they have different useages.

I used when on a hypothetical new-tile’s type to logically filter out tiles that do not exist.

“Rendering” to ascii

Code

put render type lispdef
addmember (get types-and-fun contents) render
writefil types-and-fun

similarly to live-life.

---------------------------------------------------------
-- render

[: type lispdef]
[: latest-rearchived nil]

(leodef do-render do-render ()
	(loop :for tile :in (cadr (get 'test-map 'contents))
	      :for idx :from 0
	      :for (row col) :=
			     (multiple-value-list
			      (truncate idx 5))
	      :for thing := (get tile 'contains)
	      :when (zerop col) :do
		(terpri)
	      :when thing :do
		(princ "*")
	      :else :do
		(princ #")
	      :finally (terpri)))

Details

A simple lisp loop to show the tiles to standard output for a magic-number-5 width grid on the hardcoded-in-name test-map entityfile contents. Mistakenly prints a final " character. Useable either as an action or in lisp again.

The game

Code

loadk types-and-fun
. (loop :repeat 5 :do (live-life))

Details

I am not sure how many steps I triggered interactively, about five. The grid after which was

ses.052) do-render

""*"*
*****
**""*
*""""
*""""
"

ses.053) 

Broad overview

This was not very many interactive steps, as expected to achieve the small task: A persisted random walk of dandelions growing in a grid field. Key attributes were the persisted nature, and the inclusion of the two lisp definitions as pieces of the data being persisted.

My initial plans for a fancy-research game overlooked that I did not know how this simple scenario truly shook out before doing it in this jam. In hindsight, the use of the three entityfiles is trivial. The plant-eating insects could be defined into the case statement in live-life. And sensor type of thing with a last-reading and scan values could be added and integrated algorithmically to insects and plants to become more Vehicles-ish as was originally planned.