Set puzzle: Difference between revisions
Content added Content deleted
m (→{{header|Phix}}: syntax coloured, made p2js compatible) |
|||
Line 3,158: | Line 3,158: | ||
8: red diamond two open |
8: red diamond two open |
||
</pre> |
</pre> |
||
=={{header|Picat}}== |
|||
The problem generator check that it problem has exactly one solution, so that step can take a little time (some seconds). fail/0 is used to check for unicity of the solution. |
|||
<lang Picat>import util. |
|||
import cp. |
|||
% |
|||
% Solve the task in the description. |
|||
% |
|||
go ?=> |
|||
sets(1,Sets,SetLen,NumSets), |
|||
print_cards(Sets), |
|||
set_puzzle(Sets,SetLen,NumSets,X), |
|||
print_sol(Sets,X), |
|||
nl, |
|||
fail, % check for other solutions |
|||
nl. |
|||
go => true. |
|||
% |
|||
% Generate and solve a random instance with NumCards cards, |
|||
% giving exactly NumSets sets. |
|||
% |
|||
go2 => |
|||
_ = random2(), |
|||
NumCards = 9, NumSets = 4, SetLen = 3, |
|||
generate_and_solve(NumCards,NumSets,SetLen), |
|||
fail, % prove unicity |
|||
nl. |
|||
go3 => |
|||
_ = random2(), |
|||
NumCards = 12, NumSets = 6, SetLen = 3, |
|||
generate_and_solve(NumCards,NumSets,SetLen), |
|||
fail, % prove unicity) |
|||
nl. |
|||
% |
|||
% Solve a Set Puzzle. |
|||
% |
|||
set_puzzle(Cards,SetLen,NumWanted, X) => |
|||
Len = Cards.length, |
|||
NumFeatures = Cards[1].length, |
|||
X = new_list(NumWanted), |
|||
foreach(I in 1..NumWanted) |
|||
Y = new_array(SetLen), |
|||
foreach(J in 1..SetLen) |
|||
member(Y[J], 1..Len) |
|||
end, |
|||
% unicity and symmetry breaking of Y |
|||
increasing2(Y), |
|||
% ensure unicity of the selected cards in X |
|||
if I > 1 then |
|||
foreach(J in 1..I-1) X[J] @< Y end |
|||
end, |
|||
foreach(F in 1..NumFeatures) |
|||
Z = [Cards[Y[J],F] : J in 1..SetLen], |
|||
(allequal(Z) ; alldiff(Z)) |
|||
end, |
|||
X[I] = Y |
|||
end. |
|||
% (Strictly) increasing |
|||
increasing2(List) => |
|||
foreach(I in 1..List.length-1) |
|||
List[I] @< List[I+1] |
|||
end. |
|||
% All elements must be equal |
|||
allequal(List) => |
|||
foreach(I in 1..List.length-1) |
|||
List[I] = List[I+1] |
|||
end. |
|||
% All elements must be different |
|||
alldiff(List) => |
|||
Len = List.length, |
|||
foreach(I in 1..Len, J in 1..I-1) |
|||
List[I] != List[J] |
|||
end. |
|||
% Print a solution |
|||
print_sol(Sets,X) => |
|||
println("Solution:"), |
|||
println(x=X), |
|||
foreach(R in X) |
|||
println([Sets[R[I]] : I in 1..3]) |
|||
end, |
|||
nl. |
|||
% Print the cards |
|||
print_cards(Cards) => |
|||
println("Cards:"), |
|||
foreach({Card,I} in zip(Cards,1..Cards.len)) |
|||
println([I,Card]) |
|||
end, |
|||
nl. |
|||
% |
|||
% Generate a problem instance with NumSets sets (a unique solution). |
|||
% |
|||
% Note: not all random combinations of cards give a unique solution so |
|||
% it might generate a number of deals. |
|||
% |
|||
generate_instance(NumCards,NumSets,SetLen, Cards) => |
|||
println([numCards=NumCards,numWantedSets=NumSets,setLen=SetLen]), |
|||
Found = false, |
|||
% Check that this instance has a unique solution. |
|||
while(Found = false) |
|||
if Cards = random_deal(NumCards), |
|||
count_all(set_puzzle(Cards,SetLen,NumSets,_X)) = 1 |
|||
then |
|||
Found := true |
|||
end |
|||
end. |
|||
% |
|||
% Generate a random problem instance of N cards. |
|||
% |
|||
random_deal(N) = Deal.sort() => |
|||
all_combinations(Combinations), |
|||
Deal = [], |
|||
foreach(_I in 1..N) |
|||
Len = Combinations.len, |
|||
Rand = random(1,Len), |
|||
Comb = Combinations[Rand], |
|||
Deal := Deal ++ [Comb], |
|||
Combinations := delete_all(Combinations, Comb) |
|||
end. |
|||
% |
|||
% Generate a random instance and solve it. |
|||
% |
|||
generate_and_solve(NumCards,NumSets,SetLen) => |
|||
generate_instance(NumCards,NumSets,SetLen, Cards), |
|||
print_cards(Cards), |
|||
set_puzzle(Cards,SetLen,NumSets,X), % solve it |
|||
print_sol(Cards,X), |
|||
nl. |
|||
% |
|||
% All the 81 possible combinations (cards) |
|||
% |
|||
table |
|||
all_combinations(All) => |
|||
Colors = [red, green, purple], |
|||
Symbols = [oval, squiggle, diamond], |
|||
Numbers = [one, two, three], |
|||
Shadings = [solid, open, striped], |
|||
All = findall([Color,Symbol,Number,Shading], |
|||
(member(Color,Colors), |
|||
member(Symbol,Symbols), |
|||
member(Number,Numbers), |
|||
member(Shading,Shadings))). |
|||
% |
|||
% From the task description. |
|||
% |
|||
% Solution: [[1,6,9],[2,3,4],[2,6,8],[5,6,7]] |
|||
% |
|||
sets(1,Sets,SetLen,Wanted) => |
|||
Sets = |
|||
[ |
|||
[green, one, oval, striped], % 1 |
|||
[green, one, diamond, open], % 2 |
|||
[green, one, diamond, striped], % 3 |
|||
[green, one, diamond, solid], % 4 |
|||
[purple, one, diamond, open], % 5 |
|||
[purple, two, squiggle, open], % 6 |
|||
[purple, three, oval, open], % 7 |
|||
[red, three, oval, open], % 8 |
|||
[red, three, diamond, solid] % 9 |
|||
], |
|||
SetLen = 3, |
|||
Wanted = 4.</lang> |
|||
Solving the instance in the task description (go/0): |
|||
{{out}} |
|||
<pre>[1,[green,one,oval,striped]] |
|||
[2,[green,one,diamond,open]] |
|||
[3,[green,one,diamond,striped]] |
|||
[4,[green,one,diamond,solid]] |
|||
[5,[purple,one,diamond,open]] |
|||
[6,[purple,two,squiggle,open]] |
|||
[7,[purple,three,oval,open]] |
|||
[8,[red,three,oval,open]] |
|||
[9,[red,three,diamond,solid]] |
|||
Solution: |
|||
x = [{1,6,9},{2,3,4},{2,6,8},{5,6,7}] |
|||
[[green,one,oval,striped],[purple,two,squiggle,open],[red,three,diamond,solid]] |
|||
[[green,one,diamond,open],[green,one,diamond,striped],[green,one,diamond,solid]] |
|||
[[green,one,diamond,open],[purple,two,squiggle,open],[red,three,oval,open]] |
|||
[[purple,one,diamond,open],[purple,two,squiggle,open],[purple,three,oval,open]]</pre> |
|||
Solving the two random tasks (go2/0) and go3/0): |
|||
{{out}:: |
|||
<pre>[numCards = 9,numWantedSets = 4,setLen = 3] |
|||
Cards: |
|||
[1,[green,squiggle,one,solid]] |
|||
[2,[green,squiggle,two,solid]] |
|||
[3,[purple,diamond,three,striped]] |
|||
[4,[purple,oval,two,striped]] |
|||
[5,[purple,squiggle,one,striped]] |
|||
[6,[purple,squiggle,three,solid]] |
|||
[7,[purple,squiggle,three,striped]] |
|||
[8,[red,squiggle,one,open]] |
|||
[9,[red,squiggle,three,open]] |
|||
Solution: |
|||
x = [{1,5,8},{2,5,9},{2,7,8},{3,4,5}] |
|||
[[green,squiggle,one,solid],[purple,squiggle,one,striped],[red,squiggle,one,open]] |
|||
[[green,squiggle,two,solid],[purple,squiggle,one,striped],[red,squiggle,three,open]] |
|||
[[green,squiggle,two,solid],[purple,squiggle,three,striped],[red,squiggle,one,open]] |
|||
[[purple,diamond,three,striped],[purple,oval,two,striped],[purple,squiggle,one,striped]] |
|||
[numCards = 12,numWantedSets = 6,setLen = 3] |
|||
Cards: |
|||
[1,[green,diamond,one,solid]] |
|||
[2,[green,diamond,two,solid]] |
|||
[3,[green,oval,one,open]] |
|||
[4,[purple,oval,one,solid]] |
|||
[5,[purple,squiggle,one,open]] |
|||
[6,[purple,squiggle,one,solid]] |
|||
[7,[purple,squiggle,one,striped]] |
|||
[8,[red,diamond,one,solid]] |
|||
[9,[red,diamond,two,striped]] |
|||
[10,[red,oval,one,striped]] |
|||
[11,[red,squiggle,three,solid]] |
|||
[12,[red,squiggle,three,striped]] |
|||
Solution: |
|||
x = [{1,5,10},{2,4,11},{3,4,10},{3,7,8},{5,6,7},{9,10,12}] |
|||
[[green,diamond,one,solid],[purple,squiggle,one,open],[red,oval,one,striped]] |
|||
[[green,diamond,two,solid],[purple,oval,one,solid],[red,squiggle,three,solid]] |
|||
[[green,oval,one,open],[purple,oval,one,solid],[red,oval,one,striped]] |
|||
[[green,oval,one,open],[purple,squiggle,one,striped],[red,diamond,one,solid]] |
|||
[[purple,squiggle,one,open],[purple,squiggle,one,solid],[purple,squiggle,one,striped]] |
|||
[[red,diamond,two,striped],[red,oval,one,striped],[red,squiggle,three,striped]]</pre> |
|||
Here is the additional code for a '''constraint model'''. Note that the constraint solver only handles integers so the features must be converted to integers. To simplify, the random instance generator does not check for unicity of the problem instance, so it can have (and often have) a lot of solutions. |
|||
<lang Picat>go4 => |
|||
NumCards = 18, |
|||
NumWanted = 9, |
|||
SetLen = 3, |
|||
time(generate_instance2(NumCards,NumWanted, SetLen,Sets)), |
|||
print_cards(Sets), |
|||
println(setLen=SetLen), |
|||
println(numWanted=NumWanted), |
|||
SetsConv = convert_sets_to_num(Sets), |
|||
set_puzzle_cp(SetsConv,SetLen,NumWanted, X), |
|||
println(x=X), |
|||
foreach(Row in X) |
|||
println([Sets[I] : I in Row]) |
|||
end, |
|||
nl, |
|||
fail, % more solutions? |
|||
nl. |
|||
set_puzzle_cp(Cards,SetLen,NumWanted, X) => |
|||
NumFeatures = Cards[1].len, |
|||
NumSets = Cards.len, |
|||
X = new_array(NumWanted,SetLen), |
|||
X :: 1..NumSets, |
|||
foreach(I in 1..NumWanted) |
|||
% ensure unicity of the selected sets |
|||
all_different(X[I]), |
|||
increasing_strict(X[I]), % unicity and symmetry breaking of Y |
|||
foreach(F in 1..NumFeatures) |
|||
Z = $[ S : J in 1..SetLen, matrix_element(Cards, X[I,J],F, S) ], |
|||
% all features are different or all equal |
|||
( |
|||
(sum([ Z[J] #!= Z[K] : J in 1..SetLen, K in 1..SetLen, J != K ]) |
|||
#= SetLen*SetLen - SetLen) |
|||
#\/ |
|||
(sum([ Z[J-1] #= Z[J] : J in 2..SetLen]) #= SetLen-1) |
|||
) |
|||
end |
|||
end, |
|||
% Symmetry breaking (lexicographic ordered rows) |
|||
lex2(X), |
|||
solve($[ff,split],X). |
|||
% |
|||
% Symmetry breaking |
|||
% Ensure that the rows in X are lexicographic ordered |
|||
% |
|||
lex2(X) => |
|||
Len = X[1].length, |
|||
foreach(I in 2..X.length) |
|||
lex_lt([X[I-1,J] : J in 1..Len], [X[I,J] : J in 1..Len]) |
|||
end. |
|||
% |
|||
% Convert sets of "verbose" instances to integer |
|||
% representations. |
|||
% |
|||
convert_sets_to_num(Sets) = NewSets => |
|||
Maps = new_map([ |
|||
red=1,green=2,purple=3, |
|||
1=1,2=2,3=3, |
|||
one=1,two=2,three=3, |
|||
oval=1,squiggle=2,squiggles=2,diamond=3, |
|||
solid=1,open=2,striped=3 |
|||
]), |
|||
NewSets1 = [], |
|||
foreach(S in Sets) |
|||
NewSets1 := NewSets1 ++ [[Maps.get(T) : T in S]] |
|||
end, |
|||
NewSets = NewSets1. |
|||
% |
|||
% Plain random problem instance, no check of solvability. |
|||
% |
|||
generate_instance2(NumCards,_NumSets,_SetLen, Cards) => |
|||
Cards = random_deal(NumCards).</lang> |
|||
{{out}} |
|||
This problem instance happens to have 10 solutions. |
|||
<pre>Cards: |
|||
[1,[green,diamond,one,open]] |
|||
[2,[green,diamond,one,solid]] |
|||
[3,[green,oval,one,open]] |
|||
[4,[green,oval,three,solid]] |
|||
[5,[green,oval,two,solid]] |
|||
[6,[green,squiggle,three,striped]] |
|||
[7,[green,squiggle,two,striped]] |
|||
[8,[purple,diamond,one,solid]] |
|||
[9,[purple,diamond,two,striped]] |
|||
[10,[purple,oval,one,solid]] |
|||
[11,[purple,oval,two,open]] |
|||
[12,[purple,squiggle,two,open]] |
|||
[13,[red,diamond,two,solid]] |
|||
[14,[red,oval,one,open]] |
|||
[15,[red,oval,three,solid]] |
|||
[16,[red,oval,two,solid]] |
|||
[17,[red,oval,two,striped]] |
|||
[18,[red,squiggle,one,striped]] |
|||
setLen = 3 |
|||
numWanted = 9 |
|||
x = {{1,4,7},{1,5,6},{1,10,18},{3,8,18},{4,10,16},{5,10,15},{5,11,17},{7,9,17},{7,11,13}} |
|||
[[green,diamond,one,open],[green,oval,three,solid],[green,squiggle,two,striped]] |
|||
[[green,diamond,one,open],[green,oval,two,solid],[green,squiggle,three,striped]] |
|||
[[green,diamond,one,open],[purple,oval,one,solid],[red,squiggle,one,striped]] |
|||
[[green,oval,one,open],[purple,diamond,one,solid],[red,squiggle,one,striped]] |
|||
[[green,oval,three,solid],[purple,oval,one,solid],[red,oval,two,solid]] |
|||
[[green,oval,two,solid],[purple,oval,one,solid],[red,oval,three,solid]] |
|||
[[green,oval,two,solid],[purple,oval,two,open],[red,oval,two,striped]] |
|||
[[green,squiggle,two,striped],[purple,diamond,two,striped],[red,oval,two,striped]] |
|||
[[green,squiggle,two,striped],[purple,oval,two,open],[red,diamond,two,solid]] |
|||
x = {{1,4,7},{1,5,6},{1,10,18},{3,8,18},{4,10,16},{5,10,15},{5,11,17},{7,9,17},{14,15,17}} |
|||
[[green,diamond,one,open],[green,oval,three,solid],[green,squiggle,two,striped]] |
|||
[[green,diamond,one,open],[green,oval,two,solid],[green,squiggle,three,striped]] |
|||
[[green,diamond,one,open],[purple,oval,one,solid],[red,squiggle,one,striped]] |
|||
[[green,oval,one,open],[purple,diamond,one,solid],[red,squiggle,one,striped]] |
|||
[[green,oval,three,solid],[purple,oval,one,solid],[red,oval,two,solid]] |
|||
[[green,oval,two,solid],[purple,oval,one,solid],[red,oval,three,solid]] |
|||
[[green,oval,two,solid],[purple,oval,two,open],[red,oval,two,striped]] |
|||
[[green,squiggle,two,striped],[purple,diamond,two,striped],[red,oval,two,striped]] |
|||
[[red,oval,one,open],[red,oval,three,solid],[red,oval,two,striped]] |
|||
...</pre> |
|||
=={{header|Prolog}}== |
=={{header|Prolog}}== |