AI-ExpertSystem-Simple

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for Perl extension AI::ExpertSystem::Simple

1.0  Fri Apr 25 22:02:02 2003
	- original version; created by h2xs 1.20 with options
		-AX -n AI::ExpertSystem::Simple

MANIFEST  view on Meta::CPAN

bin/simpleshell
bin/consult
Changes
examples/Animal.xml
examples/Doctor.xml
examples/Glass.xml
examples/test.xml
lib/AI/ExpertSystem/Simple/Goal.pm
lib/AI/ExpertSystem/Simple/Knowledge.pm
lib/AI/ExpertSystem/Simple/Rule.pm
lib/AI/ExpertSystem/Simple.pm
Makefile.PL
MANIFEST
README
t/empty.xml
t/Goal.t
t/Knowledge.t
t/Rule.t
t/Simple.t
t/test.xml

Makefile.PL  view on Meta::CPAN

use strict;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME         => 'AI::ExpertSystem::Simple',
    VERSION_FROM => 'lib/AI/ExpertSystem/Simple.pm',
    EXE_FILES    => [
						'bin/simpleshell',
						'bin/consult'
					],
    PREREQ_PM    => {
        'XML::Twig' => 0,
    },
);

README  view on Meta::CPAN

AI::ExpertSystem::Simple - A class implementing an expert system

This is release V1.2 of AI::ExpertSystem::Simple. The class 
implements a simple expert system that reads its knowledge base 
(set of IF .. THEN rules) from an XML file.

Three sample knowledge bases are provided along with a command line shell
that allows the knowledge bases to be consulted.

There is a Tcl/Tk based shell in which you can run a consultation, I
was going to write this in Perl/Tk as would seem obvious but this is
less than simple under OSX, infact I never got it to work. However there
was an OSX based version of Tcl/Tk and it would be easier to write the
shell in that which is much more portable (just install ActiveTcl on 
Windows, Aqua Tcl/Tk on OSX and use what you have on Linux - ideal).

So I dont have to write to a lowest common installation of PerlTk and
I don't have to explain in excruciating detail how to install PerlTk
on a variety of systems that I do not have access to. Suits me.

Also when I get to upgrade my Ruby version of this class it too can 
use the same shell. Go ahead and learn Tcl and Tk - it's rather 
usefull in it's own way.

INSTALLATION

 % perl Makefile.PL
 % make
 % make test
 % sudo make install

TEST FAILURES

The tests are there to make sure that I have broken nothing when I
fiddle with the code and will teach you very little about how to use
the code. 

To see how to use the code look at simpleshell in the bin directory
and read the contents of the docs directory (when I have written them).

I am assuming that you know what an expert system is and know why 
you are even running this code for in the first place.

TO DO

* More documentation (you can never have too much)

REQUIREMENTS

* Developed under 5.6.1 but should work on anything 5+.
* Written completely in Perl. XS is not required.
* Requires XML::Twig to parse the XML

AUTHOR

Peter Hickman <peterhi@ntlworld.com>

Copyright (c) 2003, Peter Hickman. All rights reserved.

This module is free software. It may be used, redistributed and/or 
modified under the same terms as Perl itself.

CHANGES IN THIS RELEASE

V1.02:
      - The explain method now works

V1.01:
      - Implemented the reset method
	  - Simpleshell now works with the consult the Tcl/Tk shell
	  - Included a copy of the Tcl/Tk shell

V1.00:
      - First release to CPAN

bin/consult  view on Meta::CPAN

#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@"

################################################################################
# To do
# -----
# Save defaults for program to run, text colour etc
################################################################################

package require cmdline

################################################################################
# Some global variables
################################################################################

set display_information 1
set filehandle ""
set filename ""
set program "simpleshell"
set error ""
set choice ""

toplevel .askme
wm withdraw .askme

################################################################################
# The question asking dialog
################################################################################

proc ask_the_user {message argv} {
	# Restore the toplevel item so that we can 
	# put the dialog box together

	wm deiconify .askme
	wm title .askme "What I want to know is?"

	# Set the question text

	label .askme.l -text $message -padx 10 -pady 10 -wraplength 300
	pack .askme.l -side top -anchor n

	# The is where the users choice will go
	global choice
	set choice ""

	# Set up each button
	set counter 1
	foreach option $argv {
		button .askme.$counter -text $option -command [list set choice $counter]
		pack .askme.$counter -side top -anchor n -fill x
		incr counter
	}

	vwait choice

	# Our option has been set, now dismantel the 
	# contents of the toplevel so we can build it
	# up again from scratch next time.

	wm withdraw .askme
	destroy .askme.l
	set counter 1
	foreach option $argv {
		destroy .askme.$counter
		incr counter
	}

	return $choice
}

################################################################################
# Setting up the window
################################################################################

proc main {} {
	wm title . "Simple Inference Engine"
	wm geometry . 500x500

	# The top frame holds the buttons

	frame .top
	button .load -text "Load..." -command "do_load" -width 8 
	button .run  -text "Run"     -command "do_run"  -width 8 -state disabled
	button .save -text "Save..." -command "do_save" -width 8 -state disabled
	checkbutton .display -text "Display 'information' messages" -variable display_information -onvalue 1 -offvalue 0
	pack .load .run .save .display -side left -anchor n -in .top
	pack .top -anchor nw

	# A log of the output is written here

	text .text -yscrollcommand {.textscroll set}
	scrollbar .textscroll -orient vertical -command {.text yview}
	pack .text -side top -anchor w -fill both -expand 1 
	pack .textscroll -side right -fill y -in .text

	# Define some colours

	.text tag config is_status      -foreground blue
	.text tag config is_question    -foreground red
	.text tag config is_response    -foreground cyan
	.text tag config is_answer      -foreground green
	.text tag config is_information -foreground grey

	# A nice hello message

	status "Welcome to the Tcl/Tk expert system shell"
	status ""
	status "This program is used to call the command line"
	status "expert system called simpleshell and allows"
	status "you to interact with it via the gui"
	status ""
	status "First select a knowledgebase to load with the Load button"
	status "Then start a consultation with the Run button"
	status "You can rerun the consultation as many times as you like"
	status ""
	status "The Save button allows you to save the contents of the"
	status "session to a file"
	status ""
	status "The \"Display 'information' messages\" check box is used"
	status "to turn the grey information messages on and off. The "
	status "information messages tell you what the expert system is"
	status "up to and can be quite voluminous"
	status ""
	status "Have fun - Peter Hickman"
	status ""

	# Do we have a command line file

	global filename
	if {$filename != ""} {
		load_a_file $filename
	}
}

################################################################################
# Code to handle the buttons
################################################################################

proc do_load {} {
	status "Load a file..."

	set types {{{XML Files} {.xml}} {{All Files} *}}

	global filename
	set filename [tk_getOpenFile -filetypes $types -title "Load a knowledge base"]

	if {$filename != ""} {
		load_a_file $filename
	} {
		status "No file was selected"
	}
}

proc load_a_file {filename} {
	if {[file isfile $filename] == 0} {
		status "The file you selected is not really a file"
	} elseif {[file readable $filename] == 0} {
		status "The file you selected in not readable"
	} else {
		.run  configure -state normal
		status "Loading file $filename"
		status "The next step is to Run it..."

		wm title . [append newtitle "Simple Inference Engine: " [file tail $filename]]
	}
}

proc do_run {} {
	status "Run the file..."
	status "This may take a few moments to get started..."
	.save configure -state normal
	.run  configure -state disabled

	global filename
	global program
	set filehandle [open "|$program -t $filename" "r+"]

	set question ''
	set responses [list]

	.text delete 0.1 end

	while {![eof $filehandle]} {
		set text [gets $filehandle]

		if {[regexp {^status:(.*)} $text match newtext]} {
			status $newtext
		} elseif {[regexp {^question:(.*)} $text match newtext]} {
			set question $newtext
			set responses [list]
			question $newtext
		} elseif {[regexp {^response:(.*)} $text match newtext]} {
			if {$newtext != "*"} {
				lappend responses "$newtext"
				response "One possible answer is => $newtext"
			} else {
				set is [ask_the_user $question $responses]
				puts $filehandle $is
				flush $filehandle
				incr is -1
				set word [lindex $responses $is]
				answer "Your answer is => $word"
			}
		} elseif {[regexp {^information:(.*)} $text match newtext]} {
			information $newtext
		} elseif {[regexp {^explaination:(.*)} $text match newtext]} {
			status $newtext
		} else {
			status $text
		}
	}

	.run  configure -state normal
}

proc do_save {} {
	status "Save the output..."

	set types { {{Text Files} {.txt} } {{All Files} * } }

	set filename [tk_getSaveFile -filetypes $types -title "Save the results of a run" -initialfile "Results.txt"]

	if {$filename != ""} {
		if {[file isfile $filename] == 0} {
			status "The file you selected is not really a file"
		} elseif {[file writeable $filename] == 0} {
			status "The file you selected in not writeable"
		} else {
			status "Saving the output to $filename"
			set handle [open $filename "w"]
			puts $handle [.text get 0.1 end]
			close $handle
		}
	} {
		status "No file was selected"
	}
}

################################################################################
# Utility functions
################################################################################

proc status      {text} { mymessage $text is_status }
proc question    {text} { mymessage $text is_question }
proc response    {text} { mymessage $text is_response }
proc answer      {text} { mymessage $text is_answer }

proc information {text} {
	global display_information
	if {$display_information == 1} {
		mymessage $text is_information
	}
}

proc mymessage {text tag} {
	.text insert end "$text\n" $tag
	.text see end
	update
}

################################################################################
# The program starts here
################################################################################

# Was there a filename on the command line

set p_count 0

while {[set err [cmdline::getopt argv {f.arg p.arg} opt val]] > 0} {
	switch -- $opt {
		f {
			if {$filename == ""} {
				set filename $val
			} {
				set error "The $opt switch should only be used once"
			}
		}
		p {
			if {$p_count == 0} {
				set program $val
				incr p_count
				if {[file executable $program] != 1} {
					set error "The program '$program' is not runable"
				}
			} {
				set error "The $opt switch should only be used once"
			}
		}
	}
}

if {$err < 0} {
	puts "There was an error: $val"
	exit
}\
elseif {$error != ""} {
	puts "There was an error: $error"
	exit
}

main

bin/simpleshell  view on Meta::CPAN

#!/usr/bin/perl

######################################################################
# This is a simple shell for the AI::ExpertSystem::Simple inference
# engine class. The -t switch is used to provide an interface for the 
# consult tcl/tk interface that allows the users to run the program
# with a tk based gui.
######################################################################

use strict;
use warnings;

$| = -1;

use Getopt::Std;

use AI::ExpertSystem::Simple;

my %opts;

getopts('t',\%opts);

my $tkinterface = defined $opts{t};

if ( scalar(@ARGV) != 1 ) { die "usage: $0 [-t] <file.xml>"; }

my $filename = $ARGV[0];

if ( !-f $filename ) { die "The file '$filename' is not a file"; }
if ( !-r $filename ) { die "The file '$filename' is not readable"; }

######################################################################
# The main loop
######################################################################

my $s = AI::ExpertSystem::Simple->new();

$s->load($filename);

say_status("Consulting $filename");

my $continue = 'yes';

while($continue eq 'yes') {
	my $running = 1;

	while ($running) {
		my $r = $s->process();

		process_log();

		if ( $r eq 'question' ) {
			$s->answer( ask_question( $s->get_question() ) );
		}
		elsif ( $r eq 'finished' ) {
			say_status('The answer is : ' . $s->get_answer());
			$s->explain();
			process_log( 'explaination' , 1);
			$running = undef;
		}
		elsif ( $r eq 'failed' ) {
			say_status("Unable to answer your question");
			$running = undef;
		}
	}

	if($tkinterface) {
		$continue = 'no';
	} else {
		$continue = ask_question( 'Another consoltation', 'yes', 'no' );
		$s->reset();
	}
}

######################################################################
# Ask a question of the user showing the available responses
######################################################################

sub ask_question {
	my ( $text, @responses ) = @_;

	my $number = scalar(@responses);
	my $x      = 0;

	while ( $x < 1 or $x > $number ) {
		say_question($text);

		for ( my $y = 1 ; $y <= $number ; $y++ ) {
			say_something('response', " $y : ", $responses[$y - 1]);
		}

		if($tkinterface) {
			say_something('response', '', '*');
		} else { 
			print '** ';
		}
		$x = <STDIN>;

		$x = 0 if $x !~ m#^[0-9]+$#;
	}

	return $responses[ $x - 1 ];
}

######################################################################
# The various ways of printing out a message
######################################################################

sub say_status   { say_something('status',   '>> ', shift) }
sub say_question { say_something('question', '',    shift) }

sub say_something {
	my ($tag1, $tag2, $text) = @_;

	if($tkinterface) {
		print "$tag1:$text\n";
	} else {
		print "$tag2$text\n";
	}
}

######################################################################
# The various ways of printing out a message
######################################################################

sub process_log {
	my ($prefix, $override) = @_;

	$prefix = 'information' unless $prefix;

	my @log = $s->log();

	if($tkinterface or $override) {
		foreach my $line (@log) {
			say_something($prefix, '', $line);
		}
	}
}

examples/Animal.xml  view on Meta::CPAN

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE knowledgebase SYSTEM "sie-1.0.dtd">
<knowledgebase>
 <goal>
  <attribute>type.animal</attribute>
  <text>I think your animal is a type.animal</text>
 </goal>
 <rules>
  <rule>
   <name>1</name>
   <conditions>
    <condition>
     <attribute>backbone</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>superphylum</attribute>
     <value>backbone</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>2</name>
   <conditions>
    <condition>
     <attribute>backbone</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>superphylum</attribute>
     <value>jellyback</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>3</name>
   <conditions>
    <condition>
     <attribute>warm.blooded</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>superphylum</attribute>
     <value>backbone</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>phylum</attribute>
     <value>warm</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>4</name>
   <conditions>
    <condition>
     <attribute>warm.blooded</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>superphylum</attribute>
     <value>backbone</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>phylum</attribute>
     <value>cold</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>5</name>
   <conditions>
    <condition>
     <attribute>live.prime.in.soil</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>superphylum</attribute>
     <value>jellyback</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>phylum</attribute>
     <value>soil</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>6</name>
   <conditions>
    <condition>
     <attribute>live.prime.in.soil</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>superphylum</attribute>
     <value>jellyback</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>phylum</attribute>
     <value>elsewhere</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>7</name>
   <conditions>
    <condition>
     <attribute>has.breasts</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>phylum</attribute>
     <value>warm</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>class</attribute>
     <value>breasts</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>8</name>
   <conditions>
    <condition>
     <attribute>has.breasts</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>phylum</attribute>
     <value>warm</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>bird/penguin</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>9</name>
   <conditions>
    <condition>
     <attribute>always.in.water</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>phylum</attribute>
     <value>cold</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>class</attribute>
     <value>water</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>10</name>
   <conditions>
    <condition>
     <attribute>always.in.water</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>phylum</attribute>
     <value>cold</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>class</attribute>
     <value>dry</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>11</name>
   <conditions>
    <condition>
     <attribute>phylum</attribute>
     <value>soil</value>
    </condition>
    <condition>
     <attribute>flat.bodied</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>flatworm</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>12</name>
   <conditions>
    <condition>
     <attribute>phylum</attribute>
     <value>soil</value>
    </condition>
    <condition>
     <attribute>flat.bodied</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>worm/leech</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>13</name>
   <conditions>
    <condition>
     <attribute>phylum</attribute>
     <value>elsewhere</value>
    </condition>
    <condition>
     <attribute>body.in.segments</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>class</attribute>
     <value>segments</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>14</name>
   <conditions>
    <condition>
     <attribute>phylum</attribute>
     <value>elsewhere</value>
    </condition>
    <condition>
     <attribute>body.in.segments</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>class</attribute>
     <value>unified</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>15</name>
   <conditions>
    <condition>
     <attribute>can.eat.meat</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>breasts</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>meat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>16</name>
   <conditions>
    <condition>
     <attribute>can.eat.meat</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>breasts</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>vegy</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>17</name>
   <conditions>
    <condition>
     <attribute>boney</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>water</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>fish</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>18</name>
   <conditions>
    <condition>
     <attribute>boney</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>water</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>shark/ray</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>19</name>
   <conditions>
    <condition>
     <attribute>class</attribute>
     <value>dry</value>
    </condition>
    <condition>
     <attribute>scally</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>scales</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>20</name>
   <conditions>
    <condition>
     <attribute>class</attribute>
     <value>dry</value>
    </condition>
    <condition>
     <attribute>scally</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>soft</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>21</name>
   <conditions>
    <condition>
     <attribute>class</attribute>
     <value>segments</value>
    </condition>
    <condition>
     <attribute>shell</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>shell</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>22</name>
   <conditions>
    <condition>
     <attribute>class</attribute>
     <value>segments</value>
    </condition>
    <condition>
     <attribute>shell</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>centiped/milliped/insect</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>23</name>
   <conditions>
    <condition>
     <attribute>digest.cells</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>unified</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>cells</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>24</name>
   <conditions>
    <condition>
     <attribute>digest.cells</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>class</attribute>
     <value>unified</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>order</attribute>
     <value>stomach</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>25</name>
   <conditions>
    <condition>
     <attribute>fly</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>meat</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>bat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>26</name>
   <conditions>
    <condition>
     <attribute>fly</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>meat</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>nowings</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>27</name>
   <conditions>
    <condition>
     <attribute>hooves</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>vegy</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>hooves</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>28</name>
   <conditions>
    <condition>
     <attribute>hooves</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>vegy</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>feet</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>29</name>
   <conditions>
    <condition>
     <attribute>rounded.shell</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>scales</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>turtle</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>30</name>
   <conditions>
    <condition>
     <attribute>rounded.shell</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>scales</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>noshell</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>31</name>
   <conditions>
    <condition>
     <attribute>jump</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>soft</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>frog</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>32</name>
   <conditions>
    <condition>
     <attribute>jump</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>soft</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>salamander</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>33</name>
   <conditions>
    <condition>
     <attribute>tail</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>shell</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>lobster</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>34</name>
   <conditions>
    <condition>
     <attribute>tail</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>shell</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>crab</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>35</name>
   <conditions>
    <condition>
     <attribute>stationary</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>cells</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>stationary</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>36</name>
   <conditions>
    <condition>
     <attribute>stationary</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>cells</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>jellyfish</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>37</name>
   <conditions>
    <condition>
     <attribute>multicelled</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>stomach</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>family</attribute>
     <value>multicelled</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>38</name>
   <conditions>
    <condition>
     <attribute>multicelled</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>order</attribute>
     <value>stomach</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>protozoa</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>39</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>nowings</value>
    </condition>
    <condition>
     <attribute>opposing.thumb</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>thumb</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>40</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>nowings</value>
    </condition>
    <condition>
     <attribute>opposing.thumb</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>nothumb</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>41</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>hooves</value>
    </condition>
    <condition>
     <attribute>two.toes</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>twotoes</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>42</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>hooves</value>
    </condition>
    <condition>
     <attribute>two.toes</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>onetoe</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>43</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>feet</value>
    </condition>
    <condition>
     <attribute>live.in.water</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>water</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>44</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>feet</value>
    </condition>
    <condition>
     <attribute>live.in.water</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>dry</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>45</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>noshell</value>
    </condition>
    <condition>
     <attribute>limbs</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>crocodile/alligator</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>46</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>noshell</value>
    </condition>
    <condition>
     <attribute>limbs</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>snake</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>47</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>stationary</value>
    </condition>
    <condition>
     <attribute>spikes</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>sea.anemone</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>48</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>stationary</value>
    </condition>
    <condition>
     <attribute>spikes</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>coral/sponge</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>49</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>multicelled</value>
    </condition>
    <condition>
     <attribute>spiral.shell</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>snail</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>50</name>
   <conditions>
    <condition>
     <attribute>family</attribute>
     <value>multicelled</value>
    </condition>
    <condition>
     <attribute>spiral.shell</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>genus</attribute>
     <value>noshell</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>51</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>thumb</value>
    </condition>
    <condition>
     <attribute>prehensile.tail</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>monkey</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>52</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>thumb</value>
    </condition>
    <condition>
     <attribute>prehensile.tail</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>notail</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>53</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>nothumb</value>
    </condition>
    <condition>
     <attribute>over.400</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>400</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>54</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>nothumb</value>
    </condition>
    <condition>
     <attribute>over.400</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>under400</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>55</name>
   <conditions>
    <condition>
     <attribute>horns</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>genus</attribute>
     <value>twotoes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>horns</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>56</name>
   <conditions>
    <condition>
     <attribute>horns</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>genus</attribute>
     <value>twotoes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>nohorns</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>57</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>onetoe</value>
    </condition>
    <condition>
     <attribute>plating</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>rhinoceros</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>58</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>onetoe</value>
    </condition>
    <condition>
     <attribute>plating</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>horse/zebra</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>59</name>
   <conditions>
    <condition>
     <attribute>hunted</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>genus</attribute>
     <value>water</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>whale</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>60</name>
   <conditions>
    <condition>
     <attribute>hunted</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>genus</attribute>
     <value>water</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>dolphin/porpoise</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>61</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>dry</value>
    </condition>
    <condition>
     <attribute>front.teeth</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>teeth</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>62</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>dry</value>
    </condition>
    <condition>
     <attribute>front.teeth</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>species</attribute>
     <value>noteeth</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>63</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>noshell</value>
    </condition>
    <condition>
     <attribute>bivalve</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>clam/oyster</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>64</name>
   <conditions>
    <condition>
     <attribute>genus</attribute>
     <value>noshell</value>
    </condition>
    <condition>
     <attribute>bivalve</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>squid/octopus</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>65</name>
   <conditions>
    <condition>
     <attribute>nearly.hairless</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>notail</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>man</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>66</name>
   <conditions>
    <condition>
     <attribute>nearly.hairless</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>notail</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>subspecies</attribute>
     <value>hair</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>67</name>
   <conditions>
    <condition>
     <attribute>species</attribute>
     <value>400</value>
    </condition>
    <condition>
     <attribute>land.based</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>bear/tiger/lion</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>68</name>
   <conditions>
    <condition>
     <attribute>species</attribute>
     <value>400</value>
    </condition>
    <condition>
     <attribute>land.based</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>walrus</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>69</name>
   <conditions>
    <condition>
     <attribute>thintail</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>under400</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>cat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>70</name>
   <conditions>
    <condition>
     <attribute>thintail</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>under400</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>coyote/wolf/fox/dog</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>71</name>
   <conditions>
    <condition>
     <attribute>one.horn</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>horns</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>hippopotamus</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>72</name>
   <conditions>
    <condition>
     <attribute>one.horn</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>horns</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>subspecies</attribute>
     <value>nohorn</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>73</name>
   <conditions>
    <condition>
     <attribute>lives.in.desert</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>nohorns</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>camel</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>74</name>
   <conditions>
    <condition>
     <attribute>lives.in.desert</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>nohorns</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>giraffe</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>75</name>
   <conditions>
    <condition>
     <attribute>large.ears</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>teeth</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>rabbit</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>76</name>
   <conditions>
    <condition>
     <attribute>large.ears</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>teeth</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>rat/mouse/squirrel/beaver/porcupine</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>77</name>
   <conditions>
    <condition>
     <attribute>pouch</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>noteeth</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>kangaroo/koala.bear</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>78</name>
   <conditions>
    <condition>
     <attribute>pouch</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>species</attribute>
     <value>noteeth</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>mole/shrew/elephant</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>79</name>
   <conditions>
    <condition>
     <attribute>long.powerful.arms</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>subspecies</attribute>
     <value>hair</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>orangutan/gorilla/chimpanzie</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>80</name>
   <conditions>
    <condition>
     <attribute>long.powerful.arms</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>subspecies</attribute>
     <value>hair</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>baboon</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>81</name>
   <conditions>
    <condition>
     <attribute>fleece</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>subspecies</attribute>
     <value>nohorn</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>sheep/goat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>82</name>
   <conditions>
    <condition>
     <attribute>fleece</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>subspecies</attribute>
     <value>nohorn</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>subsubspecies</attribute>
     <value>nofleece</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>83</name>
   <conditions>
    <condition>
     <attribute>domesticated</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>subsubspecies</attribute>
     <value>nofleece</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>cow</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>84</name>
   <conditions>
    <condition>
     <attribute>domesticated</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>subsubspecies</attribute>
     <value>nofleece</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.animal</attribute>
     <value>deer/moose/antelope</value>
    </action>
   </actions>
  </rule>
 </rules>
 <questions>
  <question>
   <attribute>long.powerful.arms</attribute>
   <text>Does your animal have long, powerful arms?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>lives.in.desert</attribute>
   <text>Does your animal normally live in the desert?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>thintail</attribute>
   <text>Does your animal have a thin tail?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>bivalve</attribute>
   <text>Is your animal protected by two half-shells?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>hunted</attribute>
   <text>Is your animal, unfortunately, commercially hunted?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>jump</attribute>
   <text>Does your animal jump?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>hooves</attribute>
   <text>Does your animal have hooves?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>always.in.water</attribute>
   <text>Is your animal always in water?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>horns</attribute>
   <text>Does your animal have horns?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>two.toes</attribute>
   <text>Does your animal stand on two toes/hooves per foot?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>flat.bodied</attribute>
   <text>Does your animal have a flat body?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>live.prime.in.soil</attribute>
   <text>Does your animal live primarily in soil?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>domesticated</attribute>
   <text>Is your animal domesticated?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>pouch</attribute>
   <text>Does your animal have a pouch?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>over.400</attribute>
   <text>Does an adult normally weigh over 400 pounds?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>opposing.thumb</attribute>
   <text>Does your animal have an opposing thumb?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>can.eat.meat</attribute>
   <text>Does your animal eat red meat?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>warm.blooded</attribute>
   <text>Is the animal warm blooded?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>limbs</attribute>
   <text>Does your animal have limbs?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>body.in.segments</attribute>
   <text>Is the animals body in segments?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>has.breasts</attribute>
   <text>Normally, does the female of your animal nurse its young with milk?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>fleece</attribute>
   <text>Does your animal have fleece?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>nearly.hairless</attribute>
   <text>Is your animal nearly hairless?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>prehensile.tail</attribute>
   <text>Does your animal have a prehensile tail?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>rounded.shell</attribute>
   <text>Does the animal have a rounded shell?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>scally</attribute>
   <text>Is your animal covered with scaled skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>boney</attribute>
   <text>Does your animal have a boney skeleton?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>large.ears</attribute>
   <text>Does your animal have large ears?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>one.horn</attribute>
   <text>Does your animal have one horn?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>land.based</attribute>
   <text>Is your animal land based?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>plating</attribute>
   <text>Is your animal covered with a protective plating?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>tail</attribute>
   <text>Does your animal have a tail?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>backbone</attribute>
   <text>Does your animal have a backbone?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>front.teeth</attribute>
   <text>Does your animal have large front teeth?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>multicelled</attribute>
   <text>Is your animal made up of more than one cell?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>stationary</attribute>
   <text>Is your animal attached permanently to an object?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>fly</attribute>
   <text>Can your animal fly?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>spiral.shell</attribute>
   <text>Does your animal have a spiral-shaped shell?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>spikes</attribute>
   <text>Does your animal normally have spikes radiating from it's body?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>live.in.water</attribute>
   <text>Does your animal live in water?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>digest.cells</attribute>
   <text>Does your animal use many cells to digest it's food instead of a stomach?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>shell</attribute>
   <text>Does your animal have a shell?</text>
   <response>yes</response>
   <response>no</response>
  </question>
 </questions>
</knowledgebase>

examples/Doctor.xml  view on Meta::CPAN

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE knowledgebase SYSTEM "sie-1.0.dtd">
<knowledgebase>
 <goal>
  <attribute>type.disease</attribute>
  <text>Based on rudimentary knowledge, I believe the child has type.disease</text>
 </goal>
 <rules>
  <rule>
   <name>1</name>
   <conditions>
    <condition>
     <attribute>active.temp.over.101</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>fever</attribute>
     <value>yes</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>2</name>
   <conditions>
    <condition>
     <attribute>delayed.cough</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cold</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>whooping.cough</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>3</name>
   <conditions>
    <condition>
     <attribute>scratchy.throat</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>clear.nasal.discharge</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>cold</attribute>
     <value>yes</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>4</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cold</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>measle.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>measles</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>5</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>squeaky.breath</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>vibration.chest</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>much.cough</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cold</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>bronchitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>6</name>
   <conditions>
    <condition>
     <attribute>wheezing</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cold</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>asthmatic.bronchitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>7</name>
   <conditions>
    <condition>
     <attribute>sinus.pain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cough.when.move</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>sinusitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>8</name>
   <conditions>
    <condition>
     <attribute>eczema</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>eczema</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>9</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>sputum</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cough</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>fatigue</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>tuberculosis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>10</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>tender.joints</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>fatigue</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>rheumatic.fever</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>11</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>touch.to.chest</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>headache</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>anterior.poliomyelitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>12</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>sore.throat</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>scarlet.rash</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>vomiting</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>headache</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>scarlet.fever</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>13</name>
   <conditions>
    <condition>
     <attribute>tonsils.swollen</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>vomiting</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>headache</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>high.fever</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>tonsillitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>14</name>
   <conditions>
    <condition>
     <attribute>rapid.breathing</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cough</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>high.fever</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>pneumonia</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>15</name>
   <conditions>
    <condition>
     <attribute>high.fever</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>roseola</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>16</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>difficulty.breathing</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>hoarse.cough</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cold</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>severe.croup</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>17</name>
   <conditions>
    <condition>
     <attribute>difficulty.breathing</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>hoarse.cough</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>croup</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>18</name>
   <conditions>
    <condition>
     <attribute>impetigo.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>impetigo</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>19</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>inflammed.ears</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>serious.ear.infection</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>20</name>
   <conditions>
    <condition>
     <attribute>inflammed.ears</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>ear.infection</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>21</name>
   <conditions>
    <condition>
     <attribute>hives.rash</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>itching</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>hives</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>22</name>
   <conditions>
    <condition>
     <attribute>ivy.rash</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>itching</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>poison.ivy</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>23</name>
   <conditions>
    <condition>
     <attribute>scabies.rash</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>itching</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>scabies</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>24</name>
   <conditions>
    <condition>
     <attribute>measle.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>german.measles</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>25</name>
   <conditions>
    <condition>
     <attribute>naval.pain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>tender.abdomen</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>appendicitis</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>26</name>
   <conditions>
    <condition>
     <attribute>neck.swelling</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>mumps</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>27</name>
   <conditions>
    <condition>
     <attribute>nervous.disease</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>chorea</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>28</name>
   <conditions>
    <condition>
     <attribute>pox.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>chicken.pox</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>29</name>
   <conditions>
    <condition>
     <attribute>prickly.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>prickly.heat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>30</name>
   <conditions>
    <condition>
     <attribute>rest.temp.over.100</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>fever</attribute>
     <value>yes</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>31</name>
   <conditions>
    <condition>
     <attribute>ringworm.rash</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>ringworm</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>32</name>
   <conditions>
    <condition>
     <attribute>sneeze</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>itchy.nose</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>allergy</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>33</name>
   <conditions>
    <condition>
     <attribute>fever</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>sore.throat</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>dirty.white.patches</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.disease</attribute>
     <value>diptheria</value>
    </action>
   </actions>
  </rule>
 </rules>
 <questions>
  <question>
   <attribute>squeaky.breath</attribute>
   <text>Does the child squeake as he breaths?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>itching</attribute>
   <text>Does the child complain of itchy or scratchy skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>high.fever</attribute>
   <text>Is the child's temperature over 103?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>fatigue</attribute>
   <text>Does the child complain of general fatigue?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>clear.nasal.discharge</attribute>
   <text>Does the child have a clear nasal discharge?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>tender.joints</attribute>
   <text>Does the child complain of tender joints?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sputum</attribute>
   <text>Is the child producing sputum?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sneeze</attribute>
   <text>Is the child sneezing?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>neck.swelling</attribute>
   <text>Does the child have extensive swelling in the side of his neck?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>ivy.rash</attribute>
   <text>Are there clusters of small blisters on reddened shiny skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>itchy.nose</attribute>
   <text>Does the child complain of an itchy nose?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>delayed.cough</attribute>
   <text>Did the child start coughing about one week after getting the cold?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>cough</attribute>
   <text>Does the child have a cough?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>impetigo.rash</attribute>
   <text>Are there pimples on the child with a partly brown crust?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>cough.when.move</attribute>
   <text>Does the child start coughing violently when he either lays down or gets up?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>vibration.chest</attribute>
   <text>Can you feel a vibration in the child's chest as he breaths?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>tender.abdomen</attribute>
   <text>Does the child have a tender abdomen on the right side?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>scratchy.throat</attribute>
   <text>Does the child have a scratchy throat?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>rapid.breathing</attribute>
   <text>Does the child have rapid, shallow breathing?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>vomiting</attribute>
   <text>Is the child vomiting?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>naval.pain</attribute>
   <text>Has the child complained of pain around his naval for several hours?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>inflammed.ears</attribute>
   <text>Does the child have inflammed ears?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>hoarse.cough</attribute>
   <text>Does the child have a hoarse cough?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sinus.pain</attribute>
   <text>Does the child have any sinus pain?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>dirty.white.patches</attribute>
   <text>Does the child have dirty white patches on his tonsils?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>scarlet.rash</attribute>
   <text>Does the child have a red blush-like rash on his skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>scabies.rash</attribute>
   <text>Are there groups of pimples topped with scabs on the child?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>ringworm.rash</attribute>
   <text>Are there circular patches of rough skin on the child?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>prickly.rash</attribute>
   <text>Does the child have patches of tan-pink pimples?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>headache</attribute>
   <text>Does the child complain of headache?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>difficulty.breathing</attribute>
   <text>Does the child have difficulty breathing?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>active.temp.over.101</attribute>
   <text>Has the child been active in the last hour and his temp is greater than 101?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>wheezing</attribute>
   <text>Is the child wheezing?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>touch.to.chest</attribute>
   <text>Is it impossible for the child to touch his chin to his chest?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>tonsils.swollen</attribute>
   <text>Are the child's tonsils swollen with white patches on them?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sore.throat</attribute>
   <text>Does the child have a sore throat?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>rest.temp.over.100</attribute>
   <text>Has the child been resting for over an hour and his temp is greater than 100?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>pox.rash</attribute>
   <text>Does the child's skin have separate, raised pimples, several with blisters?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>nervous.disease</attribute>
   <text>Does the child have twitching or writhing movements in DIFFERENT places?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>much.cough</attribute>
   <text>Does the child cough a lot, and cough syrup is roughly ineffective?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>measle.rash</attribute>
   <text>Does the child have flat pink spots on the skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>hives.rash</attribute>
   <text>Does the child have raised welts that are white in color?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>eczema</attribute>
   <text>Does the child have patches of rough, red, rash, scaly skin?</text>
   <response>yes</response>
   <response>no</response>
  </question>
 </questions>
</knowledgebase>

examples/Glass.xml  view on Meta::CPAN

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE knowledgebase SYSTEM "sie-1.0.dtd">
<knowledgebase>
 <goal>
  <attribute>type.glass</attribute>
  <text>The type of glass you have is type.glass</text>
 </goal>
 <rules>
  <rule>
   <name>1</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>pretzel</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>pretzel</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>2</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>square</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>block.optic</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>3</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>carribean</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>4</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>bead.block</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>beaded.block</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>5</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>bubble</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>bubble</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>6</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cube</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>cube</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>7</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>lace.edge</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>lace.edge</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>8</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>moonstone</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>moonstone</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>9</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>waterford</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>waterford</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>10</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>geometric</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>miss.america</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>11</name>
   <conditions>
    <condition>
     <attribute>avocado</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>avocado</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>12</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>cherry.blossom</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>cherry.blossom</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>13</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>fire.king</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>fire.king.oven.glass</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>14</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>sharon</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>sharon(cabbage.rose)</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>15</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>sunflower</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>sunflower</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>16</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>dogwood</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>17</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>alice</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>alice</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>18</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>iris</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>iris</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>19</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>mayfair</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>20</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>candlewick</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>candlewick</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>21</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>decagon</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>decagon</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>22</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>empress</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>empress</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>23</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>lariat</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>lariat</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>24</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>octagon</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>octagon</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>25</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>fairfax</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>26</name>
   <conditions>
    <condition>
     <attribute>ruby</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>royal.ruby</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>27</name>
   <conditions>
    <condition>
     <attribute>rays</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>plain</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>no</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>forest.green</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>28</name>
   <conditions>
    <condition>
     <attribute>fuchsia</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>fuchsia</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>29</name>
   <conditions>
    <condition>
     <attribute>emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>large.rose</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>rose</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>30</name>
   <conditions>
    <condition>
     <attribute>orchid</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>orchid</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>31</name>
   <conditions>
    <condition>
     <attribute>emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>pineapple</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>plantation</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>32</name>
   <conditions>
    <condition>
     <attribute>emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>apple.blossom</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>33</name>
   <conditions>
    <condition>
     <attribute>emblem</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>cupid</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>cupid</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>34</name>
   <conditions>
    <condition>
     <attribute>rose.in.emblem</attribute>
     <value>no</value>
    </condition>
    <condition>
     <attribute>emblem</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>cherokee.rose</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>35</name>
   <conditions>
    <condition>
     <attribute>rose.in.emblem</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>emblem</attribute>
     <value>yes</value>
    </condition>
    <condition>
     <attribute>etched</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>type.glass</attribute>
     <value>rose.point</value>
    </action>
   </actions>
  </rule>
 </rules>
 <questions>
  <question>
   <attribute>lariat</attribute>
   <text>Does the piece have a looped rim?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>cherry.blossom</attribute>
   <text>Are there small cherries in the pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>pretzel</attribute>
   <text>Does the glass have a laced pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>moonstone</attribute>
   <text>Is the glass beaded with a white edge?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>fuchsia</attribute>
   <text>Are there Fuchsia, or hanging flowers with stems in them, in the glass?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>etched</attribute>
   <text>Is the glass etched? (versus pressed)</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>empress</attribute>
   <text>Does the rim look as though there are knotches?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>waterford</attribute>
   <text>Does the glass have a laced pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>rays</attribute>
   <text>Are there 'rays' starting in the center of the piece moving toward the edge?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>geometric</attribute>
   <text>Is there a geometric pattern in the glass?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>fire.king</attribute>
   <text>Are flowers missing from the pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>emblem</attribute>
   <text>Does the glass bear an emblem?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>plain</attribute>
   <text>Is the glass basically without a pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>octagon</attribute>
   <text>Is the glass eight sided?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>iris</attribute>
   <text>Does the glass have iris flowers?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>cupid</attribute>
   <text>Is there a cupid figure in the emblem?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>decagon</attribute>
   <text>Is the glass ten sided?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>candlewick</attribute>
   <text>Does the glass have balls around the rim?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>avocado</attribute>
   <text>Are there two avocado plants present (looks like two pears)?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sunflower</attribute>
   <text>Are there sunflowers in the pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>pineapple</attribute>
   <text>Are there images of pineapples in the glass?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>orchid</attribute>
   <text>Are there orchids in the glass?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>lace.edge</attribute>
   <text>Does the piece have a flat rim with holes, giving a lacey apperance?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>alice</attribute>
   <text>Are there small flowers only around the rim of the piece?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>ruby</attribute>
   <text>Is the piece a deep ruby red in color?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>rose.in.emblem</attribute>
   <text>Is there a rose in the emblem?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>square</attribute>
   <text>Does the glass have a square pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>sharon</attribute>
   <text>Are there six 'spokes' in the piece?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>large.rose</attribute>
   <text>Are there large roses in the glass?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>cube</attribute>
   <text>Does the glass have a cubed pattern?</text>
   <response>yes</response>
   <response>no</response>
  </question>
  <question>
   <attribute>bead.block</attribute>
   <text>Does the glass have squares with beads connecting the squares?</text>
   <response>yes</response>
   <response>no</response>
  </question>
 </questions>
</knowledgebase>

examples/sie-1.0.dtd  view on Meta::CPAN

<!ELEMENT knowledgebase (goal, rules, questions)>

<!ELEMENT goal (attribute, text)>

<!ELEMENT rules (rule*)>
<!ELEMENT rule (name, conditions, actions)>
<!ELEMENT conditions (condition*)>
<!ELEMENT condition (attribute, value)>
<!ELEMENT actions (action*)>
<!ELEMENT action (attribute, value)>

<!ELEMENT questions (question*)>
<!ELEMENT question (attribute, text, response*)>

<!ELEMENT attribute (#PCDATA)>
<!ELEMENT text (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT value (#PCDATA)>
<!ELEMENT response (#PCDATA)>

examples/test.xml  view on Meta::CPAN

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE knowledgebase SYSTEM "sie-1.0.dtd">
<knowledgebase>
 <goal>
  <attribute>thegoal</attribute>
  <text>You have set the goal to thegoal</text>
 </goal>
 <rules>
  <rule>
   <name>1</name>
   <conditions>
    <condition>
     <attribute>one</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>thegoal</attribute>
     <value>pretzel</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>2</name>
   <conditions>
    <condition>
     <attribute>two</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>one</attribute>
     <value>yes</value>
    </action>
   </actions>
  </rule>
 </rules>
 <questions>
  <question>
   <attribute>two</attribute>
   <text>Answer yes to make this work</text>
   <response>yes</response>
   <response>no</response>
  </question>
 </questions>
</knowledgebase>

lib/AI/ExpertSystem/Simple.pm  view on Meta::CPAN

package AI::ExpertSystem::Simple;

use strict;
use warnings;

use XML::Twig;

use AI::ExpertSystem::Simple::Rule;
use AI::ExpertSystem::Simple::Knowledge;
use AI::ExpertSystem::Simple::Goal;

our $VERSION = '1.2';

sub new {
	my ($class) = @_;

	die "Simple->new() takes no arguments" if scalar(@_) != 1;

	my $self = {};

	$self->{_rules} = ();
	$self->{_knowledge} = ();
	$self->{_goal} = undef;
	$self->{_filename} = undef;

	$self->{_ask_about} = undef;
	$self->{_told_about} = undef;

	$self->{_log} = ();

	$self->{_number_of_rules} = 0;
	$self->{_number_of_attributes} = 0;
	$self->{_number_of_questions} = 0;

	return bless $self, $class;
}

sub reset {
	my ($self) = @_;

	die "Simple->reset() takes no arguments" if scalar(@_) != 1;

	foreach my $name (keys %{$self->{_rules}}) {
		$self->{_rules}->{$name}->reset();
	}

	foreach my $name (keys %{$self->{_knowledge}}) {
		$self->{_knowledge}->{$name}->reset();
	}

	$self->{_ask_about} = undef;
	$self->{_told_about} = undef;
	$self->{_log} = ();
}

sub load {
	my ($self, $filename) = @_;

	die "Simple->load() takes 1 argument" if scalar(@_) != 2;
	die "Simple->load() argument 1 (FILENAME) is undefined" if !defined($filename);

	if(-f $filename and -r $filename) {
		my $twig = XML::Twig->new(
			twig_handlers => { goal => sub { $self->_goal(@_) },
					   rule => sub { $self->_rule(@_) },
					   question => sub { $self->_question(@_) } }
		);

		$twig->safe_parsefile($filename);

		die "Simple->load() XML parse failed: $@" if $@;

		$self->{_filename} = $filename;

		$self->_add_to_log( "Read in $filename" );
		$self->_add_to_log( "There are " . $self->{_number_of_rules} . " rules" );
		$self->_add_to_log( "There are " . $self->{_number_of_attributes} . " attributes" );
		$self->_add_to_log( "There are " . $self->{_number_of_questions} . " questions" );
		$self->_add_to_log( "The goal attibutes is " . $self->{_goal}->name() );
		return 1;
	} else {
		die "Simple->load() unable to use file";
	}
}

sub _goal {
	my ($self, $t, $node) = @_;

	my $attribute = undef;
	my $text = undef;

	my $x = ($node->children('attribute'))[0];
	$attribute = $x->text();

	$x = ($node->children('text'))[0];
	$text = $x->text();

	$self->{_goal} = AI::ExpertSystem::Simple::Goal->new($attribute, $text);

	eval { $t->purge(); }
}

sub _rule {
	my ($self, $t, $node) = @_;

	my $name = undef;

	my $x = ($node->children('name'))[0];
	$name = $x->text();

	if(!defined($self->{_rules}->{$name})) {
		$self->{_rules}->{$name} = AI::ExpertSystem::Simple::Rule->new($name);
		$self->{_number_of_rules}++;
	}

	foreach $x ($node->get_xpath('//condition')) {
		my $attribute = undef;
		my $value = undef;

		my $y = ($x->children('attribute'))[0];
		$attribute = $y->text();

		$y = ($x->children('value'))[0];
		$value = $y->text();

		$self->{_rules}->{$name}->add_condition($attribute, $value);

		if(!defined($self->{_knowledge}->{$attribute})) {
			$self->{_number_of_attributes}++;
			$self->{_knowledge}->{$attribute} = AI::ExpertSystem::Simple::Knowledge->new($attribute);
		}
	}

	foreach $x ($node->get_xpath('//action')) {
		my $attribute = undef;
		my $value = undef;

		my $y = ($x->children('attribute'))[0];
		$attribute = $y->text();

		$y = ($x->children('value'))[0];
		$value = $y->text();

		$self->{_rules}->{$name}->add_action($attribute, $value);

		if(!defined($self->{_knowledge}->{$attribute})) {
			$self->{_number_of_attributes}++;
			$self->{_knowledge}->{$attribute} = AI::ExpertSystem::Simple::Knowledge->new($attribute);
		}
	}

	eval { $t->purge(); }
}

sub _question {
	my ($self, $t, $node) = @_;

	my $attribute = undef;
	my $text = undef;
	my @responses = ();

	$self->{_number_of_questions}++;

	my $x = ($node->children('attribute'))[0];
	$attribute = $x->text();

	$x = ($node->children('text'))[0];
	$text = $x->text();

	foreach $x ($node->children('response')) {
		push(@responses, $x->text());
	}

	if(!defined($self->{_knowledge}->{$attribute})) {
		$self->{_number_of_attributes}++;
		$self->{_knowledge}->{$attribute} = AI::ExpertSystem::Simple::Knowledge->new($attribute);
	}
	$self->{_knowledge}->{$attribute}->set_question($text, @responses);

	eval { $t->purge(); }
}

sub process {
	my ($self) = @_;

	die "Simple->process() takes no arguments" if scalar(@_) != 1;

	my $n = $self->{_goal}->name();

	if($self->{_knowledge}->{$n}->is_value_set()) {
		return 'finished';
	}

	if($self->{_ask_about}) {
		my %answers = ();

		$answers{$self->{_ask_about}}->{value} = $self->{_told_about};
		$answers{$self->{_ask_about}}->{setter} = '';

		$self->{_ask_about} = undef;
		$self->{_told_about} = undef;

		while(%answers) {
			my %old_answers = %answers;
			%answers = ();

			foreach my $answer (keys(%old_answers)) {
				my $n = $answer;
				my $v = $old_answers{$answer}->{value};
				my $s = $old_answers{$answer}->{setter};

				$self->_add_to_log( "Setting '$n' to '$v'" );

				$self->{_knowledge}->{$n}->set_value($v,$s);

				foreach my $key (keys(%{$self->{_rules}})) {
					if($self->{_rules}->{$key}->state() eq 'active') {
						my $state = $self->{_rules}->{$key}->given($n, $v);
						if($state eq 'completed') {
							$self->_add_to_log( "Rule '$key' has completed" );
							my %y = $self->{_rules}->{$key}->actions();
							foreach my $k (keys(%y)) {
								$self->_add_to_log( "Rule '$key' is setting '$k' to '$y{$k}'" );
								$answers{$k}->{value} = $y{$k};
								$answers{$k}->{setter} = $key;
							}
						} elsif($state eq 'invalid') {
							$self->_add_to_log( "Rule '$key' is now inactive" );
						}
					}
				}
			}
		}

		return 'continue';
	} else {
		my %scoreboard = ();

		foreach my $rule (keys(%{$self->{_rules}})) {
			if($self->{_rules}->{$rule}->state() eq 'active') {
				my @listofquestions = $self->{_rules}->{$rule}->unresolved();
				my $ok = 1;
				my @questionstoask = ();
				foreach my $name (@listofquestions) {
					if($self->{_knowledge}->{$name}->has_question()) {
						push(@questionstoask, $name);
					} else {
						$ok = 0;
					}
				}

				if($ok == 1) {
					foreach my $name (@questionstoask) {
						$scoreboard{$name}++;
					}
				}
			}
		}

		my $max_value = 0;

		foreach my $name (keys(%scoreboard)) {
			if($scoreboard{$name} > $max_value) {
				$max_value = $scoreboard{$name};
				$self->{_ask_about} = $name;
			}
		}

		return $self->{_ask_about} ? 'question' : 'failed';
	}
}

sub get_question {
	my ($self) = @_;

	die "Simple->get_question() takes no arguments" if scalar(@_) != 1;

	return $self->{_knowledge}->{$self->{_ask_about}}->get_question();
}

sub answer {
	my ($self, $value) = @_;

	die "Simple->answer() takes 1 argument" if scalar(@_) != 2;
	die "Simple->answer() argument 1 (VALUE) is undefined" if ! defined($value);

	$self->{_told_about} = $value;
}

sub get_answer {
	my ($self) = @_;

	die "Simple->get_answer() takes no arguments" if scalar(@_) != 1;

	my $n = $self->{_goal}->name();

	return $self->{_goal}->answer($self->{_knowledge}->{$n}->get_value());
}

sub log {
	my ($self) = @_;

	die "Simple->log() takes no arguments" if scalar(@_) != 1;

	my @return = ();
	@return = @{$self->{_log}} if defined @{$self->{_log}};

	$self->{_log} = ();

	return @return;
}

sub _add_to_log {
	my ($self, $message) = @_;

	push( @{$self->{_log}}, $message );
}

sub explain {
	my ($self) = @_;

	die "Simple->explain() takes no arguments" if scalar(@_) != 1;

	my $name  = $self->{_goal}->name();
	my $rule  = $self->{_knowledge}->{$name}->get_setter();
	my $value = $self->{_knowledge}->{$name}->get_value();

	my $x = "The goal '$name' was set to '$value' by " . ($rule ? "rule '$rule'" : 'asking a question' );
	$self->_add_to_log( $x );

	my @processed_rules;
	push( @processed_rules, $rule ) if $rule;

	$self->_explain_this( $rule, '', @processed_rules );
}

sub _explain_this {
	my ($self, $rule, $depth, @processed_rules) = @_;

	$self->_add_to_log( "${depth}Explaining rule '$rule'" );

	my %dont_do_these = map{ $_ => 1 } @processed_rules;

	my @check_these_rules = ();

	my %conditions = $self->{_rules}->{$rule}->conditions();
	foreach my $name (sort keys %conditions) {
		my $value = $conditions{$name};
		my $setter = $self->{_knowledge}->{$name}->get_setter();

		my $x = "$depth Condition '$name' was set to '$value' by " . ($setter ? "rule '$setter'" : 'asking a question' );
		$self->_add_to_log( $x );

		if($setter) {
			unless($dont_do_these{$setter}) {
				$dont_do_these{$setter} = 1;
				push( @check_these_rules, $setter );
			}
		}
	}

	my %actions = $self->{_rules}->{$rule}->actions();
	foreach my $name (sort keys %actions) {
		my $value = $actions{$name};

		my $x = "$depth Action set '$name' to '$value'";
		$self->_add_to_log( $x );
	}

	@processed_rules = keys %dont_do_these;

	foreach my $x ( @check_these_rules ) {
		push( @processed_rules, $self->_explain_this( $x, "$depth ", keys %dont_do_these ) );
	}

	return @processed_rules;
}

1;

=head1 NAME

AI::ExpertSystem::Simple - A simple expert system shell

=head1 VERSION

This document refers to verion 1.2 of AI::ExpertSystem::Simple, released June 10, 2003

=head1 SYNOPSIS

This class implements a simple expert system shell that reads the rules from an XML 
knowledge base and questions the user as it attempts to arrive at a conclusion.

=head1 DESCRIPTION

=head2 Overview

This class is where all the work is being done and the other three classes are only 
there for support. At present there is little you can do with it other than run it. Future 
version will make subclassing of this class feasable and features like logging will be introduced.

To see how to use this class there is a simple shell in the bin directory which allows you 
to consult the example knowledge bases and more extensive documemtation in the docs directory.

There is a Ruby version that reads the same XML knowledge bases, if you are interested.

=head2 Constructors and initialisation

=over 4

=item new( )

The constructor takes no arguments and just initialises a few basic variables.

=back

=head2 Public methods

=over 4

=item reset( )

Resets the system back to its initial state so that a new consoltation can be run

=item load( FILENAME )

This method takes the FILENAME of an XML knowledgebase and attempts to parse it to set up the data structures 
required for a consoltation.

=item process( )

Once the knowledgebase is loaded the consultation is run by repeatedly calling this method.

It returns four results:

=over 4

=item "question"

The system has a question to ask of the user.

The question and list of valid responses is available from the get_question( ) method and the users response should be returned via the answer( ) method. 

Then simply call the process( ) method again.

=item "continue"

The system has calculated some data but has nothing to ask the user but has still not finished.

This response will be removed in future versions.

Simply call the process( ) method again.

=item "finished"

The consoltation has finished and the system has an answer for the user which is available from the answer( ) method.

=item "failed"

The consoltation has finished and the system has failed to find an answer for the user. It happens.

=back

=item get_question( )

If the process( ) method has returned "question" then this method will return the question to ask the user 
and a list of valid responses.

=item answer( VALUE )

The user has been presented with the question from the get_question( ) method along with a set of 
valid responses and the users selection is returned by this method.

=item get_answer( )

If the process( ) method has returned "finished" then the answer to the users query will be 
returned by this method.

=item log( )

Returns a list of the actions undertaken so far and clears the log.

=item explain( )

Explain how the given answer was arrived at. The explanation is added to the log.

=back

=head2 Private methods

=over 4

=item _goal

A private method to get the goal data from the knowledgebase.

=item _rule

A private method to get the rule data from the knowledgebase.

=item _question

A private method to get the question data from the knowledgebase.

=item _add_to_log

A private method to add a message to the log.

=item _explain_this

A private method to explain how a single attribute was set.

=back

=head1 ENVIRONMENT

None

=head1 DIAGNOSTICS

=over 4

=item Simple->new() takes no arguments

When the constructor is initialised it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->reset() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->load() takes 1 argument

When the method is called it requires one argument. This message is given if more or 
less arguments were supplied.

=item Simple->load() argument 1 (FILENAME) is undefined

The corrct number of arguments were supplied with the method call, however the first 
argument, FILENAME, was undefined.

=item Simple->load() XML parse failed

XML Twig encountered some errors when trying to parse the XML knowledgebase.

=item Simple->load() unable to use file

The file supplied to the load( ) method could not be used as it was either not a file 
or not readable.

=item Simple->process() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->get_question() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->answer() takes 1 argument

When the method is called it requires one argument. This message is given if more or 
less arguments were supplied.

=item Simple->answer() argument 1 (VALUE) is undefined

The corrct number of arguments were supplied with the method call, however the first 
argument, VALUE, was undefined.

=item Simple->get_answer() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->log() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=item Simple->explain() takes no arguments

When the method is called it requires no arguments. This message is given if 
some arguments were supplied.

=back

=head1 BUGS

None

=head1 FILES

See the Simple.t file in the test directory and simpleshell in the bin directory.

=head1 SEE ALSO

AI::ExpertSystem::Simple::Goal - A utility class

AI::ExpertSystem::Simple::Knowledge - A utility class

AI::ExpertSystem::Simple::Rule - A utility class

=head1 AUTHORS

Peter Hickman (peterhi@ntlworld.com)

=head1 COPYRIGHT

Copyright (c) 2003, Peter Hickman. All rights reserved.

This module is free software. It may be used, redistributed and/or 
modified under the same terms as Perl itself.

lib/AI/ExpertSystem/Simple/Goal.pm  view on Meta::CPAN

package AI::ExpertSystem::Simple::Goal;

use strict;
use warnings;

our $VERSION = '1.0';

sub new {
	my ($class, $name, $message) = @_;

	# Check the input

	die "Goal->new() takes 2 arguments" if scalar(@_) != 3;
	die "Goal->new() argument 1 (NAME) is undefined" if ! defined($name);
	die "Goal->new() argument 2 (MESSAGE) is undefined" if ! defined($message);

	# All OK, create the object

	my $self = {};

	$self->{_name} = $name;
	$self->{_message} = $message;

	return bless $self, $class;
}

sub is_goal {
	my ($self, $name) = @_;

	# Check the input

	die "Goal->is_goal() takes 1 argument" if scalar(@_) != 2;
	die "Goal->is_goal() argument 1 (NAME) is undefined" if ! defined($name);

	# All OK, do the stuff

	return $self->{_name} eq $name;
}

sub name {
	my ($self) = @_;

	# Check the input

	die "Goal->name() takes no arguments" if scalar(@_) != 1;

	# All OK, do the stuff

	return $self->{_name};
}

sub answer {
	my ($self, $value) = @_;

	# Check the input

	die "Goal->answer() takes 1 argument" if scalar(@_) != 2;
	die "Goal->answer() argument 1 (VALUE) is undefined" if ! defined($value);

	# All OK, do the stuff

	my @text = ();

	foreach my $word (split('\s', $self->{_message})) {
		if($word eq $self->{_name}) {
			push(@text, $value);
		} else {
			push(@text, $word);
		}
	}

	return join(' ', @text);
}

1;

=head1 NAME

AI::ExpertSystem::Simple::Goal - Utility class for a simple expert system

=head1 VERSION

This document refers to verion 1.00 of AI::ExpertSystem::Simple::Goal, released April 25, 2003

=head1 SYNOPSIS

This class handles the goal in the expert system and returns the answer when the goal is matched.

=head1 DESCRIPTION

=head2 Overview

This is a utility class for AI::ExpertSystem::Simple

=head2 Constructors and initialisation

=over 4

=item new( NAME, MESSAGE )

The constructor takes two arguments. The first, NAME, is the name of the attribute that when set will
trigger the end of the consoltation. The second argument, MESSAGE, is the text that will be interpolated
as the answer for the consoltation.

=back

=head2 Public methods

=over 4

=item is_goal( NAME )

This method compares the given NAME with that of the attribute name given when the object was constructed and
returns true if they are the same or false if not.

=item name( )

This method return the value of the NAME argument that was set when the object was constructed.

=item answer( VALUE )

This method take VALUE to be the value of the goal attribute and will use it to interpolate and return the MESSAGE that was given 
when the object was constructed.

=back

=head2 Private methods

None

=head1 ENVIRONMENT

None

=head1 DIAGNOSTICS

=over 4

=item Goal->new() takes 2 arguments

When the constructor is initialised it requires two arguments. This message is given if more or less arguments were supplied.

=item Goal->new() argument 1 (NAME) is undefined

The correct number of arguments were supplied to the constructor, however the first argument, NAME, was undefined.

=item Goal->new() argument 2 (MESSAGE) is undefined

The correct number of arguments were supplied to the constructor, however the second argument, MESSAGE, was undefined.

=item Goal->is_goal() takes 1 argument

When the method is called it requires one argument. This message is given if more or less arguments were supplied.

=item Goal->is_goal() argument 1 (NAME) is undefined

The correct number of arguments were supplied with the method call, however the first argument, NAME, was undefined.

=item Goal->name() takes no arguments

When the method is called it takes no arguments. This message is given if some were supplied.

=item Goal->answer() takes 1 argument

When the method is called it requires one argument. This message is given if more or less arguments were supplied.

=item Goal->answer() argument 1 (VALUE) is undefined

The correct number of arguments were supplied with the method call, however the first argument, VALUE, was undefined.

=back

=head1 BUGS

None to date

=head1 FILES

See Goal.t in the test directory

=head1 SEE ALSO

AI::ExpertSystem::Simple - The base class for the expert system shell

AI::ExpertSystem::Simple::Knowledge - A utility class

AI::ExpertSystem::Simple::Rules - A utility class

=head1 AUTHORS

Peter Hickman (peterhi@ntlworld.com)

=head1 COPYRIGHT

Copyright (c) 2003, Peter Hickman. All rights reserved.

This module is free software. It may be used, redistributed and/or 
modified under the same terms as Perl itself.

lib/AI/ExpertSystem/Simple/Knowledge.pm  view on Meta::CPAN

package AI::ExpertSystem::Simple::Knowledge;

use strict;
use warnings;

our $VERSION = '1.2';

sub new {
	my ($class, $name) = @_;

    die "Knowledge->new() takes 1 argument" if scalar(@_) != 2;
    die "Knowledge->new() argument 1, (NAME) is undefined" if ! defined($name);

	my $self = {};

	$self->{_name} = $name;
	$self->{_value} = undef;
	$self->{_setter} = undef;
	$self->{_question} = undef;
	$self->{_responses} = ();

	return bless $self, $class;
}

sub reset {
	my ($self) = @_;

	die "Knowledge->reset() takes no arguments" if scalar(@_) != 1;

	$self->{_value} = undef;
	$self->{_setter} = undef;
}

sub set_value {
	my ($self, $value, $setter) = @_;

    die "Knowledge->set_value() takes 2 argument" if scalar(@_) != 3;
    die "Knowledge->set_value() argument 1, (VALUE) is undefined" if ! defined($value);
    die "Knowledge->set_value() argument 2, (SETTER) is undefined" if ! defined($setter);

	if(defined($self->{_value})) {
		die "Knowledge->set_value() has already been set";
	}

	$self->{_value} = $value;
	$self->{_setter} = $setter;
}

sub get_value {
	my ($self) = @_;

        die "Knowledge->get_value() takes no arguments" if scalar(@_) != 1;

	return $self->{_value};
}

sub get_setter {
	my ($self) = @_;

	die "Knowledge->get_setter() takes no arguments" if scalar(@_) != 1;

	return $self->{_setter};
}

sub is_value_set {
	my($self) = @_;

        die "Knowledge->is_value_set() takes no arguments" if scalar(@_) != 1;

	return defined($self->{_value});
}

sub set_question {
	my ($self, $question, @responses) = @_;

	if(defined($self->{_question})) {
		die "Knowledge->set_question() has already been set";
	}

        die "Knowledge->set_question() takes 2 arguments" if scalar(@_) < 3;
        die "Knowledge->set_question() argument 1, (QUESTION) is undefined" if ! defined($question);
#		This test just doesnt work for a list
#		die "Knowledge->set_question() argument 2, (RESPONSES) is undefined" if scalar(@responses) == 0;

	$self->{_question} = $question;
	push(@{$self->{_responses}}, @responses);
}

sub get_question {
	my ($self) = @_;

        die "Knowledge->get_question() takes no arguments" if scalar(@_) != 1;

	if(!defined($self->{_question})) {
		die "Knowledge->set_question() has not been set";
	}

	return ($self->{_question}, @{$self->{_responses}});
}

sub has_question {
	my ($self) = @_;

        die "Knowledge->has_question() takes no arguments" if scalar(@_) != 1;

	return (defined($self->{_question}) and !defined($self->{_value}));
}

sub name {
	my ($self) = @_;

        die "Knowledge->name() takes no arguments" if scalar(@_) != 1;

	return $self->{_name};
}

1;

=head1 NAME

AI::ExpertSystem::Simple::Knowledge - Utility class for a simple expert system

=head1 VERSION

This document refers to verion 1.2 of AI::ExpertSystem::Simple::Knowledge, released June 10, 2003

=head1 SYNOPSIS

This class handles the attributes and their values within the expert system along with the optional question that
can be asked of the user to set the value of the attribute. The valid responses to the optional question are also held.

=head1 DESCRIPTION

=head2 Overview

This is a utility class for AI::ExpertSystem::Simple

=head2 Constructors and initialisation

=over 4

=item new( NAME )

The constructor sets up the basic attribute name / value pairing. In the base case an attribute has a name with no value.

Optional questions and the valid responses can be set later and the value of the attribute is set during the consultation.

=back

=head2 Public methods

=over 4

=item reset( )

Resets the state of knowledge back to what it was when the object was created

=item set_value( VALUE, SETTER )

During the consultation process the VALUE for an attribute can be set by either asking the user a question or by deduction. The value is then recorded along with the rule that set the value (or blank it if was a question).

=item get_value( )

Returns the current value of the attribute.

=item get_setter( )

Returns the current setter of the attribute.

=item is_value_set( )

Returns true if the value of the attribute is set or false if not.

=item set_question( QUESTION, RESPONSES )

Allows a question to ask of the user to set the value of the attribute. QUESTION is the text that will be displayed to the user and RESPONSES is a list of valid responses that the user may select from.

=item get_question( )

Returns the QUESTION and list of valid RESPONSES for the attribute.

=item has_question( )

Returns true if the attribute has a question to ask the user if the VALUE of the attribute has not already been set.

=item name( )

This method returns the value of the NAME argument that was set when the object was constructed.

=back

=head2 Private methods

None

=head1 ENVIRONMENT

None

=head1 DIAGNOSTICS

=over 4

=item Knowledge->new() takes 1 argument

When the constructor is initialised it requires one argument. This message is given if more of less arguments are given.

=item Knowledge->new() argument 1, (NAME) is undefined

The correct number of arguments were supplied to the constructor, however the first argument, NAME, was undefined.

=item Knowledge->reset() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->set_value() takes 2 argument

When the method is called it requires two arguments. This message is given if more of less arguments are given.

=item Knowledge->set_value() argument 1, (VALUE) is undefined

The correct number of arguments were supplied to the method call, however the first argument, VALUE, was undefined.

=item Knowledge->set_value() argument 2, (SETTER) is undefined

The correct number of arguments were supplied to the method call, however the second argument, SETTER, was undefined.

=item Knowledge->set_value() has already been set

This method has already been called and the value set. It cannot be called twice.

=item Knowledge->get_value() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->get_setter() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->is_value_set() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->set_question() takes 2 arguments

When the method is called it requires two arguments. This message is given if more of less arguments are given.

=item Knowledge->set_question() argument 1, (QUESTION) is undefined

The correct number of arguments were supplied to the method call, however the first argument, QUESTION, was undefined.

=item Knowledge->set_question() has already been set

This method has already been called and the value set. It cannot be called twice.

=item Knowledge->get_question() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->get_question() has not been set

The value has not been set by Knowledge->set_question() and, therefore, cannot be retrieved.

=item Knowledge->has_question() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=item Knowledge->name() takes no arguments

When the method is called it requires no arguments. This message is given if some where supplied.

=back

=head1 BUGS

None

=head1 FILES

See Knowledge.t in the test directory

=head1 SEE ALSO

AI::ExpertSystem::Simple - The base class for the expert system shell

AI::ExpertSystem::Simple::Goal - A utility class

AI::ExpertSystem::Simple::Rules - A utility class

=head1 AUTHORS

Peter Hickman (peterhi@ntlworld.com)

=head1 COPYRIGHT

Copyright (c) 2003, Peter Hickman. All rights reserved.

This module is free software. It may be used, redistributed and/or 
modified under the same terms as Perl itself.

lib/AI/ExpertSystem/Simple/Rule.pm  view on Meta::CPAN

package AI::ExpertSystem::Simple::Rule;

use strict;
use warnings;

our $VERSION = '1.2';

sub new {
	my ($class, $name) = @_;

	die "Rule->new() takes 1 argument" if(scalar(@_) != 2);
	die "Rule->new() argument 1 (NAME) is undefined" if(!defined($name));

	my $self = {};

	$self->{_name} = $name;
	$self->{_conditions} = ();
	$self->{_tested} = ();
	$self->{_counter} = 0;
	$self->{_actions} = ();
	$self->{_state} = 'active';

	return bless $self, $class;
}

sub reset {
	my ($self) = @_;

	# Check the input

	die "Rule->reset() takes no arguments" if scalar(@_) != 1;

	$self->{_state} = 'active';
	$self->{_counter} = 0;

	foreach my $name (keys %{$self->{_tested}}) {
		$self->{_tested}->{$name} = 0;
		$self->{_counter}++;
	}
}

sub add_condition {
	my ($self, $name, $value) = @_;

	die "Rule->add_condition() takes 2 arguments" if(scalar(@_) != 3);
	die "Rule->add_condition() argument 1 (NAME) is undefined" if(!defined($name));
	die "Rule->add_condition() argument 2 (VALUE) is undefined" if(!defined($value));

	if(defined($self->{_conditions}->{$name})) {
		die "Rule->add_condition() has already been set";
	}

	$self->{_conditions}->{$name} = $value;
	$self->{_tested}->{$name} = 0;
	$self->{_counter}++;
}

sub add_action {
	my ($self, $name, $value) = @_;

	die "Rule->add_action() takes 2 arguments" if(scalar(@_) != 3);
	die "Rule->add_action() argument 1 (NAME) is undefined" if(!defined($name));
	die "Rule->add_action() argument 2 (VALUE) is undefined" if(!defined($value));

	if(defined($self->{_actions}->{$name})) {
		die "Rule->add_action() has already been set";
	}

	$self->{_actions}->{$name} = $value;
}

sub name {
	my ($self) = @_;

	die "Rule->name() takes no arguments" if(scalar(@_) != 1);

	return $self->{_name};
}

sub state {
	my ($self) = @_;

	die "Rule->state() takes no arguments" if(scalar(@_) != 1);

	return $self->{_state};
}

sub given {
	my ($self, $name, $value) = @_;

	die "Rule->given() takes 2 arguments" if(scalar(@_) != 3);
	die "Rule->given() argument 1 (NAME) is undefined" if(!defined($name));
	die "Rule->given() argument 2 (VALUE) is undefined" if(!defined($value));

	if(defined($self->{_conditions}->{$name})) {
		if($self->{_tested}->{$name} == 1) {
			# Already done this one
		} elsif($self->{_conditions}->{$name} eq $value) {
			$self->{_tested}->{$name} = 1;
			$self->{_counter}--;
			if($self->{_counter} == 0) {
				$self->{_state} = 'completed';
			}
		} else {
			$self->{_state} = 'invalid';
		}
	}

	return $self->{_state};
}

sub actions {
	my ($self) = @_;

	die "Rule->actions() takes no arguments" if(scalar(@_) != 1);

	return %{$self->{_actions}};
}

sub conditions {
	my ($self) = @_;

	die "Rule->conditions() takes no arguments" if(scalar(@_) != 1);

	return %{$self->{_conditions}};
}

sub unresolved {
	my ($self) = @_;

	die "Rule->unresolved() takes no arguments" if(scalar(@_) != 1);

	my @list = ();

	foreach my $name (keys(%{$self->{_tested}})) {
		if($self->{_tested}->{$name} == 0) {
			push(@list, $name);
		}
	}

	return @list;
}

1;

=head1 NAME

AI::ExpertSystem::Simple::Rule - A utility class for a simple expert system

=head1 VERSION

This document refers to verion 1.2 of AI::ExpertSystem::Simple::Rule, released June 10, 2003

=head1 SYNOPSIS

This is a utility class for AI::ExpertSystem::Simple

=head1 DESCRIPTION

=head2 Overview

This class handles the rules

=head2 Constructors and initialisation

=over 4

=item new( NAME )

The constructor takes one argument, the NAME of the rule. The consition and actions are added later.

=back

=head2 Public methods

=over 4

=item reset( )

Resets the state of the rule back to active and all the condition attributes to untested.

=item add_condition( NAME, VALUE )

This adds a condition attribute name / value pair.

=item add_action( NAME, VALUE )

This adds an action attribute name / value pair.

=item name( )

Returns the name of the rule.

=item state( )

Returns the current state of the rule.

=item given( NAME, VALUE )

The NAME / VALUE attribute pair is checked against the rule's conditions to see if a condition is met and the state of the rule 
is changed in light of the result.

=item actions( )

Returns a list of the actions set in the rule.

=item conditions( )

Returns a list of the conditions matched in the rule.

=item unresolved( )

Returns a list of all the unresolved condition of the rule.

=back

=head2 Private methods

None

=head1 ENVIRONMENT

None

=head1 DIAGNOSTICS

=over 4

=item Rule->new() takes 1 argument

When the constructor is initialised it requires one argument. This message is given if more or less arguments were supplied.

=item Rule->new() argument 1 (NAME) is undefined

The corrct number of arguments were supplied to the constructor, however the first argument, NAME, was undefined.

=item Rule->reset() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=item Rule->add_condition() takes 2 arguments

When the method is called it requires two arguments. This message is given if more or less arguments were supplied.

=item Rule->add_condition() argument 1 (NAME) is undefined

The corrct number of arguments were supplied with the method call, however the first argument, NAME, was undefined.

=item Rule->add_condition() argument 2 (VALUE) is undefined

The corrct number of arguments were supplied with the method call, however the second argument, VALUE, was undefined.

=item Rule->add_condition() has already been set

This method has already been called and the value set. It cannot be called twice.

=item Rule->add_action() takes 2 arguments

When the method is called it requires two arguments. This message is given if more or less arguments were supplied.

=item Rule->add_action() argument 1 (NAME) is undefined

The corrct number of arguments were supplied with the method call, however the first argument, NAME, was undefined.

=item Rule->add_action() argument 2 (VALUE) is undefined

The corrct number of arguments were supplied with the method call, however the second argument, VALUE, was undefined.

=item Rule->add_action() has already been set

This method has already been called and the value set. It cannot be called twice.

=item Rule->name() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=item Rule->state() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=item Rule->given() takes 2 arguments

When the method is called it requires two arguments. This message is given if more or less arguments were supplied.

=item Rule->given() argument 1 (NAME) is undefined

The corrct number of arguments were supplied with the method call, however the first argument, NAME, was undefined.

=item Rule->given() argument 2 (VALUE) is undefined

The corrct number of arguments were supplied with the method call, however the second argument, VALUE, was undefined.

=item Rule->actions() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=item Rule->conditions() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=item Rule->unresolved() takes no arguments

When the method is called it requires no arguments. This message is given if more or less arguments were supplied.

=back

=head1 BUGS

None

=head1 FILES

See Rules.t in the test directory

=head1 SEE ALSO

AI::ExpertSystem::Simple - The base class for the expert system

AI::ExpertSystem::Simple::Goal - A utility class

AI::ExpertSystem::Simple::knowledge - A utility class

=head1 AUTHORS

Peter Hickman (peterhi@ntlworld.com)

=head1 COPYRIGHT

Copyright (c) 2003, Peter Hickman. All rights reserved.

This module is free software. It may be used, redistributed and/or 
modified under the same terms as Perl itself.

t/Goal.t  view on Meta::CPAN

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 18;

################################################################################
# Load the class
################################################################################

use_ok('AI::ExpertSystem::Simple::Goal');

################################################################################
# Create a AI::ExpertSystem::Simple::Goal incorrectly
################################################################################

eval { my $x = AI::ExpertSystem::Simple::Goal->new(); };
like($@, qr/^Goal->new\(\) takes 2 arguments /, 'Too few arguments');

eval { my $x = AI::ExpertSystem::Simple::Goal->new('fred'); };
like($@, qr/^Goal->new\(\) takes 2 arguments /, 'Too few arguments');

eval { my $x = AI::ExpertSystem::Simple::Goal->new('fred', 'a message', 'that is too long'); };
like($@, qr/^Goal->new\(\) takes 2 arguments /, 'Too many arguments');

eval { my $x = AI::ExpertSystem::Simple::Goal->new(undef, 'message'); };
like($@, qr/^Goal->new\(\) argument 1 \(NAME\) is undefined /, 'Name is undefined');

eval { my $x = AI::ExpertSystem::Simple::Goal->new('fred', undef); };
like($@, qr/^Goal->new\(\) argument 2 \(MESSAGE\) is undefined /, 'Message is undefined');

################################################################################
# Create a AI::ExpertSystem::Simple::Goal correctly
################################################################################

my $x = AI::ExpertSystem::Simple::Goal->new('fred', 'this is the fred');
isa_ok($x, 'AI::ExpertSystem::Simple::Goal');

################################################################################
# Check that the name is recalled
################################################################################

eval { $x->name('fred'); };
like($@, qr/^Goal->name\(\) takes no arguments /, 'Too many arguments');

is($x->name(), 'fred', 'Remember our name');

################################################################################
# Check the goal
################################################################################

eval { $x->is_goal(); };
like($@, qr/^Goal->is_goal\(\) takes 1 argument /, 'Too few arguments');

eval { $x->is_goal(1, 2); };
like($@, qr/^Goal->is_goal\(\) takes 1 argument /, 'Too many arguments');

eval { $x->is_goal(undef); };
like($@, qr/^Goal->is_goal\(\) argument 1 \(NAME\) is undefined /, 'name is undefined');

is($x->is_goal('fred'), '1', 'Matches the goal');
is($x->is_goal('tom'), '', 'Does not matches the goal');

################################################################################
# Check the goal
################################################################################

eval { $x->answer(); };
like($@, qr/^Goal->answer\(\) takes 1 argument /, 'Too few arguments');

eval { $x->answer(1, 2); };
like($@, qr/^Goal->answer\(\) takes 1 argument /, 'Too many arguments');

eval { $x->answer(undef); };
like($@, qr/^Goal->answer\(\) argument 1 \(VALUE\) is undefined /, 'Value is undefined');

is($x->answer('banana'), 'this is the banana', 'Get the answer');

t/Knowledge.t  view on Meta::CPAN

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 44;

################################################################################
# Load the class
################################################################################

use_ok('AI::ExpertSystem::Simple::Knowledge');

################################################################################
# Create a Rule incorrectly
################################################################################

eval { my $x = AI::ExpertSystem::Simple::Knowledge->new(); };
like($@, qr/^Knowledge->new\(\) takes 1 argument /, 'Too few arguments');

eval { my $x = AI::ExpertSystem::Simple::Knowledge->new(1,2); };
like($@, qr/^Knowledge->new\(\) takes 1 argument /, 'Too many arguments');

eval { my $x = AI::ExpertSystem::Simple::Knowledge->new(undef); };
like($@, qr/^Knowledge->new\(\) argument 1, \(NAME\) is undefined /, 'Name is undefined');
isnt($@, '', 'Died, incorrect number of arguments');

################################################################################
# Create a Rule correctly
################################################################################

my $x = AI::ExpertSystem::Simple::Knowledge->new('fred');
isa_ok($x, 'AI::ExpertSystem::Simple::Knowledge');

################################################################################
# Can we remember our name
################################################################################

eval { $x->name(1); };
like($@, qr/^Knowledge->name\(\) takes no arguments /, 'Too many arguments');

is($x->name(), 'fred', 'Checking the name');

################################################################################
# Check the question attribute
################################################################################

eval { $x->has_question(1); };
like($@, qr/^Knowledge->has_question\(\) takes no arguments /, 'Too many arguments');

eval { $x->set_question(1); };
like($@, qr/^Knowledge->set_question\(\) takes 2 arguments /, 'Too few arguments');

eval { $x->set_question(undef,2); };
like($@, qr/^Knowledge->set_question\(\) argument 1, \(QUESTION\) is undefined /, 'Question is undefined');

eval { $x->get_question(); };
like($@, qr/^Knowledge->set_question\(\) has not been set /, 'Question not set');

is($x->has_question(), '', 'No question has been set');
$x->set_question('to be or not to be', ('be', 'not'));
is($x->has_question(), '1', 'The question is now set');

eval { $x->set_question('fredfred', (1,2)); };
like($@, qr/^Knowledge->set_question\(\) has already been set /, 'Is already set');

################################################################################
# Check the question attribute
################################################################################

is($x->get_value(), undef, 'The value is unset');
is($x->has_question(), '1', 'The question has not been answered');

eval { $x->set_value(); };
like($@, qr/^Knowledge->set_value\(\) takes 2 argument /, 'Too few arguments');

eval { $x->set_value(1); };
like($@, qr/^Knowledge->set_value\(\) takes 2 argument /, 'Too few arguments');

eval { $x->set_value(1,2,3); };
like($@, qr/^Knowledge->set_value\(\) takes 2 argument /, 'Too many arguments');

eval { $x->set_value(undef,2); };
like($@, qr/^Knowledge->set_value\(\) argument 1, \(VALUE\) is undefined /, 'Value is undefined');

eval { $x->set_value(1,undef); };
like($@, qr/^Knowledge->set_value\(\) argument 2, \(SETTER\) is undefined /, 'Value is undefined');

eval { $x->is_value_set(1); };
like($@, qr/^Knowledge->is_value_set\(\) takes no arguments /, 'Too many arguments');

is($x->is_value_set(), '', 'Value is not set');

$x->set_value('fred', 'banana');

is($x->is_value_set(), '1', 'Value is set');

eval { $x->set_value('fredfred', 'banana'); };
like($@, qr/^Knowledge->set_value\(\) has already been set /, 'Is already set');

eval { $x->get_value(1); };
like($@, qr/^Knowledge->get_value\(\) takes no arguments /, 'Too many arguments');

eval { $x->get_setter(1); };
like($@, qr/^Knowledge->get_setter\(\) takes no arguments /, 'Too many arguments');

is($x->get_value(), 'fred', 'The value is set');
is($x->get_setter(), 'banana', 'The value is set');
is($x->has_question(), '', 'The question has been answered');

################################################################################
# Check the question
################################################################################

eval { $x->get_question(1); };
like($@, qr/^Knowledge->get_question\(\) takes no arguments /, 'Too many arguments');

my ($y, @z) = $x->get_question();

is($y, 'to be or not to be', 'Get the question back');
is(scalar(@z), 2, 'Get the responses back');
is($z[0], 'be', 'Checking response');
is($z[1], 'not', 'Checking response');

################################################################################
# Check the reset method for the value part
################################################################################

eval { $x->reset(1); };
like($@, qr/^Knowledge->reset\(\) takes no arguments /, 'Too many arguments');

$x->reset();

is($x->is_value_set(), '', 'Value is not set');
$x->set_value('fred', '');
is($x->is_value_set(), '1', 'Value is set');

$x->reset();

is($x->is_value_set(), '', 'Value is not set');

################################################################################
# Check the reset method for both parts
################################################################################

$x->reset();

is($x->has_question(), '1', 'The question has been set');
is($x->is_value_set(), '', 'Value is not set');

$x->set_value('fred', '');

is($x->is_value_set(), '1', 'Value is set');
is($x->has_question(), '', 'The question has been answered');

t/Rule.t  view on Meta::CPAN

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 37;

use_ok('AI::ExpertSystem::Simple::Rule');

################################################################################
# Create a new rule
################################################################################

my $x;

eval { $x = AI::ExpertSystem::Simple::Rule->new(); };
like($@, qr/^Rule->new\(\) takes 1 argument /, 'Too few arguments');

eval { $x = AI::ExpertSystem::Simple::Rule->new(1,2); };
like($@, qr/^Rule->new\(\) takes 1 argument /, 'Too many arguments');

eval { $x = AI::ExpertSystem::Simple::Rule->new(undef); };
like($@, qr/^Rule->new\(\) argument 1 \(NAME\) is undefined /, 'Name is undefined');

$x = AI::ExpertSystem::Simple::Rule->new('fred');

isa_ok($x, 'AI::ExpertSystem::Simple::Rule');

eval { $x->name(1); };
like($@, qr/^Rule->name\(\) takes no arguments /, 'Too many arguments');

is($x->name(), 'fred', 'Checking the name');

################################################################################
# Populate the rule
################################################################################

eval { $x->add_condition(1); };
like($@, qr/^Rule->add_condition\(\) takes 2 arguments /, 'Too few arguments');

eval { $x->add_condition(1, 2, 3); };
like($@, qr/^Rule->add_condition\(\) takes 2 arguments /, 'Too many arguments');

eval { $x->add_condition(undef, 2); };
like($@, qr/^Rule->add_condition\(\) argument 1 \(NAME\) is undefined /, 'Name is undefined');

eval { $x->add_condition(1, undef); };
like($@, qr/^Rule->add_condition\(\) argument 2 \(VALUE\) is undefined /, 'Value is undefined');

$x->add_condition('a', 1);

eval { $x->add_condition('a', 1); };
like($@, qr/^Rule->add_condition\(\) has already been set /, 'Is already set');

$x->add_condition('b', 2);

eval { $x->add_action(1); };
like($@, qr/^Rule->add_action\(\) takes 2 arguments /, 'Too few arguments');

eval { $x->add_action(1, 2, 3); };
like($@, qr/^Rule->add_action\(\) takes 2 arguments /, 'Too many arguments');

eval { $x->add_action(undef, 2); };
like($@, qr/^Rule->add_action\(\) argument 1 \(NAME\) is undefined /, 'Name is undefined');

eval { $x->add_action(1, undef); };
like($@, qr/^Rule->add_action\(\) argument 2 \(VALUE\) is undefined /, 'Value is undefined');

$x->add_action('c', 3);

eval { $x->add_action('c', 3); };
like($@, qr/^Rule->add_action\(\) has already been set /, 'Is already set');

eval { $x->state(1); };
like($@, qr/^Rule->state\(\) takes no arguments /, 'Too many arguments');

is($x->state(), 'active', 'Is the rule active');

eval { $x->unresolved(1); };
like($@, qr/^Rule->unresolved\(\) takes no arguments /, 'Too many arguments');

is(scalar($x->unresolved()), 2, 'Unresolved list');

eval { $x->given(1); };
like($@, qr/^Rule->given\(\) takes 2 arguments /, 'Too few arguments');

eval { $x->given(1, 2, 3); };
like($@, qr/^Rule->given\(\) takes 2 arguments /, 'Too many arguments');

eval { $x->given(undef, 2); };
like($@, qr/^Rule->given\(\) argument 1 \(NAME\) is undefined /, 'Name is undefined');

eval { $x->given(1, undef); };
like($@, qr/^Rule->given\(\) argument 2 \(VALUE\) is undefined /, 'Value is undefined');

is($x->given('b', 2), 'active', 'Is the rule still active');

is(scalar($x->unresolved()), 1, 'Unresolved list');

is($x->given('a', 1), 'completed', 'Is the rule now complete');

is(scalar($x->unresolved()), 0, 'Unresolved list');

################################################################################
# Reset the rule and start again
################################################################################

eval { $x->reset(1); };
like($@, qr/^Rule->reset\(\) takes no arguments /, 'Too many arguments');

$x->reset();

is($x->state(), 'active', 'Is the rule active');

is($x->given('b', 1), 'invalid', 'Is the rule now invalid');

################################################################################
# Check the results
################################################################################

eval { $x->actions(1); };
like($@, qr/^Rule->actions\(\) takes no arguments /, 'Too many arguments');

my %r = $x->actions();

is(scalar keys %r, 1, 'Check the action is ok');
is($r{c}, 3, 'Check the action is ok');

eval { $x->conditions(1); };
like($@, qr/^Rule->conditions\(\) takes no arguments /, 'Too many arguments');

%r = $x->conditions();

is(scalar keys %r, 2, 'Check the action is ok');

t/Simple.t  view on Meta::CPAN

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 28;

use_ok('AI::ExpertSystem::Simple');

################################################################################
# Create a new expert system
################################################################################

my $x;

eval { $x = AI::ExpertSystem::Simple->new(1); };
like($@, qr/^Simple->new\(\) takes no arguments /, 'Too many arguments');

$x = AI::ExpertSystem::Simple->new();

isa_ok($x, 'AI::ExpertSystem::Simple');

################################################################################
# Load a file
################################################################################

eval { $x->load(); };
like($@, qr/^Simple->load\(\) takes 1 argument /, 'Too few arguments');

eval { $x->load(1,2); };
like($@, qr/^Simple->load\(\) takes 1 argument /, 'Too many arguments');

eval { $x->load(undef); };
like($@, qr/^Simple->load\(\) argument 1 \(FILENAME\) is undefined /, 'Filename is undefined');

eval { $x->load('no_test.xml'); };
like($@, qr/^Simple->load\(\) unable to use file /, 'Cant use this file');

eval { $x->load('t/empty.xml'); };
like($@, qr/^Simple->load\(\) XML parse failed: /, 'Cant use this file');

is($x->load('t/test.xml'), '1', 'File is loaded');

eval { $x->process(1); };
like($@, qr/^Simple->process\(\) takes no arguments /, 'Too many arguments');

is($x->process(), 'question', 'We have a question to answer');

eval { $x->get_question(1); };
like($@, qr/^Simple->get_question\(\) takes no arguments /, 'Too many arguments');

my ($t, $r) = $x->get_question();

eval { $x->answer(); };
like($@, qr/^Simple->answer\(\) takes 1 argument /, 'Too few arguments');

eval { $x->answer(1,2); };
like($@, qr/^Simple->answer\(\) takes 1 argument /, 'Too many arguments');

eval { $x->answer(undef); };
like($@, qr/^Simple->answer\(\) argument 1 \(VALUE\) is undefined /, 'Value is undefined');

$x->answer('yes');

is($x->process(), 'continue', 'Carry on');
is($x->process(), 'finished', 'Thats all folks');

eval { $x->get_answer(1); };
like($@, qr/^Simple->get_answer\(\) takes no arguments /, 'Too many arguments');

is($x->get_answer(), 'You have set the goal to pretzel', 'Got the answer');

################################################################################
# Reset and do it all again
################################################################################

eval { $x->reset(1); };
like($@, qr/^Simple->reset\(\) takes no arguments /, 'Too many arguments');

$x->reset();

is($x->process(), 'question', 'We have a question to answer');

($t, $r) = $x->get_question();

$x->answer('yes');

is($x->process(), 'continue', 'Carry on');
is($x->process(), 'finished', 'Thats all folks');

is($x->get_answer(), 'You have set the goal to pretzel', 'Got the answer');

my @log = $x->log();

isnt(scalar @log, 0, 'The log has data');

@log = $x->log();

is(scalar @log, 0, 'The log is empty');

eval { $x->explain(1); };
like($@, qr/^Simple->explain\(\) takes no arguments /, 'Too many arguments');

$x->explain();
@log = $x->log();

isnt(scalar @log, 0, 'The log has data');

t/test.xml  view on Meta::CPAN

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE knowledgebase SYSTEM "sie-1.0.dtd">
<knowledgebase>
 <goal>
  <attribute>thegoal</attribute>
  <text>You have set the goal to thegoal</text>
 </goal>
 <rules>
  <rule>
   <name>1</name>
   <conditions>
    <condition>
     <attribute>one</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>thegoal</attribute>
     <value>pretzel</value>
    </action>
   </actions>
  </rule>
  <rule>
   <name>2</name>
   <conditions>
    <condition>
     <attribute>two</attribute>
     <value>yes</value>
    </condition>
   </conditions>
   <actions>
    <action>
     <attribute>one</attribute>
     <value>yes</value>
    </action>
   </actions>
  </rule>
 </rules>
 <questions>
  <question>
   <attribute>two</attribute>
   <text>Answer yes to make this work</text>
   <response>yes</response>
   <response>no</response>
  </question>
 </questions>
</knowledgebase>



( run in 0.794 second using v1.01-cache-2.11-cpan-a5abf4f5562 )