Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active September 26, 2020 00:09
Show Gist options
  • Save ericnormand/9675d9ebea9f5e3b66474fdef9e3d1b6 to your computer and use it in GitHub Desktop.
Save ericnormand/9675d9ebea9f5e3b66474fdef9e3d1b6 to your computer and use it in GitHub Desktop.
396 - PurelyFunctional.tv Newsletter

Set Game

I used to play a game called Set. In it, you'd place 12 cards down on the table. Then the players would try to find "sets". If you found a set, you'd race to hit the cards with your hand to claim it. It was tons of fun and just mathy enough to appeal to someone like me.

In this task, you are going to take a function that judges whether three cards constitute a valid "set".

Each card has four properties:

  1. Color (red, purple, green)
  2. Number (1, 2, 3)
  3. Shading (empty, lined, full)
  4. Shape (squiggle, oval, diamond)

So one card might have 3 purple full diamonds. Search for the game and look at pictures if you want some examples.

We'll represent each card as a map, like this:

{:color :purple
 :number 3
 :shading :full
 :shape :diamond}

Three cards form a set if all of the properties are total matches or total mismatches. A property is a total match if all of the values are the same, for instance three red cards. They are a total mismatch if all the values are different, for instance a diamond, an oval, and a squiggle. If any of the properties don't match or mismatch, it's not a set.

Here's an example of a set:

[{:color :purple :number 3 :shape :diamond :shading :full}
 {:color :red    :number 3 :shape :diamond :shading :lines}
 {:color :green  :number 3 :shape :diamond :shading :empty}]

Colors are a total mismatch, numbers are a total match, shape is a total match, shading is a total mismatch.

Here's an example of a non-set:

[{:color :purple :number 3 :shape :diamond :shading :full}
 {:color :red    :number 3 :shape :diamond :shading :lines}
 {:color :purple :number 3 :shape :diamond :shading :empty}]

Above, the colors are two purples and a red. Not a total match and not a total mismatch.

Write a function that takes an array of cards and says whether they are a set.

(set? [{..} {..} {..}]) ;=> true/false

Thanks to this site for the challenge idea where it is considered Very Hard level in JavaScript.

Please submit your solutions as comments to this gist. Discussion is welcome.

@albertzak
Copy link

(defn all-distinct-or-same? [& xs]
  (or (apply distinct? xs)
      (apply = xs)))

(defn set? [xs]
  (every? true? 
          (apply mapv all-distinct-or-same? 
                 (map vals xs))))

@kolstae
Copy link

kolstae commented Sep 23, 2020

(defn set? [cs]
    (every? #{1 3}
            (for [k [:color :number :shape :shading]]
              (count (distinct (map k cs))))))

@jeroenvanwijgerden
Copy link

jeroenvanwijgerden commented Sep 23, 2020

(defn update-all
  "Similar to clojure.core/update but for all values in m."
  [m f]
  (reduce-kv (fn [acc k v]
               (assoc acc k (f v)))
             {}
             m))

(defn properties
  [cards]
  ;; sometimes let bindings are much more understandable than a threading macro,
  ;; typically when the intermediate result changes type significantly.
  (let [name+value       (apply concat cards)
        name->name+value (group-by first name+value)
        name->values     (update-all name->name+value
                                     #(map second %))]
    name->values))

(defn total-match?
  [values]
  (apply = values))

(defn total-mismatch?
  [values]
  (apply distinct? values))

(defn set?
  [cards]
  (->> (properties cards)
       (every? (fn [[_name values]]
                 (or (total-match?    values)
                     (total-mismatch? values))))))

(set? [{:color :purple :number 3 :shape :diamond :shading :full}
       {:color :red    :number 3 :shape :diamond :shading :lines}
       {:color :green  :number 3 :shape :diamond :shading :empty}]) ; => true

(set? [{:color :purple :number 3 :shape :diamond :shading :full}
       {:color :red    :number 3 :shape :diamond :shading :lines}
       {:color :purple :number 3 :shape :diamond :shading :empty}]) ; => false

@KingCode
Copy link

(defn prop-success? [prop cards]
  (let [vs (map prop cards)]
    (or (apply = vs)
        (= (count vs) (count (set vs))))))

(def props [:color :number :shape :shading])

(defn set? [cards]
  (every? #(prop-success? % cards) props))

@JonathanHarford
Copy link

(defn is-set? [cards]
  (->> cards
       first
       keys
       (every? (fn [property]
                 (let [values (mapv #(get % property) cards)]
                   (or (apply = values)
                       (apply distinct? values)))))))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment