Knowledge Representation 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: Knowledge-Representation.nlogo

WHAT IS IT?

This model shows how the standard types of knowledge representation such as logic, frames, rules and decision trees can be implemented in NetLogo. The model also shows how to perform reasoning using the different types of knowledge representation. A new type of knowledge representation, called event maps, is also implemented. Event maps are related to the concept of Event Stream Processing (ESP) where events occur concurrently on separate streams.

The knowledge for three toy problems is implemented in the model. These three problems are:
- Zoo animals: Identify the name of a zoo animal from observations.
- New Zealand birds: Guess the name of a New Zealand bird.
- Sailing boats: Identify the type of boat that is sailing past in a tall ships parade.


WHAT IS ITS PURPOSE?

The purpose of this model is to show how to represent knowledge using the traditional forms of knowledge representation such as first order logic, frames, rules and decision trees. A secondary purpose is to show how these different forms of knowledge representation can be re-cast and visualised within a new type knowledge of representation called event maps. The event maps representation also facilitates the visualisation of the reasoning process as well.


HOW IT WORKS

The different forms of knowledge are represented using state turtle agents and path link agents that are used to describe a transition path between the states. State agents own three variables:
- depth: the depth in the event tree (where the root of the tree is at depth 0, a child of the root state is at depth 1, and so on);
- stream: This is the name of the stream with which the event is associated;
- event: This is the event itself. Events can be sensory events, motor events, or abstract events (the latter is sensory, motor and abstract events that when they occur together cause this event to occur simultaneously as well).

The states and paths form a finite state network. Walker turtle agents are used to "walk" the event map network during the reasoning process. The paths (i.e. transitions) are not labelled; they just indicate the order with which events occur. The network of states and paths can be thought analogously of as a map of events (hence why it is called an event map). The event map may or may not be a faithful representation of what is really happening in the environment i.e. in this model, the event map may or may not be a faithful representation of the knowledge that it is trying to represent.

Event maps can be used to implement all the standard types of knowledge representation as shown by this model and also the associated reasoning processes such as forward and backward reasoning for rules, and frame-based reasoning using frame-matching methods. They also provide a useful way of visualising the different types of knowledge representation, and of animating the reasoning processes.


HOW TO USE IT

First pick a type of knowledge representation using the KR-type chooser in the Interface. Then pick a problem using the select-problem chooser. To load the knowledge related to the problem, press the setup-knowledge button. This will display the knowledge in the large black environment box on the left. If the visualisation is a bit cluttered at first and the layout-type has been set to spring, press the change-layout button while playing around with the spring-length, spring-constant and repulsion-constant sliders. Then make sure the reasoning process is reset to the initial state by pressing the rest-reasoning button. Finally, to perform the reasoning process, press the go-reason button.


THE INTERFACE

The model's Interface buttons are defined as follows:

- setup-knowledge: This loads the knowledge for the problem as specified by the select-problem chooser using the knowledge representation specified by the KR-type chooser. The knowledge is visualised in the environment using an event map finite state automata that depicts the relationships between knowledge states.

- dump-knowledge: This dumps into the Output box the knowledge that was loaded by the setup-knowledge button.

- reset-reasoning: This resets the reasoning to start over again from the start state. This state is determined by the type of reasoning being performed i.e. for backward reasoning using rules, the start state is the goal state; for forward reasoning, the start state is the state associated with the first rule in the rules database.

- go-reasoning: This starts the reasoning process. If rules-based reasoning is being used, the user will be asked whether they wish to perform forward or backward reasoning.

- clear-output: This clears the Output box.

- change-layout: This changes the layout if the layout-type chooser is set to spring. This can be useful to remove some of the clutter. In order to change the layout configuration, the user can alter the spring-length, spring-constant and repulsion-constant sliders while the change-layout button is pressed.

- move-layout: Sometimes the labels of states in the layout appear off the edge of the environment. This button allows the layout to be moved left, right, up or down by a user-specified distance.

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

- KR-type: This selects the type of knowledge representation e.g. rules, frames, decision trees and semantic networks.

- select-problem: This selects the type of problem:
"Zoo Animals": Identify the name of a zoo animal from observations.
"New Zealand birds": Guess the name of a New Zealand bird.
"Sailing boats": Identify the type of boat that is sailing past in a tall ships parade.

- layout-type: This selects the type of layout to be used for visualising the states and paths in the event map.

- spring-length, spring-constant, repulsion-constant: This can be used to modify the layout if the layout-type is spring.

- trace-on?: If set to On, this will write output into the Output box that traces the reasoning process.

- Output box: This is used for writing the output of the reasoning process and other information into.


THINGS TO NOTICE

Notice which of the types of knowledge representation make the reasoning process more difficult to understand when they are visualised using the event map format in the environment (for example, the walker agents for semantic networks seem to jump around a lot).

Notice which of the types of knowledge representation produce more complicated event maps for the different problems.


THINGS TO TRY

Try the different types of knowledge representation for the three types of problems. Try to see how they relate to each other and how they differ. Find out what happens during the reasoning process. Which are easy to follow, and which are difficult?

Try finding out how the knowledge for the different types of knowledge representation is initialised. (Hint: look at the setup-knowledge command). Find out how rules are defined, and then how they are visualised as event maps. Note that in the model, rules and semantic networks are defined explicitly, whereas frames and decision trees are setup by converting from rules. (How?) Have a go at explicitly defining the knowledge for frames and decision trees instead rather than use a conversion process.


EXTENDING THE MODEL

Try adding other types of knowledge representation, perhaps some that are hybrids of the ones implemented in the model. For example, the semantic event network type that comes with the model is a hybrid of the semantic networks and event map types.

Investigate alternative methods for visualising the reasoning process for each type of knowledge representations.


NETLOGO FEATURES

Note the use of the hatch command to create walker agents to continue the walking of the event map network during the reasoning process. How the walker agents walk the network determines the type of reasoning.


RELATED MODELS

For other examples of the use of event maps for event stream processing, see the Language Modelling, Central Park Events and Wall Following Events NetLogo models.


CREDITS AND REFERENCES

This model was created by William John Teahan.

To refer to this model in publications, please use:

Knowledge Representation NetLogo model.
Teahan, W. J. (2010). Artificial Intelligence. Ventus Publishing Aps.


PROCEDURES

; Knowledge Representation model.
; 
; Demonstrates different types of knowledge representation.
;
; Copyright 2010 William John Teahan. All Rights Reserved.

extensions [ table ]

breed [rules rule]
breed [frames frame]

breed [problems problem]

breed [states state]
breed [walkers walker]
directed-link-breed [paths path]

rules-own
[
  antecedents ; if part of rule - list of necessary propositions for rule to be true
  consequents ; then part of rule - list of propositions that are the conclusions of the rule 
]

problems-own
[
  non-input-attributes ; list of attributes for which user will not be asked questions about
  input-attributes     ; list of attributes for which the user will be asked questions about
  input-choices        ; table of choices for input attributes
  input-questions      ; table of choices for input attributes
  goal-attribute       ; the attribute used to determine when goal has been reached
]

frames-own
[ name        ; the name of the frame
  ako-list    ; list of frames which this frame is an instance of; ako means "a kind of"
  slots-table ; table of slots and values associated with them
]

states-own 
[
  depth     ; depth in the tree
  stream    ; the name of the stream of sensory or motor events
  event     ; the sensory or motor event
] 
walkers-own
[
  location  ; the location of a state the walker is currently visiting
  stream    ; the stream associated with the node currently being traversed
  event     ; the event associated with the node currently being traversed
]

globals
[
  animals-problem ; Problem concerning Zoo animals
  birds-problem   ; Problem concerning New Zealand birds
  boats-problem   ; Problem concerning sailing boats 
  facts-base      ; List of currently active facts
  fired-rules     ; List of rules that have been fired
  cycle-count     ; Number of cycle
  active-walker   ; Last active walker
  goal-state      ; Used by go-semantic-network-reasoning procedure
]

to setup
  ca ;; clear everything

  set-default-shape states "circle 2"

  set facts-base table:make
  set fired-rules []
  set cycle-count 0
  set active-walker nobody
  set goal-state nobody
end  

to setup-rule [alist clist]
;; Creates a new rule with the specified antecedents (alist)
;; and consequents (clist)

  create-rules 1
  [ hide-turtle ; make invisible
    set antecedents alist
    set consequents clist ]
end

to setup-input-attributes [problem]
;; Initialises the input attributes for the rules base
  ask problem
  [
    set input-attributes []
    set input-choices table:make
    set input-questions table:make
  ]
end

to add-input-attribute [problem attribute choices question]
;; Adds information about the input attribute to the rules base
  ask problem
  [
    set input-attributes fput attribute input-attributes
    table:put input-choices attribute choices
    table:put input-questions attribute question
  ]
end

to-report get-input-question [problem attribute value]
;; Reports the attribute's input question

  ifelse (member? attribute [input-attributes] of problem)
    [ report table:get [input-questions] of problem attribute ]
    [ report (word "Is " attribute " " value "? ") ] ; construct question from attribute and value
end

to-report get-input-choices [problem attribute]
;; Reports the choices to the input question

  ifelse (member? attribute [input-attributes] of problem)
    [ report table:get [input-choices] of problem attribute ]
    [ report ["Yes" "No"] ] ; default choices
end

to setup-problems
;; Sets up the problems.

  ; Zoo Animals problem
  create-problems 1
  [ hide-turtle ; make invisible
    set animals-problem self
    set non-input-attributes ["animal" "species" "species type" "mammal group"]
    setup-input-attributes self
    add-input-attribute self "has hair" ["Yes" "No"] "Does the animal have hair?"
    add-input-attribute self "gives milk" ["Yes" "No"] "Does the animal give milk?"
    add-input-attribute self "eats meat" ["Yes" "No"] "Does the animal eat meat?"
    add-input-attribute self "has pointed teeth" ["Yes" "No"] "Does the animal have pointed teeth?"
    add-input-attribute self "has claws" ["Yes" "No"] "Does the animal have claws?"
    add-input-attribute self "has forward eyes" ["Yes" "No" ] "Does the animal have forward facing eyes?"
    add-input-attribute self "has hooves" ["Yes" "No" ] "Does the animal have hooves?"
    add-input-attribute self "chews cud" ["Yes" "No" ] "Does the animal chew cud?"
    add-input-attribute self "has tawny colour" ["Yes" "No" ] "Does the animal have a tawny colour?"
    add-input-attribute self "has dark spots" ["Yes" "No"] "Does the animal have dark spots?"
    add-input-attribute self "has long neck" ["Yes" "No"] "Does the animal have a long neck?"
    add-input-attribute self "has black stripes" ["Yes" "No"] "Does the animal have black stripes?"

    set goal-attribute "animal"
  ]

  ; New Zealand birds problem
  create-problems 1
  [ hide-turtle ; make invisible
    set birds-problem self
    set non-input-attributes ["bird"]
    setup-input-attributes self
    add-input-attribute self "can fly" ["Yes" "No"] "Can the bird fly?"
    add-input-attribute self "parrot" ["Yes" "No"] "Is the bird a parrot?"
    add-input-attribute self "alpine" ["Yes" "No"] "Is the bird an apline bird?"
    add-input-attribute self "has white throat" ["Yes" "No"] "Does the bird have a white throat?"
    add-input-attribute self "large" ["Yes" "No"] "Is the bird large?"
    add-input-attribute self "extinct" ["Yes" "No"] "Is the bird extinct?"
    add-input-attribute self "has long beak" ["Yes" "No"]  "Does the bird have a long beak?"

    set goal-attribute "bird"
  ]

  ; Sailing boats problem
  create-problems 1
  [ hide-turtle ; make invisible
    set boats-problem self
    set non-input-attributes ["boat"]
    setup-input-attributes self
    add-input-attribute self "number of masts" ["one" "two"]
      "How many masts are there?"
    add-input-attribute self "shape of mainsail" ["quadrilateral" "triangular" "triangular with two foresails"]
      "What is the shape of the mainsail?"
    add-input-attribute self "main mast position" ["forward of the short mast" "aft the short mast"]
      "What is the main mast position?"
    add-input-attribute self "the short mast position" ["forward of the helm" "aft the helm"]
      "What is the short mast position?"

    set goal-attribute "boat"
  ]
end

to init-problem
;; initialises the selected problem

  setup ; clear everything
  setup-problems

  if (select-problem = "Zoo animals")
    [
      setup-rule ;; rule: IF animal has hair
                 ;;       THEN species is mammal
        [["has hair" "Yes"]]
        [["species" "mammal"]]
      setup-rule ;; rule: IF animal gives milk
                 ;;       THEN species is mammal
        [["gives milk" "Yes"]]
        [["species" "mammal"]]
      setup-rule ;; rule: IF animal eats meat
                 ;;       THEN species type is carnivore
        [["eats meat" "Yes"]]
        [["species type" "carnivore"]]
      setup-rule ;; rule: IF animal has pointed teeth AND animal has claws
                 ;;       AND animal has forward eyes
                 ;;       THEN species type is carnivore
        [["has pointed teeth" "Yes"] ["has claws" "Yes"] ["has forward eyes" "Yes"]]
        [["species type" "carnivore"]]
      setup-rule ;; rule: IF animal is mammal AND animal has hooves
                 ;;       THEN mammal group is ungulate
        [["species" "mammal"] ["has hooves" "Yes"]]
        [["species type" "ungulate"]]
      setup-rule ;; rule: IF species is mammal AND animal chews cud
                 ;;       THEN mammal group is ungulate
        [["species" "mammal"] ["chews cud" "Yes"]]
        [["species type" "ungulate"]]
      setup-rule ;; rule: IF species is mammal AND species type is carnivore
                 ;;       AND animal has tawny colour AND animal has dark spots
                 ;;       THEN animal is cheetah
        [["species" "mammal"] ["species type" "carnivore"] ["has tawny colour" "Yes"]
         ["has dark spots" "Yes"]]
        [["animal" "cheetah"]]
      setup-rule ;; rule: IF species is mammal AND species type is carnivore
                 ;;       AND animal has tawny colour AND animal has black stripes
                 ;;       THEN animal is tiger
        [["species" "mammal"] ["species type" "carnivore"] ["has tawny colour" "Yes"]
         ["has black stripes" "Yes"]]
        [["animal" "tiger"]]
      setup-rule ;; rule: IF species is ungulate AND animal has dark spots
                 ;;       AND animal has long neck
                 ;;       THEN animal is giraffe
        [["species type" "ungulate"] ["has dark spots" "Yes"] ["has long neck" "Yes"]]
        [["animal" "giraffe"]]
      setup-rule ;; rule: IF species is ungulate AND animal has black stripes
                 ;;       THEN animal is zebra
        [["species type" "ungulate"] ["has black stripes" "Yes"]]
        [["animal" "zebra"]]
    ]
 
  if (select-problem = "New Zealand birds")
    [ 
      setup-rule ;; rule: IF bird can fly AND bird is a parrot AND bird is alpine
                 ;;       THEN bird is a kea
        [["can fly" "Yes"] ["parrot" "Yes"] ["alpine" "Yes"]]
        [["bird" "Kea" ]]
      setup-rule ;; rule: IF bird can fly AND bird is a parrot AND bird is not alpine
                 ;;       THEN bird is a kaka
        [["can fly" "Yes"] ["parrot" "Yes"] ["alpine" "No"]]
        [["bird" "Kaka"]]
      setup-rule ;; rule: IF bird can fly AND bird is not a parrot AND bird has white throat
                 ;;       THEN bird is a tui
        [["can fly" "Yes"] ["parrot" "No"] ["has white throat" "Yes"]]
        [["bird" "Tui"]]
      setup-rule ;; rule: IF bird can fly AND bird is not a parrot AND bird does not have a white throat
                 ;;       THEN bird is a pukeko
        [["can fly" "Yes"] ["parrot" "No"] ["has white throat" "No"]]
        [["bird" "Pukeko"]]
      setup-rule ;; rule: IF bird cannot fly AND bird is an extinct bird AND bird is large
                 ;;       THEN bird is a moa 
        [["can fly" "No"] ["extinct" "Yes"] ["large" "Yes"]]
        [["bird" "Moa"]]
      setup-rule ;; rule: IF bird cannot fly AND bird is extinct AND bird is not large
                 ;;       THEN bird is a huia 
        [["can fly" "No"] ["extinct" "Yes"] ["large" "No"]]
        [["bird" "Huia"]]
      setup-rule ;; rule: IF bird cannot fly AND bird is not extinct AND bird has long beak
                 ;;       THEN bird is a kiwi  
        [["can fly" "No"] ["extinct" "No"] ["has long beak" "Yes"]]
        [["bird" "Kiwi"]]
      setup-rule ;; rule: IF bird cannot fly AND bird is not extinct AND bird does not have a long beak
                 ;;       THEN bird is a weta
        [["can fly" "No"] ["extinct" "No"] ["has long beak" "No"]]
        [["bird" "Weta"]]
    ]

  if (select-problem = "Sailing boats")
    [ ;; rules taken from Negnevitsky, page 313.
      setup-rule ;; rule: IF 'number of masts' is one
                 ;;       AND 'shape of mainsail' is triangular
                 ;;       THEN boat is 'Jib-headed Cutter'
         [["number of masts" "one"] ["shape of mainsail" "triangular"]]
         [["boat" "jib-headed cutter"]]
      setup-rule ;; rule: IF 'number of masts' is one
                 ;;       AND 'shape of mainsail' is quadrilateral
                 ;;       THEN boat is 'Gaff-headed Sloop'
         [["number of masts" "one"] ["shape of mainsail" "quadrilateral"]]
         [["boat" "gaff-headed sloop"]]
      setup-rule ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'forward of the short mast'
                 ;;       AND 'the short mast position' is 'forward of the helm'
                 ;;       AND 'shape of mainsail' is triangular
                 ;;       THEN boat is 'Jib-headed Ketch'
         [["number of masts" "two"] ["main mast position" "forward of the short mast"]
          ["the short mast position" "forward of the helm"] ["shape of mainsail" "triangular"]]
         [["boat" "jib-headed ketch"]]
      setup-rule ;; rule: ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'forward of the short mast'
                 ;;       AND 'the short mast position' is 'forward of the helm'
                 ;;       AND 'shape of mainsail' is quadritaleral
                 ;;       THEN boat is 'Gaff-headed Ketch'
         [["number of masts" "two"] ["main mast position" "forward of the short mast"]
          ["the short mast position" "forward of the helm"] ["shape of mainsail" "quadrilateral"]]
         [["boat" "gaff-headed ketch"]]
      setup-rule ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'forward of the short mast'
                 ;;       AND 'the short mast position' is 'aft the helm'
                 ;;       AND 'shape of mainsail' is triangular
                 ;;       THEN boat is 'Jib-headed Yawl'
         [["number of masts" "two"] ["main mast position" "forward of the short mast"]
          ["the short mast position" "aft the helm"] ["shape of mainsail" "triangular"]]
         [["boat" "jib-headed yawl"]]
      setup-rule ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'forward of the short mast'
                 ;;       AND 'the short mast position' is 'aft the helm'
                 ;;       AND 'shape of mainsail' is quadritaleral
                 ;;       THEN boat is 'Gaff-headed Yawl'
         [["number of masts" "two"] ["main mast position" "forward of the short mast"]
          ["the short mast position" "aft the helm"] ["shape of mainsail" "quadrilateral"]]
         [["boat" "gaff-headed yawl"]]
      setup-rule ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'aft the short mast'
                 ;;       AND 'shape of mainsail' is quadritaleral
                 ;;       THEN boat is 'Gaff-headed Ketch'
         [["number of masts" "two"] ["main mast position" "aft the short mast"]
          ["shape of mainsail" "quadrilateral"]]
         [["boat" "gaff-headed schooner"]]
      setup-rule ;; rule: IF 'number of masts' is two
                 ;;       AND 'main mast position' is 'aft the short mast'
                 ;;       AND 'shape of mainsail' is 'triangular with two foresails'
                 ;;       THEN boat is 'Gaff-headed Ketch'
         [["number of masts" "two"] ["main mast position" "aft the short mast"]
          ["shape of mainsail" "triangular with two foresails"]]
         [["boat" "staysail schooner"]]
    ]
end

to-report selected-problem
;; report the problem the user has selected

  if (select-problem = "Zoo animals")
    [ report animals-problem ]
  if (select-problem = "New Zealand birds")
    [ report birds-problem ]
  if (select-problem = "Sailing boats")
    [ report boats-problem ]
end

to setup-knowledge
;; sets up the knowledge according to KR-type
  if (KR-type = "rules")
    [ setup-rules ]
  if (KR-type = "frames")
    [ setup-frames ]
  if (KR-type = "decision tree")
    [ convert-rules-to-decision-tree ]
  if (KR-type = "semantic network")
    [ setup-semantic-network ]  
  if (KR-type = "semantic event network")
    [ setup-semantic-network
      convert-semantic-network-to-events ]  
end

to dump-knowledge
;; dumps the knowledge to the output
  if (KR-type = "rules")
    [ output-rules ]  
  if (KR-type = "frames")
    [ output-frames ]
  if (KR-type = "decision tree")
    [ output-decision-tree ]
  if (KR-type = "semantic network")
    [ output-semantic-network ]  
  if (KR-type = "semantic event network")
    [ output-semantic-event-network ]  
end

to go-reason
;; performs reasoning with the knowledge according to KR-type
  if (KR-type = "rules")
    [ ifelse (user-one-of "Which type of reasoning?" ["forward" "backward"] = "forward")
        [ go-rules-forward-reasoning ]
        [ go-rules-backward-reasoning ]]
  if (KR-type = "frames")
    [ go-frames-reasoning ]
  if (KR-type = "decision tree")
    [ go-decision-tree-reasoning ]
  if (KR-type = "semantic network")
    [ go-semantic-network-reasoning ]
  if (KR-type = "semantic event network")
    [ user-message "Use semantic networks instead for reasoning" ]
end

to setup-rules
;; creates the rules for the selected-problem

  init-problem ; initialise the selected problem
  setup-rules-event-map selected-problem
end

to-report attribute-found? [attribute]
; reports true if the attribute is in the facts database
; reports false otherwise

  report (table:has-key? facts-base attribute)
end

to-report fact-found? [attribute value]
; reports true if the attribute and value are in the facts database
; reports false otherwise

  report (value = table:get facts-base attribute)
end

to-report fact-get [attribute]
; gets the value associated with the attribute from the facts-base.

  report table:get facts-base attribute
end

to fact-put [attribute value]
; puts the attribute value pair into the facts-base.

  table:put facts-base attribute value
end

to reset-reasoning
; resets the facts base to empty

  set fired-rules []
  table:clear facts-base
  reset-walkers
end

to-report match-rule? [problem this-rule]
; tries to match this-rule to facts in the facts-base
; otherwise asks user for data if attribute is not in non-input-attributes list 
  let attribute ""
  let val ""
  let value ""
  let question ""
  let choices []
  let this-walker nobody

  set this-walker start-new-walker nobody "" ""
  foreach [antecedents] of this-rule
    [
      set attribute first ?
      set value last ?
      update-walker this-walker attribute value

      if not attribute-found? attribute ; not found, so need to ask user if we can
        [ ifelse (member? attribute [non-input-attributes] of problem) ; no we can't ask user
            [ report false ]
            [ ; ask user a question
              set question get-input-question problem attribute value
              set choices get-input-choices problem attribute
              set val user-one-of question choices
              fact-put attribute val
            ]
        ]
      if not fact-found? attribute value
        [ report false ]
     ]

  report true
end

to fire-rule [this-rule]
; fires the matched rule this-rule
  let attribute ""
  let value ""
  let new-walker nobody
  
  foreach [consequents] of this-rule
  [
    set attribute first ?
    set value last ?

    ask active-walker
    [ hatch-walkers 1 [ set new-walker self ]] ; clone active walker
    
    update-walker new-walker attribute value
          
    fact-put attribute value
    set fired-rules fput this-rule fired-rules
  ]
end

to output-number [output-str number]
; for writing out a number to the output
  if (trace-on?)
  [
    output-type output-str
    output-type number
    output-print ":"
  ]
end

to output-rule [this-rule]
; dumps this-rule to the output

  if (trace-on?)
  [
    output-type "Rule "
    output-print this-rule
    output-type "If:   "
    output-print [antecedents] of this-rule
    output-type "Then: "
    output-print [consequents] of this-rule
  ]
end

to output-fired-rule [this-rule]
;; dumps the fired rule this-rule to the output

  if (trace-on?) [ output-type "Fired " ]
  output-rule this-rule
  if (trace-on?)
    [
      output-print "Updated facts base: "
      output-print table:to-list facts-base
    ]
end

to output-rules
;; dumps out all the rules to the output

  ask rules
  [ output-rule self ]
end

to output-goal [attribute value]
  if (trace-on?)
  [
    output-type "Trying to find and prove goal: ["
    output-type attribute
    output-type " "
    output-type value
    output-print "]"
  ]
end  

to go-rules-forward-reasoning
;; performs forward reasoning on the problem
  let fired-count length fired-rules
  print selected-problem
  let goal-attrib [goal-attribute] of selected-problem
  
  set cycle-count cycle-count + 1
  output-number "Cycle " cycle-count

  loop
    [
      foreach sort rules
        [ ; for each rule in the rules base
          if not member? ? fired-rules
          [
            if (match-rule? selected-problem ?)
              [ 
                fire-rule ?
                output-fired-rule ?
              ]
            if (attribute-found? goal-attrib)
              [ user-message (word "The " goal-attrib " is a " fact-get goal-attrib ".")
                stop ]
           ]
        ]
    
      if (fired-count = length fired-rules)
        [ user-message "No new rules fired this cycle. Sorry - I can't help you."
          stop ]
    ]
end

to-report find-goal? [this-rule goal-attrib goal-val]
;; reports if the goal is in the consequents of this_rule
  let attribute ""
  let val ""
  let value ""
  let response ""

  foreach [consequents] of this-rule
    [
      set attribute first ?
      set value last ?
      
      if (attribute = goal-attrib) and (value = goal-val)
        [ report true ]
    ]

  report false
end

to-report prove-goal? [problem this-rule goal-attrib goal-val]
; tries to prove this-rule from facts in the facts-base
; otherwise asks user for data if attribute is not in non-input-attributes list
; otherwise recursively calls the procedure find-prove? to see if
; the non-input-attributes can be proved 
  let attribute ""
  let val ""
  let value ""
  let question ""
  let choices []
  let this-walker nobody

  set this-walker start-new-walker nobody "" ""
  
  foreach [antecedents] of this-rule
    [
      set attribute first ?
      set value last ?
      update-walker this-walker attribute value

      if not attribute-found? attribute ; not found, so need to ask user if we can
        [ ifelse (member? attribute [non-input-attributes] of problem) ; no we can't ask user
            [ ifelse not find-prove? problem attribute value
                [ report false ]
                [ fact-put attribute value ]
            ]
            [ ; ask user a question
              set question get-input-question problem attribute value
              set choices get-input-choices problem attribute
              set val user-one-of question choices
              fact-put attribute val
            ]
        ]
      if not fact-found? attribute value
        [ report false ]
    ]
  update-walker this-walker goal-attrib goal-val

  report true
end

to-report find-prove? [problem goal-attrib goal-val]
;; recursive procedure called by the go-backward-reasoning procedure

  let this-walker nobody
  
  output-goal goal-attrib goal-val
  foreach sort rules
    [ ; for each rule in the rules base
      if (find-goal? ? goal-attrib goal-val)
        [
          if (prove-goal? problem ? goal-attrib goal-val)
            [
              start-new-walkers goal-attrib goal-val
              output-fired-rule ?
              report true
            ]
        ]
    ]
  report false
end

to go-rules-backward-reasoning
;; performs backward reasoning on the problem
  
  let goal-choices []
  let goal-attrib ""
  let goal-value ""
  let attribute ""
  let value ""

  set goal-attrib [goal-attribute] of selected-problem  
  foreach sort rules
    [ ; add each goal in all the rules to the goal-choices
      foreach [consequents] of ?
      [
        set attribute first ?
        set value last ?

        if (attribute = goal-attrib) and
           (not member? value goal-choices)
          [ set goal-choices lput ? goal-choices ]
      ]         
    ]
    
  let goal user-one-of "Which goal do you wish to proove?" goal-choices
  set goal-value (last goal)
  start-new-walkers goal-attrib goal-value

  ifelse not find-prove? selected-problem goal-attrib goal-value
    [ user-message "No more rules found. The goal is not proven." ]
    [ update-walker active-walker goal-attrib goal-value
      user-message (word "The goal is proven! The " goal-attrib " is a " goal-value ".")]
end

to add-events [list-of-events root-colour node-colour link-colour]
;; add events in the list-of-events list to the events tree.
;; each item of the list-of-events list must consist of a two itemed list.
;; e.g. [[hue 0.9] [brightness 0.8]]

  let this-depth 0
  let this-stream ""
  let this-event ""
  let this-state nobody
  let next-state nobody
  let these-states states
  let matching-states []
  let matched-all-so-far true
  
  foreach list-of-events
  [ set this-stream first ?
    set this-event last ?

    ;; check to see if state already exists
    set matching-states these-states with [stream = this-stream and event = this-event and depth = this-depth]
    ifelse (matched-all-so-far = true) and (count matching-states > 0)
      [
        set next-state one-of matching-states
        ask next-state
        [ set label (word stream " = \"" event "\"  ") ]
        set these-states [out-path-neighbors] of next-state ]
      [ ;; state does not exist - create it
        set matched-all-so-far false
        create-states 1
        [
          set depth this-depth
          set stream this-stream
          set event this-event
          set label (word stream " = \"" event "\"  ")
          
          ifelse (depth = 0)
            [ set label-color root-colour ]
            [ set label-color node-colour ]
                      
          set size 6
          ifelse (depth = 0)
            [ set color root-colour ]
            [ set color node-colour ]
          set next-state self
        ]
      ]

    if (this-state != nobody)
      [ ask this-state [ create-path-to next-state [ set color link-colour ]]]
         
    ;; go down the tree
    set this-state next-state    
    set this-depth this-depth + 1
  ]
  ask links [ set thickness 1.0 ]
  
  ;; traverse-backlinks next-state
end

to reset-layout
  ifelse (layout-type = "spring")
    [ repeat 500 [ layout-spring states paths spring-constant spring-length repulsion-constant ]]
 ;else (layout-type = "radial")
    [ ask one-of states [ layout-radial states paths self ]]

  ;; leave space around the edges
  ask states [ setxy 0.9 * xcor 0.9 * ycor ]
end

to change-layout
  reset-layout
  ;display
end

to move-layout
;; moves all the turtles a certain direction in a specified direction
  let xinc 0
  let yinc 0
  let dir user-one-of "Move agents in which direction?" ["left" "right" "up" "down"]
  let dist read-from-string user-input "What distance?"
  if (dir = "left") [ set xinc (- dist) ]
  if (dir = "right") [ set xinc dist ]
  if (dir = "up") [ set yinc dist ]
  if (dir = "down") [ set yinc (- dist) ]
  
  ask states
  [ setxy xcor + xinc ycor + yinc ]
end

to add-rule-to-event-map [this-rule]
; adds the rule as a set of linked states to the event map network

  let events []
  
  foreach [consequents] of this-rule
  [
    set events lput ? ([antecedents] of this-rule)
    add-events events white white white
  ]
end

to setup-rules-event-map [problem]
;; setups an event map network of states from the rules

  foreach sort rules
  [
    add-rule-to-event-map ?
  ]

  repeat 5 [ change-layout ]
  display
end

to init-walker [this-walker this-location this-stream this-event]
; initialises the walker this-walker
  ask this-walker
    [
      pen-up
      set color magenta
      set shape "person"
      set size 10
      set location this-location
      set stream this-stream
      set event this-event
      if (location != nobody)
        [ move-to location ]
    ]
end

to-report start-new-walker [this-location this-stream this-event]
;; creates a new walker

  let this-walker nobody
  
  create-walkers 1
    [
      set this-walker self
      init-walker this-walker this-location this-stream this-event
    ]

  set active-walker this-walker
  report this-walker
end 

to start-new-walkers [attribute value]
;; start new walkers at all the states that have stream = attribute and event = value

  let new-walker nobody 
  foreach sort states with [stream = attribute and event = value]
    [ set new-walker start-new-walker ? attribute value ]
end

to update-walker [this-walker attribute value]
;; updates the walkers' positions by processing the stream's event
;; i.e. stream = attribute, event = value
  let out-paths-count 0
  let new-location nobody

  ask this-walker
    [
      ifelse (location = nobody) ; just been created
        [
          set location one-of states with [stream = attribute and event = value]
          move-to location
        ]
        [
          ask location
          [ set new-location one-of out-path-neighbors with [stream = attribute and event = value]]
          if (new-location != nobody)
            [
              ask [link-with new-location] of location [ set thickness 2.0 ] ;; 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 stream attribute
      set event value

      display
    ]
end

to reset-walkers
;; kills off all walkers and resets thickness of links
  ask walkers [ die ]
 
  ask links [ set thickness 1.0 ] ;; reset all network links to uncrossed
end

to-report find-frame? [this-ako this-name]
;; reports the frame that has slot value "ako" = ako and "name" = name.
;; reports nobody otherwise

  report one-of frames with [member? this-ako ako-list and name = this-name]
end

to output-frame [this-frame]
;; dumps out the contents of the frame to the output (for tracing purposes)

  if (trace-on?)
    [
      ask this-frame
      [
        output-type "frame: " output-print name
        output-type "ako  : " output-print ako-list
        output-type "slots: " output-print [slots-table] of this-frame
      ]
    ]
end

to output-frames
;; dumps out all the frames to the output

  ask frames
  [ output-frame self ]
end
 
to convert-rules-to-frames-base [problem]
;; Converts the rules base to a frames base

  let this-rule nobody
  let this-frame nobody
  let attribute ""
  let value ""

  ask frames [ die ] ; kill off any previous frames  

  foreach sort rules
    [ ; for each rule in the rules base
      set this-rule ?
      foreach [consequents] of this-rule
        [
          set attribute (first ?)
          set value (last ?)
          set this-frame find-frame? attribute value
          if (this-frame = nobody)
            [ ; frame does not exist with that name and type
              create-frames 1
                [
                  hide-turtle ; make invisible
                  set this-frame self
                  set name value
                  set ako-list (list attribute)
                  set slots-table table:make
                ]
            ]

          foreach [antecedents] of this-rule
            [
              ask this-frame
                [
                  set attribute (first ?)
                  set value (last ?)
                  ifelse (member? attribute [input-attributes] of problem)
                    [ table:put slots-table attribute value ]
                    [ set ako-list (lput value ako-list) ] ; add value to ako-list
                ]
            ]
        ]
    ]
end

to-report slots-events [table]
;; returns the list of events for the slots table
  let events []
  let keys []
  let value ""
  
    set keys table:keys table
    foreach keys
      [
        set value table:get table ?
        set events lput (list ? value) events
      ]
  
  report events
end

to setup-frames
;; setups a network of states from the frames

  init-problem ; initialise the selected problem

  let events []

  convert-rules-to-frames-base selected-problem  
  foreach sort frames
  [
    set events slots-events [slots-table] of ?
    set events fput (fput "ako" (list [ako-list] of ?)) events
    set events fput (list "name" [name] of ?) events

    add-events events white white white
  ]

  repeat 5 [ change-layout ]
  display
end

to convert-rules-to-decision-tree
;; Converts the rules base to a decision tree

  init-problem ; initialise the selected problem

  let this-rule nobody
  let attribute ""
  let value ""
  let events []
      
  foreach sort rules
    [ ; for each rule in the rules base
      set this-rule ?

      set events (list (list [goal-attribute] of selected-problem "Yes"))
      foreach [antecedents] of this-rule
        [          
          ask this-rule
            [
              set attribute (first ?)
              set value (last ?)
              set events lput (list attribute value) events
            ]
        ]

      foreach [consequents] of this-rule
        [
          set attribute (first ?)
          set value (last ?)
          add-events (lput (list attribute value) events) white white white
        ]
    ]

  repeat 5 [ change-layout ]
  display
end

to-report match-frame? [problem frame]
;; reports true of the frame matches the facts

  let attribute ""
  let attributes []
  let value ""
  let val ""
  let question ""
  let choices []
  let this-walker nobody
  let this-name ""

  set this-name [name] of frame  

  if (trace-on?) [ output-type "Matching frame " output-print this-name ]
  output-frame frame

  set this-walker start-new-walker nobody "" ""
  update-walker this-walker "name" this-name
  update-walker this-walker "ako" [ako-list] of frame
    
  ; first need to match against all the frames in the ako-list
  foreach [ako-list] of frame
    [
      if (count frames with [name = ?] > 0) ; check frame exists for ako-list name
        [ if not match-frame? problem one-of frames with [name = ?]
            [ report false ]
        ]
    ]

  set attributes table:keys [slots-table] of frame
  foreach attributes
  [
    set attribute ?
    set value table:get [slots-table] of frame attribute
    update-walker this-walker attribute value
    

    if not attribute-found? attribute ; not found, so need to ask user if we can
      [ ifelse (member? attribute [non-input-attributes] of problem) ; no we can't ask user
          [ report false ]
          [ ; ask user a question
            set question get-input-question problem attribute value
            set choices get-input-choices problem attribute
            set val user-one-of question choices
            fact-put attribute val
          ]
      ]
    if not fact-found? attribute value
      [ report false ]
  ]

  if (trace-on?)
    [ output-print ""
      output-type "Matched frame " output-print [name] of frame ]
  report true
end

to go-frames-reasoning
;; does frame-based reasoning

  let this-name ""
  let goal-attrib [goal-attribute] of selected-problem
  
  foreach sort frames
  [
    if (member? goal-attrib [ako-list] of ?)
      [
        set this-name [name] of ?
        ifelse (match-frame? selected-problem ?)
          [ user-message (word "The frame " this-name " has been matched.")
            stop ]
          [ if (trace-on?)
              [ output-type "Frame " output-type this-name
                output-print " did not match" ]
          ]
      ]
  ]
end

to output-tree-state [this-state]
; outputs the decision tree state this-state to the output

  if (trace-on?)
  [
    ask this-state
      [
        output-type "State "
        output-print self
        output-type "Stream: "
        output-print stream
        output-type "Event: "
        output-print event
        output-type "Out Links: ["
        foreach sort out-path-neighbors
          [
            output-type " "
            output-type ?
          ]
      ]
    output-print " ]"
  ]
end

to output-decision-tree
;; dumps out all the states in the decision tree to the output

  foreach sort states
    [ output-tree-state ? ]
end

to-report match-tree-state? [problem this-walker]
;; reports true of the state in the tree matches

  let choice ""
  let question ""
  let choices []

  let this-stream [stream] of this-walker
  let this-event [event] of this-walker

  if (trace-on?)
    [ output-type "Matching tree state with stream = "
      output-type this-stream
      output-type ", event = "
      output-print this-event ]

  if not attribute-found? this-stream ; not found, so need to ask user if we can
    [ ifelse (member? this-stream [non-input-attributes] of problem)
        [ ; no we can't ask user
          if (count [out-path-neighbors] of [location] of this-walker = 0)
            [ fact-put this-stream this-event ]
        ]
        [ ; ask user a question
          set question get-input-question problem this-stream this-event
          set choices get-input-choices problem this-stream
          set choice user-one-of question choices
          fact-put this-stream choice
        ]
    ]
  if not fact-found? this-stream this-event
    [ report false ]

  if (trace-on?)
    [ output-type "Matched tree state with stream = "
      output-type this-stream
      output-type ", event = "
      output-print this-event ]
  report true
end

to match-tree [this-walker]
;; find a match by walking the tree starting at this-walker's current state

  let new-walker nobody
  let new-walkers []
  let these-states [out-path-neighbors] of [location] of this-walker
    
  ask this-walker
  [
    foreach sort these-states
      [ ; first pass through neighbour states: create a new walker for each
        ; highlighting the link as we go
        hatch-walkers 1 [ set new-walker self ] ; clone walker
        update-walker new-walker [stream] of ? [event] of ?
        set new-walkers lput new-walker new-walkers
     ]
     foreach new-walkers
      [ ; second pass: try to match each in turn
        if (match-tree-state? selected-problem ?)
          [ match-tree ? ]
      ]
  ]
end

to go-decision-tree-reasoning
;; does tree-based reasoning

  let goal-value ""
  let goal-attrib [goal-attribute] of selected-problem
  let this-walker start-new-walker nobody "" ""
  update-walker this-walker goal-attrib "Yes"
     
  match-tree this-walker
  
  ifelse (not table:has-key? facts-base goal-attrib)
    [ user-message "Failed to reach the goal decision." ]
    [ set goal-value table:get facts-base goal-attrib
      user-message (word "Reached goal decision. The " goal-attrib " is a " goal-value ".") ]
end

to output-semantic-state [this-state]
; outputs the semantic network state this-state to the output

  if (trace-on?)
  [
    ask this-state
      [
        output-type "State "
        output-print self
        output-type "Label: "
        output-print label
        output-type "Out Links: ["
        ask my-out-paths
          [
            output-type " \""
            output-type label
            output-type "\" "
            output-type self
          ]
      ]
    output-print " ]"
  ]
end

to output-semantic-network
;; dumps out all the states in the semantic network to the output

  foreach sort states
    [ output-semantic-state ? ]
end

to-report create-state [state-label]
;; creates and returns a new semantic node
  let this-state nobody
  
  create-states 1
    [
      set this-state self
      set color sky
      set label-color white
      set label state-label
      set size 8
      set stream "state name"
      set event state-label
    ]
  report this-state
end

to create-slink [state1 slink-label state2]
;; creates a link between the two nodes state1 and state2 in the semantic network

  ask state1
    [ create-path-to state2
        [ set label slink-label
          set label-color yellow
          set color blue
        ]
    ]
end

to setup-animals-semantic-network
;; sets up the nodes and links in the Zoo Animals semantic network

  let animal-state create-state "animal"
  set goal-state animal-state

  let mammal-state create-state "mammal"
  let ungulate-state create-state "ungulate"
  let carnivore-state create-state "carnivore"
  
  let hair-state create-state "hair"
  let milk-state create-state "milk"
  let meat-state create-state "meat"
  let pointed-teeth-state create-state "pointed teeth"
  let claws-state create-state "claws"
  let forward-eyes-state create-state "forward eyes"
  let hooves-state create-state "hooves"
  let cud-state create-state "cud"
  let tawny-colour-state create-state "tawny colour"
  let dark-spots-state create-state "dark spots"
  let black-stripes-state create-state "black stripes"
  let long-neck-state create-state "long neck"
      
  let cheetah-state create-state "cheetah"
  let tiger-state create-state "tiger"
  let giraffe-state create-state "giraffe"
  let zebra-state create-state "zebra"

  create-slink cheetah-state "is a" animal-state
  create-slink tiger-state "is a" animal-state
  create-slink giraffe-state "is a" animal-state
  create-slink zebra-state "is a" animal-state

  create-slink ungulate-state "is a" mammal-state
  create-slink cheetah-state "is a" mammal-state
  create-slink tiger-state "is a" mammal-state
  create-slink cheetah-state "is a" carnivore-state
  create-slink tiger-state "is a" carnivore-state
  create-slink giraffe-state "is a" ungulate-state
  create-slink zebra-state "is a" ungulate-state
  
  create-slink mammal-state "has" hair-state 
  create-slink mammal-state "gives" milk-state

  create-slink carnivore-state "eats" meat-state 
  create-slink carnivore-state "has" pointed-teeth-state 
  create-slink carnivore-state "has" claws-state
  create-slink carnivore-state "has" forward-eyes-state

  create-slink ungulate-state "has" hooves-state
  create-slink ungulate-state "chews" cud-state
  
  create-slink cheetah-state "has" tawny-colour-state
  create-slink cheetah-state "has" dark-spots-state
  create-slink tiger-state "has" tawny-colour-state
  create-slink tiger-state "has" black-stripes-state
  create-slink giraffe-state "has" dark-spots-state
  create-slink giraffe-state "has" long-neck-state
  create-slink zebra-state "has" black-stripes-state
end

to setup-birds-semantic-network
;; sets up the nodes and links in the New Zealand birds semantic network

  let nzbird-state create-state "NZ bird"
  set goal-state nzbird-state
  
  let kea-state create-state "kea"
  let kaka-state create-state "kaka"
  let tui-state create-state "tui"
  let pukeko-state create-state "pukeko"
  let moa-state create-state "moa"
  let huia-state create-state "huia"
  let kiwi-state create-state "kiwi"
  let weka-state create-state "weka"

  create-slink kea-state "is a" nzbird-state
  create-slink kaka-state "is a" nzbird-state
  create-slink tui-state "is a" nzbird-state
  create-slink pukeko-state "is a" nzbird-state
  create-slink moa-state "is a" nzbird-state
  create-slink huia-state "is a" nzbird-state
  create-slink kiwi-state "is a" nzbird-state
  create-slink weka-state "is a" nzbird-state
  
  let fly-state create-state "fly"
  create-slink kea-state "can" fly-state
  create-slink kaka-state "can" fly-state
  create-slink tui-state "can" fly-state
  create-slink pukeko-state "can" fly-state
  create-slink moa-state "can not" fly-state
  create-slink huia-state "can not" fly-state
  create-slink kiwi-state "can not" fly-state
  create-slink weka-state "can not" fly-state

  let parrot-state create-state "parrot"
  create-slink kea-state "is a" parrot-state
  create-slink kaka-state "is a" parrot-state
  create-slink tui-state "is not a" parrot-state
  create-slink pukeko-state "is not a" parrot-state

  let extinct-state create-state "extinct"
  create-slink moa-state "is" extinct-state
  create-slink huia-state "is" extinct-state
  create-slink kiwi-state "is not" extinct-state
  create-slink weka-state "is not" extinct-state

  let alpine-state create-state "alpine"
  create-slink kea-state "is" alpine-state
  create-slink kaka-state "is not" alpine-state

  let white-throat-state create-state "white throat"
  create-slink tui-state "has" white-throat-state
  create-slink pukeko-state "has not" white-throat-state

  let large-state create-state "large"
  create-slink moa-state "is" large-state
  create-slink huia-state "is not" large-state
  
  let long-beak-state create-state "long beak"
  create-slink kiwi-state "has" long-beak-state
  create-slink weka-state "has not" long-beak-state

end

to setup-boats-semantic-network
;; sets up the nodes and links in the sailing boats semantic network

  let boat-state create-state "boat"
  set goal-state boat-state
  
  let jib-headed-cutter-state create-state "jib-headed cutter"
  let gaff-headed-sloop-state create-state "gaff-headed sloop"
  let jib-headed-ketch-state create-state "jib-headed ketch"
  let gaff-headed-ketch-state create-state "gaff-headed ketch"
  let jib-headed-yawl-state create-state "jib-headed yawl"
  let gaff-headed-yawl-state create-state "gaff-headed yawl"
  let gaff-headed-schooner-state create-state "gaff-headed schooner"
  let staysail-schooner-state create-state "staysail schooner"

  create-slink jib-headed-cutter-state "is a" boat-state
  create-slink gaff-headed-sloop-state "is a" boat-state
  create-slink jib-headed-ketch-state "is a" boat-state
  create-slink gaff-headed-ketch-state "is a" boat-state
  create-slink jib-headed-yawl-state "is a" boat-state
  create-slink gaff-headed-yawl-state "is a" boat-state
  create-slink gaff-headed-schooner-state "is a" boat-state
  create-slink staysail-schooner-state "is a" boat-state

  let masts-state create-state "masts"
  create-slink jib-headed-cutter-state "has one" masts-state
  create-slink gaff-headed-sloop-state "has one" masts-state
  create-slink jib-headed-ketch-state "has two" masts-state
  create-slink gaff-headed-ketch-state "has two" masts-state
  create-slink jib-headed-yawl-state "has two" masts-state
  create-slink gaff-headed-yawl-state "has two" masts-state
  create-slink gaff-headed-schooner-state "has two" masts-state
  create-slink staysail-schooner-state "has two" masts-state

  let mainsail-state create-state "mainsail"
  create-slink jib-headed-cutter-state "has a triangular" mainsail-state
  create-slink gaff-headed-sloop-state "has a quadrilateral" mainsail-state
  create-slink jib-headed-ketch-state "has a triangular" mainsail-state
  create-slink gaff-headed-ketch-state "has a quadrilateral" mainsail-state
  create-slink jib-headed-yawl-state "has a triangular" mainsail-state
  create-slink gaff-headed-yawl-state "has a quadrilateral" mainsail-state
  create-slink gaff-headed-schooner-state "has a quadrilateral" mainsail-state
  create-slink staysail-schooner-state "has a 'triangular with two foresails'" mainsail-state

  let main-mast-position-state create-state "main mast position"
  create-slink jib-headed-ketch-state "has a 'forward of the short mast'" main-mast-position-state
  create-slink gaff-headed-ketch-state "has a 'forward of the short mast'" main-mast-position-state
  create-slink jib-headed-yawl-state "has a 'forward of the short mast'" main-mast-position-state
  create-slink gaff-headed-yawl-state "has a 'forward of the short mast'" main-mast-position-state
  create-slink gaff-headed-schooner-state "has an 'aft the short mast'" main-mast-position-state
  create-slink staysail-schooner-state "has an 'aft the short mast'" main-mast-position-state

  let short-mast-position-state create-state "short mast position"
  create-slink jib-headed-ketch-state "has a 'forward of the helm'" short-mast-position-state
  create-slink gaff-headed-ketch-state "has a 'forward of the helm'" short-mast-position-state
  create-slink jib-headed-yawl-state "has an 'aft the helm'" short-mast-position-state
  create-slink gaff-headed-yawl-state "has an 'aft the helm'" short-mast-position-state
end

to setup-semantic-network
;; sets up the nodes and links in the semantic network

  setup ; clear everything

  if (select-problem = "Zoo animals")
    [ setup-animals-semantic-network ]
  if (select-problem = "New Zealand birds")
    [ setup-birds-semantic-network ]
  if (select-problem = "Sailing boats")
    [ setup-boats-semantic-network ]

  ask links [ set thickness 1.0 ]
    
  repeat 10 [ change-layout ]
  display
end

to-report match-network-state? [this-state this-walker]
;; reports true if the state in the semantic network matches

  let this-state-label [label] of this-state
  let goal-label [label] of goal-state
  let child-state-label ""
  let new-walker nobody
  let link-label ""
  let link-label1 ""
  let question ""
  let question-label ""
  let choice ""
  let fact ""
  let match true

  if (trace-on?)
    [ output-type "Matching semantic network state "
      output-print this-state-label ]

  ask ([out-path-neighbors] of this-state)
    [
      if match
      [
        set child-state-label label
        set link-label [label] of in-path-from this-state

        ask this-walker
        [ hatch-walkers 1 [ set new-walker self ]] ; clone walker
        update-walker new-walker "state name" child-state-label

        ifelse (link-label = "is a")
          [
            if (not match-network-state? self new-walker)
              [ set match false ]
          ]
          [
            ifelse (member? " not" link-label)
              [ ; need to modify the link label to remove the "not" from the question
                ; so that we are only ever asking positive questions
                set link-label1 (remove " not" link-label) ]
              [ set link-label1 link-label ]
            set fact (word link-label1 " " child-state-label)    
            if not attribute-found? fact ; not found, so need to ask user if we can
              [
                set question (word "Is the following statement true: The " goal-label " " link-label1 " " child-state-label "?")
                set choice user-one-of question ["Yes" "No"]
                fact-put fact choice
              ]
            ifelse (link-label = link-label1)
              [ if (not fact-found? fact "Yes") ; the question has not been modified
                [ set match false ]]        
              [ if (not fact-found? fact "No") ; the question is a negation of the link-label
                [ set match false ]]
          ]
      ]
    ]

  if (trace-on?)
    [ output-type "Match result for semantic network state "
      output-type this-state-label
      output-type " is "
      output-print match ]

  report match
end

to go-semantic-network-reasoning
;; does reasoning using the semantic network

  let root-walker start-new-walker goal-state "" ""
  let new-walker nobody
  let this-state nobody
  let state-label ""
  let link-label ""
  let match false
  let match-label ""

  ask ([in-path-neighbors] of goal-state)
    [
      set this-state self
      if (not match)
        [
          set state-label label
          set link-label [label] of out-link-to goal-state
          if (link-label = "is a")
            [
              ask root-walker
              [ hatch-walkers 1
                [ set new-walker self
                  set location this-state
                  move-to location ]] ; clone walker
              
              ask [link-with ([location] of root-walker)] of ([location] of new-walker) [ set thickness 2.0 ] ;; highlight link I am crossing

              if (match-network-state? self new-walker)
                [ set match true
                  set match-label label ]
            ]
        ]
    ]
  
  ifelse (not match)
    [ user-message "Failed to find a match." ]
    [ user-message (word "Found a match. The " [label] of goal-state " is a " match-label ".") ]
end 

to convert-semantic-state [this-state]
; converts the semantic network state this-state to the events map format

  let this-label ""
  let events []
  let event-map []
  
  ask this-state
    [
      set this-label label
      ask my-out-paths
        [
          set events (list (list "state" this-label)
                     (list "transition" label)
                     (list "state" [label] of other-end))
          set event-map fput events event-map
        ]
    ]
  foreach event-map
    [ add-events ? white white white ]
end

to convert-semantic-network-to-events
;; converts all the states in the semantic network to an event map

  foreach sort states
    [ convert-semantic-state ? ]
    
  ask states with [color = sky]
  [ die ] ; kill off old states
  ask links with [color = blue]
  [ die ] ; kill off old links

  change-layout
  display
end

to output-semantic-event [this-state]
; outputs the semantic event network state this-state to the output

  if (trace-on?)
  [
    ask this-state
      [
        output-type "State "
        output-print self
        output-type "Label: "
        output-print label
        output-type "Out Links: ["
        ask my-out-paths
          [
            output-type " \""
            output-type label
            output-type "\" "
            output-type self
          ]
      ]
    output-print " ]"
  ]
end

to output-semantic-event-network
;; dumps out all the states in the semantic network to the output

  foreach sort states
    [ output-semantic-event ? ]
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).  Knowledge Representation NetLogo model.
;   Artificial Intelligence. Ventus Publishing Aps.
;