| |
Computer Science 60
Principles of Computer Science
Fall 2009
Assignment 10: Prolog for Fun and Profit and Spampede v2.0
Due Monday, Nov 16 by 11:59 pm
Submission and pair programming
For this problem, you will submit (at least) two things:
- Spampede.java for
the Spampede v2.0 problem (plus any and all support files). [individual]
- logic.pl for the
Knights/Knaves problem [pair
or individual]
Part 1: Spampede version 2.0! (50 points) [Individual]
One of the central aims of this course is to help you become more
comfortable with larger-scale software design. Unlike your experience
in CS 5 and so far in CS 60, software programs are rarely (if ever)
shipped out as they were originally designed and written. Like
writing a paper, writing software is an interative process where
design is constantly improved and features are constantly added.
In this part of the assignment you will improve the
design and the
functionality of your spampede program. Your tasks are the following:
- In a file called part1.txt,
write a few sentences about
the main design weaknesses in your original program. Your reasoning does
not have to
be detailed, but should refer to some aspect of good coding style
(e.g., my functions were too long, I used too many magic numbers, my
lack of incremental design made my code very un-modular, etc.)
- Improve your design. Almost
all code has room for
improvement in its design, even if the grutors did not take off
points. Identify a number of ways that your design could be improved
and make those changes to your code. Detail and justify your changes in
the
part1.txt file. The exact number of
changes will depend on
your original design, but I am looking for somewhere around 2-3.
However, note that the quality of your improvements
and your
justification is more important than the quantity.
If you are really stuck on what to improve, come talk to me. In rare cases, the original code was really so good that there's nothing substantial to improve in the design.
- Improve your functionality: Fix
any problems with the
functionality of your program that you or the grutors encountered.
Document those changes in your part1.txt file. If you did not have
any errors in functionality, add one significant bonus feature to the
play of your game.
This assignment is intentionally (slightly) vague to
help get you
thinking about the issues in real-world software design. Keep in mind
the following elements of good programming as you do this part of the
assignment:
- Magic numbers (don't use them)
- Modular design (i.e. code doesn't have a lot of
interwoven dependencies)
- Getters and setters
- Good variable and function names
- Small methods
- Public vs. private
- Good comments
- Abstraction and code reuse
- Code distribution between classes
- Spacing and code readability
If you have
any questions, please send me email or come talk to me.
For this part, you should submit your part1.txt file as
well as
all of the files
needed to play your Spampede game. You
should also put your new Spampede game on the web just like you did
before. Note that while you can submit Spampede.java as a
homework file, ALL OTHER FILES SHOULD BE SUBMITTED AS SUPPORT FILES.
Part 2: The Island of Knights and Knaves! (50 Points)
[Pair or Individual]
For this problem, your task is to create a prolog program that can
identify (id) the possible speakers of statements
you overhear on
the
Island of Knights and Knaves. Your file should be named logic.pl.
On that island, there are three kinds of inhabitants:
- knights, who speak only true
statements.
- knaves, who speak only false
statements.
- and humans, who can speak both
true and false statements (and may not know which one!)
For this problem, write a Prolog predicate
id( Expr, Speaker )
in which you may assume that Expr will always be
a logical
expression in the form of a
predicate logic parse tree. The expression
will
be fully-instantiated as far as Prolog
is concerned. The goal is that your id rule will
both check and
generate the legitimate possible values of Speaker
from this set of three
options:
knight knave human
If Expr is a logical expression that is a tautology,
then a knight might have spoken it (or a human).
If it is a logical expression that is unsatisfiable,
then a knave might have spoken it (or a human).
If it is neither a tautology nor unsatisfiable, then only a human could
have spoken it.
Possible Expressions:
Although the Expr input to your id
rule will always be
a fully-instantiated list as far as Prolog is
concerned, it will represent an arbitrary
parse tree for a logical expression that may
contain compositions of
the following items:
- The literal truth values t
(meaning true) and f (meaning false)
- The logical operators and, or, not, iff,
ifthen. You are familiar with and, or, not.
As for the other two:
- [iff, L, R] is true when
the truth values of L and R
are the same.
- [ifthen, L, R] is true
when "L implies R," that is,
when L is false or
R is true.
- Of these five logical operators, not
is unary and forms logical parse trees via
- [not, T], where T
is a logical parse tree or literal or literal integer (see below).
The other four operators, and, or, iff, ifthen
are binary and form logical parse trees via
- [and, L, R]
- [or, L, R]
- [iff, L, R]
- [ifthen, L, R]
- Also, literal integers
1, 2, 3, ... may be present in Expr.
If such a literal integer is present, it represents a logical variable
that may take on either the value t or f.
If the same integer appears in more than one place in an Expr,
any substitution of t or f
for one instance of that integer must also be substituted identically
for all other instances of that integer. There are some examples of subst
rule below that will help clarify this.
Warning! These integers represent logical
variables in the parse tree, represented by a fully-instantiated Prolog
list. They are not Prolog variables in that list.
Although it might seem tempting to use Prolog variables as logical
variables, mixing the implementation language with the logical language
leads to trouble, rather than to efficiency!
Examples The examples
will help clarify the format of
- the logical parse trees
- the way in which literal integers can represent logical
(but not Prolog) variables within those logical parse trees
- the way id works (and a few
examples of moderate-sized tautologies)
- one way that subst might be
written -- you are free to
decompose this problem as you see fit, but a few possiblities are
listed after these examples.
id( t, Who ). Who = human ; Who = knight % need not wait for all repetitions of this - hit Enter Yes id( [ifthen, f, 1], Who ). Who = human ; Who = knight % need not wait for all repetitions of this - hit Enter Yes id( [ifthen, 1, [ifthen, 2, 1]], Who ). Who = human ; Who = knight % need not wait for all repetitions of this - hit Enter Yes id( [iff, [ifthen, [and, 1, 2], 3], [ifthen, 1, [ifthen, 2, 3]]], Who ). Who = human ; Who = knight % need not wait for all repetitions of this - hit Enter Yes id( [iff, 1, [not, 1]], Who ). Who = human ; Who = knave % need not wait for all repetitions of this - hit Enter Yes id( [and, 1, 2], Who ). Who = human ; No
It's not a good idea to use setof
to test your knight/knave identifier. This is because it's possible to
write the id in such a way that there are many
repeats of a single result, and setof will try to
generate them all. This can take too long!
Possible helper rules:
This problem does not require a lot of code, but it does require some
careful thinking about
- how to check whether an expression Expr
contains a literal integer representing a logical variable
- how to determine whether an expression, even
containing only leaves of t and f,
is a tautology or unsatisfiable
or neither
- how to substitute t or f
into such an expression, yielding a new expression -- there are a
couple examples of this above
- how to avoid having Prolog head off infinitely
searching for bindings that will not be helpful
To this end, here are some helper functions - and the two we wrote in
class - feel free to take this
decomposition or to try another on your own...
- noVars( Expr ) true if Expr
has no logical variables, i.e., literal integers
Here is a subset of the code we wrote in class, which you may
want to adapt:
noVars( t ). noVars( f ). noVars( [_, L, R] ) :- noVars( L ), noVars( R ).
Note that you'll need to handle not, as well.
- getVar( Expr, N ) true if N
is an integer representing a variable that is currently contained in Expr.
Here is a slight variant (that adds speed/efficiency!!) to the code we
wrote in class:
getVar( N, N ) :- number( N ). getVar( [_, L, R], N ) :- getVar( L, N ); (noVars(L), getVar( R, N )).
Notice that this variant only descends the second branch if
there are no variables in the first branch! This will make
the expansion much more succinct and, thus, faster and
memory-efficient. Be sure to use this (the code may not complete in the
time/memory available otherwise!)
Also remember to handle not
here as well.
That said, it's worth mentioning that the above code for getVar
will not bind to more than one variable in a single
invokation. For example,
?- getVar([ifthen, 1, 2], X). X = 1 ; No
Although this may seem like a problem, once you substitute a
truth value for 1, the next call to getVar
will happily return the 2. And did we mention
it's much faster?
- taut( Expr ) would be true if Expr
is a tautology (check for no logical variables first...)
- unsat( Expr ) would be true if
Expr is unsatisfiable (check for no
logical variables first...)
- subst( N, TorF, OldExpr, NewExpr )
would be true if N is an integer representing a
variable that is currently contained in OldExpr,
and TorF is t or f,
and NewExpr is identical to OldExpr,
except that all instances of N have been replaced
with the literal value of TorF.
Here is an example of how subst might work:
subst( 1, t, [and, [not, 1], 1], NewExpr ).
NewExpr = [and, [not, t], t]
In addition, keep in mind that you can use number(N),
a predicate built-in to Prolog that makes sure N
is bound to a number.
Submit this problem as logic.pl.
Optional Extra Credit (Individual)
Continuing with the theme of revisiting old code, for up to +10 points
of extra credit you can resubmit any function or functions that you did
not do well on the first time around. The number of points you
get will depend on (1) the amount of work that was remaining on the
problem in question and (2) the amount of improvement you made.
You can submit as many problems as you like here, but if you
submit one substantially improved or 2-3 slightly improved functions,
you're likely to get at least close to the full +10 points. Note
that if you only lost a point or two originally, you're unlikely to get
much credit here. Choose functions that you lost substantial
credit for. If there are none, see the next paragraph.
If you do not have any functions that need real improvement, you are
welcome to choose any extra credit problem from a previous assignment
that you did not complete.
For either of the above options submit the following files:
- ec.txt: A file describing what you are submitting for extra
credit. Be sure to include (1) what the original problem was, (2)
what you missed the first time, and (3) what you have improved this
time around.
- All files needed to run your extra credit submission (as support files).
Finally, if you have completed ALL of the extra credit on ALL of the
assignments, well then you don't need extra credit anyway, so go have
fun or get some extra sleep. :)
|