# Game of Set Puzzle

May be you know the game of Set (a wonderful game for kids btw) a card game with 81 cards, where each card has a figure on it with 4 different attributes (form , number, colour and fill). Each attribute has 3 different values:

form: wave, oval, diamond
colour: red, purple, and green
number: 1, 2, 3
fill: none, dashed, opaque.


12 cards are laid open on the table and now the challenge is to indicate sets. A set consists of three cards where every attribute value occurs 0, 1 or 3 times. having 2 cards with red figures, or opaque, or 1 number is no good. See the supplied link for a more visual explanation.

I do envision a code for a card where all attributes are encoded so

"WP2N"


stands for

2 Purple Waves with No fill


Together with for instance OR1N and DG3N

and

it is a set (3 different forms, 3 different colors, 3 different number, 1 fill).

Input is space delimited string of unique codes (randomly chosen out of 81 possible codes) representing cards.

"OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR2D OG3O OR2D"


The solution must indicate all possible sets within the given collection. So

OR1N, WP2N, DG3N


must be part of the solution together with all other sets.

• Sounds promising, but please specify more precisely what kind of input data needs to be handled (stdin, file, parameter) and how the input and output data will look like. Also provide a visual representation (screenshot or similar) of a sample run. Oct 16, 2013 at 8:17
• I have retracted my close vote and upvoted this; it's very interesting! :) Oct 16, 2013 at 12:59
• The hell do you mean by "game for kids"? Oct 17, 2013 at 18:03
• Waitaminute... there are 4 different 4th letters: N, D, S, and O. Oct 17, 2013 at 20:08
• @boothby: I would have said the opposite. If the alphabets don't overlap, for every candidate set, you can just count how many times each letter or number shows up: the set is valid if and only if no number or letter appears twice. Oct 18, 2013 at 0:59

# Mathematica 93 92 93 82 76 73

f={}⋃Select[StringSplit@#~Subsets~{3}, FreeQ[Tally/@(Characters@#^T),2]&]&


The Logic

StringSplit@#~Subsets~{3} produces a list of 3-card subsets. Each triple such as:

{{"D", "G", "3", "N"}, {"W", "G", "3", "S"}, {"O", "G", "3", "O"}}

or

is then transposed,

and Tally/@(Characters@#^T) tallies the number of distinct items in each row.

{3,1,1,3}


3 corresponds to "all different"; 1 corresponds to "all same".

FreeQ[...,2] determines whether 2 cards of the same type or in the triple. If 2 is not among the tallies, then the three cards are a "set", according to Game of Set rules.

Usage

f["OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR1D OG3O OR2D"]


{{"DG3N", "WG3S", "OG3O"}, {"OR1N", "WP2N", "DG3N"}, {"WP2N", "DR1D", "OG3O"}}

• It can be shorter if duplicates are allowed. f=Select[StringSplit@#~Subsets~{3},FreeQ[Tally/@Thread@Characters@#,2]&]& Output will be {{"OR1N", "WP2N", "DG3N"}, {"WP2N","DR1D", "OG3O"}, {"WP2N", "DR1D", "OG3O"}, {"DG3N", "WG3S", "OG3O"}} Oct 17, 2013 at 6:11
• Very clever way to check for "all same" or "all different"! Oct 17, 2013 at 10:50

## Ruby, 1049881 80 characters

$*.combination(3).map{|c|puts c*?,if(0..3).all?{|i|c.map{|x|x[i]}.uniq.size!=2}}  Sample run (using your example data): c:\a\ruby>set.rb OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR1D OG3O OR2D OR1N,WP2N,DG3N WP2N,DR1D,OG3O WP2N,DR1D,OG3O DG3N,WG3S,OG3O  It outputs WP2N,DR1D,OG3O twice because you have two DR1Ds in your sample data. Explanation: $*.combination(3).map{|c| - each combination of 3 cards
puts c*?,if - output the set, if...
(0..3).all?{|i| - if all of the numbers from 0 to 3 (the indeces of the properties in the string) evaluate to true when passed into this block
c.map{|x|x[i]} - take the ith index of each string
.uniq.size!=2} - if the amount of unique properties (form, color, etc.) is not 2 (so, 1 or 3)

• Assuming this challenge will become codegolf, may I propose two improvements: a) get rid of end plus several line breaks: transform if ... puts ... end into puts ... if ... b) all can take a block, thus x.map{}.all? is equal to x.all?{} Oct 16, 2013 at 16:38
• @How Thanks, I'll make those improvements when I get to a computer. Oct 16, 2013 at 16:39
• @Howard Edited to include both. Thanks! Oct 16, 2013 at 20:47
• Also remove the space after if. Oct 16, 2013 at 22:10
• I like the ruby solution, short, concise and more or less readable Oct 17, 2013 at 8:13

# Mathematica 73

f = Select[StringSplit@#~Subsets~{3}, FreeQ[Tally /@ Thread@Characters@#, 2] &] &


Usage

f["OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR1D OG3O OR2D"]


{{"OR1N", "WP2N", "DG3N"}, {"WP2N", "DR1D", "OG3O"}, {"WP2N", "DR1D", "OG3O"}, {"DG3N", "WG3S", "OG3O"}}

# Brachylog, 12 bytes

ṇ₁⊇Ṫz{=|≠}ᵐz


Try it online!

Takes input through the input variable and generates the output through the output variable.

Second test case taken from a recently closed duplicate in its encoding, since this solution doesn't really care what anything actually means.

                The output variable is
⊇             a subsequence
Ṫ            of length 3
ṇ₁              of the input split on spaces,
z      z    the columns of which
{   }ᵐ     are all
|        either
=         the same element repeated,
≠       or entirely free of duplicates.


### GolfScript, 53 characters

" "/:w,,{:a,{:^,{a^]{w=}%.0\zip{.&,2=|}/!{.p}*;}/}/}/


Input must be provided on STDIN, example online:

> OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR2D OG3O OR2D
["OR1N" "DG3N" "WP2N"]
["WP2N" "OG3O" "DR1D"]
["DG3N" "OG3O" "WG3S"]
["WR2D" "OR2D" "DR2D"]


Commented code:

" "/:w          # split the input at spaces and assign it to variable w
,,              # create the array [0..n-1] (n being the length of w)
{:a,{:^,{       # three nested loops: a=0..n-1, ^=0..a-1, _=0..b-1
# (third loop has no variable assigned but just pushes on stack)
a^]         # make an array [a,^,_] of the three loop variables
{w=}%       # take the corresponding list items, i.e. [w[a],w[^],w[_]]
.0\         # push zero, add duplicate of the the array
zip         # zip transposes the array, thus [OR1N WP2N DG3N] -> [OWD RPG 123 NNN]
{           # loop over those entries
.&        # unique
,2=       # length equals 2?
|         # "or" with top of stack (two zero pushed before)
}/          # end of loop, on stack remains the results of the "or"s
!{.p}*      # if no length of 2 is there, make a copy of the set and print it
}/}/}/          # closing the three nested loops

• I would say a clear winner for a code-golf. Hower totally unreadable.. Oct 17, 2013 at 8:13
• @drjerry Compared with other golfscript code it's quite readable. E.g. it contains only plain loops and no advanced tricks. I'll add an explanation of the code later. Oct 17, 2013 at 10:25
• 0\zip{.&,2=|}/! can be shortened to zip{.&,}%2&! Feb 6, 2016 at 20:55

## javascript 323 313

function a(b){d=h=[];c=e=f=0;for(i in b){for(j in b){for(k in b[i]){if(b[i][k]==b[j][k]){if(c+f<4)c++;else if(c==4){h+=b[j];if(h.length=3)return h}}else{for(l in d){for(m in d[l]){g=f;if(d[l][2]==i){if(d[l][3]==k)if(b[j][k]!=d[l][0]&&b[j][k]!=d[l][1])f++;}else{continue}}if(g==f)d[e++]=[b[i][k],b[j][k],j,k]}}}}}}


its a function that takes a array of objects, and returns a array of objects.

DEMO fiddle (with tidy-up).

• You don't have to declare variables... Oct 16, 2013 at 15:02
• @Doorknob i take it back, you are right. edited. thanks! Oct 16, 2013 at 15:05

## APL(IBM), 76

⍉¨x/⍨{{⍵≡1⌽⍵}⍵=1⌽⍵}¨x←⊃¨(∘.,/3⍴⊂⍪¨(' '≠y)⊂y←⍞)⌷⍨¨z/⍨∧/¨2</¨z←,⍳3⍴12


I don't have IBM APL, but I believe this will work.

Sample run (Emulating IBM APL in Dyalog APL)

OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR1D OG3O OR2D
OR1N  WP2N  WP2N  DG3N
WP2N  DR1D  DR1D  WG3S
DG3N  OG3O  OG3O  OG3O

• First time I see apl code thanks! Oct 17, 2013 at 18:33

## Sage, 71

If C is a string, say "OR1N WP2N DG3N DR1D WG3S WG1N WR2D WP3N DR2O DR1D OG3O OR2D", execute

[c for c in Subsets(C.split(),3)if{1,3}>={len(set(x))for x in zip(*c)}]


to get [{'DR1D', 'OG3O', 'WP2N'}, {'DR2D', 'WR2D', 'OR2D'}, {'WG3S', 'OG3O', 'DG3N'}, {'DG3N', 'WP2N', 'OR1N'}]

And here's a very different approach using the interpretation that a Set is a projective line in GF(3)^4:

[c for c in Subsets(C.split(),3)if sum(matrix(3,map('WODRPG123N'.find,''.join(c))))%3==0]


I was a little annoyed that D was used twice... until I figured how to abuse that. But even better, I abuse the find method, too. str.find returns -1 if a letter isn't found. Since -1 = 2 mod 3, the letter S is handled appropriately because it doesn't occur in 'WODRPG123N'.

# Python 2, 99 bytes

lambda A:[a for a in combinations(A,3)if all(len(set(t))-2for t in zip(*a))]
from itertools import*


Try it online!