Computer Science 60
Principles of Computer Science


Assignment 4: Prolog - true! [100 points]
Due Monday, February 24th by 11:59pm


Submitting...    For this assignment, you'll submit the two files: lists.pl and logicpuzzle.pl. Also, there are four Java problems to complete and indicate in the usual way.

The extra-credit problem, if you'd like to try it, would be submitted in a file named twentyfour.pl.

Example code and starter versions of the files are available here:

Formatting and commenting and testing...

Please use the standard commenting convention of your login, file name, and date at the top of each file. In addition, be sure to include a comment for each predicate that you implement.

For each of the prolog functions that you write in list.pl, you should write at least one additional test case. We encourage you to write these test cases BEFORE you write your code, especially as a way to deepen your understanding of the behavior of the rule.






Problem 1: Lists in Prolog

[60 points, pair or individual]

Here, you will write a few Prolog predicates that express relationships about lists (and trees and graphs, expressed as lists). Please submit these rules in one file called lists.pl.

You may create and use any helper rules that you like. In particular, some of the rules that we saw in class may be helpful here! For example, we wrote the member predicate:

member( X, [X|_] ).
member( X, [_|R] ) :- member( X, R ).
This is built in to some versions of prolog, but not others. In addition, we wrote range:
range(L,H,[]) :- L >= H.
range(L,H,[L|R]) :- L < H, M is L+1, range(M,H,R).
Both of these are at the top of lists.pl, along with example tests. You don't need to write additional tests for these two predicates.


Prolog predicates to write in lists.pl :



Problem 2: Einstein's logic-constraint puzzle (30 Points)

[30 points; pair or individual]

Note on spelling: Be careful with the various names in this problem -- you'll want to keep the spelling consistent. In particular, be sure collette is with two l's and two t's. (We've tried to be consistent ourselves; if you see any problems, let us know!)

For this problem, your task is to write a set of prolog rules that will solve the following logic puzzle (in the spirit of Einstein's Zebra Puzzle):

/*
 * algird, bruno, collette, dino, and edwina are each from different
 * one of five colleges: pomona, pitzer, hmc, cmc, and scripps.
 *
 * Each one brings a different snack:  jots, chocolate, donuts, pez, and spam.
 * They are all on the train together in seats 1 through 5.
 *
 * We want to know which student is in each seat, what college does each
 * student attend, what did each student bring for a snack?
 * Determine whether there is a solution, and if so, whether it is unique.
 *
 * 1.  bruno and dino sat in the end seats.
 * 2.  algird sat next to the student from hmc.
 * 3.  collette sat next to friends with chocolate and donuts.
 * 4.  The hmc student brought spam as a snack and sat in the middle seat.
 * 5.  chocolate was immediately to the left of pez.
 * 6.  bruno, dino, and algird do not go to Scripps.
 * 7.  The pomona student sat between the persons with jots and spam.
 * 8.  dino did not sit next to the person with donuts.
 * 9.  The cmc student did not sit next to edwina.
 *
 * Note that negation constraints don't generate values -- they can
 * only check for consistency of already-instantiated values. Thus,
 * they must be tested _after_ variables are instantiated with values.
 *
 * Here is the solution (which is helpful to have for debugging!)
 *
 * Seats =
   [[bruno,cmc,jots],          % Seat 1
    [algird,pomona,donuts],    % Seat 2
    [collette,hmc,spam],       % Seat 3
    [edwina,scripps,chocolate],% Seat 4
    [dino,pitzer,pez]]         % Seat 5
 *
 */

Be sure to format your output in an easy-to-read manner.

In particular, you need to have a solve predicate that generates and prints the solution to the five-colleges logic puzzle neatly -- modeled from the solve predicate in the einstein.pl example.

That solve predicate first generates the solution to the puzzle, and then it prints the results in a reasonable fashion:

solve :-
  einstein( [ H1, H2, H3, H4, H5 ] ),
  write( ' first house: '), write(H1), nl,
  write( 'second house: '), write(H2), nl,
  write( ' third house: '), write(H3), nl,
  write( 'fourth house: '), write(H4), nl,
  write( ' fifth house: '), write(H5), nl.

This zero-argument predicate solve is called without parentheses at all, and it produces results as follows:

?- solve.

 first house: [norwegian, cat, dunhill, water, yellow]
second house: [dane, horse, marlboro, tea, blue]
 third house: [brit, bird, pallmall, milk, red]
fourth house: [german, zebra, rothmans, coffee, green]
 fifth house: [swede, dog, winfield, beer, white]

Yes

Hints:    the "end seats" constraint is a good opportunity to use the or operator (the semicolon, ;). Specifically, you'll likely want something similar to this:

( Seats = [something] ; Seats = [somethingelse] ), ... other clauses ..., 
That "or" of two clauses can be used in a sequence of comma-separated clauses that are more customary in Prolog.

Submitting...    Be sure to submit the two files: lists.pl and logicpuzzle.pl to the usual place on the submission site



Problem 3:    Java! (10 points)

We have five additional Java problems this week, worth a total of 10 points. This week, the problems don't have solutions... We encourage you to pair up with someone to work on these, again emphasizing Java loops, lists (arrays), and strings:

With this background, head to the String-2 set of JavaBat problems, and write the solutions to these functions: Once you've done these, submit (or simply use Edit) to the submission system the sentence
I've been stringing Java along...
or a sentence of your own design expressing the same thing... .

Submission!

Be sure to submit your sentence in the usual way, under hw4pr3.txt; see below if you're in the mood for EXTRA Prolog, as well... .




[Extra]    The Game of 24! (up to +11 points)

This totally optional problem asks you to use Prolog to provide a solver for a generalized version of the game "Twenty-Four". In the original game, players view a card with four numbers on it and try to make an arithmetic combination of the numbers using the operators +, -, *, / so that the result is 24 (parentheses are allowed but are implicit). Each number must be used exactly once. Each operator can be used any number of times.

In our generalization of the game, the fixed number 24 is replaced with a variable, the set of operators is specified in a list, and the list of numbers can have any length, not just 4. (By definition, no result can be made if the list is empty.)

Define a 4-ary predicate solve such that

     solve(Ops, Values, Result, Tree)
  
will solve for an arithmetic tree named Tree using operators Ops among the integers in Values to give the final value Result. For example,
 solve(['+', '*', '-'], [2, 3, 4, 5], 24, Tree).

 Tree = [*,[+,[-,3,2],5],4] 
 

This means that we have found a solution Tree using operators +, *, and - such that the solution combines the numbers in the list [2, 3, 4, 5] to yield 24.

You may assume that the set of operators will always be a subset of ['+', '*', '-', '/'] and that '/' denotes integer division. In prolog, integer division is performed using // (which is why 1-line comments in prolog are prefaced by % and not //).

Here is an eval predicate (for evaluating trees of opreations) that will be helpful in solving this problem. You may copy this code and use it freely. Note that eval can handle numbers and it can handle trees that are bound to some value (using the nonvar predicate) Thus, the eval predicate can not generate trees. Allowing eval to generate trees can lead to infinite recursion, as it looks in the (unbounded) space of trees for operations possibly leading to R.

    eval(R, R) :- number(R).
    eval(['+', A, B], R) :- nonvar(A), nonvar(B), eval(A, AR), eval(B, BR), R is AR + BR.
    eval(['*', A, B], R) :- nonvar(A), nonvar(B), eval(A, AR), eval(B, BR), R is AR * BR.
    eval(['-', A, B], R) :- nonvar(A), nonvar(B), eval(A, AR), eval(B, BR), R is AR - BR.
    eval(['/', A, B], R) :- nonvar(A), nonvar(B), eval(A, AR), eval(B, BR), BR\==0, R is AR // BR.
 
You may use any of Prolog's built-in rules and any helper rules that you like (including those that we wrote in class).

Here are some other examples of solve in action. Warning: You may especially want to check the second of these. In particular, a common partially working solution to this problem handles the first example below, but not the second example. Note how the list of values is being split into sublists in the second case. Be careful to only allow non-empty splits; otherwise, you'll end up with an infinite recursion!

  solve(['+', '*', '-'], [1, 2, 3, 4], 24, Tree).

  Tree = [*,1,[*,2,[*,3,4]]]    % other solutions are certainly possible!



  solve(['+','-','*','/'], [2,8,22,424], 42, Tree).  % only one solution possible!

  Tree = [-,[/,424,8],[/,22,2]]



  solve(['+'], [1, 1, 1, 1], 24, Tree).   % no solution

  No.



  solve(['+'], [24], 24, Tree).    % base case

  Tree = 24
 
Notice that a plain integer is the simplest possible solution tree (not the one-element list, [24]). Also, there are many more answers to the first example above. Be careful, too, that your splitting of the input values (numbers) allows for rearrangements -- otherwise the middle example, above, will not work!

For +5 points, your solve predicate should work as above, generating trees (with a value provided for Result). For the final +6 points, your predicate should also be able to generate the Result value (as well as the tree):

?- solve(['+', '*', '-'], [1, 10,100, 1000], N, Tree).

N = 1111,
Tree = [+,1,[+,10,[+,100,1000]]] ;

N = 1110,
Tree = [*,1,[+,10,[+,100,1000]]] ;

N = -1109,
Tree = [-,1,[+,10,[+,100,1000]]] ;

N = 11001,
Tree = [+,1,[*,10,[+,100,1000]]]   % and many, many more...
Note that the eval predicate given can check or generate the result value R.

Please submit this extra-credit part of the assignment in a file called twentyfour.pl.