使用Bert Bos Puzzle,我需要打印序列的所有可能排列(点击或没有点击),这会使整个方块变为红色。这将通过点击一系列点击而没有点击的顶行来完成。然后,您转到后续行并单击那里的方块以使第一行全部为红色。你会像这样进行拼图,直到你将整个方块变成红色。
因此,对于4x4正方形的可能解决方案是第一行上的[单击,单击,无单击,无单击]。您不必按照下面任何一行的模式,只需保持翻转直到下一行的所有块都是红色并继续直到所有方块都是红色。
我试图编写一个谓词,测试所有可能的“点击”和“无点击”的排列,对于大小为N的正方形的第一行。现在我试图通过跟踪颜色来解决它点击后的第一行,然后使用它来说明应该点击第二行的哪个方块使顶行全部为红色。
问题是我无法弄清楚如何跟踪第一行点击所改变的第二行的颜色,然后如何跟踪第二行的点击以及它们如何影响其余部分的行。任何帮助将不胜感激。
这是我到目前为止所拥有的
state(no_click).
state(click).
flip(blue, red).
flip(red, blue).
board_permutations(0,[]):- !.
board_permutations(N, [H|T]) :-
state(H),
N1 is N - 1,
board_permutations(N1, T).
first_row_solutions([], []).
first_row_solutions([H1, H2|T], [FirstRow|SecondRow]):-
H1 = click,
flip(H1,C),
flip(H2,C),
first_row_solutions(H2, FirstRow).
first_row_solutions([H|T], [FRH1, FRH2, FRH3|FRT], [SR1, SR2, SR3|SRT]) :-
H = click,
flip(FRH1, C1),
flip(FRH2, C2),
flip(FRH3, C3),
%flip(SR1, S1), I was thinking I could keep track of the second row colors here
%flip(SR2, S2),
%flip(SR3, S3)
%FlipListRow1 = [C1, C2, C3 | T],
%FlipListRow2 = [S1, S2, S3|T],
first_row_solutions(H, FRH3).
%Possible predicate to handle row 2, 3, 4 etc --> ClickList is what clicks to do on row 3 to make row 2 red, etc
%row_n_solutions(FlipListRow2, ClickList)
generate_board(0, [], _).
generate_board(N, [H|T], ConstantN) :-
generate_row(ConstantN, H),
N =< 12, N >= 1,
N2 is N-1,
generate_board(N2, T, ConstantN).
generate_row(0, []) :- !.
generate_row(N, [H | T]) :-
N =< 12, N >= 1,
N2 is N-1,
H = blue,
generate_row(N2, T).
test(X) :- generate_board(5,X,5).
test1(X) :- solutions([no_click, click, no_click, no_click], X).
答案 0 :(得分:1)
@CapelliC已经提出了一种可能的方法:你可以携带矩阵(使用谓词参数),并使用它来始终检查任何周围单元格的当前状态。
补充这种方法,我还想指出一种不同的方法来处理整个任务:我们可以将这个难题视为找到一个合适的线性组合 向量来自有限域GF(2)。点击次数可以表示为每个向量的整数系数。
仅在板位和矢量索引之间建立对应。我们可以定义如下关系:
n_id_x_y(N, ID, X, Y) :- ID #= Y*N + X, N1 #= N - 1, [X,Y] ins 0..N1.
示例:
?- n_id_x_y(4, 6, X, Y). X = 2, Y = 1.
请注意,我指定4
以获取适用于4×4板的映射。
这使用 CLP(FD)约束并适用于所有方向,包括例如:
?- n_id_x_y(4, ID, 3, 2). ID = 11.
基于此,我们还可以将任何索引与其邻居相关联,再次用它们的唯一索引表示:
n_id_neighbour(N, ID, NID) :- n_id_x_y(N, ID, X, Y), ( ( NX #= X - 1, NY #= Y ; NX #= X + 1, NY #= Y ) ; ( NX #= X, NY #= Y - 1 ; NX #= X, NY #= Y + 1 ) ), n_id_x_y(N, NID, NX, NY).
单击任何纸板位置翻转该位置及其定义的邻居的颜色。我们将使用布尔向量,让1
表示与此索引对应的位置颜色会受到影响:
n_id_vector(N, ID, Vs) :- V #= N*N, V1 #= V - 1, ID in 0..V1, indomain(ID), findall(NID, n_id_neighbour(N, ID, NID), Ns), sort([ID|Ns], IDs), length(Vs, V), phrase(ids_vector(IDs, 0), Vs, Zeroes), maplist(=(0), Zeroes). ids_vector([], _) --> []. ids_vector([ID|IDs], Pos0) --> { Gap #= ID - Pos0, Pos #= ID + 1, length(Zeroes, Gap), maplist(=(0), Zeroes) }, Zeroes, [1], ids_vector(IDs, Pos).
例如,点击条目0-0
会恰好影响其他三个单元格,由1
表示:
?- n_id_vector(4, 0, Vs). Vs = [1, 1, 0, 0, 1, 0, 0, 0, 0|...].
我们现在准备描述我们对解决方案的期望:解决方案包含一个系数列表,每个矢量一个,这样标量积的总和(矢量时间系数为每个向量) modulo 2 等于(1,1,...,1)
。这意味着每个单元格的颜色已更改。
n_solution(N, Cs) :- findall(Vs, n_id_vector(N,_,Vs), Vss), same_length(Vss, Cs), Cs ins 0..1, maplist(flip_cell(Cs), Vss), label(Cs). flip_cell(Cs, Ts) :- scalar_product(Ts, Cs, #=, Sum), Sum mod 2 #= 1.
请注意,在这种情况下,由于固有的对称性,不需要转置矩阵。
我们推理布尔代数的事实已经要求单击单元格的顺序不会影响结果,并且每个向量最多需要使用一次在任何解决方案中。
以下是4×4电路板的解决方案:
?- n_solution(4, Cs). Cs = [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1] ; Cs = [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1] ; Cs = [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0] ; Cs = [0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0] ; etc.
每个解决方案都准确地指出了我们必须点击哪些单元格。例如,第一个解决方案:
以下是此电路板尺寸最长的解决方案之一:
这是最短的一个:
您当然也可以将此方法应用于其他电路板尺寸,例如7×7:
或12×12: