- Introduction
- The expert system I implemented is an aid to picky eaters looking to
dine out in Chico. It's task is to classify where the user wishes to eat
based on several facts that the user supplies. It does not have a large
working memory of restaurants, but I picked them such that they span the
criterion that the system knows about. Each restaurant is categorized
with respect to type of food, (ie. mexican, italian, etc) relative
price, (low, medium, high) class, (casual, semi-casual semi-formal)
notable dish, (may have more than one, or none at all) and the times
that they are open are also stored. I did not implement a snazzy user
interface to collect the criterion from the user; intead, these values
are set at the top of the file containing the code for the system. Once
these values are set, the user need only use the query eatat(X) to
generate a list of possible places to eat, and type a semicolon after
each suggestion to get the next one.
- Background
- This project did not require any extensive research into other
restaurant-picking expert systems. However, I used three books as
references for learning Prolog: Artificial Intelligence Through
Prolog by Neil C. Rowe, PROLOG programming for artificial
intelligence (Second Edition) by Ivan Bratko, and Artificial
Intelligence and the Design of Expert Systems by George F. Luger and
William A. Stubblefield. I found the chapters on lists especially
helpful. This system is sufficiently simple that the textbooks above
contained enough information for me to write it without other
references.
- Description
- As I mentioned in the Introduction, my expert system generates a
list of restaurants that the user may want to eat at. It is implemented
in Prolog on the linux operating system. It stores information about
each restaurant (defined in Introduction), and determines which
restaurants match the given criterion through backward chaining. The
critical rules used to decide if a restaurant matches the criterion are
the 'eatat' clauses. There are a large number of these; 4 for each
restaurant. The idea behind this is the first 'eatat' clause for each
restaurant defines the strictest match (ie. ALL possible criterion
match). However, I wanted my system to allow 'don't cares' so the
following three clauses check for fewer criterion. The last 'eatat'
clause for each restaurant checks only if it is open at the specified
time and if it is a favorite restaurant. Restaurants that are defined as
favorite ALWAYS show up on the list of restaurants. (at the end of the list,
if they don't match any other criterion and therefore show up earlier)
- Sample Run #1
- The following facts were set for this sample run:
% set preferences
foodtype(mexican).
mealtype(dinner).
dishtype(none).
pricetype(medium).
classtype(casual).
favorite('Frankies').
favorite('Paradise').
favorite('Chan Pheng').
favorite('El Ranchero').
The actual run:
4 ?- eatat(X).
X = 'La Comida' ;
X = 'La Comida' ;
X = 'El Ranchero' ;
X = 'El Ranchero' ;
X = 'Jacks' ;
X = 'Round Table' ;
X = 'Woodstocks' ;
X = 'Windys' ;
X = 'Carls Junior' ;
X = 'El Ranchero' ;
X = 'Frankies' ;
X = 'Chan Pheng' ;
No
This run actually demonstrates a successful run and/or an unsuccessful
run, depending on how you look at it. It is successful in that the best
matches come first, and it exhaustively finds ALL restaurants that match
the specified criterion, but the way that I wrote the 'eatat' clauses
allows duplicates and also matches some restaurants that don't meet all
the criterion. The reason for the latter case is that since I allow
'don't cares' the less-specific clauses will match, hence Jacks, Round
Table, Woodstocks, Windys, and Carls Junior show up on the list since
they match the mealtype, classtype, and pricetype indicated. I decided
to leave this in because it would require a lot more 'eatat' clauses to
cover every possible combination of criterion and I don't think it
detracts from the accuracy too much since these always show up later in
the list than best matches. As for the duplicates, I believe they show
up when less-specific 'eatat' clauses match in addition to more-specific
clauses, although I thought that once a match was found for a particular
restaurant it wouldn't try to match it again. Apparently that is not the
case, and I gave up on trying to fix this after experimenting with the
use of cuts (the ! character in prolog) to stop matching after a match
was found, but all I could get it to do was stop after matching ANY
'eatat' clause, therefore giving only one answer. This was unacceptable
so I also tried rearranging the order of the 'eatat' clauses to see if
that would have any effect, but this attempt was also unsuccessful. So
this is a genuine bug, but I decided not to spend any more time on it
since at least the repetition accurately tells the user that the
restaurant matches REALLY well and therefore emphasizes the degree of
certainty in that choice.
- Sample Run #2
- The following facts were set for this sample run:
% set preferences
foodtype(none).
dishtype(none).
mealtype(lunch).
pricetype(low).
classtype(casual).
favorite(none).
The actual run:
4 ?- eatat(X).
X = 'Tacos Cortez' ;
X = 'Cozy Diner' ;
X = 'Taco Bell' ;
No
This run represents a completely successful execution of my program. It
differs from the first run in that only the least-specific clause
matches for each restaurant, resulting in a single result for each
restaurant.
- Limitations
- This system is very simple and therefore limited in many ways. As
mentioned in the Introduction there is no user interface to
interactively get parameters from the user. I considered putting an
interface in, but since the user would need to input more complex syntax
than yes or no, I discovered it would involve quite a bit of extra code
to implement this. Also there are the bugs mentioned above. In addition,
in order for this system to be really useful it would need to have
information about a lot more restaurants.
- Goodness
- I am very happy with the degree of usefulness even this simple
program offers. Although it has a small list of restaurants, I can put
in what I generally feel like eating and get a valid list of places I
might want to eat. I also like the "favorite" feature because it really
simulates how we decide to eat -- we always have a default list of
places we always feel like eating.
- Where to go from here...
- This program would definitely benefit from a bit more time spent fixing the
above limitations. Also I have the nagging feeling that the code itself
could be simplified. I think I might have went the "long way" on how to
represent the clauses and facts. I think my inexperience with this type
of programming is at fault here, and hope that with more practice this
simplification will come naturally, as it does in other types of
programming. For the time being, I left the code in its original state
because it works (mostly!), and represents the instinctual way I went
about solving the problem. This might come in handy later on when I get
more familiar with AI shells and can look back at my 'first try' to see
how I was approaching the problem.
In writing this program, I quickly grew tired of repeating every
combination of rules to satisfy the "don't care" functionality, which is
why not every possible combination is accounted for. I don't know if
there is another way to do this in Prolog, or if a non-rule-based system
with better organization of the problem might offer a more flexible
solution, but this is definitely an area that I intend to explore for
the next assignment.