Walking ant
We already have a monkey and now we have a walking ant. First we have a grid of cells, each linked with the cardinal directions, N, NE, E, SE, S, SW, W, NW. So from a given cell you can apply the given direction operators in an operator chain and you will move to the appropriate grid cell. If the final cell is outside of the bounds of the grid, then the operator will return the empty ket |>. Indeed, our ant makes use of this grid structure so he can keep track of his steps, so once he finds food he can walk fairly directly back to his home nest. Noting that once he has picked up food, he switches on a scent trail for the walk home, to try to make it easier to find food next time he is out walking about.
Here is the code for the walking ant:
-- learn and randomly walk a grid -- keep a record of the pathway home, so we can walk directly towards home (a kind of path integration) -- if find food return home, leaving a scent trail -- once home, follow scent trail back to food (approximately) -- if find food again, return home, adding to the scent trail -- when reach home, store the food, switch off scent trail, and start randomly walking again -- fully updated to the version 3.1.1 language -- NB: there is a subtle bug somewhere. In one test run, the ant stopped walking. But I haven't seen the bug since. -- define our ant symbol: -- the |ant> => |###> the |ant> => |@##@> -- learn the grid: learn-grid[30, 30] -- define max steps per invocation of "walk": max |steps> => |400> -- enable or disable slow walk mode: -- for now, sleep for 20 milliseconds. -- change as desired. enable-slow-walk |*> #=> pause |*> #=> sleep[20] |_self> enable-fast-walk |*> #=> pause |*> #=> |_self> -- by default, use fast walk: enable-fast-walk -- learn current location: current |cell> => |grid: 10: 22> -- learn home location: home |cell> => current |cell> -- learn ant location: location |ant> => home |cell> -- start with no food at home: stored-food home |cell> => |0> -- learn the list of directions: list-of |directions> => |op: N> + |op: NE> + |op: E> + |op: SE> + |op: S> + |op: SW> + |op: W> + |op: NW> -- the learn path home operator: -- NB: we use add-learn instead of seq-learn so that directions can add up -- Ie, a type of path integration store-direction (*) #=> path |home> +=> |__self> -- find return path: -- "invert-direction" and "expand" operators are defined down lower: return-path |home> #=> invert-direction expand path |home> -- choose a heading when leaving the nest: heading |ops> => pick-elt list-of |directions> -- start by not carrying any food: carry |food> => |0> -- start with scent trail off: lay |scent> => |no> -- start with random walk type: type |walk> => |op: random-walk> -- place some food: food |grid: 2: 2> => |3> food |grid: 2: 3> => |3> food |grid: 2: 4> => |3> food |grid: 2: 5> => |3> food |grid: 3: 5> => |3> food |grid: 4: 5> => |3> food |grid: 5: 6> => |3> food |grid: 6: 6> => |3> food |grid: 29: 29> => |20> food |grid: 28: 3> => |20> -- show food operator: show-food |*> #=> display-grid[30, 30, food] |> -- show stored-food operator: show-stored-food |*> #=> display-grid[30, 30, stored-food] |> -- show grid operator: -- dg |*> #=> display-grid[30, 30] |> dg |*> #=> display-grid[30, 30, value, " "] |> -- define a collection of direction related operators: -- define identity direction operator: id |*> #=> |_self> -- define no operation operator: nop |*> #=> |> -- define negate direction operator: negate |*> #=> -1 |_self> -- define turn-right operators: turn-right |op: S> => |op: SW> turn-right |op: SW> => |op: W> turn-right |op: W> => |op: NW> turn-right |op: NW> => |op: N> turn-right |op: N> => |op: NE> turn-right |op: NE> => |op: E> turn-right |op: E> => |op: SE> turn-right |op: SE> => |op: S> -- define turn-left operators: turn-left |op: S> => |op: SE> turn-left |op: SW> => |op: S> turn-left |op: W> => |op: SW> turn-left |op: NW> => |op: W> turn-left |op: N> => |op: NW> turn-left |op: NE> => |op: N> turn-left |op: E> => |op: NE> turn-left |op: SE> => |op: E> -- define reverse operators: reverse-dir |op: S> => |op: N> reverse-dir |op: SW> => |op: NE> reverse-dir |op: W> => |op: E> reverse-dir |op: NW> => |op: SE> reverse-dir |op: N> => |op: S> reverse-dir |op: NE> => |op: SW> reverse-dir |op: E> => |op: W> reverse-dir |op: SE> => |op: NW> -- define expand operators: expand |op: S> => -1 |op: N> expand |op: SW> => -1 |op: N> -1 |op: E> expand |op: W> => -1 |op: E> expand |op: NW> => |op: N> -1 |op: E> expand |op: N> => |op: N> expand |op: NE> => |op: N> + |op: E> expand |op: E> => |op: E> expand |op: SE> => -1 |op: N> + |op: E> -- invert the given direction: invert-direction |*> #=> if( is-less-than[0] push-float |__self> ): -1 |__self> else: reverse-dir |__self> end: -- if there is food at the current cell, and not already carry food, then apply found-food branch, else do nothing: if-find-food |*> #=> if( and(is-greater-than[0] food current |cell>, is-equal[0] carry |food>) ): food current |cell> => minus[1] food current |cell> carry |food> => plus[1] carry |food> lay |scent> => |yes> type |walk> => |op: return-home> end: |> -- the if-reach-home operator: -- if current cell is home cell then apply reached-home branch, else do nothing: if-reach-home |*> #=> if( current |cell> == home |cell> ): stored-food current |cell> => stored-food |_self> ++ carry |food> carry |food> => |0> lay |scent> => |no> type |walk> => |op: random-walk> path |home> => |home> end: record-scent |*> #=> if( lay |scent> ): value current |cell> => plus[1] value current |cell> end: if-find-scent-change-heading |*> #=> if( value |__self> > |0> ): heading |ops> => random-if-zero reverse-dir return-path |home> end: random-if-zero (*) #=> if( do-you-know sdrop |__self> ): |__self> else: pick-elt list-of |directions> end: switch-on-random |*> #=> type |walk> => |op: random-walk> switch-on-return |*> #=> type |walk> => |op: return-home> take-a-step |*> #=> current |direction> => apply( type |walk>, current |cell>) path |home> +=> current |direction> current |cell> => apply( current |direction>, current |cell>) location |ant> => current |cell> if-find-food if-reach-home -- random-walk input is a grid location: -- random-walk has to return a direction: random-walk |*> #=> if-find-scent-change-heading |__self> -- -- blur heading: heading |ops> => normalize ( 0.1 turn-left^2 + 0.25 turn-left + 15 + 0.25 turn-right + 0.1 turn-right^2 ) heading |ops> -- -- try a direction: try |direction> => clean weighted-pick-elt heading |ops> -- -- if valid direction, step, else turn right: if( do-you-know apply(try |direction>, |__self>) ): try |direction> else: heading |ops> => pick-elt ( turn-right + turn-right^2 ) heading |ops> |op: id> end: -- return-home input is a grid location (which we ignore, instead making use of return-path |home>): -- return-home returns a direction one step closer to home: return-home |*> #=> clean weighted-pick-elt return-path |home> -- single map update: update |*> #=> record-scent take-a-step pause dg |> -- walk max steps: walk |*> #=> update (|1> .. max |steps>) -- walk given steps: walk-steps |*> #=> update (|1> .. |_self>) print-usage |*> #=> print | > print | Usage:> print | make the ant walk max steps:> print | walk> print print | make the ant walk the given number of steps:> print | walk-steps ket(500)> print print | show all the available food:> print | show-food> print print | show all the stored food:> print | show-stored-food> print print | display grid:> print | dg> print print | enable slow walk, ie a 20 ms delay between steps:> print | so you can actually see the ant walking> print | enable-slow-walk> print print | enable fast walk, ie no delay, which is the default:> print | enable-fast-walk> print print-usage
Here is our starting grid, with our ant at his nest:
sa: dg width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 @##@ 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |display-grid>
Here are the starting locations of food:
sa: show-food width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 3 3 3 3 3 4 3 5 3 6 3 7 8 9 10 @##@ 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 20 29 20 |display-grid>
Here is the final map after walking max |steps>, noting he left scent trails from a couple different food sources:
sa: walk width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 1 1 1 1 1 1 3 1 1 1 4 1 1 1 1 5 1 1 1 1 6 1 7 1 1 1 1 1 8 1 1 1 9 1 10 1 1 2 11 1 1 12 1 13 1 14 1 1 15 1 16 1 1 1 17 1 1 18 1 1 1 1 1 19 1 1 1 20 1 1 21 1 22 1 23 1 24 1 25 1 1 1 26 1 1 27 1 28 @##@ 1 1 29
Let's see the food again, now that he has taken a couple of pieces back to his home nest:
sa: show-food width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 2 3 3 3 3 3 4 3 5 3 6 3 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @##@ 19 29 20 |display-grid>
Now, let's see the stored food, and observe he has only picked up 2 pieces so far:
sa: show-stored-food width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 2 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @##@ 29 |display-grid>
Note that you can slow the ant down, or speed him up, using enable-slow-walk, and enable-fast-walk. The slow walk mode sleeps for 20ms between each step. If he walks slow enough, you can actually watch him move in real time, instead of blur of text scrolling on your screen. Also, if you want your ant to keep walking you can keep invoking walk as many times as you like. Indeed, here are the scent trails after invoking the walk operator a few more times:
width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 2 2 3 1 1 3 1 2 1 2 2 2 1 4 1 1 1 3 2 2 1 2 2 2 1 5 1 1 1 1 1 1 2 2 2 2 1 6 2 2 3 2 2 2 1 1 2 3 2 1 7 1 1 2 2 2 2 1 1 2 3 3 3 1 8 1 1 2 1 2 2 2 1 9 1 1 1 1 2 2 2 2 1 1 1 1 10 1 1 1 2 3 5 6 8 11 1 1 1 3 2 12 @##@ 1 2 3 3 13 1 1 2 3 1 1 14 1 1 4 3 1 1 15 1 1 2 3 16 1 2 1 3 2 17 1 3 2 2 2 1 18 1 1 2 4 3 1 2 1 1 19 1 1 1 1 2 2 1 1 20 1 1 1 2 2 2 1 1 21 1 1 1 1 1 1 22 1 1 1 1 1 2 1 1 23 1 2 2 2 1 1 24 1 1 1 2 3 2 1 1 1 25 1 2 2 2 3 1 26 2 3 2 2 2 27 1 3 3 1 28 5 4 2 29 |>
And the food he hasn't found yet:
sa: show-food width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 . 3 3 2 3 3 4 2 5 3 6 2 7 8 9 10 11 12 @##@ 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 15 29 20 |display-grid>
And the food stored at his home nest (he is up to 11 pieces now):
sa: show-stored-food width: 30 height: 30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 11 11 12 @##@ 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |display-grid>
Raw file here.