Nonogram Helper
Click in a clear square to mark that square. Click again to unmark.
Type in the constraint area to add/edit constraints. To remove a constraint, set it to 0.
Rows:
1
2
3
4
5
6
7
8
9
10
Columns:
1
2
3
4
5
6
7
8
9
10
Update constraint checking (click after resizing)
Add constraint row
Remove constraint row
You win!
%%% Initial board size numrows(3) numcols(3) maxconstraint(2) %%% Example initial constraints: constraint(row, 0, [3]) constraint(row, 1, [1]) constraint(row, 2, [2]) constraint(row, 3, []) constraint(row, 4, []) constraint(row, 5, []) constraint(row, 6, []) constraint(row, 7, []) constraint(row, 8, []) constraint(row, 9, []) constraint(col, 0, [3]) constraint(col, 1, [1,1]) constraint(col, 2, [1]) constraint(col, 3, []) constraint(col, 4, []) constraint(col, 5, []) constraint(col, 6, []) constraint(col, 7, []) constraint(col, 8, []) constraint(col, 9, []) cachedCellList(row, 0, []) cachedCellList(row, 1, []) cachedCellList(row, 2, []) cachedCellList(row, 3, []) cachedCellList(row, 4, []) cachedCellList(row, 5, []) cachedCellList(row, 6, []) cachedCellList(row, 7, []) cachedCellList(row, 8, []) cachedCellList(row, 9, []) cachedCellList(col, 0, []) cachedCellList(col, 1, []) cachedCellList(col, 2, []) cachedCellList(col, 3, []) cachedCellList(col, 4, []) cachedCellList(col, 5, []) cachedCellList(col, 6, []) cachedCellList(col, 7, []) cachedCellList(col, 8, []) cachedCellList(col, 9, []) %%% Initial cell values cell(0,0,blank) cell(0,1,blank) cell(0,2,blank) cell(0,3,off) cell(0,4,off) cell(0,5,off) cell(0,6,off) cell(0,7,off) cell(0,8,off) cell(0,9,off) cell(1,0,blank) cell(1,1,blank) cell(1,2,blank) cell(1,3,off) cell(1,4,off) cell(1,5,off) cell(1,6,off) cell(1,7,off) cell(1,8,off) cell(1,9,off) cell(2,0,blank) cell(2,1,blank) cell(2,2,blank) cell(2,3,off) cell(2,4,off) cell(2,5,off) cell(2,6,off) cell(2,7,off) cell(2,8,off) cell(2,9,off) cell(3,0,off) cell(3,1,off) cell(3,2,off) cell(3,3,off) cell(3,4,off) cell(3,5,off) cell(3,6,off) cell(3,7,off) cell(3,8,off) cell(3,9,off) cell(4,0,off) cell(4,1,off) cell(4,2,off) cell(4,3,off) cell(4,4,off) cell(4,5,off) cell(4,6,off) cell(4,7,off) cell(4,8,off) cell(4,9,off) cell(5,0,off) cell(5,1,off) cell(5,2,off) cell(5,3,off) cell(5,4,off) cell(5,5,off) cell(5,6,off) cell(5,7,off) cell(5,8,off) cell(5,9,off) cell(6,0,off) cell(6,1,off) cell(6,2,off) cell(6,3,off) cell(6,4,off) cell(6,5,off) cell(6,6,off) cell(6,7,off) cell(6,8,off) cell(6,9,off) cell(7,0,off) cell(7,1,off) cell(7,2,off) cell(7,3,off) cell(7,4,off) cell(7,5,off) cell(7,6,off) cell(7,7,off) cell(7,8,off) cell(7,9,off) cell(8,0,off) cell(8,1,off) cell(8,2,off) cell(8,3,off) cell(8,4,off) cell(8,5,off) cell(8,6,off) cell(8,7,off) cell(8,8,off) cell(8,9,off) cell(9,0,off) cell(9,1,off) cell(9,2,off) cell(9,3,off) cell(9,4,off) cell(9,5,off) cell(9,6,off) cell(9,7,off) cell(9,8,off) cell(9,9,off)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Updating the grid when clicked: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Clicking an active cell toggles it between blank and filled (x). click(mark(R,C)) :: cell(R,C,blank) ==> cell(R,C,x) & ~cell(R,C,blank) & updaterow(R, C, x) & updatecol(C, R, x) click(mark(R,C)) :: cell(R,C,x) ==> cell(R,C,blank) & ~cell(R,C,x) & updaterow(R, C, blank) & updatecol(C, R, blank) % Whenever a cell in a given row/column is updated, recalculate the cellList data and cache it. updaterow(R, C, X) :: cachedCellList(row, R, L0) & cellList(row, R, C, X, L) ==> ~cachedCellList(row, R, L0) & cachedCellList(row, R, L) updatecol(C, R, X) :: cachedCellList(col, C, L0) & cellList(col, C, R, X, L) ==> ~cachedCellList(col, C, L0) & cachedCellList(col, C, L) % Cell fill color is white if blank, black if clicked. style(mark(R,C), "backgroundColor", "#ffffff") :- cell(R,C,blank) & ~sat(row,R) & ~sat(col,C) style(mark(R,C), "backgroundColor", "#000000") :- cell(R,C,x) % Extra rule: color the cell light gray if it's not filled in but the row/col % constraint is fully satisfied. This marks that this row/col is "done". style(mark(R,C), "backgroundColor", "#d3d3d3") :- cell(R,C,blank) & sat(row,R) style(mark(R,C), "backgroundColor", "#d3d3d3") :- cell(R,C,blank) & sat(col,C) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Row and column selectors: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% value(numrows, R) :- numrows(R) value(numcols, C) :- numcols(C) % Selecting a new row/col size turns off all cells outside of the new range, % and turns on all cells inside the new range. % Update numrows: select(numrows, R) :: numrows(R2) ==> numrows(R) & ~numrows(R2) % Turn off all cells outside of the new range: select(numrows, R) :: cell(R1,C,X) & ~less(R1, R) ==> cell(R1, C, off) & ~cell(R1,C,X) % Turn on all cells inside the new range: select(numrows, R) :: cell(R1,C,off) & less(R1, R) ==> cell(R1,C,blank) & ~cell(R1,C,off) % Zero out all constraints and cached cell lists outside the new range: select(numrows, R) :: constraint(row,R1,L) & ~less(R1, R) ==> ~constraint(row,R1,L) & constraint(row,R1,[]) select(numrows, R) :: cachedCellList(row,R1,L) & ~less(R1, R) ==> ~cachedCellList(row,R1,L) & cachedCellList(row,R1,[]) % Same but for columns. select(numcols, C) :: numcols(C2) ==> numcols(C) & ~numcols(C2) select(numcols, C) :: cell(R,C1,X) & ~less(C1, C) ==> cell(R, C1, off) & ~cell(R,C1,X) select(numcols, C) :: cell(R,C1,off) & less(C1, C) ==> cell(R,C1,blank) & ~cell(R,C1,off) select(numcols, C) :: constraint(col,C1,L) & ~less(C1, C) ==> ~constraint(col,C1,L) & constraint(col,C1,[]) select(numcols, C) :: cachedCellList(col,C1,L) & ~less(C1, C) ==> ~cachedCellList(col,C1,L) & cachedCellList(col,C1,[]) click(updateCellList) :: numrows(RMAX) & cachedCellList(row,R,L) & less(R, RMAX) ==> updaterow(R, 11, x) click(updateCellList) :: numcols(CMAX) & cachedCellList(col,C,L) & less(C, CMAX) ==> updatecol(C, 11, x) % Hide the table rows/columns that shouldn't be shown: % Hide all cells whose column should be hidden. style(mark(R,C), "display", "none") :- numcols(CMAX) & ~less(C, CMAX) style(mark(R,C), "display", "") :- numcols(CMAX) & less(C, CMAX) % Hide all column constraints whose column should be hidden. style(constraintbox(col,C), "display", "none") :- numcols(CMAX) & ~less(C, CMAX) style(constraintbox(col,C), "display", "") :- numcols(CMAX) & less(C, CMAX) % For row hiding, we can hide the entire tr field. style(row(R), "display", "none") :- numrows(RMAX) & ~less(R, RMAX) style(row(R), "display", "") :- numrows(RMAX) & less(R, RMAX) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Constraint field addition/removal %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % addconstraint: if no more than 5 constraints, increase constraint number by 1. click(addConstraint) :: maxconstraint(I0) & less(I0, 5) & evaluate(plus(I0, 1), I) ==> maxconstraint(I) & ~maxconstraint(I0) % removeconstraint: if at least 2 constraints, reduce constraint number by 1 % then call truncate, which will chop off one subconstraint from the dataset. % e.g. if constraint for row I is [1,2,1] and maxconstraint is 3, removeconstraint % will update it to be [2,1]. %%%%% TODO: chop off end instead of start? Behavior is kinda funky right now %%%%%% click(removeConstraint) :: maxconstraint(I0) & less(1, I0) & evaluate(minus(I0, 1), I) ==> maxconstraint(I) & ~maxconstraint(I0) & truncate truncate :: constraint(D,I,X!L) & maxconstraint(CMAX) & evaluate(length(X!L), CMAX) ==> constraint(D,I,L) & ~constraint(D,I,X!L) % Hide high-index subconstraints that shouldn't be shown. style(subconstraint(D,I,ISUB), "display", "none") :- maxconstraint(CMAX) & ~less(ISUB, CMAX) style(subconstraint(D,I,ISUB), "display", "") :- maxconstraint(CMAX) & less(ISUB, CMAX) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Updating and displaying constraints: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Enforce the ith subconstraint field containts the ith element of the constraint. value(subconstraint(D,I,ISUB), N) :- constraint(D, I, L) & idx(L, ISUB, N) % Updating the subonstraint field to a non-zero value adds to the constraint list. select(subconstraint(D,I,ISUB), N) :: distinct(N,0) & constraint(D, I, L0) & newconstraint(L0, ISUB, N, L) ==> ~constraint(D, I, L0) & constraint(D, I, L) % Updating the subconstraint field to zero removes that index from the list. select(subconstraint(D,I,ISUB), 0) :: constraint(D, I, L0) & listpop(L0, ISUB, L) ==> ~constraint(D, I, L0) & constraint(D, I, L) % A row/col is fully satisfied if the constraints and cells match exactly. sat(D,I) :- constraint(D,I,L1) & cachedCellList(D,I,L2) & same(L1,L2) unsat(D,I) :- constraint(D,I,L1) & ~sat(D,I) % We "win" if all constraints are fully set. style(victory, "display", "none") :- unsat(D,I) style(victory, "display", "") :- evaluate(countofall(I, unsat(D,I)), 0) % Color all the subconstraints green if the whole constraint matches exactly. %%%%%% TODO: modify this so that sub-constraints are colored independently. %%%%% style(subconstraint(D, I, X), "color", "#00ff00") :- ~lenless(D,I) & partSat(D,I,L3,L4,L5) & member(X,L3) style(subconstraint(D, I, X), "color", "#000000") :- ~lenless(D,I) & ~(partSat(D,I,L3,L4,L5) & member(X,L3)) & ~(partSat(D,I,L3,L4,L5) & member(X,L5)) style(subconstraint(D, I, X), "color", "#ff0000") :- lenless(D,I) | partSat(D,I,L3,L4,L5) & member(X,L5) %%%%%%%%%%%%%%%%%%%%%%%%% %%% Helper functions: %%% %%%%%%%%%%%%%%%%%%%%%%%%% % Debug code: set the innerhtml of blank_cell (upper-left corner) to something % in order to "print" it. innerhtml(blank_cell, " ") % Calculating cell-list: what cell list should look like after we set cell (I, POS) to V. % If row 2 looks like [x,blank,x,x,blank,x,blank], % then we should get cellList(row, 2, 6, x [1,2,2]). % This is an expensive calculation, so we try to calculate it only when needed. % growlist will have a leading 0 if the leftmost entry is blank, so handle that. cellList(D, I, POS, V, L) :- growlist(D, I, POS, V, 0, 0!L) cellList(D, I, POS, V, X!L) :- growlist(D, I, POS, V, 0, X!L) & distinct(X, 0) % growlist is our helper to recursively traverse the row, processing the cell at index I. % Base case: if I is the number of columns (out of bounds), initialize to [0]. growlist(row, I, POS, V, N, [0]) :- numcols(N) % If cell N is filled, add 1 to front element of the list. growlist(row, I, POS, V, N, X2!L) :- distinct(POS,N) & cell(I, N, x) & evaluate(plus(N,1),N2) & growlist(row, I, POS, V, N2, X!L) & evaluate(plus(X,1),X2) growlist(D, I, POS, x, N, X2!L) :- same(POS,N) & evaluate(plus(N,1),N2) & growlist(D, I, POS, x, N2, X!L) & evaluate(plus(X,1),X2) % If cell N is blank and list starts with a 0, do nothing. growlist(row, I, POS, V, N, 0!L) :- distinct(POS,N) & cell(I, N, blank) & evaluate(plus(N,1),N2) & growlist(row, I, POS, V, N2, 0!L) & evaluate(plus(X,1),X2) growlist(D, I, POS, blank, N, 0!L) :- same(POS,N) & evaluate(plus(N,1),N2) & growlist(D, I, POS, blank, N2, 0!L) & evaluate(plus(X,1),X2) % If cell N is blank and list starts with a non-zero, append a zero. growlist(row, I, POS, V, N, 0!X!L) :- distinct(POS,N) & cell(I, N, blank) & evaluate(plus(N,1),N2) & growlist(row, I, POS, V, N2, X!L) & distinct(X,0) growlist(D, I, POS, blank, N, 0!X!L) :- same(POS,N) & evaluate(plus(N,1),N2) & growlist(D, I, POS, blank, N2, X!L) & distinct(X,0) % The same, but for columns (note that N, I, are switched). growlist(col, I, POS, V, N, [0]) :- numrows(N) growlist(col, I, POS, V, N, X2!L) :- distinct(POS,N) & cell(N, I, x) & evaluate(plus(N,1),N2) & growlist(col, I, POS, V, N2, X!L) & evaluate(plus(X,1),X2) growlist(col, I, POS, V, N, 0!L) :- distinct(POS,N) & cell(N, I, blank) & evaluate(plus(N,1),N2) & growlist(col, I, POS, V, N2, 0!L) & evaluate(plus(X,1),X2) growlist(col, I, POS, V, N, 0!X!L) :- distinct(POS,N) & cell(N, I, blank) & evaluate(plus(N,1),N2) & growlist(col, I, POS, V, N2, X!L) & distinct(X,0) % Less than. less(A, B) :- evaluate(max(A,B),B) & distinct(A,B) % Indexing operator for list. idx(X!L, 0, X) idx(X!L, I, N) :- evaluate(minus(I,1), I2) & idx(L, I2, N) % newconstraint(L0, I, N, L): L is list L0 with the Ith index replaced with N, % or appended to if there is no ith index. newconstraint(nil, I, N, [N]) newconstraint(X!L, 0, N, N!L) newconstraint(X!L, I, N, X!L2) :- less(0,I) & evaluate(minus(I,1), ISUB) & newconstraint(L, ISUB, N, L2) % listpop(L0, I, L): L is the list L0 with the Ith index removed. listpop(nil, I, nil) listpop(X!L, 0, L) listpop(X!L, I, X!L2) :- less(0,I) & evaluate(minus(I,1), ISUB) & listpop(L, ISUB, L2) % Wrapper around parsing constraint cells to list readconstraint(D, IDX, L) :- evaluate(setofall(N, value(subconstraint(D,IDX,0), N)), L) % Misc: force all borders to be black, so coloring constraints/cells doesn't affect that. style(X, "border-color", "black") %%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%Inividual Color Coding%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% partSat(D,I,L3,L4,L5) :- constraint(D,I,L1) & cachedCellList(D,I,L2) & evaluate(reverse(L1),L11) & evaluate(reverse(L2),L22) & lcl(L11,L22,L3,L4,L5,J) lenless(D,I) :- constraint(D,I,L1) & cachedCellList(D,I,L2) & len(L1,X) & len(L2,Y) & distinct(X,Y) & evaluate(min(X,Y),X) lcl(A!L1,B!L2,K!L3,L4,L5,J) :- same(A,B) & lcl(L1,L2,L3,L4,L5,K) & evaluate(plus(K,1),J) lcl(L1,[],[],[],[],J) :- len(L1,J) lcl([],L1,[],[],[],0) lcl([],[],[],[],[],0) len(L1,J) :- evaluate(length(L1),J) & ~same(J,false) len(L1,0) :- evaluate(length(L1),J) & same(J,false) lcl(A!L1,B!L2,L3,K!L4,L5,J) :- distinct(A,B) & len(A!L1,X) & len(B!L2,Y) & less(Y,X) & lcl(L1,B!L2,L3,L4,L5,K) & evaluate(plus(K,1),J) lcl(A!L1,B!L2,L3,L4,K!L5,J) :- distinct(A,B) & len(A!L1,X) & len(B!L2,Y) & same(X,Y) & lcl(L1,L2,L3,L4,L5,K) & evaluate(plus(K,1),J) & less(A,B) lcl(A!L1,B!L2,L3,K!L4,L5,J) :- distinct(A,B) & len(A!L1,X) & len(B!L2,Y) & same(X,Y) & lcl(L1,L2,L3,L4,L5,K) & evaluate(plus(K,1),J) & less(B,A)