Include Paulo Moura's Logtalk OO LP system
git-svn-id: https://yap.svn.sf.net/svnroot/yap/trunk@53 b08c6af1-5177-4d33-ba66-4b1c6b8b522a
This commit is contained in:
17
Logtalk/examples/miscellaneous/NOTES
Normal file
17
Logtalk/examples/miscellaneous/NOTES
Normal file
@@ -0,0 +1,17 @@
|
||||
=================================================================
|
||||
Logtalk - Object oriented extension to Prolog
|
||||
Release 2.8.4
|
||||
|
||||
Copyright (c) 1998-2001 Paulo Moura. All Rights Reserved.
|
||||
=================================================================
|
||||
|
||||
To load all objects in this library consult the miscellaneous.loader utility
|
||||
file (note that the *.loader files are Prolog files).
|
||||
|
||||
You will also need to load the library/types.loader file.
|
||||
|
||||
hanoi.lgt
|
||||
Towers of Hanoi example
|
||||
|
||||
queens.lgt
|
||||
N-Queens example
|
30
Logtalk/examples/miscellaneous/SCRIPT
Normal file
30
Logtalk/examples/miscellaneous/SCRIPT
Normal file
@@ -0,0 +1,30 @@
|
||||
=================================================================
|
||||
Logtalk - Object oriented extension to Prolog
|
||||
Release 2.8.4
|
||||
|
||||
Copyright (c) 1998-2001 Paulo Moura. All Rights Reserved.
|
||||
=================================================================
|
||||
|
||||
|
||||
% towers of hanoi using three disks:
|
||||
|
||||
| ?- hanoi::run(3).
|
||||
|
||||
Move a disk from left to right.
|
||||
Move a disk from left to middle.
|
||||
Move a disk from right to middle.
|
||||
Move a disk from left to right.
|
||||
Move a disk from middle to left.
|
||||
Move a disk from middle to right.
|
||||
Move a disk from left to right.
|
||||
|
||||
yes
|
||||
|
||||
|
||||
% placing eight queens in a chess table:
|
||||
|
||||
| ?- queens::queens(8).
|
||||
|
||||
[1-5,2-7,3-2,4-6,5-3,6-1,7-4,8-8]
|
||||
|
||||
yes
|
44
Logtalk/examples/miscellaneous/hanoi.lgt
Normal file
44
Logtalk/examples/miscellaneous/hanoi.lgt
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
:- object(hanoi).
|
||||
|
||||
|
||||
:- info([
|
||||
version is 1.0,
|
||||
date is 1998/3/23,
|
||||
authors is 'Paulo Moura',
|
||||
comment is 'Towers of Hanoi.']).
|
||||
|
||||
|
||||
:- public(run/1).
|
||||
:- mode(run(+integer), one).
|
||||
|
||||
:- info(run/1, [
|
||||
comment is 'Solves the towers of Hanoi problem for the specified number of disks.',
|
||||
argnames is ['Disks']]).
|
||||
|
||||
|
||||
run(Disks) :-
|
||||
move(Disks, left, middle, right).
|
||||
|
||||
|
||||
move(1, Left, _, Right):-
|
||||
!,
|
||||
report(Left, Right).
|
||||
|
||||
move(Disks, Left, Aux, Right):-
|
||||
Disks2 is Disks - 1,
|
||||
move(Disks2, Left, Right, Aux),
|
||||
report(Left, Right),
|
||||
move(Disks2, Aux, Left, Right).
|
||||
|
||||
|
||||
report(Pole1, Pole2):-
|
||||
write('Move a disk from '),
|
||||
writeq(Pole1),
|
||||
write(' to '),
|
||||
writeq(Pole2),
|
||||
write('.'),
|
||||
nl.
|
||||
|
||||
|
||||
:- end_object.
|
5
Logtalk/examples/miscellaneous/miscellaneous.loader
Normal file
5
Logtalk/examples/miscellaneous/miscellaneous.loader
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
:- initialization(
|
||||
logtalk_load([
|
||||
hanoi,
|
||||
queens])).
|
216
Logtalk/examples/miscellaneous/queens.lgt
Normal file
216
Logtalk/examples/miscellaneous/queens.lgt
Normal file
@@ -0,0 +1,216 @@
|
||||
% example adopted form:
|
||||
|
||||
% File : Queens.Pl
|
||||
% Author : Richard A O'Keefe
|
||||
% Updated: 8 Feb 84
|
||||
% Purpose: Solve the N queens problem in Prolog.
|
||||
|
||||
|
||||
:- object(queens).
|
||||
|
||||
|
||||
:- uses(list).
|
||||
|
||||
|
||||
:- public(queens/1).
|
||||
:- mode(queens(+integer), one).
|
||||
|
||||
|
||||
:- private(forbidden/4).
|
||||
:- mode(forbidden(+integer, +integer, +integer, +integer), zero_or_one).
|
||||
|
||||
:- private(least_room_to_move/4).
|
||||
:- mode(least_room_to_move(+list, -integer, -integer, -list), zero_or_more).
|
||||
|
||||
:- private(lr2m/6).
|
||||
:- mode(lr2m(+list, +integer, +integer, -integer, -integer, -list), zero_or_more).
|
||||
|
||||
:- private(make_initial_table/2).
|
||||
:- mode(make_initial_table(+integer, -list), zero_or_one).
|
||||
|
||||
:- private(make_initial_table/3).
|
||||
:- mode(make_initial_table(+integer, +list, -list), zero_or_one).
|
||||
|
||||
:- private(number_list/2).
|
||||
:- mode(number_list(+integer, -list), one).
|
||||
|
||||
:- private(place/2).
|
||||
:- mode(place(+list, -list), zero_or_more).
|
||||
|
||||
:- private(prune/4).
|
||||
:- mode(prune(+list, +integer, +integer, -list), zero_or_more).
|
||||
|
||||
:- private(prune/5).
|
||||
:- mode(prune(+list, +integer, +integer, +integer, -list), zero_or_more).
|
||||
|
||||
:- private(shorter/2).
|
||||
:- mode(shorter(+list, +list), zero_or_one).
|
||||
|
||||
|
||||
/* The N-queens problem is to place N queens on an NxN chessboard so
|
||||
that no two queens attack each other. Suppose we have a queen in
|
||||
(Row1,Col1) and a queen in (Row2,Col2). They attack each other if
|
||||
1. Same rank: Row1 = Row2.
|
||||
2. Same file: Col1 = Col2.
|
||||
3. Same NW-SE diagonal Row2 - Row1 = Col2 - Col1.
|
||||
4. Same SW-NE diagonal Row2 - Row1 = Col1 - Col2.
|
||||
We can express 3 and 4 another way:
|
||||
3. Row2 - Col2 = Row1 - Col1.
|
||||
4. Row2 + Col2 = Row1 + Col1.
|
||||
So each of the N queens has four numbers associated with it,
|
||||
(Row,Col,Dif,Sum), and any two queens must have different numbers
|
||||
in all these positions. The possible values are
|
||||
Row : 1 .. N
|
||||
Col : 1 .. N
|
||||
Sum : 1 .. 2N
|
||||
Dif : 1-N .. N-1
|
||||
|
||||
The first question is, how shall we represent the board?
|
||||
It is sufficient to have a table indexed by rows, whose elements
|
||||
are the columns in which the corresponding queen is placed. For
|
||||
example, with rows horizontal and columns vertical
|
||||
1 2 3 4 -column / row
|
||||
+---+---+---+---+
|
||||
| | Q | | | 1
|
||||
+---+---+---+---+
|
||||
| | | | Q | 2
|
||||
+---+---+---+---+
|
||||
| Q | | | | 3
|
||||
+---+---+---+---+
|
||||
| | | Q | | 4
|
||||
+---+---+---+---+
|
||||
|
||||
could be represented by board(2,4,1,3).
|
||||
|
||||
How are we going to place the queens? The first idea that springs
|
||||
to mind is to place them one after another, but we can do better than
|
||||
that. Let us maintain a table of (QueenRow/PossibleColumns), and at
|
||||
each step pick the queen with the fewest possible columns, place it,
|
||||
and prune the remaining sets.
|
||||
|
||||
Let's start by writing a predicate to generate this table. For N=4
|
||||
it will look like [1/[1,2,3,4], 2/[1,2,3,4], 3/[1,2,3,4], 4/[1,2,3,4]].
|
||||
*/
|
||||
|
||||
make_initial_table(N, Table) :-
|
||||
number_list(N, PossibleColumns), % set of all possible columns
|
||||
make_initial_table(N, PossibleColumns, Table).
|
||||
|
||||
|
||||
make_initial_table(0, _, []) :- !.
|
||||
make_initial_table(N, PossibleColumns, [N/PossibleColumns|Table]) :-
|
||||
M is N-1,
|
||||
% in C-Prolog we could write succ(M, N) which would eliminate
|
||||
% the need for the cut in the previous clause
|
||||
make_initial_table(M, PossibleColumns, Table).
|
||||
|
||||
|
||||
number_list(0, []) :- !.
|
||||
number_list(N, [N| List]) :-
|
||||
M is N-1, % see previous comment
|
||||
number_list(M, List).
|
||||
|
||||
|
||||
/* This actually generates the reverse of what I said, so we'd get
|
||||
[4/[4,3,2,1], 3/[4,3,2,1], 2/[4,3,2,1], 1/[4,3,2,1]],
|
||||
but since it was to be a set of number/set pairs, that's ok.
|
||||
We shall only be operating on these sets an element at a time,
|
||||
so it the order doesn't matter.
|
||||
|
||||
Now the problem is solved if there are no queens left to place.
|
||||
Otherwise, we pick the queen with the fewest possible columns,
|
||||
backtrack over those possible columns, prune the possible columns
|
||||
sets of the remaining queens, and recur.
|
||||
|
||||
We are given a set of Row/PossCols pairs for the unplaced queens,
|
||||
and we are to return a set of Row-Col pairs saying where we put
|
||||
the queens.
|
||||
*/
|
||||
|
||||
place([], []).
|
||||
place(UnplacedQueens, [Queen-Col|Placement]) :-
|
||||
least_room_to_move(UnplacedQueens, Queen, Columns, OtherQueens),
|
||||
list::member(Col, Columns), % backtrack over possible places
|
||||
prune(OtherQueens, Queen, Col, RemainingQueens),
|
||||
place(RemainingQueens, Placement).
|
||||
|
||||
|
||||
/* If you haven't done this sort of thing before, least_room_to_move
|
||||
can be quite tricky. The idea is the we wander down the list of
|
||||
pairs, keeping the current best pair apart, and when we find that
|
||||
there is a better pair we have to put the current pair back in the list.
|
||||
But because these are sets, it doesn't matter *where* the pairs go in
|
||||
the list. Note that we don't need a cut in the first clause of place/2
|
||||
because least_room_to_move will fail on an empty list.
|
||||
*/
|
||||
|
||||
least_room_to_move([Q/C|Table], Qbest, Cbest, Rest) :-
|
||||
lr2m(Table, Q, C, Qbest, Cbest, Rest).
|
||||
|
||||
/* This uses accumulator passing. I really have to explain this program
|
||||
to you in person.
|
||||
*/
|
||||
|
||||
lr2m([], Q, C, Q, C, []).
|
||||
lr2m([NewQ/NewC|Table], OldQ, OldC, MinQ, MinC, [OldQ/OldC|Rest]) :-
|
||||
shorter(NewC, OldC),
|
||||
!,
|
||||
lr2m(Table, NewQ, NewC, MinQ, MinC, Rest).
|
||||
lr2m([Pair|Table], OldQ, OldC, MinQ, MinC, [Pair|Rest]) :-
|
||||
lr2m(Table, OldQ, OldC, MinQ, MinC, Rest).
|
||||
|
||||
|
||||
/* shorter(L1, L2) is true when the list L1 is strictly shorter than
|
||||
the list L2
|
||||
*/
|
||||
shorter([], [_|_]).
|
||||
shorter([_|L1], [_|L2]) :-
|
||||
shorter(L1, L2).
|
||||
|
||||
|
||||
/* Now we have to code prune. To prune all the queens, we prune each
|
||||
queen in turn.
|
||||
*/
|
||||
prune([], _, _, []).
|
||||
prune([Queen/Columns|Queens], Row, Col, [Queen/Pruned|RestPruned]) :-
|
||||
prune(Columns, Queen, Row, Col, Pruned),
|
||||
prune(Queens, Row, Col, RestPruned).
|
||||
|
||||
/* To prune a single queen, we have to eliminate all the positions
|
||||
forbidden by the queen wee have just placed.
|
||||
*/
|
||||
|
||||
prune([], _, _, _, []).
|
||||
prune([Col2|Cols], Row2, Row1, Col1, Permitted) :-
|
||||
forbidden(Row1, Col1, Row2, Col2),
|
||||
!,
|
||||
prune(Cols, Row2, Row1, Col1, Permitted).
|
||||
prune([Col2|Cols], Row2, Row1, Col1, [Col2|Permitted]) :-
|
||||
prune(Cols, Row2, Row1, Col1, Permitted).
|
||||
|
||||
|
||||
/* Finally, since we have ensured that two queens are automatically in
|
||||
different rows, we have only to check rules 2, 3, and 4.
|
||||
*/
|
||||
forbidden(_, Col, _, Col).
|
||||
forbidden(Row1, Col1, Row2, Col2) :-
|
||||
Row2 - Col2 =:= Row1 - Col1.
|
||||
forbidden(Row1, Col1, Row2, Col2) :-
|
||||
Row2 + Col2 =:= Row1 + Col1.
|
||||
|
||||
|
||||
/* The last thing left for us to do is to write the top level predicate
|
||||
that ties all the pieces together. Because the 'place' predicate
|
||||
may place the queens in any order, we keysort the list to make it more
|
||||
readable. I'm afraid I had that in mind when I decided that it would
|
||||
be a list of Row-Col pairs. I seem to recommend sorting for everything.
|
||||
Well, it IS a panacea.
|
||||
*/
|
||||
queens(N) :-
|
||||
make_initial_table(N, Table),
|
||||
place(Table, Placement),
|
||||
list::keysort(Placement, DisplayForm),
|
||||
write(DisplayForm), nl.
|
||||
|
||||
|
||||
:- end_object.
|
Reference in New Issue
Block a user