--- layout: ../Site.layout.js --- # Leonardo Calculus Knowledge Representation: Organisms 2 knowledgebase starting with local spatial walks After getting my [Braitenbergian Vehicles](/complex/book-review-braitenberg-vehicles/) [knowledgebase working initially](/lispgames/LCKR-object-oriented-simulation-simulation/), 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](/lispgames/LCKR-object-oriented-simulation-simulation/) or if you don't have lisp installed yet, initially [over here on installing lisp and emacs](/fundamental/installing-lisp-etc/) ## 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 `tile`s, 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!](https://gamerplus.org/@screwlisp/114917368062659379)