-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsolve.fsx
104 lines (77 loc) · 2.58 KB
/
solve.fsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
open System.Text.RegularExpressions
type Command =
| Nop = 0
| Jump = 1
| Acc = 2
type Program = (Command * int) array
let parseCommand s =
let m =
Regex(@"(nop|jmp|acc) ((\+|\-)?\d+)").Match(s)
let (g1, g2) = m.Groups.[1].Value, m.Groups.[2].Value
let command =
match g1 with
| "nop" -> Command.Nop
| "jmp" -> Command.Jump
| "acc" -> Command.Acc
(command, g2 |> int)
let rec readProgram () =
seq {
let line = System.Console.ReadLine()
if line <> null then
yield parseCommand line
yield! readProgram ()
}
type ProgramState = (Program * int * int * Set<int>)
let nop (ps: ProgramState) =
match ps with
| (p, acc, ip, history) -> (p, acc, ip + 1, Set.add ip history)
let jump (ps: ProgramState) v: ProgramState =
match ps with
| (p, acc, ip, history) -> (p, acc, ip + v, Set.add ip history)
let acc (ps: ProgramState) v: ProgramState =
match ps with
| (p, acc, ip, history) -> (p, acc + v, ip + 1, Set.add ip history)
let halts (ps: ProgramState) =
match ps with
| (_, _, ip, history) -> Set.contains ip history
let terminates (ps: ProgramState) =
match ps with
| (p, _, ip, _) -> ip >= p.Length
let curAcc (ps: ProgramState) =
match ps with
| (_, acc, _, _) -> acc
let curCommand (ps: ProgramState) =
match ps with
| (p, _, ip, _) -> (ip, p.[ip])
let start (p: Program): ProgramState = (p, 0, 0, Set.empty)
let rec solve1 (ps: ProgramState): int =
if halts ps then
curAcc ps
else
let (_, cmd) = curCommand ps
match cmd with
| (Command.Nop, _) -> solve1 (nop ps)
| (Command.Acc, v) -> solve1 (acc ps v)
| (Command.Jump, v) -> solve1 (jump ps v)
let rec solve2 (ps: ProgramState) skipped =
if halts ps then
None
elif terminates ps then
Some(curAcc ps)
else
let (ip, cmd) = curCommand ps
// try including this jump, if halts, try excluding it
let doJump =
fun v ->
match solve2 (jump ps v) skipped with
| None -> if skipped < 0 then (solve2 (nop ps) ip) else None
| result -> result
let doNop = fun () -> solve2 (nop ps) skipped
match cmd with
| (Command.Nop, _) -> doNop ()
| (Command.Acc, v) -> solve2 (acc ps v) skipped
| (Command.Jump, v) -> if ip = skipped then doNop () else doJump v
let program = Seq.toArray (readProgram ())
let sol1 = solve1 (start program)
let sol2 = (solve2 (start program) -1).Value
printfn "%d %d" sol1 sol2