diff --git a/aoc2025/Day10.newt b/aoc2025/Day10.newt new file mode 100644 index 0000000..2c462ae --- /dev/null +++ b/aoc2025/Day10.newt @@ -0,0 +1,126 @@ +module Day10 + +import Prelude +import Node +import Aoc +import Parser +import Data.Vect +import Data.Fin +import Z3 + +instance ∀ a b. {{Show a}} → {{Show b}} → Show (Either a b) where + show (Left a) = "Left \{show a}" + show (Right b) = "Right \{show b}" + +record Machine where + goal : Int + buttons : List Int + jolts : List Int + +infixr 2 _**_ + +abs : Int → Int +abs x = if x < 0 then 0 - x else x + +data Σ : (a : U) → (a → U) → U where + _**_ : ∀ A. {0 B : A → U} → (x : A) → (_ : B x) → Σ A B + +instance Show Machine where + show m = "Mach{goal=\{show m.goal}, buttons=\{show m.buttons}, goal=\{show m.jolts}}" + +parseGroup : Char → Char → Parser (List Int) +parseGroup start end = do + match start + nums <- some $ number <* optional (match ',') + match end + ws + pure nums + +pfunc pow : Int → Int → Int := `(x,y) => x ** y` +pfunc xor : Int → Int → Int := `(x,y) => x ^ y` + +parseMachine : Parser Machine +parseMachine = do + match '[' + marks <- some $ match '.' <|> match '#' + match ']' + ws + buttons <- some $ parseGroup '(' ')' + costs <- parseGroup '{' '}' + pure $ MkMachine (mkgoal marks) (map mkbutton buttons) costs + where + mkbutton : List Int → Int + mkbutton (n :: ns) = pow 2 n + mkbutton ns + mkbutton Nil = 0 + + mkgoal : List Char → Int + mkgoal ('#' :: xs) = 1 + 2 * mkgoal xs + mkgoal (_ :: xs) = 2 * mkgoal xs + mkgoal Nil = 0 + +parseFile : Parser (List Machine) +parseFile = some $ parseMachine <* match '\n' + +solve : Machine → Int +solve m = go 0 m.buttons + where + go : Int → List Int → Int + go st Nil = if st == m.goal then 0 else 999 + go st (b :: bs) = + if st == m.goal + then 0 + else min (1 + go (xor st b) bs) (go st bs) + +infixl 7 _&_ +pfunc _&_ : Int → Int → Int := `(x,y) => x & y` + +part2z3 : {{Context}} → Machine → Promise Int +part2z3 {{context}} mach = do + let rows = length mach.jolts + let (Just todo) = toVect rows mach.jolts | _ => fatalError "jolt/rows length" + let vars = map (\i => z3const "x\{show $ fst i}") $ enumerate mach.buttons + let solver = newOptimizer context + + traverse_ (constrain solver $ zip vars mach.buttons) (enumerate mach.jolts) + for_ vars $ \v => z3add solver $ z3ge v (z3int 0) + z3minimize solver $ sum vars + "sat" <- z3check solver | res => fatalError "GOT \{res}" + model <- getModel solver + pure $ foldl _+_ 0 $ map getInt vars + where + sum : List Arith → Arith + sum Nil = z3int 0 + sum (a :: as) = foldl _+_ a as + + constrain : ∀ b. Solver b → List (Arith × Int) → (Nat × Int) → Promise Unit + constrain solver bs (ix, goal) = + let mask = pow 2 $ cast ix in + z3add solver $ z3eq (z3int goal) $ row mask bs (z3int 0) + where + row : Int → List (Arith × Int) → Arith → Arith + row mask Nil acc = acc + row mask ((x , b) :: bs) acc = if b & mask == 0 then row mask bs acc else row mask bs (acc + x) + + button2List : ∀ rows. Vect rows Int → Int → Int → Vect rows Int + button2List VNil _ _ = VNil + button2List (_ :- rest) mask b = + (if (b & mask) == 0 then 0 else 1) :- button2List rest (mask * 2) b + +run : String -> Promise Unit +run fn = do + putStrLn {Promise} fn + text <- liftIO {Promise} $ readFile fn + let (Right (probs,_)) = parseFile (unpack text) + | Left msg => putStrLn "ERR: \{msg}" + let part1 = foldl _+_ 0 $ map solve probs + putStrLn $ "part1 \{show part1}" + z3 <- initZ3 + let context = initContext z3 "main" + p2s <- traverse part2z3 probs + let p2 = foldl _+_ 0 p2s + putStrLn "part2 \{show p2}" + +main : IO Unit +main = runPromise $ do + run "aoc2025/day10/eg.txt" + run "aoc2025/day10/input.txt" diff --git a/aoc2025/Node.newt b/aoc2025/Node.newt index 98bcdb8..dc108a2 100644 --- a/aoc2025/Node.newt +++ b/aoc2025/Node.newt @@ -2,6 +2,31 @@ module Node import Prelude +-- Promise has some sequencing issues with IO +ptype Promise : U → U +pfunc promise_bind : ∀ a b. Promise a → (a → Promise b) → Promise b := `(a,b, ma, mab) => ma.then(v => mab(v))` +pfunc promise_pure : ∀ a. a → Promise a := `(_, a) => Promise.resolve(a)` +-- The potential issue here is that fa runs before it is even passed in! +pfunc promise_app : ∀ a b. → Promise (a → b) → Promise a → Promise b := `(a,b,fab,fa) => fab.then(ab => fa.then(a => Promise.resolve(ab(a))))` +-- This runs everything immediately... +pfunc promise_lift : ∀ a. IO a → Promise a := `(a,f) => Promise.resolve(f('WORLD').h1)` + +instance Monad Promise where + bind = promise_bind + pure = promise_pure + +instance Applicative Promise where + return = pure + fab <*> fa = promise_app fab fa + +instance HasIO Promise where + liftIO = promise_lift + pfunc fs : JSObject := `require('fs')` pfunc getArgs : List String := `arrayToList(String, process.argv)` pfunc readFile uses (MkIORes) : (fn : String) -> IO String := `(fn) => (w) => Prelude_MkIORes(require('fs').readFileSync(fn, 'utf8'), w)` + +pfunc runPromise uses (MkIORes MkUnit) : ∀ a. Promise a → IO Unit := `(a,p) => (w) => { + // p(w) + return Prelude_MkIORes(Prelude_MkUnit, w) +}` diff --git a/aoc2025/Z3.newt b/aoc2025/Z3.newt new file mode 100644 index 0000000..a44b5b9 --- /dev/null +++ b/aoc2025/Z3.newt @@ -0,0 +1,46 @@ +module Z3 + +import Prelude +import Node + +ptype Context +-- Flag if optimizer or solver +ptype Solver : Bool → U +ptype Arith +ptype Z3Bool +ptype Model +ptype Z3 + +pfunc initZ3 : Promise Z3 := `require('z3-solver').init()` +pfunc z3ResetMemory : Promise Z3 := `(z3) => z3.Z3.reset_memory()` +pfunc initContext : Z3 → String → Context := `(z3, name) => z3.Context(name)` + +pfunc newSolver : Context → Solver False := `(Context) => new Context.Solver()` +pfunc newOptimizer : Context → Solver True := `(Context) => new Context.Optimize()` + + +-- These probably should be IO or Promise +pfunc freshInt : {{Context}} → Promise Arith := `(Context) => Promise.resolve(Context.Int.fresh())` +pfunc z3const : {{Context}} → String → Arith := `(Context, name) => Context.Int.const(name)` +pfunc arith_add : Arith → Arith → Arith := `(a,b) => a.add(b)` +pfunc z3int : {{Context}} → Int → Arith := `(Context,a) => Context.Int.val(a)` + +-- We can't overload operators for these because of the return type + +pfunc z3eq : Arith → Arith → Z3Bool := `(a,b) => a.eq(b)` +pfunc z3ge : Arith → Arith → Z3Bool := `(a,b) => a.ge(b)` + +pfunc z3add : ∀ b. Solver b → Z3Bool → Promise Unit := `(_, solver, exp) => Promise.resolve(solver.add(exp))` +pfunc z3minimize : Solver True → Arith → Promise Unit := `(solver, exp) => Promise.resolve(solver.minimize(exp))` + +pfunc z3check : ∀ b. Solver b → Promise String := `(b, solver) => solver.check()` + +pfunc getModel : ∀ b. Solver b → Promise Model := `(b, solver) => Promise.resolve(solver.model())` + +pfunc getInt : {{Model}} → Arith → Int := `(model, exp) => { + let n = +(model.eval(exp).toString()) + return isNaN(n) ? 0 : n +}` + +instance Add Arith where + a + b = arith_add a b diff --git a/aoc2025/Z3Test.newt b/aoc2025/Z3Test.newt new file mode 100644 index 0000000..ae00b25 --- /dev/null +++ b/aoc2025/Z3Test.newt @@ -0,0 +1,38 @@ +module Z3Test + +import Prelude +import Node +import Z3 + +test : Promise Unit +test = do + context <- initZ3 "main" + x0 <- freshInt + x1 <- freshInt + x2 <- freshInt + x3 <- freshInt + x4 <- freshInt + x5 <- freshInt + let solver = newOptimizer context + + z3add solver $ z3ge x0 $ z3int 0 + z3add solver $ z3ge x1 $ z3int 0 + z3add solver $ z3ge x2 $ z3int 0 + z3add solver $ z3ge x3 $ z3int 0 + z3add solver $ z3ge x4 $ z3int 0 + z3add solver $ z3ge x5 $ z3int 0 + + z3add solver $ z3eq (x4 + x5) (z3int 3) + z3add solver $ z3eq (x1 + x5) (z3int 5) + z3add solver $ z3eq (x2 + x3 + x4) (z3int 4) + z3add solver $ z3eq (x0 + x1 + x3) (z3int 7) + + z3minimize solver $ x0 + x1 + x2 + x3 + x4 + x5 + + res <- z3check solver + liftIO $ putStrLn "GOT \{res}" + + +-- switch to promise mode +main : IO (Promise Unit) +main = pure test diff --git a/aoc2025/day10/eg.txt b/aoc2025/day10/eg.txt new file mode 100644 index 0000000..dd91d7b --- /dev/null +++ b/aoc2025/day10/eg.txt @@ -0,0 +1,3 @@ +[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7} +[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2} +[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}