Skip to content

Instantly share code, notes, and snippets.

@jimwhite
Last active December 16, 2015 22:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimwhite/5507166 to your computer and use it in GitHub Desktop.
Save jimwhite/5507166 to your computer and use it in GitHub Desktop.
A Groovy and a Clojure solution for Google Code Jam practice Treasure [https://code.google.com/codejam/contest/2270488/dashboard#s=p3].
;; My first bit of Clojure.
;; https://code.google.com/codejam/contest/2270488/dashboard#s=p3
(def test_chests '({:name 1 :opener 1 :contents ()} {:name 2 :opener 1 :contents (1 3)} {:name 3 :opener 2 :contents ()} {:name 4 :opener 3 :contents (2)}))
(defn remove-first [pred lst]
(if (pred (first lst)) (rest lst) (cons (first lst) (remove-first pred (rest lst)))))
;; Return sequence of chest names to open all the chests starting with the given list (multiset) of keys.
;; Return nil if there is no combination that opens all the chests.
(defn unlock_chests [keys chests]
(if (empty? chests) '()
(first
(for [this_chest (filter #((set keys) (:opener %1)) chests)
:let [open_the_rest
(unlock_chests
(concat (:contents this_chest) (remove-first #{(:opener this_chest)} keys))
(remove-first #{this_chest} chests))]
:when open_the_rest]
(cons (:name this_chest) open_the_rest)
)
)
)
)
;; If there are no chests to open then the opening sequence is an empty list.
(println (unlock_chests [1] '()))
;; This should be '((2 1 4 3)).
(println (unlock_chests [1] test_chests))
;; The answer for an unopenable set of chests should be nil.
(println (first (unlock_chests [2] test_chests)))
;; Here we see keys are a multiset and we're supposed to give lexicographically ordered first answer: (1 2 3).
(println (unlock_chests '(1 1 1) '({:name 1 :opener 1 :contents ()} {:name 2 :opener 1 :contents ()} {:name 3 :opener 1 :contents ()})))
// https://code.google.com/codejam/contest/2270488/dashboard#s=p3
test_chests = [[name:1, opener:1, contains:[]], [name:2, opener:1, contains:[1, 3]], [name:3, opener:2, contains: []], [name:4, opener:3, contains: [2]]]
println unlock_all([1], test_chests)
def unlock_all(keys, chests)
{
if (chests.isEmpty()) return []
for (def this_chest in chests) {
def this_key = this_chest.opener
def this_key_count = keys.count(this_key)
if (this_key_count) {
// Can't use this shorter one-liner since the keys are a multiset.
// def remaining_keys = (keys - [this_key]) + this_chest.contains
def remaining_keys = (keys - this_key) + ([this_key] * (this_key_count - 1)) + this_chest.contains
def chest_unlock_seq = unlock_all(remaining_keys, chests - this_chest)
if (chest_unlock_seq != null) return [this_chest.name] + chest_unlock_seq
}
}
return null
}
run_test_cases(new BufferedReader(new StringReader("""\
3
1 4
1
1 0
1 2 1 3
2 0
3 1 2
3 3
1 1 1
1 0
1 0
1 0
1 1
2
1 1 1
""")))
def run_test_cases(input)
{
def cases = read_numbers(input)[0]
cases.times { case_i ->
def (num_keys, num_chests) = read_numbers(input)
def keys = read_numbers(input)
def chests = (1..num_chests).collect { chest_x ->
def chest_nums = read_numbers(input)
[name:chest_x, opener:chest_nums[0], contains: chest_nums.drop(2)]
}
def opening_seq = unlock_all(keys, chests)
println "Case #${case_i + 1}: ${(opening_seq == null) ? 'IMPOSSIBLE' : opening_seq.join(' ')}"
}
}
def read_numbers(input)
{
input.readLine().split(/\s+/).collect { it as Integer }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment