rubik.pro
来自「prolog开发工具」· PRO 代码 · 共 670 行 · 第 1/2 页
PRO
670 行
% CUBE SOLVER II
% A Rubik's Cube Solver
% written by Dennis Merritt
% as described in Building Expert Systems in Prolog (Springer-Verlag)
% available from:
% Amzi! inc.
% 40 Samuel Prescott Dr.
% Stow, MA 01775 USA
% Tel 508/897-7332, FAX 508/897-2784
% e-mail amzi@world.std.com
%
% This program may be copied, modified and redistributed although proper
% acknowledgement is appreciated.
%
% This implementation was done with Cogent Prolog, also available
% from Amzi! inc.
%
% This is the main module which contains the predicates for
% the main control loop,
% manual mode,
% solve mode, and
% utility functions.
%
% Note - The Cogent/Prolog compiler supports modules. The export declarations
% are for predicates defined in the current module which may be used
% by other modules. The import declarations are for predicates
% defined in other modules.
:-export main/0.
:-export append/3.
:-export get_flag/2.
:-export set_flag/2.
:-export error/1.
:-export reverse/2.
:-import add_history/1. % rubhist
:-import cnd/2. % rubdata
:-import cube_print/1. % rubdisp
:-import get_color/1. % rubedit
:-import pristine/1. % rubdata
:-import rub_help/0. % rubhelp
:-import m_disp/1. % rubdisp
:-import m_choose/2. % rubdisp
:-import move/3. % rubmov
:-import orientation/2. % rubdata
:-import pln/2. % rubdata
:-import rdfield/2. % rubdisp
:-import rdchar/2. % rubdisp
:-import redit/1. % rubedit
:-import rewrite/2. % rubedit
:-import rot/3. % rubmov
:-import seq/2. % rubdata
:-import side_color/1. % rubdata
:-import s_r/2. % rubdata
:-import vw/2. % rubdata
:-import wrfield/2. % rubdisp
:-import writec/2. % rubdisp
:-import logfile/1. % dynamic db
:-import impplan/1. % dynamic db
:-import state/1. % dynamic db
:-import crit/1. % dynamic db
:-import ghoul/1. % dynamic db
:-import sidecolor/1. % dynamic db
:-import flag/2. % dynamic db
:-import cand/1. % dynamic db
:-import candmove/1. % dynamic db
:-op(500,xfy,:).
main :- banner, go. % The start up entry point
go:- % The main control loop
repeat,
init_color,
m_disp(main), % The main menu
m_choose(main,X), % Select an item
do(X), % Execute it
X == exit. % Go back to the repeat or end
% These are the predicates which are called for the various
% main menu choices. The cut after each ensures they wont be
% backtracked into when the main loop fails.
do(solve):-solve,!. % in this module
do(manual):-manual,!. % in this module
do(help):-rub_help,!. % in rubhelp
do(exit). % built-in predicate to exit
banner:-
nl,nl,
write($Cube Solver II$),nl,
write($An illustrative Prolog program from$),nl,
write($Building Expert Systems in Prolog (Springer-Verlag) by Dennis Merritt$),nl,
write($implemented in Cogent Prolog$),nl,nl,
write($For more information contact:$),nl,
write($Amzi! inc.$),nl,
write($40 Samuel Prescott Dr.$),nl,
write($Stow, MA 01775 USA$),nl,
write($Tel 508/897-7332, FAX 508/897-2784$),nl,
write($e-mail amzi@world.std.com$),nl,nl.
% These predicates initialize the state to the goal state (ghoul),
% and allow you to enter single moves. They are intended to demonstrate the
% effects of the various sequences used by the solve routines.
% They are also called by the solve routine if manual scrambling
% is requested
manual:-
pristine(G), % Start with the goal state
retractif(state(_)),
assert(state(G)),
cube_print(G), % Display it
disp_moves, % List the possible moves
repeat, % Start repeat-fail loop
rdfield(move,M), % Get a move
(M==q, nl, ! % If '', clear and end
;
state(S),
man_move(M,S,S2), % Apply move to it
retract(state(_)),
assert(state(S2)),
cube_print(S2),fail). % Print it and fail back
man_move(M,S,S2):-
movel(M,S,S2),!.
man_move(M,S,S2):- % Pop a + in front of an unsigned move
movel(+M,S,S2),!.
man_move(M,_,_):-
error('Unknown move'-M),!,fail.
disp_moves:- % List the three types of moves
wrfield(moves,''), % Heading
move(X,_,_), % Will backtrack through all moves
write(X),tab(1), % Write move
fail. % Go back for the next one
disp_moves:-
nl,
wrfield(rotations,''), % No more moves, do the same for rots
rot(X,_,_),
write(X),tab(1),
fail.
disp_moves:- % And again for seqs
nl,
wrfield(sequences,''),
seq(X,_),
write(X),tab(1),
fail.
disp_moves:- % Got em all, end
nl,
wrfield(end_disp,'').
% This is the main body of the program which actually solves the cube.
% See rubdoc1 and rubdoc2 for the big picture
solve:-
m_disp(solve), % solve submenu
m_choose(solve,X),
rdchar(stepmode,SM),
(SM==`y , set_flag(stepmode,on) % check for a y (scan code 21)
;
set_flag(stepmode,off)),
solve(X). % call solve w/ arity one with menu choice
solve(X):-
init_solve(X), % initialize all the stuff
T1 is cputime,
stages,
T is cputime - T1,
state(S),
cube_print(S),
write($Done time = $),
write(T), nl, nl.
solve(X):-
error('failing to solve'),
halt. % something wrong, back to main
init_solve(X):-
wrfield(prob,X),
initialize(X), % getting closer to the real work
!.
initialize(X):-
pristine(G),
retractall(ghoul(_)),
assert(ghoul(G)),
init_crit(Crit), % set up the initial criteria (all variables
retractall(crit(_)),
assert(crit(Crit)),
retractall(stage(_)),
assert(stage(1)), % the first stage will call the others
!,initial(X). % get specific start state in the database
initial(random):- % create a new random cube
random_cube(Cube),
retractall(state(_)),
assert(state(Cube)), !.
initial(edit):- % edit your own
redit(Cube),
retractall(state(_)),
assert(state(Cube)),
new_colors(Cube), !.
initial(manual):- % scramble your own
manual,
state(Cube),
new_colors(Cube),!.
stages:-
repeat,
retract(stage(N)),
init_stage(N,Plan), % Set the stage, get the plan
state(S),
cube_print(S),
build_plan(Plan),
improve(N,Plan), % Put the pieces in the plan in place
vw(N,V), % undo the stage view (done by init_stage)
undo_view(V),
N2 is N + 1, % next stage
assert(stage(N2)),
N2 >= 7.
build_plan([]) :- !.
build_plan([H|T]) :-
assert(impplan(H)),
build_plan(T).
% init_stage goes to rubdata to get the table entries which define
% the heuristics for the stage
init_stage(N,Plan):- % return list of target pieces for this stage
wrfield(stage,N),
cnd(N,Cands), % set up candidate moves used by search
build_cand(Cands),
vw(N,V), % set up preferred view for stage
set_view(V),
pln(N,Plan),!. % get list of target pieces
% improve - works through the list of target pieces for the stage.
% it first checks to see if its already in place
improve(Stage,[]) :- !.
improve(Stage,[Piece|Rest]) :-
impro(Stage,Piece),
!, improve(Stage,Rest).
improve(Stage):-
impplan(Piece),
impro(Stage,Piece).
impro(Stage,Piece) :-
add_criteria(Piece,Crit), % Add new piece to criteria
target_loc(Piece,Pos,Orient), % Where is it
impr(Orient,Stage,Pos,Piece),
!.
impr(0,_,_,_) :- !. % In place and oriented
impr(_,Stage,Pos,Piece) :- imp(Stage,Pos,Piece).
% imp - getting into the real work
imp(Stage,Pos,Piece):-
color_piece(PieceC,Piece), % translate side notation to
wrfield(target,PieceC), % color notation for display
heuristics(Stage,Pos), % See if special help is needed.
orientation(Piece, View), % Preferred view for this piece.
set_view(View),
crit(Crit),
state(State),
cntr_set(4,0), % to limit wild searches
% gc(7),
rotate(Moves,State,Crit), % Search for moves which transform
retract(state(_)),
assert(state(Crit)),
wrfield(rot,Moves),
add_history(Moves),
undo_view(View),!.
heuristics(Stage,Pos):-
(shift_right_1(Stage,Pos);
shift_right_2(Stage,Pos)),!.
heuristics(_,_):-true.
% The shift_right heuristics are used to avoid the situations where
% the piece is in one of the target positions for the stage, but the
% wrong one, or mis-oriented. By blindly moving it to the right the
% search is reduced since it doesn't have to search to move it both
% out of a critical target position and back into the correct one.
shift_right_1(1,Pos):-
smember('L',Pos), % Is the target piece already on the left?
s_r(Pos,Moves), % If so get the canned moves to move it
change(Moves), % right for easy search.
!.
shift_right_2(Stage,Pos):-
Stage < 4, % If the target piece is not on the right
notsmember('R',Pos), % side, get the canned moves to put it
s_r(Pos,Moves), % there to allow easier search
change(Moves),
!.
% rotate - the real guts of the solution, all the rest of the code provides
% support for these six lines.
% These lines illustrate the power and obscurity of Prolog.
% Prolog can be very expressive when the main information is carried
% in the predicate. However, sometimes the work is being done by
% unification, and it is not at all apparent by reading the code.
% Furthermore, since Prolog predicates often work backwards and
% forwards, it is not clear in a given case what is intended to be
% be input, and what is the output, and, as in this case, what might
% be an in-out.
% The input and output states of rotate are:
% Input: Moves - unbound
% State - bound to the cube structure for the current state
% Crit - partially bound cube structure. the bound portions
% represent the pieces in place + the current goal piece
% Output: Moves - a list of moves
% State - same as input
% Crit - fully bound to the new state
% rotate does a breadth first search by recursively calling itself
% before it calls get_move which trys new moves. it does not save the
% search trees as most breadth first algorithms do, but rather recalculates
% the moves since they can be executed so fast.
% get_move fails when called with the partially bound Crit, unless
% it is a move which reaches the desired state. The failure causes
% backtracking. However when rotate calls itself, it gives it a
% fully unbound variable NextState. This call to rotate succeeds and
% keeps adding new moves generated by get_move on backtracking.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?