[go: up one dir, main page]

Being Kevin Bacon 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: Being-Kevin-Bacon.nlogo

WHAT IS IT?

This model applies various algorithms to the problem of searching for a specific goal node in a network. The problem is that you do not know where the node is, or how the network is configured. To solve this problem, search needs to be employed to try out the different paths that might lead to the goal node. This model implements some multi-agent based solutions where knowledge is shared between agents in a dynamic way. These can be applied to this problem separately to see how they perform against each other.

The model uses "Kevin Bacon" in its title as Kevin Bacon has been chosen as the "centre" of the Hollywood Universe based on the catch-phrase "degrees of separation from Kevin Bacon". This catch-phrase is based on the related mathematical pastime of measuring the degrees of separation of mathematicians from Paul Erdos, a well-known Hungarian mathematician who was prolific at publishing papers. Paul Erdos has been chosen as the focal point for measuring how well connected a person is amongst the mathematical community. The degrees of separation measure is based on whether as person has published a paper with Paul Erdos (which equates to an Erdos number of 1), or if not then published a paper with someone who has published a paper with Paul Erdos (an Erdos number of 2), or if not then published a paper with someone who has published a paper with someone who has published a paper with Paul Erdos (an Erdos number of 3), and so on.

The model allows the user to select either Paul Erdos or Kevin Bacon as the "centre of the universe". Then it can measure the average degree of separation using Dijkstra's algorithm, or it can search for the node associated with these persons using various distributed algorithms for sharing of knowledge between agents, such as word-of-mouth.


WHAT IS ITS PURPOSE?

The purpose of this model is to demonstrate some important concepts in relation to communication amongst agents in a network such as the small world phenomenon, degrees of separation, super-nodes in peer to peer networks and communication via word-of-mouth.


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. The model adopts a purely agent-oriented solution where the information is distributed amongst NetLogo agents. Unlike the traditional search algorithms (such as uninformed searches like Breadth-first Search, and Depth-first Search, or the informed searches such as Greedy Best-first Search and A* that are implemented in the Searching for Kevin Bacon model), these agents conduct the search by sharing knowledge with other agents that are concurrently conducting the search. In the word-of-mouth knowledge sharing method, if agents have found the goal, then they share their knowledge with other agents they meet while returning to their start destination. With the blackboard method, the agents share their knowledge by writing what they know into communal "blackboards" that are found at each node. This can then subsequently be read by agents who visit the node latter on. Two further knowledge sharing methods combine these two approaches - word-of-mouth and blackboard - in slightly different ways to create two new hybrid knowledge sharing schemes.

A breed of walker turtle agents is used for the agents 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 path they have taken. Each searcher agent expands the search by following the outgoing paths that lead out of the current node that were suggested as a result of the method of knowledge sharing they adopt.

A breed of dwalker agents are used to walk the network during the execution of Dijkstra's algorithm. This can be used to calculate the degree of separation for every node in the network from the goal node.


HOW TO USE IT

To initialise the search and create a random network (whose type is specified by the network-type chooser), press the setup-network button in the Interface. To setup a walker agent to search the network, first press the reset-blackboards and reset-walkers buttons to clear blackboards and remove any existing walkers, then press the setup-walkers button to create new ones. Several walker turtle agents (whose number is specified by the new-walkers-per-tick slider) will then be created and randomly located through the network. The setup-walkers button can be pressed multiple times if you wish more walkers to start the search.

To start the simulation, either press the go-walkers once button to make the search proceed one step at a time, or press the go-walkers button to make the search proceed continuously. The walkers will continue walking the network until they find the goal node or they expire without success if they have exceeded the time-to-live slider value. The links that the walker agents traverse across are briefly thickened to show the paths the agents are taking as they proceed in their search.

To plot the shortest path distances and find the average degree of separation from the goal node using Dijkstra's algorithm, press the go-dijkstra button.


THE INTERFACE

The model's Interface buttons are defined as follows:

- setup-network: This will clear the environment and all variables, and create a random network, whose type is specified by the network-type chooser. Its layout is specified by the layout-type chooser. The four sliders no-of-nodes, no-of-super-nodes, links-per-node and links-per-super-node control the number of nodes and super-nodes, and the number of links between them.

- change-layout: If the layout is of type spring, then this may help to remove some of the clutter.

- go-dijkstra: This plots the shortest path distances and find the average degree of separation from the goal node using Dijkstra's algorithm. The goal node is specified using the centre-of-the-universe chooser.

- reset-blackboards: This resets the blackboards that are associated with each node in the network. This is used by the Blackboard knowledge sharing mechanism.

- setup-walkers: This creates new walkers as specified by the number in the slider new-walkers-per-tick. These walkers will then start walking around the network trying to find the goal node.

- go-walkers-once: This will make the simulation proceed one step at a time.

- go-walkers-forever: This will make the simulation proceed continuously until it has reached the goal node or it gets stuck.

- reset-walkers: This kills off all existing searchers and creates new ones to restart the search as specified by the number in the slider new-walkers-per-tick.

- dump-blackboards: This will dump the contents of all the blackboards in the network (this can be used for debugging purposes).

- load-blackboards: This pre-loads the blackboards for each node using the shortest paths found using the Dijkstra algorithm. (This will improve the performance of the Blackboard knowledge sharing mechanism).

The model's Interface choosers, sliders, and switches are defined as follows:

- animate-dijkstra: This will animate the execution of Dijkstra's algorithm (that runs when the go-dijkstra button is pressed).

- revisits-allowed: If set to On, this will allow the agents to revisit already visited nodes.

- centre-of-the-universe: This sets the goal node that the agents are trying to find. It can be set to either Paul Erdos or Kevin Bacon. (These nodes are randomly created when the network is created).

- knowledge-sharing: This specifies the type of knowledge sharing the agents employ while walking around the network. The types of knowledge-sharing is as follows:
"None": The agents do not exchange any knowledge with other agents. As a result, the agents end up randomly walking around the network.
"Word-of-mouth": If the agents have found the goal, then they share their knowledge with other agents they meet while returning to their start destination.
"Blackboard": The agents share their knowledge by writing what they know into communal "blackboards" that are found at each node. This can then subsequently be read by agents who visit the node latter on.
"Combined 1" and "Combined 2": These combine the knowledge of the word-of-mouth and blackboard approaches in slightly different ways to create two new hybrid knowledge sharing schemes. The first scheme checks the blackboard first, then defaults to the word-of-mouth scheme if no knowledge exists in the blackboard. The second scheme does the reverse - it will apply word-of-mouth knowledge first before defaulting to the blackboard knowledge.

- network-type: This selects the type of network that is created when the setup-network button is pressed. The type of networks are as follows:
"P2P-no-super-nodes": This simulates a peer-to-peer network with no super-nodes.
"P2P-has-super-nodes": This simulates a peer-to-peer network with super-nodes.
"P2P-random-single-link": This simulates a peer-to-peer network where each node has only one link with another random node.
"P2P-incremental": This creates the simulated peer-to-peer network by incrementally building the links to other random nodes one at a time.
"P2P-incremental-1": This creates the simulated peer-to-peer network by incrementally building links to other random nodes.
"Star-central-hub": This creates a network with one node (the Kevin Bacon node; i.e. the goal node) as the central hub and all other nodes linked to it and not to any other node.
"Hierarchical": This creates a tree network with the Kevin Bacon goal node at the root.

- layout-type: This specifies how the network should be laid out when it is visualised.

- no-of-nodes: This is the number of nodes to place in the network.

- no-of-super-nodes: This is the number of super-nodes to place in the network. (These are nodes that usually will have significantly more links than standard nodes as specified by links-per-super-node slider).

- links-per-node: This specifies the maximum number of links a standard node will have. The actual number chosen for a particular node will be a random number between 1 and this number.

- links-per-super-node: This specifies the maximum number of links a super-node will have. The actual number chosen for a particular super-node will be a random number between 1 and this number.

- simulation-ticks: This specifies how long the simulation should run for.

- network-update: If non-zero, this will cause the network to be updated at an interval according to a random number from 0 up to the value of this slider.

- nodes-to-add: The number of nodes specified by this slider will be added to the network when the next update occurs according to the network-update slider.

- nodes-to-delete: The number of nodes specified by this slider will be deleted from the network when the next update occurs according to the network-update slider.

- new-walkers-per-tick: This sets the number of walker agents that are added each tick of the simulation.

- time-to-live: This sets how long the walker agents should remain active if they have not found the goal node before being killed off.

- new-walkers-per-node: This sets the number of walkers that are created to continue the search in multiple different directions when each walker visits each node. Setting this number higher than 1 will quickly flood the network in most circumstances.

- percent-in-both: When the network is created, a number of nodes will be created that are connected to the Paul Erdos node (i.e. a path exists that will eventually lead to Paul Erdos), and a number will be created that are connected to Kevin Bacon. This slider controls how many of the nodes will be doubly connected (i.e. paths exist from the node that will lead to both the Paul Erdos and Kevin Bacon nodes).

The model's Interface plots monitors (shown on the right) are defined as follows:

- Number of walkers: This plot shows the number of walkers per tick.

- Successful searches: This plot shows the number of successful searches (i.e. walkers that have found the goal node) per tick.

- Shortest path distances: This plot is plotted when the go-dijkstra button is pressed. It plots the shortest path distances from each node to the goal node.

- #Nodes: This monitor reports the number of nodes in the network. It will change if the network is updated when the network-update, nodes-to-add and nodes-to-delete sliders are all non-zero.

- #Walkers: This monitor reports the number of active walker agents in the network.

- #Visited: This monitor reports the number of nodes visited by the walker agents.

- %Success: This monitor reports the percentage of walker agents that have been successful in reaching the goal node.


THINGS TO NOTICE

Notice how well the agents perform at finding the goal node when the knowledge sharing method is set to None. Compare this with the other knowledge sharing methods.

Notice that the blackboard knowledge sharing mechanism usually outperforms the word of mouth mechanism in terms of the percentage of successful searches.

Notice that pre-loading the blackboards with shortest path information gained from execution of Dijkstra's algorithm can significantly improve the search.

Notice that when there is a learning effect due to the knowledge sharing mechanism, the Successful Searches plot will start to curve upwards rather then continue as a straight line. Why is this? What settings and type of networks does this occur for? Can you get the curve to bend downwards instead of upwards? (Hint: Try changing the value of one or two of the sliders mid-stream).

Notice how the effectiveness of the search deteriorates as the network changes when nodes are either added or deleted. Which type of knowledge sharing method seems to cope best with a dynamic network?

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 how the agents shown when the Dijkstra's algorithm is animated seem to jump all around the network. Why is this?


THINGS TO TRY

Which knowledge sharing behaviour seems to be the best at this problem? Which seems to be the worst? Try out each of the different knowledge sharing behaviours to see which one is the most effective.

Try setting low and high values for the time-to-live slider. How does this affect the percentage of successful searches and the number of nodes visited as a result?

Try pressing the setup-walkers button multiple times. This will start the search with more than one walker.

Try out all the different types of networks with different slider settings. Which types of network causes problems for the various knowledge sharing methods, and which do not? i.e. Which seem to be more difficult to search?

Try switching the knowledge sharing behaviour mid-stream to see if there is any noticeable effect. For example, try switching from a Word-of-Mouth sharing method to a Blackboard sharing method then back again.


EXTENDING THE MODEL

Try adding other knowledge sharing mechanisms, or adding your own variations to the ones implemented in the model.

Try adding your own type of network, either randomly created or with a correspondence to a network in real-life. You will need to add input routines to create the network layout for the latter.

Instead of having the walker agents searching for a single node in the network, try adding resources to each node so that the simulation is similar to the resource discovery problem where a resource (such as an MP3 file) might appear at multiple nodes throughout the network, and each node will have many resources (up to tens of 1000s). You will need to add a resources variable to the nodes-own field where the resources is a table that contains the names of the resources that the node holds locally.

Try turning off the visualisation of the network by using the hide-turtle command to hide all agents and links. This will allow you to run much larger simulations - up to 100,000s of nodes.


NETLOGO FEATURES

Note the use of the hatch and die commands to create new child walkers at each node and terminate all existing walkers. Note also how the next-locations-list parameter for the expand-walkers command determines the knowledge sharing strategy being used that defines the different searches.


RELATED MODELS

See the Searching for Kevin Bacon model.


CREDITS AND REFERENCES

This model was created by William John Teahan.

To refer to this model in publications, please use:

Being Kevin Bacon NetLogo model.
Teahan, W. J. (2010). Artificial Intelligence. Ventus Publishing Aps.


PROCEDURES

; Being Kevin Bacon model.
;
; Demonstrates communication in a network, Dijkstra's algorithm and the small world
; phenomenon.
;
; Copyright 2010 William John Teahan. All Rights Reserved.


extensions [ array table ]

breed [nodes node]
breed [walkers walker]
breed [dwalkers dwalker] ;; for performing the Dijkstra algorithm

nodes-own
[link-type    ;; link type: erdos, bacon and both
 blackboard   ;; table used when the knowledge-sharing of the walkers = "Blackboard"
 net-depth]   ;; used when building some of the networks  

walkers-own
[location     ;; the node where the walker is located
 path         ;; the path taken by the walker
 wom-path     ;; the path suggested by another walker by "word-of-mouth"
 goal         ;; the goal node that the walker wants to reach
 ticks-alive] ;; the number of ticks the walker has been alive

globals
[erdos-set              ;; nodes linked with Paul Erdos node
 bacon-set              ;; nodes linked with Kevin Bacon node
 paul-erdos             ;; node associated with Paul Erdos
 kevin-bacon            ;; node associated with Kevin Bacon
 paul-erdos-no          ;; node number associated with Paul Erdos node
 kevin-bacon-no         ;; node number associated with Kevin Bacon node
 network-update-count   ;; used for controlling when the network gets updated
 dijkstra-distances     ;; shortest path distance from initial node to all other nodes
 dijkstra-directions    ;; which direction to head in for shortest path
 nodes-visited          ;; total number of nodes visited by all walkers
 successful-searches    ;; number of successful searches completed by the walkers
 unsuccessful-searches  ;; number of unsuccessful searches not completed by the walkers
 max-distance ]         ;; a number that exceeds the maximum possible distance in the network

to reset-counts
;; Resets the counts used in some of the plots.
  set nodes-visited 0
  set successful-searches 0
  set unsuccessful-searches 0
end

to create-network [number-of-nodes]
;; Creates number-of-nodes new nodes in the network.
  create-nodes number-of-nodes
  [
    set blackboard table:make ;; used by walkers if applying the blackboard knowledge sharing method
    
    ifelse (random 100 < percent-in-both)
      [ set link-type 0 ;; both erdos and bacon node
        set color red
        set label (word "eb" who "    ") ]
   ;else
      [ ifelse (random 2 = 0)
          [ set link-type 1 ;; erdos node
            set color lime
            set label (word "e" who "    ") ]
          [ set link-type 2 ;; bacon-node
            set color blue
            set label (word "b" who "    ") ]]
  ]

  set erdos-set nodes with [link-type = 1 or link-type = 0] ;; erdos nodes
  set bacon-set nodes with [link-type = 2 or link-type = 0] ;; bacon nodes
end

to setup-network
  clear-all
  set-default-shape nodes "circle 2"
  set-default-shape dwalkers "person"
  ;; create a random network

  reset-counts
  
  set max-distance 9999999 ;; this number must be greater than maximum path length in the network
  
  if (network-update > 0) [ set network-update-count random network-update ]

  create-network no-of-nodes

  set paul-erdos min-one-of erdos-set [ who ]
  set kevin-bacon min-one-of bacon-set [ who ]
  ask paul-erdos [ set color magenta set paul-erdos-no who ]
  ask kevin-bacon [ set color orange set kevin-bacon-no who ]
  
  create-network-links
                
  reset-layout
end

to create-network-links
;; creates the network links for the nodes that have none
;; according to the network-type
  ifelse (network-type = "P2P-no-supernodes")
    [ create-network-P2P-no-super-nodes ]
    [ ifelse (network-type = "P2P-has-super-nodes")
      [ create-network-P2P-has-super-nodes ]
      [ ifelse (network-type = "P2P-random-single-link")
        [ create-network-P2P-random-single-link ]
        [ ifelse (network-type = "P2P-incremental")
          [ create-network-P2P-incremental ]
          [ ifelse (network-type = "P2P-incremental-1")
            [ create-network-P2P-incremental-1 ]
              [ ifelse (network-type = "Star-central-hub")
                [ create-network-star-central-hub ]
                [ create-network-hierarchical]]]]]]
end

to reset-layout
  clear-drawing
  
  layout
  
  ;; leave space around the edges
  ask nodes [ setxy 0.95 * xcor 0.95 * ycor ]
end

to change-layout
  ask walkers [ die ] ;; kill off all current walkers as where their current location may no longer be a correct node position 
  reset-layout
end

to layout
  ifelse layout-type = "spring"
    [ ifelse (network-type = "P2P-no-supernodes" or network-type = "P2P-has-super-nodes")
    ;;incremental" or network-type = "P2P-incremental-1" or network-type = "hierarchical")
      [ repeat 500 [ layout-spring nodes links 0.1 9 5 ]]
      [ repeat 500 [ layout-spring nodes links 0.5 2 1 ]]]

;;else
    [ ifelse layout-type = "circle"
      [ let radius ifelse-value (max-pxcor <= max-pycor) [max-pxcor - 1] [max-pycor - 1]
        layout-circle nodes radius ]
      [ ifelse (centre-of-the-universe = "Paul Erdos")
        [ layout-radial nodes links paul-erdos ]
        [ layout-radial nodes links kevin-bacon ]]
    ]
end

to update-network
;; updates the network by adding or deleting nodes
  if (network-update > 0)
  [
    ifelse (network-update-count > 0)
      [ set network-update-count network-update-count - 1 ] ;; count down to next update
      [ ;; delete some random nodes first
        if (nodes-to-delete > 0) and (nodes-to-delete < count nodes)
          [ ask n-of nodes-to-delete nodes
            [ if self != paul-erdos and self != kevin-bacon ;; don't delete Paul Erdos or Kevin Bacon
              [ ask my-links [ die ]
                ask walkers-here [ die ]
                die ]]]
        ;; now add in new nodes at random
        if (nodes-to-add > 0)
          [ create-network nodes-to-add
            create-network-links ]

        set network-update-count random network-update ;; reset count down to next update
      ]
  ]
end

to create-network-P2P-no-super-nodes
;; creates a P2P (peer-to-peer) layout without "super-nodes"
  let erdos-count count erdos-set
  let bacon-count count bacon-set
  let erdos-links ifelse-value (links-per-node < erdos-count) [links-per-node] [erdos-count]
  let bacon-links ifelse-value (links-per-node < bacon-count) [links-per-node] [bacon-count]

  let n 0
  ask erdos-set
  [ if count my-links = 0
    [ set n random (erdos-links - 1) + 1
      create-links-with n-of n other erdos-set ]];; create at least one link, but not to itself
  ask bacon-set
  [ if count my-links = 0
    [ set n random (bacon-links - 1) + 1
      create-links-with n-of n other bacon-set ]];; create at least one link, but not to itself
end

to create-network-P2P-has-super-nodes
;; creates a P2P (peer-to-peer) layout without "super-nodes"
  let erdos-count count erdos-set
  let bacon-count count bacon-set
  let erdos-links ifelse-value (links-per-node < erdos-count) [links-per-node] [erdos-count]
  let bacon-links ifelse-value (links-per-node < bacon-count) [links-per-node] [bacon-count]
  let erdos-slinks ifelse-value (links-per-super-node < erdos-count - 1) [links-per-super-node] [erdos-count - 1]
  let bacon-slinks ifelse-value (links-per-super-node < bacon-count - 1) [links-per-super-node] [bacon-count - 1]

  let n 0
  ask erdos-set
  [ if count my-links = 0
    [ set n random erdos-links + 1
      create-links-with n-of n other erdos-set ]];; create at least one link, but not to itself
  ask bacon-set
  [ if count my-links = 0
    [ set n random bacon-links + 1
      create-links-with n-of n other bacon-set ]];; create at least one link, but not to itself

  ;; now create super-nodes, making sure paul erdos and kevin bacon are included
  ask paul-erdos 
  [ if count my-links = 0
    [ set n random erdos-slinks + 1 ;; create at least one link
      create-links-with n-of n other erdos-set ]] ;; but not to itself
  ask kevin-bacon
  [ if count my-links = 0
    [ set n random bacon-slinks + 1 ;; create at least one link
      create-links-with n-of n other bacon-set ]] ;; but not to itself
  ask n-of (no-of-super-nodes - 1) erdos-set
  [ if count my-links = 0
    [ set n random erdos-slinks + 1 ;; create at least one link
      create-links-with n-of n other erdos-set ]] ;; but not to itself
  ask n-of (no-of-super-nodes - 1) bacon-set
  [ if count my-links = 0
    [ set n random bacon-slinks + 1 ;; create at least one link
      create-links-with n-of n other bacon-set ]] ;; but not to itself
end

to create-network-P2P-random-single-link
  ask erdos-set
      [ if count my-links = 0
        [ create-link-with one-of other erdos-set ]]
  ask bacon-set
      [ if count my-links = 0
        [ create-link-with one-of other bacon-set ]]
end

to create-network-P2P-incremental
  let erdos-set1 (list paul-erdos)
  let bacon-set1 (list kevin-bacon)
  
  foreach but-first sort erdos-set
  [ ask ?
      [ if count my-links = 0
        [ create-link-with one-of erdos-set1
          set erdos-set1 fput ? erdos-set1 ]]]
  foreach but-first sort bacon-set
  [ ask ?
      [ if count my-links = 0
        [ create-link-with one-of bacon-set1
          set bacon-set1 fput ? bacon-set1 ]]]
end

to create-network-P2P-incremental-1
  ask nodes [ set net-depth 0 ]

  ask paul-erdos
      [ create-link-with one-of other erdos-set
        ask link-neighbors [set net-depth 1 ]]
  ask erdos-set
      [ if (net-depth = 0) and (count my-links = 0)
        [
          create-link-with one-of erdos-set with [net-depth = 1]  
          set net-depth 1
        ]
      ]

  ask kevin-bacon
      [ create-link-with one-of other bacon-set
        ask link-neighbors [set net-depth 1 ]]
  ask bacon-set
      [ if (net-depth = 0) and (count my-links = 0)
        [
          create-link-with one-of bacon-set with [net-depth = 1]  
          set net-depth 1
        ]
      ]
end

to create-network-star-central-hub
  let erdos-count-1 count erdos-set - 1
  let bacon-count-1 count bacon-set - 1
  ask paul-erdos
      [ if count my-links = 0
        [ create-links-with n-of erdos-count-1 other erdos-set ]]
  ask kevin-bacon
      [ if count my-links = 0
        [ create-links-with n-of bacon-count-1 other bacon-set ]]
end

to create-network-hierarchical
  ask nodes [ set net-depth 0 ]

  ask paul-erdos ;; root of Paul Erdos sub-network
    [ set net-depth 1
      ifelse (count other erdos-set < links-per-node)
        [ create-links-with other erdos-set ]
        [ create-links-with n-of links-per-node other erdos-set ]
      ask link-neighbors [ set net-depth 2 ]]
  let depth 2
  let this-links 0
  let this-count 0
  while [count erdos-set with [net-depth = 0] > 0] ;; while still more nodes to place in network
      [ ask erdos-set with [net-depth = depth]
          [ set this-count count erdos-set with [net-depth = 0]
            set this-links ifelse-value (links-per-node < this-count) [links-per-node] [this-count]
            create-links-with n-of this-links erdos-set with [net-depth = 0]
            ask link-neighbors [ set net-depth depth + 1 ]]
        set depth depth + 1]
             
  ask nodes [ set net-depth 0 ]
  ask kevin-bacon ;; root of Kevin-Bacon sub-network
      [ set net-depth 1
        ifelse (count other bacon-set < links-per-node)
          [ create-links-with other bacon-set ]
          [ create-links-with n-of links-per-node other bacon-set ]
        ask link-neighbors [ set net-depth 2 ]]
  set depth 2
  set this-links 0
  set this-count 0
  while [count bacon-set with [net-depth = 0] > 0] ;; while still more nodes to place in network
      [ ask bacon-set with [net-depth = depth]
          [ set this-count count bacon-set with [net-depth = 0]
            set this-links ifelse-value (links-per-node < this-count) [links-per-node] [this-count]
            create-links-with n-of this-links bacon-set with [net-depth = 0]
            ask link-neighbors [ set net-depth depth + 1 ]]
        set depth depth + 1]

end

to reset-blackboards
;; resets the blackboards at each node
  ask nodes [ table:clear blackboard ]
end

to dump-blackboards
;; dumps out the blackboards at each node
  ask nodes [ type "Blackboard at " print self print table:to-list blackboard ]
end

to setup-walkers
;; creates some new walkers
  create-walkers new-walkers-per-tick
  [
    set color red
    set goal one-of nodes
    set location one-of nodes
    move-to location
    set path (list location)
    set wom-path []
    set ticks-alive 0
  ]
end

to reset-walkers
;; resets the walkers by first killing off all existing ones, then
;; creating new ones, and resetting related counts and plots
  ask walkers [ die ] ;; kill off existing walkers
  ask links [ set thickness 0 ] ;; reset all network links to uncrossed
  reset-counts
  setup-walkers
  set-current-plot "Number of walkers"
  clear-plot
  set-current-plot "Successful searches"
  clear-plot
  reset-ticks
end

to expand-walkers [new-locations-list]
  let new-walkers-count length new-locations-list
  let new-location 0

  hatch-walkers new-walkers-count
  [ ;; create a new walker at location to continue the search
    ;; and move to new location
    set ticks-alive ticks-alive + 1
    ; print ticks-alive
    set new-location first new-locations-list ;; pop off the next location to visit
    set new-locations-list but-first new-locations-list
    ifelse (not revisits-allowed and member? new-location path)
    [ die ] ;; don't revisit nodes already visited unless allowed to
    [
      if (new-location != nobody) and ([link-with new-location] of location != nobody)
      [  
        ask [link-with new-location] of location [ set thickness 0.4 ] ;; highlight link I am crossing
        face new-location  ;; not strictly necessary, but improves the visuals a bit
        move-to new-location
        set location new-location
        set path fput new-location path
        if new-location = goal ;; success - no need to hang around any longer
        [ set successful-searches successful-searches + 1
          update-along-path self "S" ;; update information along path if necessary; "S" means success
          die ] ;; die happy
      ]
    ]
  ]
end

to go-walkers
;; make the walkers continue the search
  if (simulation-ticks > 0) and (ticks > simulation-ticks)
    [ stop ]

  update-network

  setup-walkers
  ask links [ set thickness 0 ]
  ask walkers
  [
    set nodes-visited nodes-visited + 1
    ifelse (ticks-alive >= time-to-live) ;; lived too long without success - kill it off
    [ set unsuccessful-searches unsuccessful-searches + 1
      update-along-path self "F";; update information along path if necessary; "F" means failure
    ]
    [ ;; create new walkers to move to new location(s)
      expand-walkers (next-locations location)
    ]
    die ;; always die - the minions have already been sent out to continue the search for me
  ]

  plot-walkers
  tick
end

to-report next-locations [this-location]
;; find out the new locations to search for this-location
;; apply the search method using the knowledge sharing method or none e.g. choose randomly

  if (knowledge-sharing = "Word of mouth")
    [ ;; Word of mouth knowledge sharing method
      report next-locations-word-of-mouth this-location
    ]

  if (knowledge-sharing = "Blackboard")
    [ ;; Blackboard knowledge sharing method
      report next-locations-blackboard this-location
    ]

  if (knowledge-sharing = "Combined 1")
    [ ;; Blackboard knowledge sharing method
      report next-locations-combined-1 this-location
    ]
 
   if (knowledge-sharing = "Combined 2")
    [ ;; Blackboard knowledge sharing method
      report next-locations-combined-2 this-location
    ]

  ;; Default method is No knowledge sharing; i.e. knowledge-sharing = "None"
  report next-locations-no-knowledge-sharing this-location
end

to-report next-locations-no-knowledge-sharing [this-location]
;; returns the new locations to search for this location
;; uses random choices; no knowledge sharing
  let new-locations [link-neighbors] of this-location
  if count new-locations > new-walkers-per-node
    [ set new-locations n-of new-walkers-per-node new-locations ]

  report sort new-locations
end

to-report next-locations-word-of-mouth [this-location]
;; returns the new locations to search for this location
;; using the word-of-mouth knowledge sharing method
  if empty? wom-path
    [ ask walkers-here
      [ ;; does any of them know where the goal is?
        if (this-location = first path) and (member? [goal] of myself path)
          [ ask myself
            [ set wom-path but-first path ]
            ;; type "asked " type who type " goal = " type [goal] of myself type " this-loc = " type this-location type " path = " print path
            stop ]
      ]
    ]
  if not empty? wom-path
    [ ;; we have been told where to go
      ;; head back along the word of mouth path
      let this-loc first wom-path
      set wom-path but-first wom-path
      ;; type "found goal " type goal type " at " type this-loc type " in path " print wom-path
      report (list this-loc)
    ]

  ;; no word of mouth path; default to random choices (i.e. same as no knowledge sharing)
  report next-locations-no-knowledge-sharing this-location
end

to-report next-locations-blackboard [this-location]
;; returns the new locations to search for this location
;; using the blackboard knowledge sharing method

  ;;type "Blackboard for location " type this-location type " = " print [blackboard] of this-location

  let dist 0
  let this-path nobody
  let key-dist (word ([goal] of self) " dist")
  let key-path (word ([goal] of self) " path")

  ifelse (not table:has-key? [blackboard] of this-location key-path)
    [ set dist max-distance ] ;; no blackboard path
    [ set dist table:get [blackboard] of this-location key-dist
      set this-path table:get [blackboard] of this-location key-path ]

  ifelse (dist >= max-distance)
    [ ;; no blackboard path; default to random choices (i.e. same as no knowledge sharing)
      report next-locations-no-knowledge-sharing this-location ]
    [ ;; follow the blackboard path that has been found to be shortest
      report (list this-path) ]
  
end

to-report next-locations-combined-1 [this-location]
;; returns the new locations to search for this location
;; using the combined-1 knowledge sharing method
  let dist 0
  let this-path nobody
  let key-dist (word ([goal] of self) " dist")
  let key-path (word ([goal] of self) " path")

  ifelse (not table:has-key? [blackboard] of this-location key-path)
    [ set dist max-distance ] ;; no blackboard path
    [ set dist table:get [blackboard] of this-location key-dist
      set this-path table:get [blackboard] of this-location key-path ]

  ifelse (dist >= max-distance)
    [ ;; no blackboard path; default to word-of-mouth
      report next-locations-word-of-mouth this-location ]
    [ ;; follow the blackboard path that has been found to be shortest
      report (list this-path) ]
end

to-report next-locations-combined-2 [this-location]
;; returns the new locations to search for this location
;; using the combined-2 knowledge sharing method
  if empty? wom-path
    [ ask walkers-here
      [ ;; does any of them know where the goal is?
        if (this-location = first path) and (member? [goal] of myself path)
          [ ask myself
            [ set wom-path but-first path ]
            ;; type "asked " type who type " goal = " type [goal] of myself type " this-loc = " type this-location type " path = " print path
            stop ]
      ]
    ]
  if not empty? wom-path
    [ ;; we have been told where to go
      ;; head back along the word of mouth path
      let this-loc first wom-path
      set wom-path but-first wom-path
      ;; type "found goal " type goal type " at " type this-loc type " in path " print wom-path
      report (list this-loc)
    ]

  ;; no word of mouth path; default to blackboard
  report next-locations-blackboard this-location
end

to update-along-path [this-walker value]
;; updates information along the path when the knowledge sharing method
;; is "Blackboard" or combined

  let p 0
  let val 0
  let val1 0
  let key-dist ""
  let key-path ""
  
  if knowledge-sharing = "Blackboard" or
     knowledge-sharing = "Combined 1" or
     knowledge-sharing = "Combined 2"
  [
    ask this-walker
    [
      ;;type "goal = " type goal type " path = " show path
      let this-goal goal
      let prev first path
      foreach but-first path
      [
        set key-dist (word this-goal " dist")
        set key-path (word this-goal " path")

        ifelse (value = "F")
          [ set val max-distance ] ;; failure - node was not found
          [ set val p + 1 ] ;; calculate the position in the path
        ;;type "putting into blackboard for node " type ? type " : " type key-dist type " val: " print val
        
        if is-turtle? ? ;; does the node exist? (it may have been deleted)
        [
          ask ? ;; update blackboards along the path if path to goal is shorter
          [ ifelse (not table:has-key? blackboard key-dist)
              [ set val1 max-distance + 1 ] ;; no entry exists - make sure new entry is added
              [ set val1 table:get blackboard key-dist ]
            if (val < val1)
              [ table:put blackboard key-dist val
                table:put blackboard key-path prev ]]
        ]
        set prev ?
        set p p + 1
      ]
    ]
  ]
end

to plot-walkers
;; Reports results from data gathered by the walkers.

  set-current-plot "Successful searches"
  ;;let total successful-searches + unsuccessful-searches
  ;;let s ifelse-value (total = 0) [0] [100 * successful-searches / total]
  ;;plot s
  plotxy successful-searches + unsuccessful-searches successful-searches
    
  set-current-plot "Number of walkers"
  plot count walkers
end

to-report report-nodes
  report count nodes
end

to-report report-walkers
  report count walkers
end

to-report report-nodes-visited
  report nodes-visited
end

to-report report-success-rate
  report 100.0 * successful-searches / (successful-searches + unsuccessful-searches)
end 

to initialise-distances [initial-node]
;; initialises the shortest path distances between nodes in the network to 0
  let number-of-nodes max [who] of nodes + 1

  set dijkstra-distances array:from-list n-values number-of-nodes [number-of-nodes + 1] ;; "infinity"
  set dijkstra-directions array:from-list n-values number-of-nodes [nobody]
  array:set dijkstra-distances initial-node 0 ;; set distance to 0 for initial node
end

to perform-dijkstra [initial-node nodes-to-visit]
;; calculates the distance array for the Dijkstra algorithm using the initial node
;; as the focal point

  set nodes-visited 0
  
  initialise-distances [who] of initial-node
  
  let visited-set [] ;; this is the list of nodes that have been visited
  let unvisited-set nodes-to-visit ;; this is the list of nodes yet to have been visited

  let this-dwalker nobody
  if (animate-dijkstra)
    [ create-dwalkers 1 [ set color white set size 2 set this-dwalker who ]]
      
  let current-node initial-node
  while [count unvisited-set > 0]
  [
    if (animate-dijkstra)
      [ ask dwalker this-dwalker [ setxy [xcor] of current-node [ycor] of current-node display wait 0.1 ]]
   
    ask current-node
    [
      set nodes-visited nodes-visited + 1
      set visited-set fput who visited-set
      set unvisited-set other unvisited-set

      ask link-neighbors
      [
        let dist-thru-here (array:item dijkstra-distances [who] of current-node) + 1
        let dist-thru-to-there array:item dijkstra-distances who
        if (dist-thru-here < dist-thru-to-there)
          [ array:set dijkstra-distances who dist-thru-here
            array:set dijkstra-directions who [who] of current-node ]
      ]
      
      ;; set the current-node to the remaining unvisited node that has the smallest distance to the initial node      
      set current-node min-one-of unvisited-set [array:item dijkstra-distances who]
    ]
  ]
  
  ;; print array:to-list dijkstra-distances

  if (animate-dijkstra)
    [ wait 0.2 ask dwalker this-dwalker [ die ]]
end

to go-dijkstra
;; Run directly by the interface button - calculates the distance array for the Dijkstra algorithm

  let initial-node nobody
  ifelse (centre-of-the-universe = "Paul Erdos")
    [ set initial-node paul-erdos ]  ;; set the initial node to the node associated with Paul Erdos
    [ set initial-node kevin-bacon ] ;; set the initial node to the node associated with Kevin Bacon
  
  initialise-distances [who] of initial-node
  
  let nodes-to-visit [] ;; this is the list of nodes yet to have been visited
  ifelse (centre-of-the-universe = "Paul Erdos")
    [ set nodes-to-visit erdos-set ]
    [ set nodes-to-visit bacon-set ]

  perform-dijkstra initial-node nodes-to-visit

  dump-dijkstra-directions
      
  plot-dijkstra
end

to dump-dijkstra-directions
  let i 0
  foreach array:to-list dijkstra-directions
  [
    if (? != nobody) [ type " from: " type i type " thru: " type ? type " dist: " print array:item dijkstra-distances i ]
    set i i + 1
  ]
end

to plot-dijkstra
;; Reports results from data gathered by execution of the dijkstra
;; algorithm.
  set-current-plot "Shortest path distances"
  clear-plot
  let number-of-nodes report-nodes
  let d array:to-list dijkstra-distances
  let dmin number-of-nodes + 1
  let dmax 0
  let cnt 0
  let avg-total 0
  foreach d
  [ ;; find min and max of x and y ranges for the plot first
    ;; also calculate cumulative total for average
    if (? <= number-of-nodes)
      [
        set cnt cnt + 1
        set avg-total ? + avg-total
        if (? > dmax) [ set dmax ? ]
        if (? < dmin) [ set dmin ? ]
      ]
  ]
  set-plot-x-range 0 cnt
  set-plot-y-range dmin dmax
  foreach d
  [ ;; now plot it
    if (? <= number-of-nodes)
      [ plot ? ]
  ]
  user-message (word "The average path length to " centre-of-the-universe " is " (avg-total / cnt))
end

to load-blackboards-using-shortest-paths
;; pre-loads the blackboards for each node using the shortest paths
;; found using the Dijkstra algorithm

  let i 0
  let this-node nobody
  let key-dist ""
  let key-path ""
  
  foreach sort nodes
  [
    set this-node ?
    perform-dijkstra this-node nodes

    set i 0
    foreach array:to-list dijkstra-directions
    [
      if (? != nobody)
      [ ;; type "goal: " type this-node type " from: " type i type " thru: " type ? type " dist: " print array:item dijkstra-distances i

        set key-dist (word this-node " dist")
        set key-path (word this-node " path")
        table:put [blackboard] of node i key-dist (array:item dijkstra-distances i)
        table:put [blackboard] of node i key-path node ?
        ask node i
        [ 
        ]
      ]
      set i i + 1
    ]
  ] 
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).  Being Kevin Bacon NetLogo model.
;   Artificial Intelligence. Ventus Publishing Aps.
;