mirror of
https://github.com/Smaug123/AdventOfCode2022
synced 2025-10-06 10:08:42 +00:00
Day 11 (#14)
This commit is contained in:
12
.github/workflows/dotnet.yaml
vendored
12
.github/workflows/dotnet.yaml
vendored
@@ -13,6 +13,12 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- Release
|
||||
- Debug
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -24,11 +30,11 @@ jobs:
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
run: dotnet build --no-restore --configuration ${{matrix.config}}
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
run: dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
|
||||
- name: Run app
|
||||
run: dotnet run --project AdventOfCode2022.App/AdventOfCode2022.App.fsproj --no-build
|
||||
run: dotnet run --project AdventOfCode2022.App/AdventOfCode2022.App.fsproj --no-build --configuration ${{matrix.config}}
|
||||
|
||||
check-format:
|
||||
runs-on: ubuntu-latest
|
||||
|
@@ -17,6 +17,7 @@
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day8.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day9.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day10.txt" />
|
||||
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day11.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -20,7 +20,7 @@ module Program =
|
||||
|
||||
[<EntryPoint>]
|
||||
let main _ =
|
||||
let days = Array.init 10 (fun day -> readResource $"Day%i{day + 1}.txt")
|
||||
let days = Array.init 11 (fun day -> readResource $"Day%i{day + 1}.txt")
|
||||
|
||||
let inline day (i : int) = days.[i - 1]
|
||||
|
||||
@@ -73,12 +73,16 @@ module Program =
|
||||
printfn "%i" (Day9.part1 day9)
|
||||
printfn "%i" (Day9.part2 day9)
|
||||
|
||||
|
||||
do
|
||||
let day10 = StringSplitEnumerator.make '\n' (day 10)
|
||||
printfn "%i" (Day10.part1 day10)
|
||||
Day10.render (printfn "%s") (Day10.part2 day10)
|
||||
|
||||
do
|
||||
let day11 = StringSplitEnumerator.make '\n' (day 11)
|
||||
printfn "%i" (Day11.part1 day11)
|
||||
printfn "%i" (Day11.part2 day11)
|
||||
|
||||
time.Stop ()
|
||||
printfn $"Took %i{time.ElapsedMilliseconds}ms"
|
||||
0
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<Compile Include="Day8.fs" />
|
||||
<Compile Include="Day9.fs" />
|
||||
<Compile Include="Day10.fs" />
|
||||
<Compile Include="Day11.fs" />
|
||||
<EmbeddedResource Include="Inputs\Day1.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day2.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day3.txt" />
|
||||
@@ -28,6 +29,7 @@
|
||||
<EmbeddedResource Include="Inputs\Day8.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day9.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day10.txt" />
|
||||
<EmbeddedResource Include="Inputs\Day11.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
67
AdventOfCode2022.Test/Day11.fs
Normal file
67
AdventOfCode2022.Test/Day11.fs
Normal file
@@ -0,0 +1,67 @@
|
||||
namespace AdventOfCode2022.Test
|
||||
|
||||
open NUnit.Framework
|
||||
open FsUnitTyped
|
||||
open AdventOfCode2022
|
||||
|
||||
[<TestFixture>]
|
||||
module TestDay11 =
|
||||
|
||||
let input =
|
||||
"""Monkey 0:
|
||||
Starting items: 79, 98
|
||||
Operation: new = old * 19
|
||||
Test: divisible by 23
|
||||
If true: throw to monkey 2
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 1:
|
||||
Starting items: 54, 65, 75, 74
|
||||
Operation: new = old + 6
|
||||
Test: divisible by 19
|
||||
If true: throw to monkey 2
|
||||
If false: throw to monkey 0
|
||||
|
||||
Monkey 2:
|
||||
Starting items: 79, 60, 97
|
||||
Operation: new = old * old
|
||||
Test: divisible by 13
|
||||
If true: throw to monkey 1
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 3:
|
||||
Starting items: 74
|
||||
Operation: new = old + 3
|
||||
Test: divisible by 17
|
||||
If true: throw to monkey 0
|
||||
If false: throw to monkey 1
|
||||
"""
|
||||
|
||||
[<Test>]
|
||||
let ``Part 1, given`` () =
|
||||
Day11.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 10605
|
||||
|
||||
[<Test>]
|
||||
let ``Part 1, single round, given`` () =
|
||||
let monkeys = Day11.parse (StringSplitEnumerator.make '\n' input)
|
||||
let inspections = Array.zeroCreate monkeys.Count
|
||||
|
||||
Day11.oneRoundDivThree monkeys inspections
|
||||
|
||||
inspections |> shouldEqual [| 2 ; 4 ; 3 ; 5 |]
|
||||
|
||||
[<Test>]
|
||||
let ``Part 1`` () =
|
||||
let input = Assembly.readResource "Day11.txt"
|
||||
|
||||
Day11.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 120384
|
||||
|
||||
|
||||
[<Test>]
|
||||
let ``Part 2, given 1`` () =
|
||||
Day11.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 2713310158L
|
||||
|
||||
[<Test>]
|
||||
let ``Part 2`` () =
|
||||
let input = Assembly.readResource "Day11.txt"
|
||||
Day11.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 32059801242L
|
55
AdventOfCode2022.Test/Inputs/Day11.txt
Normal file
55
AdventOfCode2022.Test/Inputs/Day11.txt
Normal file
@@ -0,0 +1,55 @@
|
||||
Monkey 0:
|
||||
Starting items: 99, 67, 92, 61, 83, 64, 98
|
||||
Operation: new = old * 17
|
||||
Test: divisible by 3
|
||||
If true: throw to monkey 4
|
||||
If false: throw to monkey 2
|
||||
|
||||
Monkey 1:
|
||||
Starting items: 78, 74, 88, 89, 50
|
||||
Operation: new = old * 11
|
||||
Test: divisible by 5
|
||||
If true: throw to monkey 3
|
||||
If false: throw to monkey 5
|
||||
|
||||
Monkey 2:
|
||||
Starting items: 98, 91
|
||||
Operation: new = old + 4
|
||||
Test: divisible by 2
|
||||
If true: throw to monkey 6
|
||||
If false: throw to monkey 4
|
||||
|
||||
Monkey 3:
|
||||
Starting items: 59, 72, 94, 91, 79, 88, 94, 51
|
||||
Operation: new = old * old
|
||||
Test: divisible by 13
|
||||
If true: throw to monkey 0
|
||||
If false: throw to monkey 5
|
||||
|
||||
Monkey 4:
|
||||
Starting items: 95, 72, 78
|
||||
Operation: new = old + 7
|
||||
Test: divisible by 11
|
||||
If true: throw to monkey 7
|
||||
If false: throw to monkey 6
|
||||
|
||||
Monkey 5:
|
||||
Starting items: 76
|
||||
Operation: new = old + 8
|
||||
Test: divisible by 17
|
||||
If true: throw to monkey 0
|
||||
If false: throw to monkey 2
|
||||
|
||||
Monkey 6:
|
||||
Starting items: 69, 60, 53, 89, 71, 88
|
||||
Operation: new = old + 5
|
||||
Test: divisible by 19
|
||||
If true: throw to monkey 7
|
||||
If false: throw to monkey 1
|
||||
|
||||
Monkey 7:
|
||||
Starting items: 72, 54, 63, 80
|
||||
Operation: new = old + 3
|
||||
Test: divisible by 7
|
||||
If true: throw to monkey 1
|
||||
If false: throw to monkey 3
|
@@ -19,6 +19,7 @@
|
||||
<Compile Include="Day8.fs" />
|
||||
<Compile Include="Day9.fs" />
|
||||
<Compile Include="Day10.fs" />
|
||||
<Compile Include="Day11.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
253
AdventOfCode2022/Day11.fs
Normal file
253
AdventOfCode2022/Day11.fs
Normal file
@@ -0,0 +1,253 @@
|
||||
namespace AdventOfCode2022
|
||||
|
||||
open System.Collections.Generic
|
||||
open System
|
||||
#if DEBUG
|
||||
open Checked
|
||||
#endif
|
||||
|
||||
[<Measure>]
|
||||
type monkey
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Day11 =
|
||||
|
||||
type Monkey =
|
||||
{
|
||||
Number : int<monkey>
|
||||
StartingItems : int64 ResizeArray
|
||||
OperationIsPlus : bool
|
||||
Argument : int64 option
|
||||
TestDivisibleBy : int64
|
||||
TrueCase : int<monkey>
|
||||
FalseCase : int<monkey>
|
||||
}
|
||||
|
||||
let parse (lines : StringSplitEnumerator) : Monkey IReadOnlyList =
|
||||
use mutable enum = lines
|
||||
let output = ResizeArray ()
|
||||
|
||||
while enum.MoveNext () do
|
||||
let monkey =
|
||||
let line = enum.Current.TrimEnd ()
|
||||
let mutable enum = StringSplitEnumerator.make' ' ' (line.TrimEnd ':')
|
||||
StringSplitEnumerator.chomp "Monkey" &enum
|
||||
let monkey = StringSplitEnumerator.consumeInt &enum * 1<monkey>
|
||||
|
||||
if enum.MoveNext () then
|
||||
failwith "Bad Monkey row"
|
||||
|
||||
monkey
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "Ran out of rows"
|
||||
|
||||
let line = enum.Current
|
||||
|
||||
let startItems =
|
||||
let line = line.Trim ()
|
||||
let mutable enum = StringSplitEnumerator.make' ' ' line
|
||||
StringSplitEnumerator.chomp "Starting" &enum
|
||||
StringSplitEnumerator.chomp "items:" &enum
|
||||
let items = ResizeArray ()
|
||||
|
||||
while enum.MoveNext () do
|
||||
let s = enum.Current.TrimEnd ','
|
||||
items.Add (Int64.Parse s)
|
||||
|
||||
items
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "Ran out of rows"
|
||||
|
||||
let line = enum.Current
|
||||
|
||||
let operationIsPlus, arg =
|
||||
let line = line.Trim ()
|
||||
let mutable enum = StringSplitEnumerator.make' ':' line
|
||||
StringSplitEnumerator.chomp "Operation" &enum
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "expected an operation"
|
||||
|
||||
let line = enum.Current.Trim ()
|
||||
|
||||
if enum.MoveNext () then
|
||||
failwith "bad formatting on operation"
|
||||
|
||||
let mutable enum = StringSplitEnumerator.make' '=' line
|
||||
StringSplitEnumerator.chomp "new " &enum
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "expected an RHS"
|
||||
|
||||
let rhs = enum.Current.Trim ()
|
||||
|
||||
if enum.MoveNext () then
|
||||
failwith "too many equals signs"
|
||||
|
||||
let mutable enum = StringSplitEnumerator.make' ' ' rhs
|
||||
|
||||
StringSplitEnumerator.chomp "old" &enum
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "expected three elements on RHS"
|
||||
|
||||
let operationIsPlus =
|
||||
if enum.Current.Length > 1 then
|
||||
failwith "expected operation of exactly 1 char"
|
||||
|
||||
match enum.Current.[0] with
|
||||
| '*' -> false
|
||||
| '+' -> true
|
||||
| c -> failwithf "Unrecognised op: %c" c
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "expected three elements on RHS"
|
||||
|
||||
let arg =
|
||||
if EfficientString.equals "old" enum.Current then
|
||||
None
|
||||
else
|
||||
let literal = Int64.Parse enum.Current
|
||||
Some literal
|
||||
|
||||
if enum.MoveNext () then
|
||||
failwith "too many entries on row"
|
||||
|
||||
operationIsPlus, arg
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "Ran out of rows"
|
||||
|
||||
let line = enum.Current.Trim ()
|
||||
|
||||
let test =
|
||||
if not (line.StartsWith "Test: divisible by ") then
|
||||
failwith "bad formatting on test line"
|
||||
|
||||
Int32.Parse (line.Slice 19)
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "Ran out of rows"
|
||||
|
||||
let line = enum.Current.Trim ()
|
||||
|
||||
let ifTrue =
|
||||
if not (line.StartsWith "If true: throw to monkey ") then
|
||||
failwith "bad formatting for ifTrue line"
|
||||
|
||||
Int32.Parse (line.Slice 24) * 1<monkey>
|
||||
|
||||
if not (enum.MoveNext ()) then
|
||||
failwith "Ran out of rows"
|
||||
|
||||
let line = enum.Current.Trim ()
|
||||
|
||||
let ifFalse =
|
||||
if not (line.StartsWith "If false: throw to monkey ") then
|
||||
failwith "bad formatting for ifFalse line"
|
||||
|
||||
Int32.Parse (line.Slice 25) * 1<monkey>
|
||||
|
||||
// We may be at the end, in which case there's no empty row.
|
||||
enum.MoveNext () |> ignore
|
||||
|
||||
if ifTrue = monkey then
|
||||
failwith "assumption broken: throws to self"
|
||||
|
||||
if ifFalse = monkey then
|
||||
failwith "assumption broken: throws to self"
|
||||
|
||||
{
|
||||
Number = monkey
|
||||
StartingItems = startItems
|
||||
OperationIsPlus = operationIsPlus
|
||||
Argument = arg
|
||||
TestDivisibleBy = test
|
||||
TrueCase = ifTrue
|
||||
FalseCase = ifFalse
|
||||
}
|
||||
|> output.Add
|
||||
|
||||
output :> IReadOnlyList<_>
|
||||
|
||||
let oneRoundDivThree (monkeys : IReadOnlyList<Monkey>) (inspections : int64 array) =
|
||||
for i in 0 .. monkeys.Count - 1 do
|
||||
let monkey = monkeys.[i]
|
||||
inspections.[i] <- inspections.[i] + int64 monkey.StartingItems.Count
|
||||
|
||||
for worry in monkey.StartingItems do
|
||||
let newWorry =
|
||||
let arg =
|
||||
match monkey.Argument with
|
||||
| None -> worry
|
||||
| Some l -> l
|
||||
|
||||
if monkey.OperationIsPlus then worry + arg else worry * arg
|
||||
|
||||
let newWorry = newWorry / 3L
|
||||
|
||||
let target =
|
||||
if newWorry % monkey.TestDivisibleBy = 0 then
|
||||
monkey.TrueCase
|
||||
else
|
||||
monkey.FalseCase
|
||||
|
||||
monkeys.[target / 1<monkey>].StartingItems.Add newWorry
|
||||
|
||||
monkey.StartingItems.Clear ()
|
||||
|
||||
let part1 (lines : StringSplitEnumerator) : int64 =
|
||||
let monkeys = parse lines
|
||||
|
||||
let mutable inspections = Array.zeroCreate<int64> monkeys.Count
|
||||
|
||||
for _round in 1..20 do
|
||||
oneRoundDivThree monkeys inspections
|
||||
|
||||
inspections |> Array.sortInPlace
|
||||
inspections.[inspections.Length - 1] * inspections.[inspections.Length - 2]
|
||||
|
||||
let oneRound (modulus : int64) (monkeys : IReadOnlyList<Monkey>) (inspections : int64 array) =
|
||||
for i in 0 .. monkeys.Count - 1 do
|
||||
let monkey = monkeys.[i]
|
||||
inspections.[i] <- inspections.[i] + int64 monkey.StartingItems.Count
|
||||
|
||||
for worryIndex in 0 .. monkey.StartingItems.Count - 1 do
|
||||
let worry = monkey.StartingItems.[worryIndex]
|
||||
|
||||
let newWorry =
|
||||
let arg =
|
||||
match monkey.Argument with
|
||||
| None -> worry
|
||||
| Some l -> l
|
||||
|
||||
if monkey.OperationIsPlus then worry + arg else worry * arg
|
||||
|
||||
let newWorry = newWorry % modulus
|
||||
|
||||
let target =
|
||||
if newWorry % monkey.TestDivisibleBy = 0 then
|
||||
monkey.TrueCase
|
||||
else
|
||||
monkey.FalseCase
|
||||
|
||||
monkeys.[target / 1<monkey>].StartingItems.Add newWorry
|
||||
|
||||
monkey.StartingItems.Clear ()
|
||||
|
||||
|
||||
let part2 (lines : StringSplitEnumerator) : int64 =
|
||||
let monkeys = parse lines
|
||||
|
||||
let mutable inspections = Array.zeroCreate<int64> monkeys.Count
|
||||
|
||||
let modulus =
|
||||
(1L, monkeys) ||> Seq.fold (fun i monkey -> i * monkey.TestDivisibleBy)
|
||||
|
||||
for _round in 1..10000 do
|
||||
oneRound modulus monkeys inspections
|
||||
|
||||
inspections |> Array.sortInPlace
|
||||
inspections.[inspections.Length - 1] * inspections.[inspections.Length - 2]
|
@@ -83,7 +83,7 @@ module StringSplitEnumerator =
|
||||
|
||||
let chomp (s : string) (e : byref<StringSplitEnumerator>) =
|
||||
if not (e.MoveNext ()) || not (EfficientString.equals s e.Current) then
|
||||
failwithf "expected '%s'" s
|
||||
failwithf "expected '%s', got '%s'" s (e.Current.ToString ())
|
||||
|
||||
let consumeInt (e : byref<StringSplitEnumerator>) : int =
|
||||
if not (e.MoveNext ()) then
|
||||
|
Reference in New Issue
Block a user