Day 22 and speedups (#34)

This commit is contained in:
Patrick Stevens
2022-12-22 11:51:54 +00:00
committed by GitHub
parent 6a08987e2a
commit 8a14768375
20 changed files with 1042 additions and 104 deletions

View File

@@ -31,6 +31,7 @@
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day19.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day20.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day21.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day22.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,5 +2,5 @@ namespace AdventOfCode2022.App
[<RequireQualifiedAccess>]
module Inputs =
let days = Array.init 21 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let days = Array.init 22 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let inline day (i : int) = days.[i - 1]

View File

@@ -79,7 +79,7 @@ type Benchmark21To25 () =
[<GlobalSetup>]
member _.Setup () = Run.shouldWrite <- false
[<Params(21)>]
[<Params(21, 22)>]
member val Day = 0 with get, set
[<Params(false, true)>]

View File

@@ -303,6 +303,20 @@ module Run =
if shouldWrite then
printfn "%i" output
let day22 (partTwo : bool) (input : string) =
let day22 = StringSplitEnumerator.make '\n' input
if not partTwo then
let output = Day22.part1 day22
if shouldWrite then
printfn "%i" output
else
let output = Day22.part2 day22
if shouldWrite then
printfn "%i" output
let allRuns =
[|
day1
@@ -326,4 +340,5 @@ module Run =
day19
day20
day21
day22
|]

View File

@@ -29,6 +29,7 @@
<Compile Include="Day19.fs" />
<Compile Include="Day20.fs" />
<Compile Include="Day21.fs" />
<Compile Include="Day22.fs" />
<EmbeddedResource Include="Inputs\Day1.txt" />
<EmbeddedResource Include="Inputs\Day2.txt" />
<EmbeddedResource Include="Inputs\Day3.txt" />
@@ -50,6 +51,7 @@
<EmbeddedResource Include="Inputs\Day19.txt" />
<EmbeddedResource Include="Inputs\Day20.txt" />
<EmbeddedResource Include="Inputs\Day21.txt" />
<EmbeddedResource Include="Inputs\Day22.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,46 @@
namespace AdventOfCode2022.Test
open NUnit.Framework
open FsUnitTyped
open AdventOfCode2022
[<TestFixture>]
module TestDay22 =
let input =
""" ...#
.#..
#...
....
...#.......#
........#...
..#....#....
..........#.
...#....
.....#..
.#......
......#.
10R5L5R10L4R5L5
"""
[<Test>]
let ``Part 1, given`` () =
Day22.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 6032
[<Test>]
let ``Part 1`` () =
let input = Assembly.readResource "Day22.txt"
Day22.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 103224
[<Test>]
let ``Part 2, given`` () =
Day22.part2Example (StringSplitEnumerator.make '\n' input) |> shouldEqual 5031
[<Test>]
let ``Part 2`` () =
let input = Assembly.readResource "Day22.txt"
Day22.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 189097

File diff suppressed because one or more lines are too long

View File

@@ -33,6 +33,7 @@
<Compile Include="Day19.fs" />
<Compile Include="Day20.fs" />
<Compile Include="Day21.fs" />
<Compile Include="Day22.fs" />
</ItemGroup>
<ItemGroup>

View File

@@ -11,6 +11,7 @@ open Checked
#nowarn "9"
#endif
[<Struct>]
type Coordinate =
{
X : int

View File

@@ -12,28 +12,73 @@ open Checked
[<Struct>]
[<IsReadOnly>]
type Day13Packet =
| PacketList of leaf : Day13Packet[]
| Int of int : int
{
Packet : Day13Packet ResizeArray ValueOption
/// If Packet is None, this is an int.
/// If Packet is Some, this is the start index of a slice.
StartIndexOrInt : int
}
[<RequireQualifiedAccess>]
module Day13 =
/// True if they're in the right order.
let rec cmp (packet1 : Day13Packet) (packet2 : Day13Packet) : bool option =
match packet1, packet2 with
| Day13Packet.Int i1, Day13Packet.Int i2 ->
if i1 < i2 then Some true
elif i1 > i2 then Some false
else None
| PacketList [||], PacketList [||] -> None
| PacketList [||], PacketList _ -> Some true
| PacketList _, PacketList [||] -> Some false
| PacketList arr1, PacketList arr2 ->
match cmp arr1.[0] arr2.[0] with
| Some v -> Some v
| None -> cmp (PacketList arr1.[1..]) (PacketList arr2.[1..])
| PacketList _, Int _ -> cmp packet1 (PacketList [| packet2 |])
| Int _, PacketList _ -> cmp (PacketList [| packet1 |]) packet2
let rec cmp (packet1 : Day13Packet) (packet2 : Day13Packet) : bool ValueOption =
match packet1.Packet, packet2.Packet with
| ValueNone, ValueNone ->
if packet1.StartIndexOrInt < packet2.StartIndexOrInt then
ValueSome true
elif packet1.StartIndexOrInt > packet2.StartIndexOrInt then
ValueSome false
else
ValueNone
| ValueSome arr1, ValueSome arr2 ->
if packet1.StartIndexOrInt = arr1.Count then
if packet2.StartIndexOrInt = arr2.Count then
ValueNone
else
ValueSome true
elif packet2.StartIndexOrInt = arr2.Count then
ValueSome false
else
match cmp arr1.[packet1.StartIndexOrInt] arr2.[packet2.StartIndexOrInt] with
| ValueSome v -> ValueSome v
| ValueNone ->
let advanced1 =
{
Packet = ValueSome arr1
StartIndexOrInt = packet1.StartIndexOrInt + 1
}
let advanced2 =
{
Packet = ValueSome arr2
StartIndexOrInt = packet2.StartIndexOrInt + 1
}
cmp advanced1 advanced2
| ValueSome arr1, ValueNone ->
if packet1.StartIndexOrInt = arr1.Count then
ValueSome true
else
match cmp arr1.[packet1.StartIndexOrInt] packet2 with
| ValueSome v -> ValueSome v
| ValueNone ->
if packet1.StartIndexOrInt + 1 = arr1.Count then
ValueNone
else
ValueSome false
| ValueNone, ValueSome arr2 ->
if packet2.StartIndexOrInt = arr2.Count then
ValueSome false
else
match cmp packet1 arr2.[packet2.StartIndexOrInt] with
| ValueSome v -> ValueSome v
| ValueNone ->
if packet2.StartIndexOrInt + 1 = arr2.Count then
ValueNone
else
ValueSome true
let parse (lines : StringSplitEnumerator) : Day13Packet ResizeArray =
use mutable enum = lines
@@ -54,12 +99,20 @@ module Day13 =
curr <- curr.Slice 1
if curr.[0] <> ']' then
stack
.Peek()
.Add (Day13Packet.Int (Int32.Parse (curr.TrimEnd ']', NumberStyles.None)))
let int =
{
StartIndexOrInt = Int32.Parse (curr.TrimEnd ']', NumberStyles.None)
Packet = ValueNone
}
stack.Peek().Add int
while curr.Length > 0 && curr.[curr.Length - 1] = ']' do
let closed = Day13Packet.PacketList (stack.Pop().ToArray ())
let closed =
{
Packet = ValueSome (stack.Pop ())
StartIndexOrInt = 0
}
if stack.Count = 0 then
output.Add closed
@@ -77,39 +130,66 @@ module Day13 =
for i in 0..2 .. data.Count - 1 do
match cmp data.[i] data.[i + 1] with
| None -> failwith "should have decided"
| Some false -> ()
| Some true -> sum <- sum + (i / 2 + 1)
| ValueNone -> failwith "should have decided"
| ValueSome false -> ()
| ValueSome true -> sum <- sum + (i / 2 + 1)
sum
let part2 (lines : StringSplitEnumerator) : int =
let data = parse lines
let marker1 =
Day13Packet.PacketList [| Day13Packet.PacketList [| Day13Packet.Int 2 |] |]
let arr = ResizeArray 1
let arr2 = ResizeArray 1
data.Add marker1
arr2.Add
{
StartIndexOrInt = 2
Packet = ValueNone
}
arr.Add
{
StartIndexOrInt = 0
Packet = ValueSome arr2
}
{
Packet = ValueSome arr
StartIndexOrInt = 0
}
let marker2 =
Day13Packet.PacketList [| Day13Packet.PacketList [| Day13Packet.Int 6 |] |]
let arr = ResizeArray 1
let arr2 = ResizeArray 1
data.Add marker2
let data = data.ToArray () |> Array.indexed
Array.sortInPlaceWith (fun (_, a) (_, b) -> if Option.get (cmp a b) then -1 else 1) data
arr2.Add
{
StartIndexOrInt = 6
Packet = ValueNone
}
let mutable answer = 1
let mutable keepGoing = true
let mutable i = 0
arr.Add
{
StartIndexOrInt = 0
Packet = ValueSome arr2
}
while keepGoing do
if answer = 1 && fst data.[i] = data.Length - 2 then
answer <- answer * (i + 1)
elif fst data.[i] = data.Length - 1 then
answer <- answer * (i + 1)
keepGoing <- false
{
Packet = ValueSome arr
StartIndexOrInt = 0
}
i <- i + 1
let mutable howManyLessThan = 0
let mutable howManyGreaterThan = 0
answer
for i in 0 .. data.Count - 1 do
match cmp data.[i] marker1 with
| ValueSome true -> howManyLessThan <- howManyLessThan + 1
| _ ->
match cmp marker2 data.[i] with
| ValueSome true -> howManyGreaterThan <- howManyGreaterThan + 1
| _ -> ()
(howManyLessThan + 1) * (data.Count - howManyGreaterThan + 2)

View File

@@ -14,6 +14,13 @@ open Checked
type Day14Shape = Coordinate ResizeArray
[<RequireQualifiedAccess>]
type MutableCoordinate =
{
mutable X : int
mutable Y : int
}
[<RequireQualifiedAccess>]
module Day14 =
@@ -33,7 +40,7 @@ module Day14 =
let y = StringSplitEnumerator.consumeInt &coords
{
X = x
Coordinate.X = x
Y = y
}
|> thisLine.Add
@@ -55,12 +62,6 @@ module Day14 =
for x = min point1.X point2.X to max point1.X point2.X do
Arr2D.set arr x point1.Y true
type MutableCoordinate =
{
mutable X : int
mutable Y : int
}
let inline fallOnce (sandPos : MutableCoordinate) (arr : Arr2D<bool>) : bool =
if not (Arr2D.get arr sandPos.X (sandPos.Y + 1)) then
sandPos.Y <- sandPos.Y + 1
@@ -101,7 +102,7 @@ module Day14 =
let sandPos =
{
MutableCoordinate.X = 500
Y = 0
MutableCoordinate.Y = 0
}
let mutable stillFalling = true
@@ -166,7 +167,7 @@ module Day14 =
let sandPos =
{
MutableCoordinate.X = 1000
Y = 0
MutableCoordinate.Y = 0
}
let mutable stillFalling = true

View File

@@ -79,14 +79,13 @@ module Day15 =
keepGoing
let toHashSet (arr : Coordinate ResizeArray) : Coordinate HashSet =
let output = HashSet ()
let output = HashSet arr.Count
for beacon in arr do
output.Add beacon |> ignore
output
let part1 (y : int) (lines : StringSplitEnumerator) : int =
let sensors, beacons = parse lines

View File

@@ -31,7 +31,6 @@ module Day21 =
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 =
@@ -77,20 +76,28 @@ module Day21 =
| Day21Operation.Divide -> v1 / v2
| _ -> failwith "bad enum"
let rec evaluate (d : Dictionary<Day21Name, Day21Input>) (s : Day21Name) : float =
let rec evaluate
(calculated : Dictionary<Day21Name, float>)
(d : Dictionary<Day21Name, Day21Input>)
(s : Day21Name)
: float
=
match calculated.TryGetValue s with
| true, v -> v
| false, _ ->
match d.[s] with
| Day21Input.Literal v ->
let result = float v
d.[s] <- Day21Input.Calculated result
calculated.[s] <- result
result
| Day21Input.Calculated f -> f
| Day21Input.Operation (s1, s2, op) ->
let v1 = evaluate d s1
let v2 = evaluate d s2
let v1 = evaluate calculated d s1
let v2 = evaluate calculated d s2
let result = compute v1 v2 op
d.[s] <- Day21Input.Calculated result
calculated.[s] <- result
result
let inline round (v : float) : int64 =
@@ -103,8 +110,9 @@ module Day21 =
let part1 (lines : StringSplitEnumerator) : int64 =
let original, root, _ = parse lines
let calculated = Dictionary original.Count
let result = evaluate original root
let result = evaluate calculated original root
round result
@@ -135,7 +143,6 @@ module Day21 =
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
@@ -153,8 +160,7 @@ module Day21 =
let lhs, rhs =
match original.[root] with
| Day21Input.Literal _
| Day21Input.Calculated _ -> failwith "expected operation"
| Day21Input.Literal _ -> failwith "expected operation"
| Day21Input.Operation (s1, s2, _) -> s1, s2
let converted = Dictionary original.Count

550
AdventOfCode2022/Day22.fs Normal file
View File

@@ -0,0 +1,550 @@
namespace AdventOfCode2022
open System
#if DEBUG
open Checked
#else
#nowarn "9"
#endif
[<RequireQualifiedAccess>]
module Day22 =
/// Returns the board (where 0 means "nothing here", 1 means "space", 2 means "wall"),
/// the set of instructions (move * change-direction), and the final trailing instruction to move.
let parse (line : StringSplitEnumerator) : int[][] * ResizeArray<struct (int * Direction)> * int =
use mutable enum = line.GetEnumerator ()
let output = ResizeArray ()
let mutable maxBoardLength = 0
let row = ResizeArray ()
// 2 here in case there's a trailing \r
while enum.MoveNext () && enum.Current.Length >= 2 do
for i = 0 to enum.Current.Length - 1 do
match enum.Current.[i] with
| ' ' -> row.Add 0
| '.' -> row.Add 1
| '#' -> row.Add 2
| '\r' -> ()
| c -> failwithf "unexpected char: %c" c
output.Add (row.ToArray ())
maxBoardLength <- max maxBoardLength row.Count
row.Clear ()
if not (enum.MoveNext ()) then
failwith "expected instruction line"
let directions = ResizeArray ()
let line = enum.Current.TrimEnd ()
let mutable i = 0
for count = 0 to line.Length - 1 do
if '0' <= line.[count] && line.[count] <= '9' then
i <- i * 10 + (int line.[count] - int '0')
else
let dir =
match line.[count] with
| 'L' -> Direction.Left
| 'R' -> Direction.Right
| c -> failwithf "Unexpected: %c" c
directions.Add (struct (i, dir))
i <- 0
let finalOutput =
Array.init
output.Count
(fun i -> Array.append output.[i] (Array.zeroCreate (maxBoardLength - output.[i].Length)))
finalOutput, directions, i
let inline rotateRight (dir : Direction) =
match dir with
| Direction.Up -> Direction.Right
| Direction.Right -> Direction.Down
| Direction.Down -> Direction.Left
| Direction.Left -> Direction.Up
| _ -> failwith "bad direction"
let inline rotateLeft (dir : Direction) =
match dir with
| Direction.Up -> Direction.Left
| Direction.Right -> Direction.Up
| Direction.Down -> Direction.Right
| Direction.Left -> Direction.Down
| _ -> failwith "bad direction"
/// Returns false if we got stuck due to a wall.
let private moveOneStep (currPos : MutableCoordinate) (direction : Direction) (board : int[][]) : bool =
let answer =
{
MutableCoordinate.X = currPos.X
MutableCoordinate.Y = currPos.Y
}
match direction with
| Direction.Up ->
if answer.Y = 0 then
answer.Y <- board.Length - 1
else
answer.Y <- answer.Y - 1
while board.[answer.Y].[answer.X] = 0 do
if answer.Y = 0 then
answer.Y <- board.Length - 1
else
answer.Y <- answer.Y - 1
| Direction.Down ->
if answer.Y = board.Length - 1 then
answer.Y <- 0
else
answer.Y <- answer.Y + 1
while board.[answer.Y].[answer.X] = 0 do
if answer.Y = board.Length - 1 then
answer.Y <- 0
else
answer.Y <- answer.Y + 1
| Direction.Left ->
if answer.X = 0 then
answer.X <- board.[0].Length - 1
else
answer.X <- answer.X - 1
while board.[answer.Y].[answer.X] = 0 do
if answer.X = 0 then
answer.X <- board.[0].Length - 1
else
answer.X <- answer.X - 1
| Direction.Right ->
if answer.X = board.[0].Length - 1 then
answer.X <- 0
else
answer.X <- answer.X + 1
while board.[answer.Y].[answer.X] = 0 do
if answer.X = board.[0].Length - 1 then
answer.X <- 0
else
answer.X <- answer.X + 1
| _ -> failwith "noooo"
if board.[answer.Y].[answer.X] <> 2 then
currPos.X <- answer.X
currPos.Y <- answer.Y
true
else
false
let moveDistance (currPos : MutableCoordinate) (direction : Direction) (distance : int) (board : int[][]) : unit =
let mutable i = 0
let mutable keepGoing = true
while keepGoing && i < distance do
keepGoing <- moveOneStep currPos direction board
i <- i + 1
let inline answer (position : Coordinate) (direction : Direction) =
let finalFacing =
match direction with
| Direction.Right -> 0
| Direction.Down -> 1
| Direction.Left -> 2
| Direction.Up -> 3
| _ -> failwith "oh no"
1000 * (position.Y + 1) + 4 * (position.X + 1) + finalFacing
let part1 (lines : StringSplitEnumerator) : int =
let board, instructions, finalDistance = parse lines
let position =
{
MutableCoordinate.Y = 0
MutableCoordinate.X = Array.IndexOf (board.[0], 1)
}
let mutable direction = Direction.Right
for struct (distance, rotation) in instructions do
moveDistance position direction distance board
direction <-
match rotation with
| Direction.Right -> rotateRight direction
| Direction.Left -> rotateLeft direction
| _ -> failwith "bad rotation"
moveDistance position direction finalDistance board
answer
{
X = position.X
Y = position.Y
}
direction
/// If we walk off the given number of face in the given direction, where do we end up?
/// Returns an additional "true" if we need to interchange Y and X.
let newFaceOnExample
(cubeSize : int)
(face : int)
(direction : Direction)
: int * Direction * (struct (int * int) -> struct (int * int))
=
match face, direction with
| 1, Direction.Up -> 2, Direction.Down, (fun (struct (x, y)) -> struct (cubeSize - x - 1, 0))
| 1, Direction.Down -> 4, Direction.Down, (fun (struct (x, _)) -> struct (x, 0))
| 1, Direction.Left -> 3, Direction.Down, (fun (struct (_, y)) -> struct (y, 0))
| 1, Direction.Right -> 6, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, cubeSize - y - 1))
| 2, Direction.Up -> 1, Direction.Down, (fun (struct (x, y)) -> struct (cubeSize - x - 1, 0))
| 2, Direction.Down -> 5, Direction.Up, (fun (struct (x, y)) -> struct (cubeSize - x - 1, cubeSize - 1))
| 2, Direction.Left -> 6, Direction.Up, (fun (struct (_, y)) -> struct (cubeSize - y - 1, cubeSize - 1))
| 2, Direction.Right -> 3, Direction.Right, (fun (struct (x, y)) -> struct (0, y))
| 3, Direction.Up -> 1, Direction.Right, (fun (struct (x, y)) -> struct (0, x))
| 3, Direction.Down -> 5, Direction.Right, (fun (struct (x, y)) -> struct (0, cubeSize - 1 - y))
| 3, Direction.Left -> 2, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, y))
| 3, Direction.Right -> 4, Direction.Right, (fun (struct (x, y)) -> struct (0, y))
| 4, Direction.Up -> 1, Direction.Up, (fun (struct (x, _)) -> struct (x, cubeSize - 1))
| 4, Direction.Down -> 5, Direction.Down, (fun (struct (x, _)) -> struct (x, 0))
| 4, Direction.Left -> 3, Direction.Left, (fun (struct (x, _)) -> struct (x, cubeSize - 1))
| 4, Direction.Right -> 6, Direction.Down, (fun (struct (_, y)) -> struct (cubeSize - 1 - y, 0))
| 5, Direction.Up -> 4, Direction.Up, (fun (struct (x, y)) -> struct (x, cubeSize - 1))
| 5, Direction.Down -> 2, Direction.Up, (fun (struct (x, y)) -> struct (cubeSize - x - 1, cubeSize - 1))
| 5, Direction.Left -> 3, Direction.Up, (fun (struct (x, y)) -> struct (cubeSize - y - 1, cubeSize - 1))
| 5, Direction.Right -> 6, Direction.Right, (fun (struct (x, y)) -> struct (0, y))
| 6, Direction.Up -> 4, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, cubeSize - x - 1))
| 6, Direction.Down -> 2, Direction.Right, (fun (struct (x, y)) -> failwith "TODO")
| 6, Direction.Left -> 5, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, y))
| 6, Direction.Right -> 1, Direction.Left, (fun (struct (x, y)) -> failwith "TODO")
| _ -> failwith "bad face"
let inline toArrayElementOnExample (faceSize : int) (face : int) (position : Coordinate) : Coordinate =
match face with
| 1 ->
{
X = position.X + 2 * faceSize
Y = position.Y
}
| 2 ->
{
X = position.X
Y = position.Y + faceSize
}
| 3 ->
{
X = position.X + faceSize
Y = position.Y + faceSize
}
| 4 ->
{
X = position.X + 2 * faceSize
Y = position.Y + faceSize
}
| 5 ->
{
X = position.X + 2 * faceSize
Y = position.Y + 2 * faceSize
}
| 6 ->
{
X = position.X + 3 * faceSize
Y = position.Y + 2 * faceSize
}
| _ -> failwith "bad face"
/// Returns false if we got stuck due to a wall.
/// The position is referring to the position within the given face.
let inline private moveOneStepCube
(cubeSize : int)
([<InlineIfLambda>] toArrayElement : int -> Coordinate -> Coordinate)
([<InlineIfLambda>] newFace : int -> Direction -> int * Direction * (struct (int * int) -> struct (int * int)))
(currFace : byref<int>)
(currPos : MutableCoordinate)
(direction : byref<Direction>)
(board : int[][])
: bool
=
// If we do walk off this face, where do we end up?
let faceAfterSpill, directionAfterSpill, transformPosition =
newFace currFace direction
let intendedDest, face, newDirection =
match direction with
| Direction.Up ->
if currPos.Y = 0 then
let struct (x, y) = transformPosition (struct (currPos.X, currPos.Y))
{
X = x
Y = y
},
faceAfterSpill,
directionAfterSpill
else
{
X = currPos.X
Y = currPos.Y - 1
},
currFace,
direction
| Direction.Down ->
if currPos.Y = cubeSize - 1 then
let struct (x, y) = transformPosition (struct (currPos.X, currPos.Y))
{
X = x
Y = y
},
faceAfterSpill,
directionAfterSpill
else
{
X = currPos.X
Y = currPos.Y + 1
},
currFace,
direction
| Direction.Left ->
if currPos.X = 0 then
let struct (x, y) = transformPosition (struct (currPos.X, currPos.Y))
{
X = x
Y = y
},
faceAfterSpill,
directionAfterSpill
else
{
X = currPos.X - 1
Y = currPos.Y
},
currFace,
direction
| Direction.Right ->
if currPos.X = cubeSize - 1 then
let struct (x, y) = transformPosition (struct (currPos.X, currPos.Y))
{
X = x
Y = y
},
faceAfterSpill,
directionAfterSpill
else
{
X = currPos.X + 1
Y = currPos.Y
},
currFace,
direction
| _ -> failwith "noooo"
let pos = toArrayElement face intendedDest
if board.[pos.Y].[pos.X] <> 2 then
currPos.X <- intendedDest.X
currPos.Y <- intendedDest.Y
currFace <- face
direction <- newDirection
true
else
false
let inline moveCubeDistance
(cubeSize : int)
([<InlineIfLambda>] toArrayElement : int -> Coordinate -> Coordinate)
([<InlineIfLambda>] newFace : int -> Direction -> int * Direction * (struct (int * int) -> struct (int * int)))
(currFace : byref<int>)
(currPos : MutableCoordinate)
(direction : byref<Direction>)
(distance : int)
(board : int[][])
: unit
=
let mutable i = 0
let mutable keepGoing = true
while keepGoing && i < distance do
keepGoing <- moveOneStepCube cubeSize toArrayElement newFace &currFace currPos &direction board
i <- i + 1
let part2Example (lines : StringSplitEnumerator) : int =
let board, instructions, finalDistance = parse lines
let faceSize = 4
let position =
{
MutableCoordinate.X = 0
MutableCoordinate.Y = 0
}
let mutable direction = Direction.Right
let mutable face = 1
for struct (distance, rotation) in instructions do
moveCubeDistance
faceSize
(toArrayElementOnExample faceSize)
(newFaceOnExample faceSize)
&face
position
&direction
distance
board
direction <-
match rotation with
| Direction.Right -> rotateRight direction
| Direction.Left -> rotateLeft direction
| _ -> failwith "bad rotation"
moveCubeDistance
faceSize
(toArrayElementOnExample faceSize)
(newFaceOnExample faceSize)
&face
position
&direction
finalDistance
board
let faceDimension = board.Length / 3
let position =
toArrayElementOnExample
faceDimension
face
{
X = position.X
Y = position.Y
}
answer position direction
// The real thing has shape:
// _12
// _3_
// 45_
// 6__
let inline toArrayElement (faceSize : int) (face : int) (position : Coordinate) : Coordinate =
match face with
| 1 ->
{
X = position.X + faceSize
Y = position.Y
}
| 2 ->
{
X = position.X + 2 * faceSize
Y = position.Y
}
| 3 ->
{
X = position.X + faceSize
Y = position.Y + faceSize
}
| 4 ->
{
X = position.X
Y = position.Y + 2 * faceSize
}
| 5 ->
{
X = position.X + faceSize
Y = position.Y + 2 * faceSize
}
| 6 ->
{
X = position.X
Y = position.Y + 3 * faceSize
}
| _ -> failwith "bad face"
let inline newFace
(cubeSize : int)
(face : int)
(direction : Direction)
: int * Direction * (struct (int * int) -> struct (int * int))
=
match face, direction with
| 1, Direction.Up -> 6, Direction.Right, (fun (struct (x, y)) -> struct (0, x))
| 1, Direction.Down -> 3, Direction.Down, (fun (struct (x, _)) -> struct (x, 0))
| 1, Direction.Left -> 4, Direction.Right, (fun (struct (x, y)) -> struct (0, cubeSize - 1 - y))
| 1, Direction.Right -> 2, Direction.Right, (fun (struct (x, y)) -> struct (0, y))
| 2, Direction.Up -> 6, Direction.Up, (fun (struct (x, y)) -> (x, cubeSize - 1))
| 2, Direction.Down -> 3, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, x))
| 2, Direction.Left -> 1, Direction.Left, (fun (struct (_, y)) -> struct (cubeSize - 1, y))
| 2, Direction.Right -> 5, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, cubeSize - 1 - y))
| 3, Direction.Up -> 1, Direction.Up, (fun (struct (x, y)) -> struct (x, cubeSize - 1))
| 3, Direction.Down -> 5, Direction.Down, (fun (struct (x, y)) -> struct (x, 0))
| 3, Direction.Left -> 4, Direction.Down, (fun (struct (x, y)) -> struct (y, 0))
| 3, Direction.Right -> 2, Direction.Up, (fun (struct (x, y)) -> struct (y, cubeSize - 1))
| 4, Direction.Up -> 3, Direction.Right, (fun (struct (x, _)) -> struct (0, x))
| 4, Direction.Down -> 6, Direction.Down, (fun (struct (x, _)) -> struct (x, 0))
| 4, Direction.Left -> 1, Direction.Right, (fun (struct (x, y)) -> struct (0, cubeSize - 1 - y))
| 4, Direction.Right -> 5, Direction.Right, (fun (struct (_, y)) -> struct (0, y))
| 5, Direction.Up -> 3, Direction.Up, (fun (struct (x, y)) -> struct (x, cubeSize - 1))
| 5, Direction.Down -> 6, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, x))
| 5, Direction.Left -> 4, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, y))
| 5, Direction.Right -> 2, Direction.Left, (fun (struct (x, y)) -> struct (cubeSize - 1, cubeSize - 1 - y))
| 6, Direction.Up -> 4, Direction.Up, (fun (struct (x, y)) -> struct (x, cubeSize - 1))
| 6, Direction.Down -> 2, Direction.Down, (fun (struct (x, y)) -> struct (x, 0))
| 6, Direction.Left -> 1, Direction.Down, (fun (struct (x, y)) -> struct (y, 0))
| 6, Direction.Right -> 5, Direction.Up, (fun (struct (x, y)) -> struct (y, cubeSize - 1))
| _ -> failwith "bad face"
let part2 (lines : StringSplitEnumerator) : int =
let board, instructions, finalDistance = parse lines
let faceSize = board.[0].Length / 3
let position =
{
MutableCoordinate.X = 0
MutableCoordinate.Y = 0
}
let mutable direction = Direction.Right
let mutable face = 1
for struct (distance, rotation) in instructions do
moveCubeDistance
faceSize
(toArrayElement faceSize)
(newFace faceSize)
&face
position
&direction
distance
board
direction <-
match rotation with
| Direction.Right -> rotateRight direction
| Direction.Left -> rotateLeft direction
| _ -> failwith "bad rotation"
moveCubeDistance
faceSize
(toArrayElement faceSize)
(newFace faceSize)
&face
position
&direction
finalDistance
board
let position =
toArrayElement
faceSize
face
{
X = position.X
Y = position.Y
}
answer position direction

View File

@@ -5,7 +5,7 @@ open System
[<RequireQualifiedAccess>]
module Day6 =
let findDuplicateSorted (a : 'a array) : int ValueOption =
let inline findDuplicateSorted< ^a when ^a : equality> (a : 'a array) : int ValueOption =
let mutable i = 0
let mutable result = ValueNone

View File

@@ -1,37 +1,38 @@
namespace AdventOfCode2022
open System.Collections.Generic
open System
#if DEBUG
#else
#nowarn "9"
#endif
[<RequireQualifiedAccess>]
module Day8 =
let parse (lines : StringSplitEnumerator) : byte[,] =
let parse (lines : StringSplitEnumerator) : byte array * int =
use mutable enum = lines
let output = ResizeArray ()
let mutable lineCount = 0
for line in enum do
let line = line.TrimEnd ()
if not (line.IsWhiteSpace ()) then
let arr = Array.zeroCreate line.Length
let mutable i = 0
lineCount <- lineCount + 1
for c in line do
arr.[i] <- byte c - byte '0'
i <- i + 1
output.Add (byte c - byte '0')
output.Add arr
output.ToArray (), lineCount
Array2D.init output.Count output.[0].Length (fun x y -> output.[x].[y])
let isVisible (board : byte[,]) (x : int) (y : int) : bool =
let isVisible (board : Arr2D<byte>) (x : int) (y : int) : bool =
// From the left?
let mutable isVisible = true
let mutable i = 0
while i < x && isVisible do
if board.[y, i] >= board.[y, x] then
if Arr2D.get board i y >= Arr2D.get board x y then
isVisible <- false
i <- i + 1
@@ -42,10 +43,10 @@ module Day8 =
// From the right?
let mutable isVisible = true
let mutable i = board.GetLength 1 - 1
let mutable i = board.Height - 1
while i > x && isVisible do
if board.[y, i] >= board.[y, x] then
if Arr2D.get board i y >= Arr2D.get board x y then
isVisible <- false
i <- i - 1
@@ -59,7 +60,7 @@ module Day8 =
let mutable i = 0
while i < y && isVisible do
if board.[i, x] >= board.[y, x] then
if Arr2D.get board x i >= Arr2D.get board x y then
isVisible <- false
i <- i + 1
@@ -70,10 +71,10 @@ module Day8 =
// From the bottom?
let mutable isVisible = true
let mutable i = board.GetLength 0 - 1
let mutable i = board.Width - 1
while i > y && isVisible do
if board.[i, x] >= board.[y, x] then
if Arr2D.get board x i >= Arr2D.get board x y then
isVisible <- false
i <- i - 1
@@ -81,18 +82,34 @@ module Day8 =
isVisible
let part1 (lines : StringSplitEnumerator) : int =
let board = parse lines
let board, height = parse lines
#if DEBUG
let board =
{
Arr2D.Elements = board
Width = board.Length / height
}
#else
use p = fixed board
let board =
{
Arr2D.Elements = p
Length = board.Length
Width = board.Length / height
}
#endif
let mutable visibleCount = 0
for y = 0 to board.GetLength 0 - 1 do
for x = 0 to board.GetLength 1 - 1 do
for y = 0 to board.Height - 1 do
for x = 0 to board.Width - 1 do
if isVisible board x y then
visibleCount <- visibleCount + 1
visibleCount
let scenicScore (board : byte[,]) (x : int) (y : int) : int =
let scenicScore (board : Arr2D<byte>) (x : int) (y : int) : int =
let mutable scenicCount = 0
do
@@ -100,7 +117,7 @@ module Day8 =
let mutable i = y - 1
while i >= 0 && isVisible do
if board.[i, x] >= board.[y, x] then
if Arr2D.get board x i >= Arr2D.get board x y then
isVisible <- false
scenicCount <- scenicCount + 1
@@ -112,8 +129,8 @@ module Day8 =
let mutable i = y + 1
let mutable subCount = 0
while i < board.GetLength 0 && isVisible do
if board.[i, x] >= board.[y, x] then
while i < board.Height && isVisible do
if Arr2D.get board x i >= Arr2D.get board x y then
isVisible <- false
subCount <- subCount + 1
@@ -128,7 +145,7 @@ module Day8 =
let mutable subCount = 0
while i >= 0 && isVisible do
if board.[y, i] >= board.[y, x] then
if Arr2D.get board i y >= Arr2D.get board x y then
isVisible <- false
subCount <- subCount + 1
@@ -142,8 +159,8 @@ module Day8 =
let mutable i = x + 1
let mutable subCount = 0
while i < board.GetLength 1 && isVisible do
if board.[y, i] >= board.[y, x] then
while i < board.Width && isVisible do
if Arr2D.get board i y >= Arr2D.get board x y then
isVisible <- false
subCount <- subCount + 1
@@ -156,11 +173,28 @@ module Day8 =
let part2 (lines : StringSplitEnumerator) : int =
let board = parse lines
let board, height = parse lines
#if DEBUG
let board =
{
Arr2D.Elements = board
Width = board.Length / height
}
#else
use p = fixed board
let board =
{
Arr2D.Elements = p
Length = board.Length
Width = board.Length / height
}
#endif
let mutable scenicMax = 0
for y = 0 to board.GetLength 0 - 1 do
for x = 0 to board.GetLength 1 - 1 do
for y = 0 to board.Height - 1 do
for x = 0 to board.Width - 1 do
scenicMax <- max scenicMax (scenicScore board x y)
scenicMax

View File

@@ -74,7 +74,7 @@ module Day9 =
tailVisits.Add (struct (0, 0)) |> ignore
for direction, distance in directions do
for _ in 1uy .. distance do
for _ = 1 to int distance do
let newHead = newHead (fst knots.[0]) (snd knots.[0]) direction
knots.[0] <- newHead

View File

@@ -10,5 +10,5 @@ Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
```
| 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** |
| **Benchmark** | **21** | **False** | **654.8 μs** | **12.87 μs** | **13.22 μs** |
| **Benchmark** | **21** | **True** | **577.8 μs** | **6.50 μs** | **6.08 μs** |

View File

@@ -1,3 +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
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,654.8 μs,12.87 μs,13.22 μ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,577.8 μs,6.50 μs,6.08 μs
1 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
2 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 654.8 μs 13.00 μs 12.87 μs 15.48 μs 13.22 μs
3 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 577.8 μs 9.40 μs 6.50 μs 8.79 μs 6.08 μs

View File

@@ -2,7 +2,7 @@
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>AdventOfCode2022.App.Benchmark21To25-20221221-100214</title>
<title>AdventOfCode2022.App.Benchmark21To25-20221221-110833</title>
<style type="text/css">
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
@@ -24,8 +24,8 @@ Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
<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 &mu;s</td><td>13.00 &mu;s</td><td>15.48 &mu;s</td>
</tr><tr><td>Benchmark</td><td>21</td><td>True</td><td>612.3 &mu;s</td><td>9.40 &mu;s</td><td>8.79 &mu;s</td>
</thead><tbody><tr><td>Benchmark</td><td>21</td><td>False</td><td>654.8 &mu;s</td><td>12.87 &mu;s</td><td>13.22 &mu;s</td>
</tr><tr><td>Benchmark</td><td>21</td><td>True</td><td>577.8 &mu;s</td><td>6.50 &mu;s</td><td>6.08 &mu;s</td>
</tr></tbody></table>
</body>
</html>