This assignment has several purposes:
Source code for uProlog is available in several
places, but only
the version on the FAS servers,
in ~cs152/software/bare/uprolog/upr-with-unify.sml
,
gives you code for substitution and unification, which is not in the book.
(This code is from next year's version of the book; be aware that page
numbers may have changed.)
If you have already made a private copy of the book software, you will
need to go back to the FAS server and get the current version, so
that you won't have to re-implement substitution and unification.
Otherwise, the source code should be reasonable, although it is still
a bit of a mess in spots; you should be
especially careful to ignore redundant ``readers'' functions.
Note: do not write a lot of code for Exercises 20 and 21. I added not by adding 4 lines to the interpreter, and I added the cut by changing about 7 lines of existing code. (But I confess that I did exploit my knowledge that existing primitives would not trigger a cut. It is possible to build a principled solution that does treat cut as an ordinary primitive, and that such a solution would require changing more code.) Focus on test cases that convince us you have got the correct semantics.
_ o o o o o o o o owhere _ represents an empty hole and o represents a hole with a peg in it. A ``move'' results when one peg jumps over another to land in a hole. The two pegs and hole must be colinear, and the stationary peg that was jumped over is removed from the board. So after a legal first move of the 1st peg on the third row (peg 4) we have:
o _ o _ o o o o o oand after moving the last peg on the same row (peg 6) we have:
o _ o o _ _ o o o oand so on. When no peg can jump over any adjacent peg to land in a hole, the game is over. The object of the game is to leave a single peg, preferably in a designated hole. After my first attempt, I left this configuration:
_ o o _ _ _ _ _ _ oIf you want to play the game yourself, try it with small coins, or you can run
~cs152/bin/peg10
or ~cs152/bin/peg
.
For the problems below, number the pegs from 1, i.e., number the 10-hole layout like this:
1 2 3 4 5 6 7 8 9 10
cansolve(n)
succeeds if and only
if 10-hole peg solitaire has a solution leaving n
or fewer pegs.
You can assume that n
will always be passed in, e.g., we should expect
cansolve(3)
to succeed always.
[15 points]
minleaving
such that querying minleaving(N)
puts
in N
the minimum number of pegs that can be left on the board.
Hint: this is much easier with the cut.
[10 points]
_ o o o o o o o o o o o o o o
solution(n, M)
either produces in M
a list of moves leaving a single peg in hole n
, or fails if
there is no such sequence.
Represent a single move by the term move(Start, Finish)
, so for example
the two possible initial moves would be represented as move(4,1)
and move(6,1)
.
[10 points]
moves(S, F, M)
produces a
sequences of moves M
that takes the board from a configuration in
which all holes except S
have pegs to a configuration in
which only hole F
has a peg.
Using these rules,
Someone has stolen the jam! The March Hare said he didn't do it (naturally!). The Mad Hatter proclaimed one of them (the Hare, the Hatter, or the Dormouse) stole the jam, but of course it wasn't the Hatter himself. When asked whether the Mad Hatter and March Hare spoke the truth, the Dormouse said that one of the three (including herself) must have stolen the jam.Write a Prolog program to solve this logical puzzle. In particular, write rules for a predicateBy employing the very expensive services of Dr. Himmelheber, the famous psychiatrist, we eventually learned that not both the Dormouse and the March Hare spoke the truth. Assuming, as we do, that fairy-tale characters either always lie or always tell the truth, it remains to discover who really stole the jam.
stole
such that the
query stole(X)
succeeds if and only if X
could have stolen the
jam.
The query should work even if X
is left as a variable, in which
case it should produce all the suspects who could possibly
have stolen the jam.
Approaches to the problem. There are two ways to approach this kind of problem.
Hints:
not
predicate on anything
except a ground term.
For problems where you're trying to reach particular final position, it is never necessary to count pegs — you know what configuration you want, so just look for that configuration. Don't count.
Even for problems where you do have to count pegs, it's rarely necessary to count pegs on individual boards, because you know that each move takes away one peg.
More symmetry. Use symmetry to speed up your solution to problem D. Measure the speedup.
Generality.
Solve one or more of problems C–E, but make the number of
holes in the triangle
a parameter to the problem. So for example, I would try to solve the
board in the introduction by solution(4, 1, M)
where 4 is the
number of holes along one side of the triangle, 1 is the desired final
hole, and M is the desired sequence of moves.
Measure the performance cost of this generalization.
Hint: The tough part is figuring out what's the numbering for a potential move.
Think about shearing the board to form a lower-triangular matrix.
What are the rules then for the permissible directions of motion?
You may find it useful to number by row and column instead of just numbering the
individual holes. This is perfectly OK.
Complementarity.
The ``complementarity problem'' for peg solitaire asks for which holes H we can start
with a peg in every hole except H, then finish with a board that is
empty except for a single peg in H.
Write Prolog rules for complements(HS)
that leaves in HS
a
list of holes for which the complementarity problem can be solved.
Exploit symmetry to reduce searching time.
Which holes do you actually have to check?
Types.
This programming-language stuff all fits together.
In Prolog, write a type checker for the first-order typed lambda
calculus with products (pair
, fst
, snd
) and integer
literals.
The core of your type checker should be a relation
has_type(Gamma, Term, Type)
where you supply the environment and
the term and Prolog computes the type.
For even more extra credit, add polymorphism.
For the simplest possible type system, a checker in Prolog should take about a dozen lines of code. Adding sums, products, and polymorphism will more than double that. Here's a sample from my code, running on an old homework problem:
<sample run of a type checker in Prolog>= | ?- has_type([], tylambda(alpha, tylambda(beta, lambda(p, cross(alpha, beta), pair(snd(var(p)), fst(var(p)))))), T). T = forall(alpha,forall(beta,arrow(cross(alpha,beta),cross(beta,alpha))))
Can you ``run it backwards'' and get the engine to exhibit a term with a particular type? If not, why not? Can you modify your code to produce a derivation as well as a type? If not, why not?
Pegs as games. Implement peg solitaire as a game, and use the AGS from the Standard ML Modules homework to solve problem E and the Complementarity extra credit. Hint: you'll find it easiest if you make the solitaire itself a functor, which you can then parameterize by the initial and final positions desired.
<peg solitaire sketch>= signature PEG_BASICS = sig ... end structure PegBasics : PEG_BASICS = struct type config ... end signature PEG_PARMS = sig structure Basics : PEG_BASICS val initial_hole : int val final_peg : int end functor PegGame(Parms : PEG_PARMS) : GAME = struct open Parms.Basics (* justified because all these names are re-exported *) fun who_won conf = if pegs_left conf = 1 andalso contents(conf, Parms.final_peg) = Peg then Player.WINS Player.X else Player.TIE ... end
and so on... You might start out simply by implementing a game like ~cs152/bin/peg, in which you win if you clear the board.
README
, which should contain a brief discussion of your
solutions as well as your answer to Exercise 6.
Your README
file should also contain any short transcripts and
test cases that demonstrate your implementation of the cut and of
not
, as well as a discussion of any false starts or bugs you
uncovered in these codes.
uprolog.sml
, which should contain your solutions to
Exercises 20 and 21 (the cut and not
).
pegA.pl
through pegE.pl
, containing your solutions to
peg-solitaire problems. Don't worry if these files have a lot of
duplicate code—we'll sort out the differences.
jam.pl
, containing your solution to problem J.
~cs152/bin/uprolog
)
or
XSB Prolog (~cs152/bin/xsb -i
or
/usr/local/XSB/3.0.1/bin/xsb -i
).
XSB Prolog is faster, and this may be helpful for some of the peg problems.
For extra credit, you may also turn in pegBx.pl
, pegDx.pl
,
general.pl
, complement.pl
, types.pl
, and more.