outputFile('./neighbours_solved.txt'). inputFile('./neighbours_unsolved.txt'). /********************* dummy solution algorithms -> fill your correct algorithm here */ :- use_module(library(clpfd)). valid_neighbors([_]). % If two numbers are neighbors, they must be within +1 of each other valid_neighbors([A, N, B|T]):- N #= 1, abs(A-B) #= 1, A #\= B, valid_neighbors([B|T]). % If two numbers are not neighbors, they cannot be within +1 of each other valid_neighbors([A, N, B|T]):- N #= 0, abs(A-B) #\= 1, A #\= B, valid_neighbors([B|T]). % Numbers have to be between 1 and the size of the board valid_numbers(Size, [H]):- H in 1..Size. valid_numbers(Size, [H, _|T]):- H in 1..Size, valid_numbers(Size, T). % Remove every other element of a list. r([], []). r([X], [X]). r([X,_|Xs], [X|Ys]):- r(Xs, Ys). % Use constraint propagation in order to solve boards % Inspired by the techniques used at https://www.swi-prolog.org/pldoc/man?section=clpfd-sudoku solve(Size, Grid):- % A board with size 4 needs three extra lines for the neighbor rows. % A board of size N needs N * 2 - 1 lines to accommodate for the neighbor rows. N #= Size * 2 - 1, length(Grid, N), maplist(same_length(Grid), Grid), % The final solution is presented without neighbor symbols removeNeighbors(Grid, Solution), % Transpose the grid in order to apply the same rules to the columns transpose(Grid, Transposed), % Every other line of the grid is a value row removeNeighborLines(Grid, VRows), % The rules of the neighbor relations need to be upheld for the rows maplist(valid_neighbors, VRows), % Every other element of the value rows are values maplist(r, VRows, RowValues), % Each value can only appear once on every row maplist(all_distinct, RowValues), % Ensure all numbers on a line are valid. maplist(valid_numbers(Size), VRows), % Every other row of the columns is a value column removeNeighborLines(Transposed, VColumns), % The rules of the neighbor relations need to be upheld for the columns maplist(valid_neighbors, VColumns), % Ensure all numbers on a line are valid. maplist(valid_numbers(Size), VColumns), % Every other element of a value column is a value maplist(r, VColumns, Columns), % Each value can only appear once on every column maplist(all_distinct, Columns), % Assign a value from the available ones to each element of the rows maplist(label, Solution). doSolve(neighbors(size(Size),grid(Problem)),neighbors(size(Size),grid(Solution))):- solve(Size, Problem), removeNeighbors(Problem, Solution). removeNeighbors([P],[S]):- removeNeighborLines(P,S). removeNeighbors([P,_|PT],[S|ST]):- removeNeighborLines(P,S), removeNeighbors(PT,ST). removeNeighborLines([P],[P]). removeNeighborLines([P,_|PT],[P|ST]):- removeNeighborLines(PT,ST). /********************* writing the result */ writeFullOutput(neighbors(size(N),grid(Grid))):- write('size '), write(N), write('x'), write(N), nl, writeGrid(Grid). writeGrid([]). writeGrid([E|R]):- writeGridLine(E), writeGrid(R). writeGridLine([]):- nl. writeGridLine([E|R]):- E=0, !, write(E), write(' '), writeGridLine(R). writeGridLine([E|R]):- write(E), write(' '), writeGridLine(R). /********************** reading the input */ readProblem(neighbors(size(N),grid(Grid))):- findKW(size), readInt(N), readInt(M), M=N, GridLength is N*2-1, length(Grid,GridLength), readGridLines(GridLength,Grid). findKW(KW):- string_codes(KW,[H|T]), peek_code(H), readKW([H|T]), !. findKW(_):- peek_code(-1), !, fail. findKW(KW):- get_code(_), findKW(KW). readKW([]):- get_code(_). readKW([H|T]):- get_code(H), readKW(T). readGridLines(N,[A]):- length(A,N), readGridLine(A). readGridLines(N,[A,B|T]):- length(A,N), readGridLine(A), length(B,N), readNeighborLine(B), readGridLines(N,T). readGridLine([N]):- readInt(I), makeHint(I,N). readGridLine([N,X|T]):- readInt(I), makeHint(I,N), get_code(M), translate(M,X), !, readGridLine(T). readNeighborLine([X]):- get_code(M), translate(M,X), !. readNeighborLine([X,0|T]):- get_code(M), translate(M,X), get_code(_), get_code(_), get_code(_), !, readNeighborLine(T). makeHint(X,X):- X>0. makeHint(0,_). translate(-1,'ERROR: EOF'). translate(120,1). translate(32,0). translate(X,X). translate(X,E):- whitespace(X), get_code(Y), translate(Y,E). translate(X,E):- string_codes(E,[X]). whitespace(10). whitespace(12). whitespace(32). readInt(N):- get_code(M), handleCode(M,N). handleCode(M,N):- is_number_code(M,N1), !, continueInt(N1,N). handleCode(-1,_):- !, fail. /* EOF */ handleCode(_,N):- readInt(N). continueInt(O,N):- get_code(M), is_number_code(M,M1), !, H is 10*O+M1, continueInt(H,N). continueInt(N,N). is_number_code(N, N1):- N>=48, N<58, N1 is N-48. is_number_code(95,0). /*********************** global control: starting the algorithm and the reading */ run:- inputFile(IF), see(IF), outputFile(F), tell(F), findKW(puzzles), readInt(N), write('puzzles '), write(N), nl, solveProblems(N), told, seen, !. run:- told, seen. /* close the files */ solveProblems(0). solveProblems(N):- N>0, readProblem(P), doSolve(P, S), writeFullOutput(S), !, N1 is N-1, solveProblems(N1). :- nl,nl,write(' try running "?- run."'), nl,nl,nl. :- run. :- halt.