mirror of
https://github.com/Smaug123/AdventOfCode2022
synced 2025-10-06 10:08:42 +00:00
Day 21 (#33)
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day18.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day19.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day20.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day21.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -2,5 +2,5 @@ namespace AdventOfCode2022.App
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Inputs =
|
||||
let days = Array.init 20 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
|
||||
let days = Array.init 21 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
|
||||
let inline day (i : int) = days.[i - 1]
|
||||
|
@@ -74,6 +74,24 @@ type Benchmark16To20 () =
|
||||
[<GlobalCleanup>]
|
||||
member _.Cleanup () = Run.shouldWrite <- true
|
||||
|
||||
|
||||
type Benchmark21To25 () =
|
||||
[<GlobalSetup>]
|
||||
member _.Setup () = Run.shouldWrite <- false
|
||||
|
||||
[<Params(21)>]
|
||||
member val Day = 0 with get, set
|
||||
|
||||
[<Params(false, true)>]
|
||||
member val IsPartOne = false with get, set
|
||||
|
||||
[<Benchmark>]
|
||||
member this.Benchmark () : unit =
|
||||
Run.allRuns.[this.Day - 1] (not this.IsPartOne) (Inputs.day this.Day)
|
||||
|
||||
[<GlobalCleanup>]
|
||||
member _.Cleanup () = Run.shouldWrite <- true
|
||||
|
||||
module Program =
|
||||
|
||||
[<EntryPoint>]
|
||||
@@ -89,6 +107,7 @@ module Program =
|
||||
let _summary = BenchmarkRunner.Run<Benchmark6To10> config
|
||||
let _summary = BenchmarkRunner.Run<Benchmark11To15> config
|
||||
let _summary = BenchmarkRunner.Run<Benchmark16To20> config
|
||||
let _summary = BenchmarkRunner.Run<Benchmark21To25> config
|
||||
0
|
||||
| _ ->
|
||||
|
||||
|
@@ -288,6 +288,21 @@ module Run =
|
||||
if shouldWrite then
|
||||
printfn "%i" output
|
||||
|
||||
|
||||
let day21 (partTwo : bool) (input : string) =
|
||||
let day21 = StringSplitEnumerator.make '\n' input
|
||||
|
||||
if not partTwo then
|
||||
let output = Day21.part1 day21
|
||||
|
||||
if shouldWrite then
|
||||
printfn "%i" output
|
||||
else
|
||||
let output = Day21.part2 day21
|
||||
|
||||
if shouldWrite then
|
||||
printfn "%i" output
|
||||
|
||||
let allRuns =
|
||||
[|
|
||||
day1
|
||||
@@ -310,4 +325,5 @@ module Run =
|
||||
day18
|
||||
day19
|
||||
day20
|
||||
day21
|
||||
|]
|
||||
|
@@ -28,6 +28,7 @@
|
||||
<Compile Include="Day18.fs" />
|
||||
<Compile Include="Day19.fs" />
|
||||
<Compile Include="Day20.fs" />
|
||||
<Compile Include="Day21.fs" />
|
||||
<EmbeddedResource Include="Inputs\Day1.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day2.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day3.txt" />
|
||||
@@ -48,6 +49,7 @@
|
||||
<EmbeddedResource Include="Inputs\Day18.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day19.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day20.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day21.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
49
AdventOfCode2022.Test/Day21.fs
Normal file
49
AdventOfCode2022.Test/Day21.fs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace AdventOfCode2022.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open FsUnitTyped
|
||||
open AdventOfCode2022
|
||||
|
||||
[<TestFixture>]
|
||||
module TestDay21 =
|
||||
|
||||
let input =
|
||||
"""root: pppw + sjmn
|
||||
dbpl: 5
|
||||
cczh: sllz + lgvd
|
||||
zczc: 2
|
||||
ptdq: humn - dvpt
|
||||
dvpt: 3
|
||||
lfqf: 4
|
||||
humn: 5
|
||||
ljgn: 2
|
||||
sjmn: drzm * dbpl
|
||||
sllz: 4
|
||||
pppw: cczh / lfqf
|
||||
lgvd: ljgn * ptdq
|
||||
drzm: hmdt - zczc
|
||||
hmdt: 32
|
||||
"""
|
||||
|
||||
[<Test>]
|
||||
let ``Part 1, given`` () =
|
||||
Day21.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 152L
|
||||
|
||||
[<Test>]
|
||||
let ``Part 1`` () =
|
||||
let input = Assembly.readResource "Day21.txt"
|
||||
|
||||
Day21.part1 (StringSplitEnumerator.make '\n' input)
|
||||
|> shouldEqual 54703080378102L
|
||||
|
||||
|
||||
[<Test>]
|
||||
let ``Part 2, given`` () =
|
||||
Day21.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 301L
|
||||
|
||||
[<Test>]
|
||||
let ``Part 2`` () =
|
||||
let input = Assembly.readResource "Day21.txt"
|
||||
|
||||
Day21.part2 (StringSplitEnumerator.make '\n' input)
|
||||
|> shouldEqual 3952673930912L
|
2569
AdventOfCode2022.Test/Inputs/Day21.txt
Normal file
2569
AdventOfCode2022.Test/Inputs/Day21.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@
|
||||
<Compile Include="Day18.fs" />
|
||||
<Compile Include="Day19.fs" />
|
||||
<Compile Include="Day20.fs" />
|
||||
<Compile Include="Day21.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -21,13 +21,6 @@ module Day20 =
|
||||
|
||||
output.ToArray ()
|
||||
|
||||
[<Struct>]
|
||||
type Day20Entry =
|
||||
{
|
||||
OriginalPos : int
|
||||
Value : int
|
||||
}
|
||||
|
||||
let inline clone< ^T when ^T : struct> (arr : ^T[]) : ^T[] =
|
||||
let newArr = Array.zeroCreate arr.Length
|
||||
Buffer.BlockCopy (arr, 0, newArr, 0, arr.Length * sizeof< ^T>)
|
||||
|
200
AdventOfCode2022/Day21.fs
Normal file
200
AdventOfCode2022/Day21.fs
Normal file
@@ -0,0 +1,200 @@
|
||||
namespace AdventOfCode2022
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
|
||||
#if DEBUG
|
||||
open Checked
|
||||
#else
|
||||
#nowarn "9"
|
||||
#endif
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Day21 =
|
||||
|
||||
type Day21Operation =
|
||||
| Add = 0
|
||||
| Times = 1
|
||||
| Divide = 2
|
||||
| Minus = 3
|
||||
|
||||
let inline parseOp (c : char) =
|
||||
match c with
|
||||
| '+' -> Day21Operation.Add
|
||||
| '*' -> Day21Operation.Times
|
||||
| '/' -> Day21Operation.Divide
|
||||
| '-' -> Day21Operation.Minus
|
||||
| _ -> failwithf "bad op char: %c" c
|
||||
|
||||
type Day21Name = string
|
||||
|
||||
type Day21Input =
|
||||
| Literal of int
|
||||
| Operation of Day21Name * Day21Name * Day21Operation
|
||||
| Calculated of float
|
||||
|
||||
/// Returns the name of the root node and human node, too.
|
||||
let parse (line : StringSplitEnumerator) : Dictionary<Day21Name, Day21Input> * Day21Name * Day21Name =
|
||||
use mutable enum = line.GetEnumerator ()
|
||||
let output = Dictionary ()
|
||||
|
||||
while enum.MoveNext () do
|
||||
if not (enum.Current.IsWhiteSpace ()) then
|
||||
let colon = enum.Current.IndexOf ':'
|
||||
let name = enum.Current.Slice(0, colon).ToString ()
|
||||
|
||||
let rhs = enum.Current.Slice(colon + 2).TrimEnd ()
|
||||
|
||||
let expr =
|
||||
match Int32.TryParse rhs with
|
||||
| true, v -> Day21Input.Literal v
|
||||
| false, _ ->
|
||||
let space1 = rhs.IndexOf ' '
|
||||
let space2 = rhs.LastIndexOf ' '
|
||||
let s1 = rhs.Slice(0, space1).ToString ()
|
||||
|
||||
let op =
|
||||
let op = rhs.Slice (space1 + 1, space2 - space1 - 1)
|
||||
|
||||
if op.Length <> 1 then
|
||||
failwithf "Expected exactly one char for op, got %i" op.Length
|
||||
|
||||
parseOp op.[0]
|
||||
|
||||
let s2 = rhs.Slice(space2 + 1).ToString ()
|
||||
|
||||
Day21Input.Operation (s1, s2, op)
|
||||
|
||||
output.Add (name, expr)
|
||||
|
||||
output, "root", "humn"
|
||||
|
||||
let inline compute (v1 : float) (v2 : float) (op : Day21Operation) : float =
|
||||
match op with
|
||||
| Day21Operation.Add -> v1 + v2
|
||||
| Day21Operation.Times -> v1 * v2
|
||||
| Day21Operation.Minus -> v1 - v2
|
||||
| Day21Operation.Divide -> v1 / v2
|
||||
| _ -> failwith "bad enum"
|
||||
|
||||
let rec evaluate (d : Dictionary<Day21Name, Day21Input>) (s : Day21Name) : float =
|
||||
match d.[s] with
|
||||
| Day21Input.Literal v ->
|
||||
let result = float v
|
||||
d.[s] <- Day21Input.Calculated result
|
||||
result
|
||||
| Day21Input.Calculated f -> f
|
||||
| Day21Input.Operation (s1, s2, op) ->
|
||||
let v1 = evaluate d s1
|
||||
let v2 = evaluate d s2
|
||||
|
||||
let result = compute v1 v2 op
|
||||
|
||||
d.[s] <- Day21Input.Calculated result
|
||||
result
|
||||
|
||||
let inline round (v : float) : int64 =
|
||||
let rounded = int64 v
|
||||
|
||||
if abs (float rounded - v) > 0.00000001 then
|
||||
failwith "not an int"
|
||||
|
||||
rounded
|
||||
|
||||
let part1 (lines : StringSplitEnumerator) : int64 =
|
||||
let original, root, _ = parse lines
|
||||
|
||||
let result = evaluate original root
|
||||
|
||||
round result
|
||||
|
||||
type Day21Expr =
|
||||
| Literal of float
|
||||
| Variable
|
||||
| Calc of Day21Expr * Day21Expr * Day21Operation
|
||||
|
||||
let rec convert
|
||||
(human : Day21Name)
|
||||
(key : Day21Name)
|
||||
(d : Dictionary<Day21Name, Day21Input>)
|
||||
(result : Dictionary<Day21Name, Day21Expr>)
|
||||
: Day21Expr
|
||||
=
|
||||
match result.TryGetValue key with
|
||||
| true, v -> v
|
||||
| false, _ ->
|
||||
|
||||
if key = human then
|
||||
let answer = Day21Expr.Variable
|
||||
result.[human] <- answer
|
||||
answer
|
||||
else
|
||||
|
||||
match d.[key] with
|
||||
| Day21Input.Literal v ->
|
||||
let answer = Day21Expr.Literal (float v)
|
||||
result.[key] <- answer
|
||||
answer
|
||||
| Day21Input.Calculated _ -> failwith "no never"
|
||||
| Day21Input.Operation (s1, s2, op) ->
|
||||
let v1 = convert human s1 d result
|
||||
let v2 = convert human s2 d result
|
||||
// One wave of simplification
|
||||
let answer =
|
||||
match v1, v2 with
|
||||
| Day21Expr.Literal l1, Day21Expr.Literal l2 -> Day21Expr.Literal (compute l1 l2 op)
|
||||
| _, _ -> Day21Expr.Calc (v1, v2, op)
|
||||
|
||||
result.[key] <- answer
|
||||
answer
|
||||
|
||||
let part2 (lines : StringSplitEnumerator) : int64 =
|
||||
let original, root, human = parse lines
|
||||
|
||||
let lhs, rhs =
|
||||
match original.[root] with
|
||||
| Day21Input.Literal _
|
||||
| Day21Input.Calculated _ -> failwith "expected operation"
|
||||
| Day21Input.Operation (s1, s2, _) -> s1, s2
|
||||
|
||||
let converted = Dictionary original.Count
|
||||
let mutable lhs = convert human lhs original converted
|
||||
let mutable rhs = convert human rhs original converted
|
||||
|
||||
let mutable answer = nan
|
||||
|
||||
while Double.IsNaN answer do
|
||||
match lhs, rhs with
|
||||
| Day21Expr.Literal l, Day21Expr.Variable
|
||||
| Day21Expr.Variable, Day21Expr.Literal l -> answer <- l
|
||||
| Day21Expr.Literal l, Day21Expr.Calc (v1, v2, op)
|
||||
| Day21Expr.Calc (v1, v2, op), Day21Expr.Literal l ->
|
||||
match v1, v2 with
|
||||
| v1, Day21Expr.Literal v2 ->
|
||||
lhs <- v1
|
||||
|
||||
rhs <-
|
||||
match op with
|
||||
| Day21Operation.Add -> Day21Expr.Literal (l - v2)
|
||||
| Day21Operation.Times -> Day21Expr.Literal (l / v2)
|
||||
| Day21Operation.Divide -> Day21Expr.Literal (l * v2)
|
||||
| Day21Operation.Minus -> Day21Expr.Literal (l + v2)
|
||||
| _ -> failwith "bad op"
|
||||
| Day21Expr.Literal v1, v2 ->
|
||||
lhs <- v2
|
||||
|
||||
rhs <-
|
||||
match op with
|
||||
| Day21Operation.Add -> Day21Expr.Literal (l - v1)
|
||||
| Day21Operation.Times -> Day21Expr.Literal (l / v1)
|
||||
| Day21Operation.Divide -> Day21Expr.Literal (v1 / l)
|
||||
| Day21Operation.Minus -> Day21Expr.Literal (v1 - l)
|
||||
| _ -> failwith "bad op"
|
||||
| _, _ -> failwith "problem is too hard: had variables on both sides"
|
||||
| Day21Expr.Variable, Day21Expr.Variable
|
||||
| Day21Expr.Variable, Day21Expr.Calc _
|
||||
| Day21Expr.Calc _, Day21Expr.Calc _
|
||||
| Day21Expr.Calc _, Day21Expr.Variable -> failwith "one side is always a literal"
|
||||
| Day21Expr.Literal _, Day21Expr.Literal _ -> failwith "can't both be literals"
|
||||
|
||||
round answer
|
@@ -0,0 +1,14 @@
|
||||
``` ini
|
||||
|
||||
BenchmarkDotNet=v0.13.2, OS=macOS 13.0.1 (22A400) [Darwin 22.1.0]
|
||||
Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
|
||||
.NET SDK=7.0.100
|
||||
[Host] : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD DEBUG
|
||||
DefaultJob : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD
|
||||
|
||||
|
||||
```
|
||||
| Method | Day | IsPartOne | Mean | Error | StdDev |
|
||||
|---------- |---- |---------- |---------:|---------:|---------:|
|
||||
| **Benchmark** | **21** | **False** | **678.3 μs** | **13.00 μs** | **15.48 μs** |
|
||||
| **Benchmark** | **21** | **True** | **612.3 μs** | **9.40 μs** | **8.79 μs** |
|
@@ -0,0 +1,3 @@
|
||||
Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Day,IsPartOne,Mean,Error,StdDev
|
||||
Benchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,0000000000,Empty,RyuJit,Arm64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 7.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,21,False,678.3 μs,13.00 μs,15.48 μs
|
||||
Benchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,0000000000,Empty,RyuJit,Arm64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 7.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,21,True,612.3 μs,9.40 μs,8.79 μs
|
|
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<title>AdventOfCode2022.App.Benchmark21To25-20221221-100214</title>
|
||||
|
||||
<style type="text/css">
|
||||
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
|
||||
td, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }
|
||||
tr { background-color: #fff; border-top: 1px solid #ccc; }
|
||||
tr:nth-child(even) { background: #f8f8f8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre><code>
|
||||
BenchmarkDotNet=v0.13.2, OS=macOS 13.0.1 (22A400) [Darwin 22.1.0]
|
||||
Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
|
||||
.NET SDK=7.0.100
|
||||
[Host] : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD DEBUG
|
||||
DefaultJob : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD
|
||||
</code></pre>
|
||||
<pre><code></code></pre>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Method</th><th>Day</th><th>IsPartOne</th><th>Mean</th><th>Error</th><th>StdDev</th>
|
||||
</tr>
|
||||
</thead><tbody><tr><td>Benchmark</td><td>21</td><td>False</td><td>678.3 μs</td><td>13.00 μs</td><td>15.48 μs</td>
|
||||
</tr><tr><td>Benchmark</td><td>21</td><td>True</td><td>612.3 μs</td><td>9.40 μs</td><td>8.79 μs</td>
|
||||
</tr></tbody></table>
|
||||
</body>
|
||||
</html>
|
@@ -9,3 +9,5 @@ BenchmarkDotNet reports:
|
||||
* [Day 1 through Day 5](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark1To5-report-github.md).
|
||||
* [Day 6 through Day 10](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark6To10-report-github.md).
|
||||
* [Day 11 through Day 15](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark11To15-report-github.md).
|
||||
* [Day 16 through Day 20](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark16To20-report-github.md).
|
||||
* [Day 21 through Day 25](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark21To25-report-github.md).
|
||||
|
Reference in New Issue
Block a user