;;; structs.clj       Dave Reed         4/1/26

(defn my-count [lst]
  (if (empty? lst)
    0
    (inc (my-count (rest lst)))))

(defn my-nth [arblist index]
  (cond (empty? arblist) nil
        (zero? index) (first arblist)
        :else (recur (rest arblist) (dec index))))

(def NAMES '(("Smith" "Pat" \Q)
             ("Jones" "Chris" \J)
             ("Walker" "Kelly" \T)
             ("Thompson" "Shelly" \P)))

(defn my-get [records key]
  (cond (empty? records) nil
        (= (first (first records)) key) (first records)
        :else (recur (rest records) key)))

(def MENU '((:bean-burger 2.99)
               (:tofu-dog 2.49)
               (:fries 0.99)
               (:medium-soda 0.79)
               (:large-soda 0.99)))

(defn price [item]
  (second (my-get MENU item)))

(defn meal-price [meal]
  (if (empty? meal)
    0.0
    (+ (price (first meal)) (meal-price (rest meal)))))

(defn meal-price [meal]
  (apply + (map price meal)))

(def FRIENDS {"amy"   #{"bob" "dan" "elle"}
              "bob"   #{"amy" "dan"}
              "chaz"  #{"dan" "elle"}
              "dan"   #{"chaz"}
              "elle"  #{"amy" "bob" "chaz" "dan"}
              "fred"  #{"dan"} })

(defn get-friends [person]
  (get FRIENDS person))

(defn get-circle [person N]
  (if (= N 1)
    (get-friends person)
    (let [circle (get-circle person (dec N))]
      (disj (clojure.set/union circle (apply clojure.set/union (map get-friends circle)))
            person))))

(def ANIMALS '(:dog
                (:bird (:aardvark () ()) (:cat () ()))
                (:possum (:frog () ()) (:wolf () ()))))

(defn root [tree]
  (nth tree 0))

(defn left-subtree [tree]
  (nth tree 1))

(defn right-subtree [tree]
  (nth tree 2))

(defn size [tree]
  (if (empty? tree)
    0
    (+ 1 (size (left-subtree tree)) (size (right-subtree tree)))))

(defn height [tree]
  (if (empty? tree)
    0
    (inc (max (height (left-subtree tree)) (height (right-subtree tree))))))

(defn tree-contains? [tree item]
  (cond (empty? tree) false
        (= (root tree) item) true
        :else (or (tree-contains? (left-subtree tree) item)
                  (tree-contains? (right-subtree tree) item))))

(defn bst-contains? [tree item]
  (cond (empty? tree) false
        (= (root tree) item) true
        (pos? (compare (root tree) item))  (bst-contains? (left-subtree tree) item)
        :else (bst-contains? (right-subtree tree) item)))

(defn die-roll []
  (inc (rand-int 6)))

(defn dice-roll []
  (+ (die-roll) (die-roll)))

;;; first version of craps game (just returns :WINNER or :LOSER)
(defn craps1 []
  (defn roll-until [point]
    (let [next-roll (dice-roll)]
      (cond (== next-roll 7) :LOSER
            (== next-roll point) :WINNER
            :else (recur point))))
  (let [roll (dice-roll)]
    (cond (or (== roll 2) (== roll 12)) :LOSER
          (== roll 7) :WINNER
          :else (roll-until roll))))

;;; second version of craps game (returns history list)
(defn craps2 []
  (defn roll-until [point history]
    (let [next-roll (dice-roll)]
      (cond (== next-roll 7) (reverse (cons :LOSER (cons next-roll history)))
            (== next-roll point) (reverse (cons :WINNER (cons next-roll history)))
            :else (recur point (cons next-roll history)))))

  (let [roll (dice-roll)]
    (cond (or (== roll 2) (== roll 12)) (list roll :LOSER)
          (== roll 7) (list roll :WINNER)
          :else (roll-until roll (list roll)))))

;;; third version of craps game (prints rolls)
(defn craps3 []
  (defn roll-until [point]
    (let [next-roll (dice-roll)]
      (do (print "Roll: ") (println next-roll)
          (cond (== next-roll 7) :LOSER
                (== next-roll point) :WINNER
                :else (recur point)))))

  (let [roll (dice-roll)]
    (do (print "Point: ") (println roll)
        (cond (or (== roll 2) (== roll 12)) :LOSER
              (== roll 7) :WINNER
              :else (roll-until roll)))))
