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
|
||||
|
||||
-- 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)
|
||||
}`
|
||||
|
||||
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