screwlisp proposes kittens

Leonardo Calculus Knowledge Representation: Organisms 2 knowledgebase starting with local spatial walks

After getting my Braitenbergian Vehicles knowledgebase working initially, I have enough architectual changes I want to christen an organisms-2-kb.

In particular, instead of traversing every-organism-in-the-world to find spatial neighbors, I need to spatially walk to find neighbors.

So I am going to tile the world, and an organism is going to be on a particular tile, and each tile might provide n s e w neighbors. Iterating out like this to find the bounding box of tiles is what I am calling spatially walking.

A key point is that the world’s tiles can hold more than one organism, so I have some control over how fine-grain I am searching.

In my urgency to make something that gets game-y to put on https://itch.io, I wrote some dense common lisp loops for my knowledgebase’s new actions.

There is quite a lot of concluding written in the conclusion if you just breeze past the overly dense loops I wrote here.

Set up eepitch

Like over here enliving our Leonardo software individual

or if you don’t have lisp installed yet, initially over here on installing lisp and emacs

Make the new knowledgebase and some entityfiles.

crek organisms-2-kb

setk organisms-2-kb

crefil world

crefil organisms

crefil tiles

crefil current-tiles

let’s go out of our way not to pre-add anything. Instead, we’ll go back and add or change things when they come up.

The final frontier

We have to do space properly this time. My idea is that the world is made out of a coarse grid of tiles, and organisms know which tile they’re on: From the tile they’re on, they can walk adjacent tiles until they collect the least upper bound-ing box of organisms which could be in their sensors: Then they do a single pass of these candidate organisms to count which if any sensor is registering them.

loadk tiles

put tile type thingtype

put tile attributes {xmin xmax ymin ymax contents n s e w}

addmember (get tiles contents) tile

put tile-world type lispdef

addmember (get tiles contents) tile-world

writefil tiles

Sorry about these huge loops recently.

---------------------------------------------------------
-- tile-world

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

(leodef tile-world tile-world (xmin xmax ymin ymax side)
       (loop :for x-start :from xmin :to xmax :by side
	     :collect
	     (loop :for y-start :from ymin :to ymax :by side
		   :collect
		   (let*
		       ((new-tile (intern
				   (symbol-name
				    (gensym "tile")))))
		     (setf
		      (get new-tile 'xmin)
		      x-start
		      (get new-tile 'xmax)
		      (+ x-start side)
		      
		      (get new-tile 'ymin)
		      y-start
		      (get new-tile 'ymax)
		      (+ y-start side)
		      (get new-tile 'contents)
		      '(set& ()))
		      
		     new-tile))
	       :into columns
	     :finally
		(loop
		  :for column :in columns :do
		    (loop
		      :for tile :in column
		      :for tile-up :in (cdr column) :do
			(setf
			 (get tile 'n)
			 tile-up
			 (get tile-up 's)
			 tile)))
		(loop
		  :for column :in columns
		  :for right-column :in (cdr columns)
		  :do
		     (loop
		       :for tile :in column
		       :for right-tile :in right-column :do
		     (setf
		      (get tile 'e)
		      right-tile
		      (get right-tile 'w)
		      tile)))
		(nconc
		 (cadr
		  (get 'current-tiles 'contents))
		 (apply 'nconc columns))
	     (return `(set& ,(car columns)))))

writefil tiles

ses.141) loadk tiles
Load-ef: tiles at ../../../demus/Organisms-2/tiles.leo

ses.142) loadk current-tiles
Load-ef: current-tiles at ../../../demus/Organisms-2/current-tiles.leo

ses.143) tile-world 1 10 2 12 3
{TILE39334 TILE39335 TILE39336 TILE39337 TILE39338 TILE39339 TILE39340 TILE39341 TILE39342 TILE39343 TILE39344 TILE39345 TILE39346 TILE39347 TILE39348 TILE39349}

we better get our bounding tiles too.

put tiles-bounding type lispdef

addmember (get tiles contents) tiles-bounding

writefil tiles

---------------------------------------------------------
-- tiles-bounding

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

(leodef tiles-bounding tiles-bounding (tile range)
       (let*
	   ((starting-tile
	      (loop
		:for current-tile := tile
		  :then west-tile
		:for west-tile := (get current-tile 'w)
		:while (and west-tile
			    (> (get west-tile 'xmax)
			       (- (get tile 'xmin)
				  range)))
		:finally (return current-tile)))
	    (tiles (list starting-tile)))
	 (loop
	   :for current-tile := starting-tile
	     :then tile-east
	   :for tile-east := (get current-tile 'e)
	   :do
	      
	      (let ((n-tile (get current-tile 'n)))
		(loop :for ti := n-tile
			:then
			(get ti 'n)
		      :while (and
			      ti
			      (<
			       (get ti 'ymin)
			       (+
				(get tile 'ymax)
				range)))
		      :collect ti :into n-tiles
		      :finally (nconc tiles n-tiles)))
	      
	      (let ((s-tile (get current-tile 's)))
		(loop :for ti := s-tile
			:then
			(get ti 's)
		      :while (and
			      ti
			      (>
			       (get ti 'ymax)
			       (-
				(get tile 'ymin)
				range)))
		      :collect ti :into s-tiles
		      :finally (nconc tiles s-tiles)))
					
	   :while (and
		   tile-east
		   (<
		    (get tile-east 'xmin)
		    (+ (get tile 'xmax) range)))
	   :do (nconc tiles (list tile-east))
	   :finally
	   (return `(set& ,tiles)))))

ooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

loadk tiles

Okay, I think it’s working because with a tile side of 1, a 0 neighborhood retrieves 1 tile (the tile), a neighborhood of 0.1 retrieves 9 tiles, and a neighborhood of 1.1 retrieves 25 tiles. Though I have not written in any assertions about the nature of tiles being retrieved.

ses.168) loadk tiles
Load-ef: tiles at ../../../demus/Organisms-2/tiles.leo

ses.169) tile-world 1 6 7 12 1
{TILE39866 TILE39867 TILE39868 TILE39869 TILE39870 TILE39871 TILE39872 TILE39873 TILE39874 TILE39875 TILE39876 TILE39877 TILE39878 TILE39879 TILE39880 TILE39881 TILE39882 TILE39883 TILE39884 TILE39885 TILE39886 TILE39887 TILE39888 TILE39889 TILE39890 TILE39891 TILE39892 TILE39893 TILE39894 TILE39895 TILE39896 TILE39897 TILE39898 TILE39899 TILE39900 TILE39901}

ses.170) bounding-tiles TILE39887 0
Precondition failed
Reject:  No definition for the culprit verb
Culprit: bounding-tiles

ses.171) tiles-bounding TILE39887 0
{TILE39887}

ses.172) tiles-bounding TILE39887 0.1
{TILE39881 TILE39882 TILE39880 TILE39887 TILE39888 TILE39886 TILE39893 TILE39894 TILE39892}

ses.173) tiles-bounding TILE39887 1.1
{TILE39875 TILE39876 TILE39877 TILE39874 TILE39873 TILE39881 TILE39882 TILE39883 TILE39880 TILE39879 TILE39887 TILE39888 TILE39889 TILE39886 TILE39885 TILE39893 TILE39894 TILE39895 TILE39892 TILE39891 TILE39899 TILE39900 TILE39901 TILE39898 TILE39897}

ses.174) 

Conclusions and thoughts

I am pretty happy with my world tiles, the tile-world generate-a-rectangle-of-tiles action and the tiles-bounding query.

One thing I noticed is that since the tiles connected by a double linked list, it is possible to have disjoint tiles in the same space (different heights? But it’s actually hyperspatial.), and it would be possible to moo-style-dig different exits, though I am constraining exits to always be n s e w.

The inside of a house might be disjoint from the outside world (so outside the house cannot be freely seen from inside the house using the tiles-bounding function for example, and the house could be entered and exited through specially dug doorways.

I can also salvage all my dense sensor logic from organisms-kb into organisms-2-kb, but where they were previously always traversing the whole (necessarily small) world, now they only need check the known-nearby world.

Movement will need to be solely handled through special actions that unwind-protect the connection between the organism’s current tile and current x/y.

Another idea is that in the future, an edge tile in one knowledgebase’s exit could connect into a different knowledgebase, providing a portal between worlds that might have quite different ontologies, though both respecting the world tiles/movement/sensors system.

A detail that I kind of breeze past is that while the interface of the Leonardo system looks like this stuff,

---------------------------------------------------------
-- tile

[: type thingtype]
[: attributes {xmin xmax ymin ymax contents n s e w}]
[: nullvalued {description subsumed-by has-attributes has-categories create-proc registr-proc latest-rearchived}]

this always has a direct and 1:1 relation to concrete host language implementation, like when we look directly at a symbol’s symbol-plist using the host language access.

ses.177) . (symbol-plist 'tile)
(has-phrases nil latest-rearchived nil registr-proc nil create-proc nil
 has-categories nil has-attributes nil subsumed-by nil description nil
 textprops nil read-in-file tiles nullvalued
 (set&
  (description subsumed-by has-attributes has-categories create-proc
   registr-proc latest-rearchived))
 attributes (set& (xmin xmax ymin ymax contents n s e w)) type thingtype)

or a particular tile:

ses.178) . (symbol-plist 'TILE39876)
(e TILE39882 w TILE39870 n TILE39877 s TILE39875 contents (set& nil) ymax 12
 ymin 11 xmax 3 xmin 2)

the host language versions could just be dropped into other lisp programs as such. Which makes this ontology something like a world and map editor upper ontology itself, whose inhabiting ontologies and their concrete realisations might eventually be deployed into a low-power, production environment (like someone who wants to play a game and not install the “map editor” itself).

Latently, this map-editor-ish Leonardo system is a good example of how lisp as such can be homed in the Leonardo system, but the definition-of-an-action-as-being-whatever-its-concrete-implementation-does is not very powerful for reasoning. It’s just that a way of defining a way of defining a way of defining worlds is a pretty low level thing by its nature. As we start to get into the more high level stuff-happening-in-the-world we will see more sane actions in the form of “currently holds preconditions then postcondition” fluent stuff which you may have encountered in Sandewall’s writing on your own already. I am especially looking forward to eventually reaching Bayesian nets (decision trees).

Fin.

Sorry about the dense article.

Talk on the Mastodon as always please!

screwlisp proposes kittens