Skip to content

Instantly share code, notes, and snippets.

@werenall
Last active December 14, 2023 13:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save werenall/bba5438c3e78a1cca63d4d2ab14561b7 to your computer and use it in GitHub Desktop.
Save werenall/bba5438c3e78a1cca63d4d2ab14561b7 to your computer and use it in GitHub Desktop.
Secret santa
(defn- rand-🎁
[πŸŽ… 🎁-pool exclusions]
(let [potential-🎁 (-> 🎁-pool
(disj πŸŽ…)
(set/difference (get exclusions πŸŽ…)))]
(first (shuffle potential-🎁))))
(defn frivolously-draw-secret-santa
"Tries to randomize πŸŽ…s hoping that with power of Christmas everything will just work.
Will give up after some retrials."
([names]
(frivolously-draw-secret-santa names {}))
([names exclusions]
(frivolously-draw-secret-santa names exclusions 1))
([names exclusions attempt]
{:pre [(s/valid? ::names names)
(s/valid? ::exclusions exclusions)
(s/valid? ::attempt attempt)]}
(let [result (reduce
(fn [acc πŸŽ…]
(let [🎁 (rand-🎁 πŸŽ… (get acc :🎁-pool) exclusions)]
(-> acc
(update :🎁-pool disj 🎁)
(update :πŸŽ…s assoc πŸŽ… 🎁))))
{:🎁-pool names
:πŸŽ…s {}}
names)
πŸŽ…s (get result :πŸŽ…s)]
(if (some #(nil? (val %)) πŸŽ…s)
(do
(println "Some πŸŽ… drew themselves. Retrying...")
(if (> attempt 10)
(frivolously-draw-secret-santa names exclusions (inc attempt))
(throw (Exception. "Couldn't find a solution in 10 retrials. Giving up."))))
πŸŽ…s))))
(defn- gen-santa-tree
[names exclusions]
(let [start {:πŸŽ…->🎁 {}
:available-🎁 names}]
(tree-seq
(fn [x]
(and (get x :πŸŽ…->🎁)
(get x :available-🎁)))
(fn [{:keys [available-🎁 πŸŽ…->🎁] :as node}]
(let [available-πŸŽ… (set/difference names (set (keys πŸŽ…->🎁)))]
(let [new-children
(reduce (fn [acc πŸŽ…]
(let [available-🎁 (set/difference
(disj available-🎁 πŸŽ…)
(get exclusions πŸŽ…))
children-for-πŸŽ… (map (fn [🎁]
(-> node
(update :πŸŽ…->🎁 assoc πŸŽ… 🎁)
(update :available-🎁 disj 🎁)))
available-🎁)]
(concat acc children-for-πŸŽ…)))
[]
available-πŸŽ…)]
new-children)))
start)))
(defn seriously-draw-secret-santa
"Christmas is serious business. Builds a tree of possible solutions and randomly chooses a valid one."
([names]
(seriously-draw-secret-santa names {}))
([names exclusions]
{:pre [(s/valid? ::names names)
(s/valid? ::exclusions exclusions)]}
(let [possible-solutions (->>
(gen-santa-tree names exclusions)
(filter #(empty? (:available-🎁 %))))]
(if (seq possible-solutions)
(-> possible-solutions
rand-nth
(get :πŸŽ…->🎁))
(throw (Exception. "Solution doesn't exist! Check your exclusions"))))))
(s/def ::names (s/and set?
(s/coll-of string? :min-count 2)))
(s/def ::exclusions (s/map-of string?
(s/and set?
(s/coll-of string?))))
(s/def ::attempt int?)
@werenall
Copy link
Author

(frivolously-draw-secret-santa #{"a" "b" "c"} {"a" #{"b" "c"}})
Some πŸŽ… drew themselves. Retrying...
Execution error at local/frivolously-draw-secret-santa (local.clj:238).
Couldn't find a solution in 10 retrials. Giving up.

(seriously-draw-secret-santa #{"a" "b" "c"} {"a" #{"b" "c"}})
Execution error at local/seriously-draw-secret-santa (local.clj:279).
Solution doesn't exist! Check your exclusions

(frivolously-draw-secret-santa #{"a" "b" "c"} {"a" #{"b"}})
=> {"a" "c", "b" "a", "c" "b"}

(seriously-draw-secret-santa #{"a" "b" "c"} {"a" #{"b"}})
=> {"a" "c", "b" "a", "c" "b"}

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