Note that the second midterm will be handed out on Wednesday, Nov 8 at 8am and due at 10am on Friday, Nov 10. I STRONGLY suggest that you finish the homework before taking the midterm.
This assignment has 4 parts. Please be sure to name your prolog rules exactly as we've specified in the assignment. This is important since some of your code will be tested by an automatic code tester which will look for precisely these names.
Recall Prolog's built-in rules that may be useful here:
Finally, remember that it's fine if your prolog rule finds the same solution more than once as long as it doesn't find it an infinite number of times! In other words, your prolog rules should halt, but may find the same solution a finite number of time.
This 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 an arbitrary value, 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 rule called solve such that
solve(Operators, Numbers, Result, Tree)will solve for an arithmetic tree named Tree using operators Operators among the integers in Numbers to give the final value Result. You may assume that when we use the solve rule the operators, numbers, and result will always be specified but that the fourth argument, the tree itself, may be left as a variable. For example,
| ?- solve(['+'], [1, 2, 3], 6, ['+', 1, ['+', 2, 3]]).
yes
| ?- solve(['+', '*', '-'], [2, 3, 4, 5], 24, Tree).
Tree = [*,[+,[-,3,2],5],4]
This means that we have found a solution Tree using operators +, *, and - on that will combine with set of numbers [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, '/' is performed using // (which is why 1-line comments in prolog are prefaced by % and not //).
We provide an eval predicate that will be quite useful in solving this problem. Here it is:
eval(R, R) :- number(R).
eval(['+', A, B], R) :- eval( A, R1 ), eval( B, R2 ), R is R1 + R2.
eval(['-', A, B], R) :- eval( A, R1 ), eval( B, R2 ), R is R1 - R2.
eval(['*', A, B], R) :- eval( A, R1 ), eval( B, R2 ), R is R1 * R2.
eval(['/', A, B], R) :- eval( A, R1 ), eval( B, R2 ), R is R1 // R2.
You may also 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:
?- solve(['+'], [1, 1, 1, 1], 24, Tree).Notice that a plain integer (e.g. 24) is the simplest possible solution tree (not [24]), and there are many more answers to the third example above.
no.
?- solve(['+'], [24], 24, Tree).
Tree = 24
?- solve(['+', '*', '-'], [1, 2, 3, 4], 24, Tree).
Tree = [*,1,[*,2,[*,3,4]]]
This problem will require that you to think very carefully about the order in which rules are specified in your prolog rules. Remember from class that order matters!
A very good approach to this problem is to write a helper rule called tree that takes as input a list Operations of valid operations to be used, a list Numbers of numbers to be used and a Tree which is a tree in which the leaves of the tree are the numbers, where each number is used exactly once, the internal nodes in the tree are selected from Operations (with operations permitted to be used more than once). In fact, you might find it convenient to stipulate that the numbers at the leaves must occur exactly in the order (from left-to-right) that they are given in Numbers. (You can deal with permuting these numbers someplace else.) For example:
| ?- tree(['+', '*'], [1, 2, 3, 4], ['*', ['+', 1, 2], ['*', 3, 4]]).
yes
| ?- tree(['+', '*'], [1, 2, 3], X).
X = [+,1,[+,2,3]] ;
X = [+,1,[*,2,3]] ;
X = [+,[+,1,2],3] ;
X = [+,[*,1,2],3] ;
X = [*,1,[+,2,3]] ;
X = [*,1,[*,2,3]] ;
X = [*,[+,1,2],3] ;
X = [*,[*,1,2],3] ;
no
Notice that in the first case we gave tree all three lists and it verified that the third list (a tree) could be constructed from the operations in the first list and the numbers in the second list. Then, in the second case, we "tricked" prolog into generating all possible trees with a given set of operations and numbers. Once you can do this, you're nearly done!
Please submit this part of the assignment in a file called part1.pl.
For example, the following example illustrates a winning set of moves:
Start with: [1, 0, 1, 0, 0, 0]Note that at the edges (0 and 5) only 2 squares change.
Choose square 2: [1, 1, 0, 1, 0, 0]
Choose square 0: [0, 0, 0, 1, 0, 0]
Choose square 5: [0, 0, 0, 1, 1, 1]
Choose square 4: [0, 0, 0, 0, 0, 0]
You have probably written a lights out implementation in another programming class (like CS5). However, now we are going to write a "program" in Prolog that will find winning lists of moves for us!
Your task is to write a predicate lightsOut(Config, Moves) that returns true if applying the moves in the list Moves starting with the configuration Config will yield a winning configuration. You do not have to handle lists of moves that contain "loops", or moves that toggle the lights back to a state you have seen before (i.e., it's OK if prolog says No to such lists of moves). However, your predicate should support the situation where Moves is a variable, hence "tricking" prolog into solving the game for us! (You can assume Config will always be bound).
For example:
| ?- lightsOut([1, 1, 1, 0, 0, 0], X).
X = [0,1,0] ;Note that there are many possible solutions. It is not necessary that prolog find all of them or even that prolog find the shortest solution, simply that it finds a finite number of solutions. Note also that some initial configurations of this game do not have a solution, so prolog may occasionally say no.
X = [0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,5] ;
X = [0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,4] ;
X = [0,1,0,2,0,1,0,3,0,1,0,2,3] ;
X = [0,1,0,2,2] ;
X = [1] ;
no
In this part of the assignment, you will write a Prolog program to solve the Towers of Hanoi puzzle. Please submit your solution in a file called part3.pl. You should carefully review the "fox, lettuce, hare" solution that we examined in class before embarking on this program. I also recommend that you solve problem 2 (lights out!) before solving this problem. The big ideas in all three programs are very similar. Fortunately though, this Hanoi program is considerably shorter than the fox, lettuce, hare problem. (You should expect your program to be about 20 lines of Prolog code, not including comments and spacing between rules.) Your solution should specify valid moves in this puzzle and Prolog should use these rules to infer correct solutions to the puzzle. Here are the details:
[ [1, 2], [3, 4], [] ]represents disk 1 on top of disk 2 on peg 1, disk 3 on top of disk 4 on peg 2, and nothing on peg 3.
[ [1, 2, 3, 4], [], [] ]
[ [], [], [1, 2, 3, 4] ]
| ?- valid([ [1, 2, 3], [], [] ], 12, [ [2, 3], [1], [] ]).This is valid because we went from the first configuration to the second configuration via move "12". Here's another example:
yes
| ?- valid([ [1, 2], [], [3] ], 12, [ [2], [], [1, 3] ]).In this case, it's possible to move from the first configuration to the second configuration, but not by move "12". Here's yet another example:
no
| ?- valid([ [1 , 2], [], [3] ], 31, [ [3, 1, 2], [], [] ]).Although we did move a disk from peg 3 to peg 1 here, this move was invalid because disk 3 cannot be placed on top of disk 1.
no
| ?- hanoi([ [1], [2], [3] ], [23, 13]).The answer here is "yes" because move "23" followed by "13" leaves us in the final configuration. Once this function is written, you can do the following:
yes
| ?- hanoi( [ [1, 2, 3], [], [] ], Solution).(Your Prolog program may find a different solution. The solution found depends on the order in which the rules were defined. As long as your Prolog program finds a correct solution, that's fine!)
Solution = [12,13,21,13,12,31,12,31,21,23,12,13,21,13]
| ?- retractall(marked(_)).Then, you should be able to run it again... .
yes