comment file and functions, reorder for niceness
This commit is contained in:
parent
61661e7d25
commit
1067bfe0c2
1 changed files with 83 additions and 46 deletions
129
main.pl
129
main.pl
|
|
@ -1,74 +1,111 @@
|
||||||
% Three = [[0,14,10,35],[14,_,_,_],[15,_,_,_],[28,_,1,_]], puzzle_solution(Three)
|
%% Will Holdsworth 1353032
|
||||||
% Three = [[0, 14, 10, 35], [14, 7, 2, 1], [15, 3, 7, 5], [28, 4, 1, 7]].
|
%
|
||||||
|
% Implements puzzle_solution/1 which solves incomplete proper math puzzles and
|
||||||
|
% validates complete proper math puzzles.
|
||||||
|
%
|
||||||
|
% A math puzzle is a matrix with a size between 2 and 4. The first row and first
|
||||||
|
% column of the puzzle are reserved for totals, which should always be ground. A
|
||||||
|
% total is either the sum or the product of its respective row/column. The top
|
||||||
|
% and left corner can be ignored. A puzzle can be incomplete, partially
|
||||||
|
% complete, or complete. An incomplete/partially complete puzzle should have
|
||||||
|
% "_" where the cell is empty. A proper math puzzle has at most one solution.
|
||||||
|
%
|
||||||
|
% We approach puzzle validation via the clpfd library. We apply constraints to
|
||||||
|
% the puzzle in order of restrictiveness.
|
||||||
|
%
|
||||||
|
% First, we unify the main diagonal by ensuring all cells are the same.
|
||||||
|
% Second, we verify that the rows are valid. A valid row's cells should all be
|
||||||
|
% digits from 1 to 9 (inclusive), contain only distinct digits. Also, a valid
|
||||||
|
% row's head should be the sum or the product of the cells in the row.
|
||||||
|
% Finally, we take the transpose of the puzzle and apply the row validation
|
||||||
|
% predicate to the puzzle again, this time effectively validting the columns.
|
||||||
|
%
|
||||||
|
% The resulting puzzle is labelled using the "first fail" strategy outlined in
|
||||||
|
% the clpfd docs.
|
||||||
|
|
||||||
|
|
||||||
:- use_module(library(clpfd)).
|
:- use_module(library(clpfd)).
|
||||||
|
|
||||||
|
|
||||||
%% puzzle_solution(+Puzzle)
|
%% puzzle_solution(+Puzzle)
|
||||||
%
|
%
|
||||||
|
% Holds when `Puzzle` is a solved math puzzle.
|
||||||
|
% See the top of this file for more information.
|
||||||
puzzle_solution(Puzzle) :-
|
puzzle_solution(Puzzle) :-
|
||||||
Puzzle = [_|Rows],
|
Puzzle = [_|Rows],
|
||||||
unify_diagonal(Puzzle),
|
unify_diagonal(Puzzle),
|
||||||
maplist(valid_row, Rows),
|
maplist(valid_row, Rows),
|
||||||
transpose(Puzzle, TransposedPuzzle),
|
transpose(Puzzle, Transposed_puzzle),
|
||||||
TransposedPuzzle = [_|Columns],
|
Transposed_puzzle = [_|Columns],
|
||||||
maplist(valid_row, Columns).
|
maplist(valid_row, Columns).
|
||||||
|
|
||||||
|
|
||||||
%% valid(+Row)
|
|
||||||
%
|
|
||||||
valid_row([Head|Row]) :-
|
|
||||||
Row ins 1..9,
|
|
||||||
all_distinct(Row),
|
|
||||||
valid_head(Head, Row),
|
|
||||||
label(Row).
|
|
||||||
|
|
||||||
|
|
||||||
%% valid_head(+Head, +Tail)
|
|
||||||
%
|
|
||||||
valid_head(Head, List) :-
|
|
||||||
sum(List, #=, Head)
|
|
||||||
; product(List, Head).
|
|
||||||
|
|
||||||
|
|
||||||
%% product(+List, -Product)
|
|
||||||
%
|
|
||||||
product(List, Product) :-
|
|
||||||
foldl(times, List, 1, Product).
|
|
||||||
|
|
||||||
|
|
||||||
%% times(?Int1, ?Int2, ?Int3)
|
|
||||||
%
|
|
||||||
% true if Int3 #= Int1 * Int2
|
|
||||||
times(Int1, Int2, Int3) :-
|
|
||||||
Int3 #= Int1 * Int2.
|
|
||||||
|
|
||||||
|
|
||||||
%% unify_diagonal(+Puzzle)
|
%% unify_diagonal(+Puzzle)
|
||||||
%
|
%
|
||||||
|
% Holds when every variable in the main diagonal of `Puzzle` is the same.
|
||||||
unify_diagonal(Puzzle) :-
|
unify_diagonal(Puzzle) :-
|
||||||
main_diagonal(Puzzle, [_|Diag]),
|
main_diagonal(Puzzle, [_|Diag]),
|
||||||
all_same(Diag).
|
all_same(Diag).
|
||||||
|
|
||||||
|
|
||||||
%% main_diagonal(+Matrix, -Diag)
|
%% main_diagonal(+Matrix, -Diag)
|
||||||
%
|
%
|
||||||
|
% Holds when the list `Diag` is the main diagonal of the 2d list `Matrix`.
|
||||||
main_diagonal(Matrix, Diag) :-
|
main_diagonal(Matrix, Diag) :-
|
||||||
main_diagonal(Matrix, 0, Diag).
|
main_diagonal(Matrix, 0, Diag).
|
||||||
|
|
||||||
main_diagonal([], _, []).
|
main_diagonal([], _, []).
|
||||||
main_diagonal([M|Ms], I, [D|Ds]) :-
|
main_diagonal([Row|Rows], Column, [D|Ds]) :-
|
||||||
nth0(I, M, D),
|
nth0(Column, Row, D),
|
||||||
I1 is I + 1,
|
Next_column is Column + 1,
|
||||||
main_diagonal(Ms, I1, Ds).
|
main_diagonal(Rows, Next_column, Ds).
|
||||||
|
|
||||||
|
|
||||||
%% all_same(+List)
|
%% all_same(+Vars)
|
||||||
%
|
%
|
||||||
all_same([Head|Tail]) :-
|
% Holds when the variables in the list `Vars` can be unified
|
||||||
all_same(Head, Tail).
|
all_same([Var|Vars]) :-
|
||||||
|
all_same(Var, Vars).
|
||||||
|
|
||||||
|
all_same(Var, [Var]).
|
||||||
|
all_same(Var, [Var|Vars]) :-
|
||||||
|
all_same(Var, Vars).
|
||||||
|
|
||||||
|
|
||||||
|
%% valid(+Row)
|
||||||
|
%
|
||||||
|
% Holds when `Row` is valid.
|
||||||
|
% A row is valid when:
|
||||||
|
% 1. All elements except the head are integers from 1 to 9 (inclusive)
|
||||||
|
% 2. All elements except the head are distinct
|
||||||
|
% 3. The head of the row is either the sum or the product of the tail of `Row`
|
||||||
|
valid_row([Total|Vars]) :-
|
||||||
|
Vars ins 1..9,
|
||||||
|
all_distinct(Vars),
|
||||||
|
valid_total(Total, Vars),
|
||||||
|
labeling([ff], Vars).
|
||||||
|
|
||||||
|
|
||||||
|
%% valid_total(+Total, +Vars)
|
||||||
|
%
|
||||||
|
% Holds when the integer `Total` is either the sum or the product of the list
|
||||||
|
% `Vars`.
|
||||||
|
valid_total(Total, Vars) :-
|
||||||
|
sum(Vars, #=, Total)
|
||||||
|
; product(Vars, Total).
|
||||||
|
|
||||||
|
|
||||||
|
%% product(+Vars, -Product)
|
||||||
|
%
|
||||||
|
% Holds when the integer `Product` is the product of the list `Vars`.
|
||||||
|
product(Vars, Product) :-
|
||||||
|
foldl(times, Vars, 1, Product).
|
||||||
|
|
||||||
|
|
||||||
|
%% times(?Int1, ?Int2, ?Int3)
|
||||||
|
%
|
||||||
|
% Holds when Int3 #= Int1 * Int2.
|
||||||
|
times(Int1, Int2, Int3) :-
|
||||||
|
Int3 #= Int1 * Int2.
|
||||||
|
|
||||||
|
|
||||||
all_same(X, [X]).
|
|
||||||
all_same(Head, [X|Xs]) :-
|
|
||||||
Head #= X,
|
|
||||||
all_same(Head, Xs).
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue