Day 10 (z3 version)
This commit is contained in:
126
aoc2025/Day10.newt
Normal file
126
aoc2025/Day10.newt
Normal file
@@ -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"
|
||||||
@@ -2,6 +2,31 @@ module Node
|
|||||||
|
|
||||||
import Prelude
|
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 fs : JSObject := `require('fs')`
|
||||||
pfunc getArgs : List String := `arrayToList(String, process.argv)`
|
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 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)
|
||||||
|
}`
|
||||||
|
|||||||
46
aoc2025/Z3.newt
Normal file
46
aoc2025/Z3.newt
Normal file
@@ -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
|
||||||
38
aoc2025/Z3Test.newt
Normal file
38
aoc2025/Z3Test.newt
Normal file
@@ -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
|
||||||
3
aoc2025/day10/eg.txt
Normal file
3
aoc2025/day10/eg.txt
Normal file
@@ -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}
|
||||||
Reference in New Issue
Block a user