Missionaries and Cannibals NetLogo Model
Produced for the book series "Artificial Intelligence";
Author: W. J. Teahan; Publisher: Ventus Publishing Aps, Denmark.
powered by NetLogo
view/download model file: Missionaries-and-Cannibals.nlogo
WHAT IS IT?
This model applies standard search algorithms to the classic search problem called Missionaries and Cannibals. In this toy problem, there are 3 missionaries and 3 cannibals. They have arrived together on one side of a river and they all wish to cross to the other side. Luckily, there is available a boat for crossing the river. However, there are two catches to the problem. The first catch is that only two people can fit in the boat at any one time. The second catch is that the cannibals are not allowed to outnumber the missionaries at any stage. If they do, they will overpower the missionaries and then eat them.
The problem is the following: Is it possible to get all the missionaries and cannibals safely across to the other side of the river? To solve this problem, search needs to be employed to try out the different scenarios. This model implements some of the classic search algorithms and these can be applied to this toy problem to see how the different search strategies perform against each other.
WHAT IS ITS PURPOSE?
The purpose of this model is to show how some of the classic search algorithms such as breadth-first search and depth-first search can be implemented in NetLogo, and also how they can then be applied to solving a classic toy problem in order to compare how they perform.
HOW IT WORKS
The model implements the search algorithms in a novel way by making use of an agent-oriented approach which is relatively easy to implement in NetLogo. Rather than use a queue data structure to implement the classic search algorithms (this is a standard solution; see, for example, Russell & Norvig's AI textbook), the model instead adopts a purely agent-oriented solution where the information is distributed amongst NetLogo agents. These agents conduct the search by transferring information to other agents that continue the search. Hence, an explicit queue data structure separate from what is happening in the search is not needed (although technically the information is still being stored in a queue behind the scenes, but the implementation of this is hidden behind the top-level commands that NetLogo provides).
The hope is that this "queueless" agent-oriented approach provides a more intuitive solution that makes it easier to grasp how the search strategies work. It uses a situated, embodied first person design perspective, therefore arguably making it easier to understand the essential differences between the search strategies.
The breed of searcher turtle agents is used for the searchers that move throughout the maze trying to get to the goal. These searcher agents maintain information about the current state of the search such as the time taken and the path and estimated costs. Each searcher agent expands the search by seeing if the following actions are possible:
- two missionaries get in the row-boat and row to the other side;
- two cannibals get in the row-boat and row to the other side;
- one missionary and one cannibal get in the row-boat and row to the other side;
- just one missionary gets in the row-boat and rows to the other side;
- just one cannibal gets in the row-boat and rows to the other side.
Note that the states of the search for this problem can be represented by a 3-tuple:
(#missionaries on start side of river, #cannibals on start side of river, #boats on start side of river)
Hence, the start state is represented by the tuple (3, 3, 1) and the goal state by (0, 0, 0).
In the Interface, a graph using parallel co-ordinates is used to visualise the search as it proceeds. An animation of one possible solution to the problem is also provided below the graph. The graph is updated as the animated solution proceeds.
HOW TO USE IT
To initialise the search, press the setup button in the Interface. A person shape will appear at the start state (3, 3, 1) at the top left of the graph. The goal state is at the bottom of the graph, so a rough estimate of whether a search is making progress is how far it has progressed downwards.
To start the search, either press the go-once button to make the search proceed one step at a time, or press the go-forever button to make the search proceed continuously until it reaches the goal state or other termination criteria are satisfied (i.e. it reaches the right side of the graph without finding a solution).
Pressing the go-animation button will start the animation and show the path taken at the same time in the graph. This is shown using the red colour.
THE INTERFACE
The model's Interface buttons are defined as follows:
- setup: This will clear the environment and all variables, reset the animation and redraw the graph.
- go-once: This will make the search proceed one step at a time.
- go-forever: This will make the search proceed continuously until it has reached the goal state or it is deemed to be unsuccessful.
- go-animation: This will start the animation shown at the middle bottom of the Interface.
The model's Interface choosers, sliders and switch are defined as follows:
- search-behaviour: This specifies what search strategy the agents should employ when performing the search. A full description of the different search strategies can be found in Chapter 8 of Volume 1 of the Artificial Intelligence book (see reference at the bottom of this Information).
- heuristic: This specifies the heuristic to be used for the Informed searches - Greedy Best First Search and A* Search.
- max-depth: This sets the maximum depth of the search.
- max-agents-to-expand: This sets the maximum number of agents to expand the search each step. It is only used by the Multi-Agent Depth First Search.
- allow-revisited-states?: This is a flag that if set to On will allow the search to proceed to already visited states.
The model's Interface monitors are defined as follows:
- Active Searcher Agents: This shows the current number of active searcher agents still participating in the search.
- Maximum Active Searcher Agents: This shows the maximum value that the Active Searcher Agents monitor has achieved throughout the search.
- Total Searcher Agents: This shows the total number of searcher agents that the search has used.
- IDS Depth: This is the current depth during the execution of the Iterative Deepening Search.
THINGS TO NOTICE
Notice the effect of turning the flag allow-revisited-states? On and Off. Why is turning it Off so effective at dramatically reducing the search for the different search behaviours?
Notice that some of the searches if repeated with the same settings end up in different places (for example, try Greedy Best First Search several times using the heuristic "Min. no. boat trips needed" and with the flag allow-revisited-states? set to Off.). Why is this?
THINGS TO TRY
Which search behaviour seems to be the best at this problem? Which seems to be the worst? Try out each of the different search behaviours to see which one is the most effective. Find out how well each search algorithm performs against the four evaluation criteria - time complexity, space complexity, optimality and complexity. Relate the theory to what happens in practice.
Try figuring out how many solutions there are to the problem (the one shown in the animation is not the only one).
Try out the different heuristics for the informed searches (Greedy Best First Search and A* Search). Do they have any effect on how effective the search is?
EXTENDING THE MODEL
Try adding other search algorithms, or adding your own variations to the ones implemented in the model.
NETLOGO FEATURES
Note the use of the hatch and die commands to clone child searchers and terminate parent searchers thus avoiding the need for a separate queue data structure to maintain the information during the search. Note also the variation in the ask command that determines the strategy being used that defines the different searches.
RELATED MODELS
For other search problems, see the Searching For Kevin Bacon model, and the Searching Mazes model. For an insight into the Manhattan distance and Euclidean distance metrics used as heuristics for the Informed searches, see the Manhattan Distance model.
CREDITS AND REFERENCES
This model was created by William John Teahan.
To refer to this model in publications, please use:
Missionaries and Cannibals NetLogo model.
Teahan, W. J. (2010). Artificial Intelligence. Ventus Publishing Aps.
PROCEDURES
; Missionaries and Cannibals model,
;
; Shows how the Missionaries and Cannibals search problem works..
;
; Copyright 2010 William John Teahan. All Rights Reserved.
;
breed [missionaries missionary]
breed [cannibals cannibal]
breed [searchers searcher]
searchers-own
[ time ; time step
height ; used for hill-climbing search
path-cost ; actual cost of search path so far (used for A-star search)
estimated-cost ; estimated cost of cheapest path to goal
m-count c-count r-count] ; #missionaries, #cannibals, #rowboats on left side of river
breed [axis-points axis-point]
globals
[ search-time-step ; indicates what the current time step is during the search
visited-states ; indicates which states have aleady been visited - do not revisit them in that case
search-completed ; true when search is completed
width-between-axes ; horizontal gap used when drawing vertical axes
path-found ; true if we have found a path to expand; e.g. used by DFS to signal a dead-end
max-active-searchers; maximum number of active searchers
searchers-used ; total number of searcher agents used
person-colour ; colour of person shown doing the search in the visualisation
IDS-depth ] ; current depth limit for IDS (Iterative Deepening Search)
to setup
ca ;; clear everything
set search-time-step 0
set visited-states []
set search-completed false
set searchers-used 1
set max-active-searchers 1
set width-between-axes 18
set-default-shape turtles "person"
set person-colour sky
;set-default-shape searchers "person"
set-default-shape axis-points "line"
let apos -80 ; position of animation
create-missionaries 3 [set size 18 set color gray]
create-cannibals 3 [set size 18 set color red]
ask missionary 0 [set xcor -90 set ycor apos]
ask missionary 1 [set xcor -82 set ycor apos]
ask missionary 2 [set xcor -74 set ycor apos]
ask cannibal 3 [set xcor -90 set ycor apos - 20]
ask cannibal 4 [set xcor -82 set ycor apos - 20]
ask cannibal 5 [set xcor -74 set ycor apos - 20]
draw-rowboat -48 -90
create-searchers 1 [ setup-searcher ]
ask patches
[
set pcolor white ;; make background full of white patches
if (pxcor >= -62 and pxcor <= 62 and pycor >= apos - 40 and pycor <= apos + 15)
[set pcolor blue] ;; draws the "river" in blue
if (pxcor >= -65 and pxcor <= -63) and (pycor >= apos - 40 and pycor <= apos + 15)
[set pcolor brown] ;; draws the left "bank" in brown
if (pxcor >= 63 and pxcor <= 65) and (pycor >= apos - 40 and pycor <= apos + 15)
[set pcolor brown] ;; draws the left "bank" in brown
; draw the axes
let xpos -140
let t 0
repeat max-depth + 1
[
if (pxcor = xpos and pycor >= (- 45) and pycor <= 110)
[set pcolor black] ;; draws the axis
if (pxcor = xpos + 4 and pycor = (- 50))
[set plabel (word "t=" t) set plabel-color black] ;; draws its label
set xpos xpos + width-between-axes
set t t + 1
]
]
setup-axis-points -140
end
to setup-searcher
; Initialises the searcher agent.
set xcor -140 set ycor 110 pen-down set color sky set size 8 stamp
set time 0
set path-cost 0
set estimated-cost 0
set m-count 3
set c-count 3
set r-count 1
set height hill-height m-count c-count r-count
if not allow-revisited-states?
[ set visited-states fput self visited-states ]
end
to animate-solution-path [agent m c r]
; draw the solution path one state at a time
ask searcher agent
[ pen-down
set time [time] of searcher agent + 1
let xpos xcor + width-between-axes ; move to next time step i.e. next axis
let ypos -45 + (state-number m c r) ; use the state (m,c,r) encoded as an integer
setxy xpos ypos stamp
set m-count m set c-count c set r-count r]
end
to go-animation
; performs the animation
let left-to-right true
let right-to-left false
let agent 0
create-searchers 1
[ setup-searcher
set agent who
set color red ]
cross-river left-to-right 4 5 ; (3,1,0) cannibal & cannibal -->
animate-solution-path agent 3 1 0
wait 1.0
cross-river right-to-left nobody 4 ; (3,2,1) <-- cannibal
animate-solution-path agent 3 2 1
wait 1.0
cross-river left-to-right 3 4 ; (3,0,0) cannibal & cannibal -->
animate-solution-path agent 3 0 0
wait 1.0
cross-river right-to-left nobody 3 ; (3,1,1) <-- cannibal
animate-solution-path agent 3 1 1
wait 1.0
cross-river left-to-right 1 2 ; (1,1,0) missionary & missionary -->
animate-solution-path agent 1 1 0
wait 1.0
cross-river right-to-left 1 4 ; (2,2,1) <-- missionary & cannibal
animate-solution-path agent 2 2 1
wait 1.0
cross-river left-to-right 0 1 ; (0,2,0) missionary & missionary -->
animate-solution-path agent 0 2 0
wait 1.0
cross-river right-to-left nobody 5 ; (0,3,1) <-- cannibal
animate-solution-path agent 0 3 1
wait 1.0
cross-river left-to-right 4 5 ; (0,1,0) cannibal & cannibal -->
animate-solution-path agent 0 1 0
wait 1.0
cross-river right-to-left nobody 4 ; (0,2,1) <-- cannibal
animate-solution-path agent 0 2 1
wait 1.0
cross-river left-to-right 3 4 ; (0,0,0) missionary & cannibal -->
animate-solution-path agent 0 0 0
end
to-report state-number [m c r]
;; reports the state (m, c, r) as a single integer
report m * 40 + c * 10 + r * 5
end
to draw-rowboat [x y]
;; for drawing the rowing boat
create-turtles 1 [set xcor x set ycor y set color lime set shape "canoe" set size 35 set heading 90]
end
to move-rowboat [agent1 agent2 step]
;; moves the canoe and the agents along by step increments horizontally
;; if either agent1 or agent2 is nobody, then doesn't draw them
let xpos [xcor] of turtle 6
let ypos [ycor] of turtle 6
if agent1 != nobody
[ask turtle agent1 [set xcor xpos + step - 4 set ycor ypos + 6]]
if agent2 != nobody
[ask turtle agent2 [set xcor xpos + step + 4 set ycor ypos + 6]]
ask turtle 6 [set xcor xcor + step] wait 0.1
; draw canoe last so that it covers the agents; turtles sit "inside" the canoe
wait 0.1 ; slow down the animation so that it is not too quick
end
to cross-river [left-to-right agent1 agent2]
;; draws the canoe crossing the river. If left-to-right, it will draw it from the left to the right,
;; otherwise it will draw it from the other direction.
let step ifelse-value left-to-right [5] [-5] ; go right or left 5
repeat 19 [move-rowboat agent1 agent2 step]
; now disembark the agents
; first find position for each agent on the bank
let x-offset ifelse-value left-to-right [75] [-90] ; set x-offset to the left side of people on this bank
foreach list agent1 agent2
[if ? != nobody
[ask turtle ?
[set xcor x-offset + (who mod 3) * 8
set ycor ifelse-value (who < 3) [-80] [-100]]]
]
end
to setup-axis-points [xpos]
let ypos 110
let m 3 ; m for missionaries
repeat 4
[ let c 3 ; c for cannibals
repeat 4
[ let r 1 ; r for row-boat
repeat 2
[ create-axis-points 1
[ set color black set size 6 set heading 90
set xcor xpos set ycor ypos
set label (word "(" m "," c "," r ") ") set label-color blue]
set ypos ypos - 5
set r r - 1
]
set c c - 1
]
set m m - 1
]
end
to go
if search-completed
[ user-message "Search is completed!"
stop ]
; uninformed search strategies (blind search)
if search-behaviour = "Breadth First Search"
[ expand-breadth-first-search ]
if search-behaviour = "Uniform Cost Search"
[ expand-uniform-cost-search ]
if search-behaviour = "Depth First Search"
[ expand-depth-first-search ]
if search-behaviour = "Multi-Agent Depth First Search"
[ expand-MA-depth-first-search ]
if search-behaviour = "Depth Limited Search"
[ expand-depth-limited-search ]
if search-behaviour = "Iterative Deepening Search"
[ expand-iterative-deepening-search ]
; informed search strategies
if search-behaviour = "Greedy Best First Search"
[ expand-greedy-best-first-search ]
if search-behaviour = "A* Search"
[ expand-A-star-search ]
; local search strategies
if search-behaviour = "Hill Climbing Search"
[ expand-hill-climbing-search ]
set search-time-step search-time-step + 1
if (count searchers = 0)
[ user-message "No more searchers! Abort!"
stop ]
tick
end
to expand-breadth-first-search
; expand the search by adding more searcher-agents
ask searchers
[
expand-paths (searcher who)
die ; prune away parent agents used at lower time-steps
]
end
to expand-uniform-cost-search
; expand the search using uniform cost search by adding more searcher-agents
set path-found false
ask first (sort-by [[path-cost] of ?1 < [path-cost] of ?2] searchers)
[
expand-paths (searcher who)
die ; this agent has done its work; it's children are now doing the work
]
end
to expand-depth-first-search
; expand the search by following longest path
; do not follow the other paths in parallel; just follow one of them
set path-found false
ask first (sort-by [[time] of ?1 > [time] of ?2] searchers)
[
if time >= max-depth
[ user-message "This search is taking too long! Abort!"
stop ]
expand-paths (searcher who)
die ; this agent has done its work; it's children are now doing the work
]
if not path-found
[ user-message "Dead-end found. I have to back-track!" ]
end
to expand-MA-depth-first-search
; expand the search by following longest path
; follow the other paths in parallel; but do not follow all of them
set path-found false
let agents ifelse-value (count searchers < max-agents-to-expand)
[ count searchers ]
[ max-agents-to-expand ]
ask n-of agents turtle-set (sort-by [[time] of ?1 > [time] of ?2] searchers)
[
expand-paths (searcher who)
die ; this agent has done its work; it's children are now doing the work
]
if not path-found
[ user-message "Dead-end found. I have to back-track!" ]
end
to expand-depth-limited-search
; expand the search by following longest path
; do not follow the other paths in parallel; just follow one of them
; limit the search depth to max-depth - 1
expand-depth-limited-search-1 (max-depth - 1)
end
to expand-depth-limited-search-1 [maxdepth]
; expand the search by following longest path
; do not follow the other paths in parallel; just follow one of them
; limit the search depth to maxdepth
set path-found false
ask first (sort-by [[time] of ?1 > [time] of ?2] searchers)
[
if (time <= maxdepth)
[ expand-paths (searcher who) ] ; only expand if not exceeded depth limit
die ; this agent has done its work; it's children are now doing the work
]
end
to expand-iterative-deepening-search
; expand the search by iteratively performing depth-limited-search
; to increasingly greater depths
set IDS-depth 1
set person-colour sky
while [ IDS-depth <= max-depth ]
[
while [count searchers != 0]
[ expand-depth-limited-search-1 IDS-depth ]
set IDS-depth IDS-depth + 1
; change colour of person to reflect the increased maxdepth of search
if (person-colour > 5)
[ set person-colour person-colour - 5 ]
create-searchers 1 [ setup-searcher ]
]
set person-colour sky ; restore default colour
end
to expand-greedy-best-first-search
; expand the search using greedy best first search method
set path-found false
ask first (sort-by [[estimated-cost] of ?1 < [estimated-cost] of ?2] searchers)
[
if time >= max-depth
[ user-message "This search is taking too long! Abort!"
stop ]
expand-paths (searcher who)
die ; this agent has done its work; it's children are now doing the work
]
end
to expand-A-star-search
; expand the search using A* search method
set path-found false
ask first (sort-by [[estimated-cost + path-cost] of ?1 < [estimated-cost + path-cost] of ?2] searchers)
[
if time >= max-depth
[ user-message "This search is taking too long! Abort!"
stop ]
expand-paths (searcher who)
die ; this agent has done its work; it's children are now doing the work
]
end
to expand-hill-climbing-search
; expand the search using hill-climbing search method
set path-found false
ask searchers ; there should always be only one searcher at this stage
[ ; look to see where I can go next
if time >= max-depth
[ user-message "This search is taking too long! Abort!"
stop ]
expand-paths (searcher who)
]
foreach but-first (sort-by [[height] of ?1 < [height] of ?2] searchers)
[ ; kill off all but the first
ask ? [ die ]; only let the best of the new searchers continue
]
end
to-report count-active-searchers
report count searchers
end
to-report maximum-active-searchers
let c count searchers
if (c > max-active-searchers)
[ set max-active-searchers c]
report max-active-searchers
end
to-report total-searchers
report searchers-used
end
to-report path-ok [m c r m-in-rowboat c-in-rowboat]
; report whether this search path option is valid and safe to expand
let m1 0
let c1 0
ifelse r = 1 ; i.e. row-boat is on left-hand-side
[ set m1 m - m-in-rowboat
set c1 c - c-in-rowboat ]
;else row-boat is on right-hand-side
[ set m1 m + m-in-rowboat
set c1 c + c-in-rowboat ]
ifelse m1 < 0 or c1 < 0 or m1 > 3 or c1 > 3
[ report false ]
;else
[ report (m1 = 0 or m1 = 3) or ((m1 >= c1) and (3 - m1 >= 3 - c1)) ]
;report true only if missionaries are not outnumbered on both sides of river
end
to-report euclidean-distance [x y z x1 y1 z1]
;; reports the euclidean distance between points (x,y,z) and (x1,y1,z1)
report sqrt ((x1 - x) ^ 2 + (y1 - y) ^ 2 + (z1 - z) ^ 2)
end
to-report manhattan-distance [x y z x1 y1 z1]
;; reports the manhattan distance between points (x,y,z) and (x1,y1,z1)
report abs (x1 - x) + abs (y1 - y) + abs (z1 - z)
end
to-report heuristic-function [mcount ccount rcount]
;; reports the heuristic evaluation function value
if (heuristic = "Zero")
[ report 0 ]
if (heuristic = "People on the left side")
[ report mcount + ccount ]
if (heuristic = "Min. no. boat trips needed")
[ report (mcount + ccount) / 2 ] ; 2 is the capacity of the boat
if (heuristic = "Euclidean distance")
[ report euclidean-distance mcount ccount rcount 0 0 0 ] ; goal is point (0,0,0)
if (heuristic = "Manhattan distance")
[ report manhattan-distance mcount ccount rcount 0 0 0 ] ; goal is point (0,0,0)
end
to-report hill-height [mcount ccount rcount]
;; reports the "height" of the current search position
;; the zero height is the goal; the maximum height is the start
report mcount * 8 + ccount * 2 + rcount
end
to expand-path [searcher-agent m-in-rowboat c-in-rowboat]
; the searcher-agent creates a new searcher-agent that draws a path on the plot from its
; current position to (m,c,r) if it is a valid move
let s 0
let m [m-count] of searcher-agent
let c [c-count] of searcher-agent
let r [r-count] of searcher-agent
if not search-completed and path-ok m c r m-in-rowboat c-in-rowboat
[ ; path is ok, so create it by creating an agent to search it
ifelse r = 1 ; i.e. row-boat is on left-hand-side
[ set m m - m-in-rowboat
set c c - c-in-rowboat ]
;else row-boat is on the right-hand-side
[ set m m + m-in-rowboat
set c c + c-in-rowboat ]
set r ifelse-value (r = 1) [0] [1] ; toggle r
; check to see if the path has already been visited
set s (state-number m c r) ; convert state to a single number to make it easier to search visited-set
if allow-revisited-states? or not member? s visited-states
[
set path-found true
if not allow-revisited-states?
[set visited-states fput s visited-states] ; add to front of visited-states set
hatch-searchers 1 ; clone searcher
[ set searchers-used searchers-used + 1
set xcor [xcor] of searcher-agent
set ycor [ycor] of searcher-agent
set m-count m
set c-count c
set r-count r
set height hill-height m c r
set time [time] of searcher-agent + 1
set path-cost [path-cost] of searcher-agent + 1 ; 1 is the cost of rowing across the river once
set estimated-cost (heuristic-function m c r)
;; type "path cost = " type path-cost type " estimated cost = " print estimated-cost
set color sky set size 8 pen-down
let xpos xcor + 18 ; move to next time step i.e. next axis
let ypos -45 + s; use the state (m,c,r) encoded as an integer
setxy xpos ypos stamp
]
]
if s = 0 ; goal state
[ set search-completed true ]
]
end
to expand-paths [searcher-agent]
;; expands all the possible paths for searcher-agent
expand-path searcher-agent 2 0 ; two missionaries in row-boat
expand-path searcher-agent 0 2 ; two cannibals in row-boat
expand-path searcher-agent 1 1 ; one missionary and one cannibal in row-boat
expand-path searcher-agent 1 0 ; one missionary in row-boat
expand-path searcher-agent 0 1 ; one cannibal in row-boat
end
;
; Copyright 2010 by William John Teahan. All rights reserved.
;
; Permission to use, modify or redistribute this model is hereby granted,
; provided that both of the following requirements are followed:
; a) this copyright notice is included.
; b) this model will not be redistributed for profit without permission
; from William John Teahan.
; Contact William John Teahan for appropriate licenses for redistribution for
; profit.
;
; To refer to this model in publications, please use:
;
; Teahan, W. J. (2010). Missionaries and Cannibals NetLogo model.
; Artificial Intelligence. Ventus Publishing Aps.
;
