janet-lang / janet

A dynamic language and bytecode vm
https://janet-lang.org
MIT License
3.48k stars 225 forks source link

Advent of Code, day 7 - non-deterministic output #1338

Closed jcalve closed 9 months ago

jcalve commented 9 months ago

First of all, forgive my ignorance if this is my fault and I can't see it.

I'm trying to solve the advent of code with janet and on day 7 my code passes the example input but if I use the full input I get a different result every time.

Janet version: Janet 1.32.1-a0cb7514 mingw/x64/gcc - compiled from master a few days ago with msys2.

Here is my code, criticism is very welcome:

#!/usr/bin/env janet

(def deck-scores {
                  :5kind 19
                  :4kind 18
                  :full 17
                  :3kind 16
                  :2pair 15
                  :pair 14
                  "A" 13
                  "K" 12
                  "Q" 11
                  "J" 10
                  "T" 9
                  "9" 8
                  "8" 7
                  "7" 6
                  "6" 5
                  "5" 4
                  "4" 3
                  "3" 2
                  "2" 1
                 })

(def card-grammar '(choice (set "AKQJT") (range "29")))
(def hand-grammar ~'(* (<- (any ,card-grammar)) -1))
(def hand-grammar-arr ~'(* (any (<- ,card-grammar)) -1))

(def input-grammar
  ~{ :main (sequence (<- (any ,card-grammar)) " " (<- :d+)) })

# (parse-hand "A23A4")
(defn parse-hand [str]
  (array/slice (peg/match hand-grammar-arr str) 0 -2))

(defn is-n-kind? [hand-arr n]
  (def freq (frequencies hand-arr))
  (var n-kind false)
  (loop [[card count] :pairs freq]
    (when (= count n)
      (set n-kind true)
      (break)))
  n-kind)

(defn is-n-pair? [hand-arr n]
  (def freq (frequencies hand-arr))
  (def unique-cards (keys freq))
  (def count-pairs (count (fn [card] (= 2 (get freq card))) unique-cards))
  (= n count-pairs))

# In order:
(defn is-five-kind? [hand-arr] (= ;hand-arr))
(defn is-four-kind? [hand-arr] (is-n-kind? hand-arr 4))
(defn is-full-house? [hand-arr]
  (def freq (frequencies hand-arr))
  (def unique-cards (keys freq))
  (if (= 2 (length unique-cards))
    (or (= 3 (get freq (get unique-cards 0)))
        (= 3 (get freq (get unique-cards 1))))
    false))
(defn is-three-kind? [hand-arr] (is-n-kind? hand-arr 3))
(defn is-two-pair? [hand-arr] (is-n-pair? hand-arr 2))
(defn is-pair? [hand-arr] (is-n-pair? hand-arr 1))
(defn is-high-card? [hand-arr] (= (length (frequencies hand-arr)) (length hand-arr)))
(defn combo? [hand-type]
  (def combo-types [:5kind :4kind :full :3kind :2pair :pair])
  (some (fn [t] (= t hand-type)) combo-types))

(defn get-high-card [hand-arr]
  (var high-card nil)
  (each card hand-arr
    (when (or (nil? high-card)
              (> (deck-scores card) (deck-scores high-card)))
      (set high-card card)))
  high-card)

(defn get-hand-type [hand-arr]
  (cond
    (is-five-kind? hand-arr) :5kind
     (is-four-kind? hand-arr) :4kind
     (is-full-house? hand-arr) :full
     (is-three-kind? hand-arr) :3kind
     (is-two-pair? hand-arr) :2pair
     (is-pair? hand-arr) :pair
     (get-high-card hand-arr)))

(defn get-play-score [play]
  (def hand-type (get-hand-type (play :hand)))
  (deck-scores hand-type))

(defn parse-play [line]
  (defn inv-score [card-score]
    (* 10 (- (inc (deck-scores "A")) card-score)))
  (def matched (peg/match input-grammar line))
  (def hand-arr (parse-hand (matched 0)))
  (def hand-type (get-hand-type hand-arr))
  (def score (deck-scores hand-type))
  (var sort-score (if (combo? hand-type)
                    (* score (math/pow 10 6))
                    (* (inv-score score) (math/pow 10 6))))
  (var sort-scores @[])
  (forv i 1 (+ 1 (length hand-arr))
        (def card (hand-arr (dec i)))
        (def card-score (inv-score (deck-scores card)))
        (defn pad-num [n] (math/pow 10 (- n i)))
        (def pad (* 100 (- (length hand-arr) i)))
        (def card-sort-score (+ pad card-score))
        (array/push sort-scores card-sort-score)
        (+= sort-score card-sort-score))
  (struct
   :hand hand-arr
   :bid (scan-number (matched 1))
   :type hand-type
   :score score
   :sort-score sort-score
   :sorting sort-scores))

(defn print-play [play] # debug
  (defn pk [k] (prinf "%.20Q" (get play k "")))
  (prin "Type: " ) (pk :type)
  (prin " Score: " ) (pk :score)
  (prin " Sorting: " ) (pk :sort-score)
  # (prin " Bid: " ) (pk :bid)
  (prin " Hand: " ) (pk :hand)
  # (prin " Sort: " ) (pk :sorting)
  (print ""))

(defn sort-by-cards [play-a play-b card-i]
    (def res (cmp ((play-a :sorting) card-i)
                  ((play-b :sorting) card-i)))
    (if (= 0 res)
      (sort-by-cards play-a play-b (inc card-i))
      res))

(defn parse-file [filename]
  (def plays @[])
  (with [f (file/open filename)]
        (each line (file/lines f)
          (array/push plays (parse-play (string/trimr line)))))

  # Sort in-place by custom score
  (sort-by (fn [t] (t :sort-score)) plays)

  # Swap hands by lower card score
  (for i 0 (- (length plays) 1)
    (def play (plays i))
    (def next (plays (inc i)))
    (when (= (play :type) (next :type))
      (def c (sort-by-cards play next 0))
      (when (= -1 c)
        (put plays i next)
        (put plays (inc i) play))))

  (var total-earnings 0)
  (loop [[index play] :pairs plays]
    # (prin "Rank " (inc index) ": ") (print-play play) # Debug
    (+= total-earnings (* (inc index) (play :bid))))
  (pp total-earnings))

(defn main [& args]
  (parse-file (get args 1)))

I run it with a file as argument:

$ cat day7_demo.txt
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483

$ ./day7.janet day7_demo.txt  # Correct
6440

But if I use the full input every time it's different:

$ ./day7.janet day7.txt
246955891

$ ./day7.janet day7.txt
246971472

$ ./day7.janet day7.txt
246938094

I didn't know how to get a minimally reproducible example so I pasted my solution to this puzzle.

Edit

I'm dumb, my swapping loop wasn't ordering the full array, also my solution is somehow wrong.