--- /dev/null
+(ns adventofcode2019.day14
+ [:require [adventofcode2019.lib :refer :all]
+ [adventofcode2019.intcode :as i]
+ [clojure.string :as str]
+ [clojure.set :as set]
+ [clojure.core.match :refer [match]]
+ [clojure.math.combinatorics :as combo]])
+
+(defn parse-reaction [reaction-text]
+ (let [parse-chemical (fn [text]
+ (let [[ct chem] (str/split (str/trim text) #" ")]
+ {chem (parse-int ct)}))
+ [inputs output] (str/split reaction-text #"=>")
+ [[out-chem out-ct]] (seq (parse-chemical output))]
+ {out-chem {(reduce merge {} (map parse-chemical (str/split inputs #","))) out-ct}}))
+
+(defn normalize-reaction [reaction]
+ (let [[output] (keys reaction)
+ arrows (reaction output)
+ inputs (keys arrows)
+ normalize-arrow (fn [[akey aval]]
+ {(reduce-kv (fn [m k v]
+ (assoc m k (rationalize (/ v aval))))
+ {} akey) 1})]
+ {output (reduce merge {} (map normalize-arrow arrows))}))
+
+(defn make-graph [input]
+ (reduce (partial merge-with merge)
+ (map (comp normalize-reaction parse-reaction) input)))
+
+(defn find-lowest-exchange-rate [graph start end]
+ (let [reactions (graph start)
+ input-lists (keys reactions)
+ input-chems (mapv keys input-lists)
+ trade (fn [recv exch]
+ (let [rx (if (ratio? exch) (numerator exch) exch)
+ xc (if (ratio? exch) (denominator exch) 1)
+ units (int (Math/ceil (/ recv xc)))]
+ (* units rx)))
+ find-terminal #(match [%]
+ [({end v} :only [end])] v
+ :else nil)
+ calculate-er (fn [[chem ct]]
+ (trade ct (find-lowest-exchange-rate graph chem end)))
+ reaction-er #(reduce + (map calculate-er %))]
+ (if (some #{[end]} input-chems)
+ (->> input-lists
+ (map find-terminal)
+ (remove nil?)
+ (reduce min))
+ (->> input-lists
+ (map reaction-er)
+ (reduce min)))))
+
+(defn day14 []
+ (let [graphed (make-graph (get-list-from-file (input-file)))]
+ (part1 (find-lowest-exchange-rate graphed "FUEL" "ORE"))
+ #_(part2)))
--- /dev/null
+(ns adventofcode2019.day14-test
+ (:require [clojure.test :refer :all]
+ [adventofcode2019.day14 :refer :all]))
+
+(deftest check-make-graph
+ (is (= (make-graph ["3 ORE => 2 ABC"
+ "3 ABC => 1 FUEL"])
+ {"ABC" {{"ORE" 3/2} 1}
+ "FUEL" {{"ABC" 3} 1}})))
+
+(deftest simple-exchange
+ (is (= 3 (find-lowest-exchange-rate {"FUEL" {{"ORE" 1, "ABC" 2, "DEF" 3} 1
+ {"ORE" 3} 1
+ {"GHI" 5/2} 1}}
+ "FUEL" "ORE"))))
+
+(deftest trading-up
+ (is (= 6 (find-lowest-exchange-rate (make-graph ["3 ORE => 2 ABC"
+ "3 ABC => 1 FUEL"])
+ "FUEL" "ORE"))))
+
+(deftest complicated-exchange
+ (is (= 2 (find-lowest-exchange-rate (make-graph ["3 ORE => 2 ABC"
+ "3 ABC => 1 FUEL"
+ "2 ORE => 1 CDE"
+ "1 CDE => 1 FUEL"])
+ "FUEL" "ORE"))))
+
+(deftest very-tricky-exchange
+ (is (= 3 (find-lowest-exchange-rate (make-graph ["3 ORE => 2 ABC"
+ "3 ABC => 1 FUEL"
+ "4 ORE => 1 CDE"
+ "1 CDE => 1 FUEL"
+ "3 ORE => 2 FGH"
+ "1 FGH => 1 IJK"
+ "1 IJK => 1 FUEL"])
+ "FUEL" "ORE"))))