# Data structures

```;;;  A FACT is just a name.
;;;    Ex. HUSBAND-HAS-DISEASE
;;;
;;;  A FACT-SET is a list of exhaustive, mutually exclusive facts.
;;;    Ex. (HUSBAND-HAS-DISEASE HUSBAND-HAS-TRAIT HUSBAND-OK)
;;;
;;;  A SCENARIO is a list of facts, one from each possible fact-set.
;;;    Ex. (HUSBAND-HAS-TRAIT WIFE-OK)
;;;
;;;  A PROBE has the components:
;;;     name        the probe's name
;;;     rules       a list of result rules, where a rule has the
;;;                 components:
;;;                     result      the name of the result
;;;                     causes      a list of facts that would cause
;;;                                 that result
;;;    Ex. (LOOK-HUSBAND-BLOOD
;;;          (HUSBAND-SICKLE-BLOOD HUSBAND-HAS-DISEASE)
;;;          (HUSBAND-ROUND-BLOOD HUSBAND-HAS-TRAIT)
;;;          (HUSBAND-ROUND-BLOOD HUSBAND-OK))
;;;
;;;  A RESULT-SET is a list of probe results, appearing in the order
;;;  of probes stored in *PROBE-RULES*.
;;;    Ex. (HUSBAND-ROUND-BLOOD WIFE-ROUND-BLOOD)
;;;
;;;  A SCENARIO-RESULTS (SR) structure has the components:
;;;     scenario    a scenario
;;;     results     a result-set
;;;    Ex. Scenario: (HUSBAND-HAS-TRAIT WIFE-OK)
;;;        Results:  (HUSBAND-ROUND-BLOOD WIFE-ROUND-BLOOD)

(defun probe-name (probe) (first probe))
(defun probe-rules (probe) (rest probe))

(defun rule-result (rule) (first rule))
(defun rule-causes (rule) (rest rule))

(defstruct sr scenario results)
```

# Global variables

```;;;  The fact-sets are stored in *POSSIBLE-FACTS*.
;;;
;;;  The probes and their rules are stored in *PROBE-RULES*.
;;;
;;;  The current scenario is stored in *SCENARIO*.
;;;
;;;  All possible scenarios and their result-sets are stored in
;;;  *SCENARIO-RESULTS*.
;;;
;;;  The results that the student has seen for far are stored in
;;;  *RESULT-SET*, with NIL for results not seen yet.

(defvar *possible-facts* nil
"A list of all fact-sets.")

(defvar *probe-rules* nil
"A list of all probes.")

(defvar *scenario* nil
"The current scenario.")

(defvar *scenario-results* nil
"A list of all possible scenarios and their results.")

(defvar *result-set* nil
"The results the students has seen so far.")
```

# DO-PROBE

```;;; (DO-PROBE probe-name [scenario]) => result
;;;   Given a probe name and a list of facts, returns the name of the
;;;   result that probe yields in that scenario.  Also updates the
;;;   set of results probed so far for later use by CONSISTENT-P.
;;;
;;; This just looks at each (result . causes) for the probe until
;;; it finds ones that fits the current scenario, i.e., the causes are
;;; a subset of the facts in the scenario.

(defun do-probe (name &optional (scenario *scenario*))
(let ((result (find-probe-result name scenario)))
(update-result-set name result)
result))

(defun find-probe-result (name scenario)
(loop for rule in (probe-rules (find-probe name))
when (triggers-rule-p scenario rule)
return (rule-result rule)))

(defun find-probe (name)
(find name *probe-rules* :key #'probe-name))

(defun triggers-rule-p (scenario rule)
(subsetp (rule-causes rule) scenario))

(defun update-result-set (name result)
(setf (elt *result-set*
(position name *probe-rules* :key #'probe-name))
result))
```

# CONSISTENT-P

```;;; (CONSISTENT-P hypothesis) => true or false
;;;   Returns true if the hypothesized fact is consistent with the
;;;   results probed so far.
;;;
;;; Rather than trying to figure this out dynamically, we generate
;;; a table of results for all probes for all scenarios. The table for
;;; a typical UGH-style GBS is probably a thousand entries or less.
;;; Then we just have to see if there's a line in the table with the
;;; hypothesized fact and results that include the results probed by
;;; the student so far. By storing results in a canonical form, sorted
;;; by probe order, the match function is more efficient than using
;;; SUBSETP.

(defun consistent-p (hypothesis)
(find-if #'(lambda (sr)
(and (member hypothesis (sr-scenario sr))
(result-matches-p *result-set* (sr-results sr))))
*scenario-results*))

(defun result-matches-p (pattern result-set)
(every #'(lambda (x y) (or (null x) (eql x y)))
pattern result-set))
```

# Generating the scenario results table

```;;; (GENERATE-SCENARIO-RESULTS) => list of scenario results
;;;   Returns a list of all possible scenarios and their result-sets.

(defun generate-scenario-results ()
(mapcar #'(lambda (scenario)
(make-sr :scenario scenario
:results (make-scenario-result-set scenario)))
(generate-all-scenarios)))

(defun generate-all-scenarios (&optional (fact-sets *possible-facts*))
(if (null fact-sets)
(list nil)
(mapcan #'(lambda (scenario)
(mapcar #'(lambda (hx) (cons hx scenario))
(first fact-sets)))
(generate-all-scenarios (rest fact-sets)))))

(defun make-scenario-result-set (scenario)
(mapcar #'(lambda (probe)
(find-probe-result (probe-name probe) scenario))
*probe-rules*))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Generating an empty result set
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun generate-empty-result-set ()
(make-list (length *probe-rules*)))
```

# Define the example

```(setq *possible-facts*
'((husband-has-disease husband-has-trait husband-ok)
(wife-has-disease wife-has-trait wife-ok)))

(setq *probe-rules*
'((look-husband-blood
(husband-sickle-blood husband-has-disease)
(husband-round-blood husband-has-trait)
(husband-round-blood husband-ok))
(look-wife-blood
(wife-sickle-blood wife-has-disease)
(wife-round-blood wife-has-trait)
(wife-round-blood wife-ok))
))

(setq *scenario* '(husband-has-trait wife-ok))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Calculate the scenario results from the above data.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(setq *scenario-results* (generate-scenario-results))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Initialize the student's result set.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(setq *result-set* (generate-empty-result-set))
```