96 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
% Utility Predicates
 | 
						|
 | 
						|
% The following predicates are used in the puzzle solutions. 
 | 
						|
% unique_solution( +Goal ) holds when Goal has one ground solution. Operationally,
 | 
						|
% Goal may produce several solutions, ("don't care" non-deterministically), but they
 | 
						|
% must all be identical (==). 
 | 
						|
 | 
						|
unique_solution( Goal ) :-
 | 
						|
	findall( Goal, Goal, [Solution|Solutions] ),
 | 
						|
	same_solution( Solutions, Solution ),
 | 
						|
	Solution = Goal.
 | 
						|
 | 
						|
same_solution( [], _Solution ).
 | 
						|
same_solution( [Solution0|Solutions], Solution ) :-
 | 
						|
	Solution0 == Solution,
 | 
						|
	same_solution( Solutions, Solution ).
 | 
						|
 | 
						|
% forall( +Enumerator, +Test ) is true if Enumerator and Test are goals and Test holds everywhere
 | 
						|
% that Enumerator does. NB: does not further instantiate arguments.
 | 
						|
 | 
						|
%% forall( Enumerator, Test ) :-
 | 
						|
%% 	\+ (call(Enumerator), \+ call(Test)).
 | 
						|
 | 
						|
% member( ?Element, ?List ) holds when Element is a member of List. 
 | 
						|
member( H, [H|_] ).
 | 
						|
member( H, [_|T] ):-
 | 
						|
	member( H, T ).
 | 
						|
 | 
						|
% select( ?Element, ?List0, ?List1 ) is true if List1 is equal to List1 with Element removed.
 | 
						|
 | 
						|
select( H, [H|T], T ).
 | 
						|
select( Element, [H|T0], [H|T1] ):-
 | 
						|
	select( Element, T0, T1 ).
 | 
						|
 | 
						|
% memberchk( +Element, +List ) succeeds (once) if Element is a member of List. 
 | 
						|
memberchk( Element, List ):-
 | 
						|
	member( Element, List ),
 | 
						|
	!.
 | 
						|
 | 
						|
% between( +Lower, +Upper, ?Index ) is true if Lower =< Index =< Upper. Two valid cases are
 | 
						|
% possible:
 | 
						|
% - Index is already instantiated to an integer so the checks on order are applied (test).
 | 
						|
% - Index is a logical variable so a series of alternative solutions is generated as the
 | 
						|
%   monotonic sequence of values between Lower and Upper (non-deterministic generator).
 | 
						|
 | 
						|
%% between( Lower, Upper, Index ):-
 | 
						|
%% 	integer( Lower ),
 | 
						|
%% 	integer( Upper ),
 | 
						|
%% 	Lower =< Upper,
 | 
						|
%% 	( integer( Index ) ->   % Case 1: "test"
 | 
						|
%% 		Index >= Lower,
 | 
						|
%% 		Index =< Upper
 | 
						|
%% 	; var( Index ) ->	  % Case 2: "generate".
 | 
						|
%% 		generate_between( Lower, Upper, Index )
 | 
						|
%% 	).
 | 
						|
 | 
						|
generate_between( Lower, Upper, Index ) :-
 | 
						|
	( Lower =:= Upper ->
 | 
						|
		Index = Lower
 | 
						|
	;   Index = Lower
 | 
						|
	;   Next is Lower + 1,
 | 
						|
		Next =< Upper,
 | 
						|
		generate_between( Next, Upper, Index )
 | 
						|
	).
 | 
						|
 | 
						|
% sum( +List, ?Sum ) holds when the List of numbers sum to Sum.
 | 
						|
 | 
						|
sum( [H|T], Sum ) :-
 | 
						|
	sum1( T, H, Sum ).
 | 
						|
 | 
						|
sum1( [], Sum, Sum ).
 | 
						|
sum1( [H|T], Sum0, Sum ):-
 | 
						|
	Sum1 is Sum0 + H,
 | 
						|
	sum1( T, Sum1, Sum ).
 | 
						|
 | 
						|
% put_chars( +Chars ) if Chars is a (possibly empty) list of character codes and the
 | 
						|
% corresponding characters are written to the current output stream.
 | 
						|
 | 
						|
put_chars( [] ).
 | 
						|
put_chars( [Char|Chars] ) :-
 | 
						|
	put( Char ),
 | 
						|
	put_chars( Chars ).
 | 
						|
 | 
						|
% get_chars( ?Chars ) if Chars is a (possibly empty) list of character codes read
 | 
						|
% from the current input stream.
 | 
						|
 | 
						|
get_chars( Input ) :-
 | 
						|
	get0( Char ),
 | 
						|
	( Char > -1 ->
 | 
						|
		Input = [Char|Chars],
 | 
						|
		get_chars( Chars )
 | 
						|
	; otherwise ->
 | 
						|
		Input = []
 | 
						|
	).
 | 
						|
   
 |