Compare commits
13 Commits
bcd2bb6349
...
minimal
Author | SHA1 | Date | |
---|---|---|---|
|
337d11e0e6 | ||
|
a401d8cf5c | ||
|
61f15726a1 | ||
|
d50618676f | ||
|
ad28718b3e | ||
|
8c244330b7 | ||
|
3cd5ae8635 | ||
|
aa2ace9bf9 | ||
|
e64b9f7cf2 | ||
|
4efbddd9d2 | ||
|
56d0b0c44e | ||
|
331092ff44 | ||
|
89958b0fa0 |
@@ -3,17 +3,9 @@ steps:
|
|||||||
image: nixos/nix
|
image: nixos/nix
|
||||||
commands:
|
commands:
|
||||||
- echo 'experimental-features = flakes nix-command' >> /etc/nix/nix.conf
|
- echo 'experimental-features = flakes nix-command' >> /etc/nix/nix.conf
|
||||||
# Lint
|
- nix develop --command dotnet --info
|
||||||
- "nix flake check"
|
- nix develop --command dotnet publish AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj --configuration Release
|
||||||
# Test
|
- nix develop --command sh -c "$(find . -type f -name AdventOfCode2023.FSharp | grep Release | grep publish) AdventOfCode2023.FSharp/Test/samples"
|
||||||
- nix develop --command dotnet test AdventOfCode2023.FSharp
|
|
||||||
- nix develop --command dotnet test AdventOfCode2023.FSharp --configuration Release
|
|
||||||
- nix develop --command alejandra --check .
|
|
||||||
- nix develop --command dotnet tool restore
|
|
||||||
- nix develop --command dotnet fantomas --check .
|
|
||||||
# TODO: if https://github.com/dotnet/sdk/issues/37295 ever gets fixed, remove the PublishAot=false
|
|
||||||
- "nix develop --command dotnet publish AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj --configuration Release -p:PublishAot=false -p:SelfContained=true"
|
|
||||||
- '$(find . -type f -name AdventOfCode2023.FSharp | grep Release | grep publish) "$(pwd)/AdventOfCode2023.FSharp/Test/samples"'
|
|
||||||
|
|
||||||
when:
|
when:
|
||||||
- event: "push"
|
- event: "push"
|
||||||
|
@@ -7,15 +7,10 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Arr2D.fs" />
|
<Compile Include="Arr2D.fs" />
|
||||||
<Compile Include="ResizeArray.fs" />
|
|
||||||
<Compile Include="EfficientString.fs" />
|
<Compile Include="EfficientString.fs" />
|
||||||
<Compile Include="Day1.fs" />
|
<Compile Include="Day1.fs" />
|
||||||
<Compile Include="Day2.fs" />
|
<Compile Include="Day2.fs" />
|
||||||
<Compile Include="Day3.fs" />
|
<Compile Include="Day3.fs" />
|
||||||
<Compile Include="Day4.fs" />
|
|
||||||
<Compile Include="Day5.fs" />
|
|
||||||
<Compile Include="Day6.fs" />
|
|
||||||
<Compile Include="Day7.fs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -5,13 +5,21 @@ open System
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Day1 =
|
module Day1 =
|
||||||
|
|
||||||
let inline firstDigit (s : ReadOnlySpan<char>) =
|
let firstDigit (s : ReadOnlySpan<char>) =
|
||||||
let pos = s.IndexOfAnyInRange ('0', '9')
|
let mutable pos = 0
|
||||||
|
|
||||||
|
while '0' > s.[pos] || s.[pos] > '9' do
|
||||||
|
pos <- pos + 1
|
||||||
|
|
||||||
byte s.[pos] - byte '0'
|
byte s.[pos] - byte '0'
|
||||||
|
|
||||||
// No surrogate pairs please!
|
// No surrogate pairs please!
|
||||||
let inline lastDigit (s : ReadOnlySpan<char>) =
|
let lastDigit (s : ReadOnlySpan<char>) =
|
||||||
let pos = s.LastIndexOfAnyInRange ('0', '9')
|
let mutable pos = s.Length - 1
|
||||||
|
|
||||||
|
while '0' > s.[pos] || s.[pos] > '9' do
|
||||||
|
pos <- pos - 1
|
||||||
|
|
||||||
byte s.[pos] - byte '0'
|
byte s.[pos] - byte '0'
|
||||||
|
|
||||||
let part1 (s : string) =
|
let part1 (s : string) =
|
||||||
@@ -27,66 +35,35 @@ module Day1 =
|
|||||||
|
|
||||||
total
|
total
|
||||||
|
|
||||||
let isDigitSpelled (s : ReadOnlySpan<char>) (pos : int) (answer : byref<byte>) =
|
let table =
|
||||||
// Can't be bothered to write a jump-table compiler
|
[|
|
||||||
if s.[pos] >= '0' && s.[pos] <= '9' then
|
"one", 1uy
|
||||||
answer <- byte s.[pos] - byte '0'
|
"two", 2uy
|
||||||
else if s.[pos] = 'o' then
|
"three", 3uy
|
||||||
if pos + 2 < s.Length && s.[pos + 1] = 'n' && s.[pos + 2] = 'e' then
|
"four", 4uy
|
||||||
answer <- 1uy
|
"five", 5uy
|
||||||
elif s.[pos] = 't' then
|
"six", 6uy
|
||||||
if pos + 2 < s.Length && s.[pos + 1] = 'w' && s.[pos + 2] = 'o' then
|
"seven", 7uy
|
||||||
answer <- 2uy
|
"eight", 8uy
|
||||||
elif
|
"nine", 9uy
|
||||||
pos + 4 < s.Length
|
|]
|
||||||
&& s.[pos + 1] = 'h'
|
|
||||||
&& s.[pos + 2] = 'r'
|
|
||||||
&& s.[pos + 3] = 'e'
|
|
||||||
&& s.[pos + 4] = 'e'
|
|
||||||
then
|
|
||||||
answer <- 3uy
|
|
||||||
elif s.[pos] = 'f' then
|
|
||||||
if pos + 3 < s.Length then
|
|
||||||
if s.[pos + 1] = 'o' && s.[pos + 2] = 'u' && s.[pos + 3] = 'r' then
|
|
||||||
answer <- 4uy
|
|
||||||
elif s.[pos + 1] = 'i' && s.[pos + 2] = 'v' && s.[pos + 3] = 'e' then
|
|
||||||
answer <- 5uy
|
|
||||||
elif s.[pos] = 's' then
|
|
||||||
if pos + 2 < s.Length && s.[pos + 1] = 'i' && s.[pos + 2] = 'x' then
|
|
||||||
answer <- 6uy
|
|
||||||
elif
|
|
||||||
pos + 4 < s.Length
|
|
||||||
&& s.[pos + 1] = 'e'
|
|
||||||
&& s.[pos + 2] = 'v'
|
|
||||||
&& s.[pos + 3] = 'e'
|
|
||||||
&& s.[pos + 4] = 'n'
|
|
||||||
then
|
|
||||||
answer <- 7uy
|
|
||||||
elif s.[pos] = 'e' then
|
|
||||||
if
|
|
||||||
pos + 4 < s.Length
|
|
||||||
&& s.[pos + 1] = 'i'
|
|
||||||
&& s.[pos + 2] = 'g'
|
|
||||||
&& s.[pos + 3] = 'h'
|
|
||||||
&& s.[pos + 4] = 't'
|
|
||||||
then
|
|
||||||
answer <- 8uy
|
|
||||||
elif s.[pos] = 'n' then
|
|
||||||
if
|
|
||||||
pos + 3 < s.Length
|
|
||||||
&& s.[pos + 1] = 'i'
|
|
||||||
&& s.[pos + 2] = 'n'
|
|
||||||
&& s.[pos + 3] = 'e'
|
|
||||||
then
|
|
||||||
answer <- 9uy
|
|
||||||
|
|
||||||
let firstDigitIncSpelled (s : ReadOnlySpan<char>) =
|
let firstDigitIncSpelled (s : ReadOnlySpan<char>) =
|
||||||
let mutable pos = 0
|
let mutable pos = 0
|
||||||
let mutable answer = 255uy
|
let mutable answer = 255uy
|
||||||
|
|
||||||
while answer = 255uy do
|
while answer = 255uy do
|
||||||
isDigitSpelled s pos &answer
|
if s.[pos] >= '0' && s.[pos] <= '9' then
|
||||||
pos <- pos + 1
|
answer <- byte s.[pos] - byte '0'
|
||||||
|
else
|
||||||
|
for i, value in table do
|
||||||
|
if
|
||||||
|
pos + i.Length < s.Length
|
||||||
|
&& MemoryExtensions.SequenceEqual (s.Slice (pos, i.Length), i)
|
||||||
|
then
|
||||||
|
answer <- value
|
||||||
|
|
||||||
|
pos <- pos + 1
|
||||||
|
|
||||||
answer
|
answer
|
||||||
|
|
||||||
@@ -95,8 +72,17 @@ module Day1 =
|
|||||||
let mutable answer = 255uy
|
let mutable answer = 255uy
|
||||||
|
|
||||||
while answer = 255uy do
|
while answer = 255uy do
|
||||||
isDigitSpelled s pos &answer
|
if s.[pos] >= '0' && s.[pos] <= '9' then
|
||||||
pos <- pos - 1
|
answer <- byte s.[pos] - byte '0'
|
||||||
|
else
|
||||||
|
for i, value in table do
|
||||||
|
if
|
||||||
|
pos - i.Length + 1 >= 0
|
||||||
|
&& MemoryExtensions.SequenceEqual (s.Slice (pos - i.Length + 1, i.Length), i)
|
||||||
|
then
|
||||||
|
answer <- value
|
||||||
|
|
||||||
|
pos <- pos - 1
|
||||||
|
|
||||||
answer
|
answer
|
||||||
|
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
namespace AdventOfCode2023
|
namespace AdventOfCode2023
|
||||||
|
|
||||||
open System
|
open System
|
||||||
open System.Globalization
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Day2 =
|
module Day2 =
|
||||||
let inline parseInt (s : ReadOnlySpan<char>) : int =
|
|
||||||
Int32.Parse (s, NumberStyles.None, CultureInfo.InvariantCulture)
|
|
||||||
|
|
||||||
let part1 (s : string) =
|
let part1 (s : string) =
|
||||||
use lines = StringSplitEnumerator.make '\n' s
|
use lines = StringSplitEnumerator.make '\n' s
|
||||||
@@ -21,20 +18,20 @@ module Day2 =
|
|||||||
while isOk && words.MoveNext () do
|
while isOk && words.MoveNext () do
|
||||||
match words.Current.[0] with
|
match words.Current.[0] with
|
||||||
| 'b' ->
|
| 'b' ->
|
||||||
if parseInt prevWord > 14 then
|
if Int32.Parse prevWord > 14 then
|
||||||
isOk <- false
|
isOk <- false
|
||||||
| 'r' ->
|
| 'r' ->
|
||||||
if parseInt prevWord > 12 then
|
if Int32.Parse prevWord > 12 then
|
||||||
isOk <- false
|
isOk <- false
|
||||||
| 'g' ->
|
| 'g' ->
|
||||||
if parseInt prevWord > 13 then
|
if Int32.Parse prevWord > 13 then
|
||||||
isOk <- false
|
isOk <- false
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
|
|
||||||
prevWord <- words.Current
|
prevWord <- words.Current
|
||||||
|
|
||||||
if isOk then
|
if isOk then
|
||||||
answer <- answer + parseInt (line.Slice (5, line.IndexOf ':' - 5))
|
answer <- answer + Int32.Parse (line.Slice (5, line.IndexOf ':' - 5))
|
||||||
|
|
||||||
answer
|
answer
|
||||||
|
|
||||||
@@ -52,9 +49,9 @@ module Day2 =
|
|||||||
|
|
||||||
while words.MoveNext () do
|
while words.MoveNext () do
|
||||||
match words.Current.[0] with
|
match words.Current.[0] with
|
||||||
| 'b' -> blues <- max blues (parseInt prevWord)
|
| 'b' -> blues <- max blues (Int32.Parse prevWord)
|
||||||
| 'r' -> reds <- max reds (parseInt prevWord)
|
| 'r' -> reds <- max reds (Int32.Parse prevWord)
|
||||||
| 'g' -> greens <- max greens (parseInt prevWord)
|
| 'g' -> greens <- max greens (Int32.Parse prevWord)
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
|
|
||||||
prevWord <- words.Current
|
prevWord <- words.Current
|
||||||
|
@@ -146,7 +146,7 @@ module Day3 =
|
|||||||
|
|
||||||
let mutable answer = 0
|
let mutable answer = 0
|
||||||
|
|
||||||
for gears in gears.Values do
|
for KeyValue (_gearPos, gears) in gears do
|
||||||
if gears.Count = 2 then
|
if gears.Count = 2 then
|
||||||
answer <- answer + gears.[0] * gears.[1]
|
answer <- answer + gears.[0] * gears.[1]
|
||||||
|
|
||||||
|
@@ -1,101 +0,0 @@
|
|||||||
namespace AdventOfCode2023
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Globalization
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module Day4 =
|
|
||||||
|
|
||||||
let inline parseByte (chars : ReadOnlySpan<char>) : byte =
|
|
||||||
Byte.Parse (chars, NumberStyles.None, NumberFormatInfo.InvariantInfo)
|
|
||||||
//let mutable answer = 0uy
|
|
||||||
//for c in chars do
|
|
||||||
// answer <- answer * 10uy + (byte c - 48uy)
|
|
||||||
//answer
|
|
||||||
|
|
||||||
let part1 (s : string) =
|
|
||||||
use lines = StringSplitEnumerator.make '\n' s
|
|
||||||
let mutable total = 0
|
|
||||||
let winningNumbers = ResizeArray<byte> ()
|
|
||||||
|
|
||||||
for line in lines do
|
|
||||||
if not (line.IsWhiteSpace ()) then
|
|
||||||
let mutable accumulatingWinning = true
|
|
||||||
winningNumbers.Clear ()
|
|
||||||
use mutable split = StringSplitEnumerator.make' ' ' line
|
|
||||||
StringSplitEnumerator.chomp "Card" &split
|
|
||||||
|
|
||||||
while split.Current.IsEmpty || split.Current.[split.Current.Length - 1] <> ':' do
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
while accumulatingWinning do
|
|
||||||
while split.Current.IsEmpty do
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
if split.Current.[0] = '|' then
|
|
||||||
accumulatingWinning <- false
|
|
||||||
else
|
|
||||||
winningNumbers.Add (parseByte split.Current)
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
let mutable answer = 0
|
|
||||||
|
|
||||||
while split.MoveNext () do
|
|
||||||
if not split.Current.IsEmpty then
|
|
||||||
let n = parseByte split.Current
|
|
||||||
|
|
||||||
if winningNumbers.Contains n then
|
|
||||||
answer <- answer + 1
|
|
||||||
|
|
||||||
if answer > 0 then
|
|
||||||
total <- total + (1 <<< (answer - 1))
|
|
||||||
|
|
||||||
total
|
|
||||||
|
|
||||||
let part2 (s : string) =
|
|
||||||
use lines = StringSplitEnumerator.make '\n' s
|
|
||||||
let winningNumbers = ResizeArray<byte> ()
|
|
||||||
let winners = ResizeArray<int> ()
|
|
||||||
|
|
||||||
for line in lines do
|
|
||||||
if not (line.IsWhiteSpace ()) then
|
|
||||||
let mutable accumulatingWinning = true
|
|
||||||
winningNumbers.Clear ()
|
|
||||||
use mutable split = StringSplitEnumerator.make' ' ' line
|
|
||||||
StringSplitEnumerator.chomp "Card" &split
|
|
||||||
|
|
||||||
while split.Current.IsEmpty || split.Current.[split.Current.Length - 1] <> ':' do
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
while accumulatingWinning do
|
|
||||||
while split.Current.IsEmpty do
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
if split.Current.[0] = '|' then
|
|
||||||
accumulatingWinning <- false
|
|
||||||
else
|
|
||||||
winningNumbers.Add (parseByte split.Current)
|
|
||||||
split.MoveNext () |> ignore
|
|
||||||
|
|
||||||
let mutable answer = 0
|
|
||||||
|
|
||||||
while split.MoveNext () do
|
|
||||||
if not split.Current.IsEmpty then
|
|
||||||
let n = parseByte split.Current
|
|
||||||
|
|
||||||
if winningNumbers.Contains n then
|
|
||||||
answer <- answer + 1
|
|
||||||
|
|
||||||
winners.Add answer
|
|
||||||
|
|
||||||
let ans = Array.create winners.Count 1
|
|
||||||
|
|
||||||
for i = 0 to winners.Count - 1 do
|
|
||||||
for j = i + 1 to winners.[i] + i do
|
|
||||||
ans.[j] <- ans.[j] + ans.[i]
|
|
||||||
|
|
||||||
ans |> Array.sum
|
|
@@ -1,191 +0,0 @@
|
|||||||
namespace AdventOfCode2023
|
|
||||||
|
|
||||||
open System
|
|
||||||
|
|
||||||
[<Struct>]
|
|
||||||
type Range =
|
|
||||||
{
|
|
||||||
SourceStart : uint32
|
|
||||||
DestStart : uint32
|
|
||||||
Len : uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module Day5 =
|
|
||||||
|
|
||||||
let parse (s : string) =
|
|
||||||
use mutable lines = StringSplitEnumerator.make '\n' s
|
|
||||||
lines.MoveNext () |> ignore
|
|
||||||
|
|
||||||
let seeds =
|
|
||||||
use mutable line1 = StringSplitEnumerator.make' ' ' lines.Current
|
|
||||||
StringSplitEnumerator.chomp "seeds:" &line1
|
|
||||||
let result = ResizeArray ()
|
|
||||||
|
|
||||||
while line1.MoveNext () do
|
|
||||||
result.Add (UInt32.Parse line1.Current)
|
|
||||||
|
|
||||||
result.ToArray ()
|
|
||||||
|
|
||||||
lines.MoveNext () |> ignore
|
|
||||||
|
|
||||||
let mappings = ResizeArray ()
|
|
||||||
|
|
||||||
let mutable currentMapping = null
|
|
||||||
|
|
||||||
for line in lines do
|
|
||||||
if line.IsEmpty then
|
|
||||||
if not (isNull currentMapping) then
|
|
||||||
mappings.Add currentMapping
|
|
||||||
currentMapping <- null
|
|
||||||
else if isNull currentMapping then
|
|
||||||
currentMapping <- ResizeArray ()
|
|
||||||
else
|
|
||||||
use mutable line = StringSplitEnumerator.make' ' ' line
|
|
||||||
let destStart = StringSplitEnumerator.consumeU32 &line
|
|
||||||
let sourceStart = StringSplitEnumerator.consumeU32 &line
|
|
||||||
let rangeLen = StringSplitEnumerator.consumeU32 &line
|
|
||||||
|
|
||||||
{
|
|
||||||
SourceStart = sourceStart
|
|
||||||
DestStart = destStart
|
|
||||||
Len = rangeLen
|
|
||||||
}
|
|
||||||
|> currentMapping.Add
|
|
||||||
|
|
||||||
seeds, mappings
|
|
||||||
|
|
||||||
let part1 (s : string) =
|
|
||||||
let seeds, mappings = parse s
|
|
||||||
|
|
||||||
let mutable best = UInt32.MaxValue
|
|
||||||
|
|
||||||
for seed in seeds do
|
|
||||||
let mutable remapped = seed
|
|
||||||
|
|
||||||
for map in mappings do
|
|
||||||
let mutable hasRemappedThisLayer = false
|
|
||||||
|
|
||||||
for interval in map do
|
|
||||||
if not hasRemappedThisLayer then
|
|
||||||
if
|
|
||||||
interval.SourceStart <= remapped
|
|
||||||
&& remapped - interval.SourceStart < interval.Len
|
|
||||||
then
|
|
||||||
hasRemappedThisLayer <- true
|
|
||||||
remapped <- remapped + (interval.DestStart - interval.SourceStart)
|
|
||||||
|
|
||||||
if remapped < best then
|
|
||||||
best <- remapped
|
|
||||||
|
|
||||||
best
|
|
||||||
|
|
||||||
// The input ranges are inclusive at both ends.
|
|
||||||
// Returns any range we didn't map.
|
|
||||||
let private split
|
|
||||||
(resultStarts : ResizeArray<uint32>)
|
|
||||||
(resultEnds : ResizeArray<uint32>)
|
|
||||||
start
|
|
||||||
finish
|
|
||||||
(rangeFromLayer : Range)
|
|
||||||
: (uint32 * uint32 * (uint32 * uint32) voption) voption
|
|
||||||
=
|
|
||||||
let low = rangeFromLayer.SourceStart
|
|
||||||
let high = rangeFromLayer.SourceStart + rangeFromLayer.Len - 1ul
|
|
||||||
|
|
||||||
if low <= start then
|
|
||||||
if finish <= high then
|
|
||||||
// low ... start .. finish .. high
|
|
||||||
// so the entire input range gets mapped down
|
|
||||||
resultStarts.Add (start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
resultEnds.Add (finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
|
|
||||||
ValueNone
|
|
||||||
elif start <= high then
|
|
||||||
// low .. start .. high .. finish
|
|
||||||
// so start .. high gets mapped down
|
|
||||||
// and high + 1 .. finish stays where it is.
|
|
||||||
// high < finish is already guaranteed by previous if block.
|
|
||||||
resultStarts.Add (start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
resultEnds.Add (high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
|
|
||||||
ValueSome (high + 1ul, finish, ValueNone)
|
|
||||||
else
|
|
||||||
ValueSome (start, finish, ValueNone)
|
|
||||||
else if high <= finish then
|
|
||||||
// start .. low .. high .. finish
|
|
||||||
// so start .. low - 1 stays where it is
|
|
||||||
// low .. high gets mapped down
|
|
||||||
// and high + 1 .. finish stays where it is
|
|
||||||
resultStarts.Add (low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
resultEnds.Add (high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
|
|
||||||
ValueSome (start, low - 1ul, ValueSome (high + 1ul, finish))
|
|
||||||
elif low < finish then
|
|
||||||
// start .. low .. finish .. high
|
|
||||||
// so start .. low - 1 stays where it is
|
|
||||||
// and low .. finish gets mapped down
|
|
||||||
resultStarts.Add (low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
resultEnds.Add (finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart)
|
|
||||||
|
|
||||||
ValueSome (start, low - 1ul, ValueNone)
|
|
||||||
else
|
|
||||||
ValueSome (start, finish, ValueNone)
|
|
||||||
|
|
||||||
let part2 (s : string) : uint32 =
|
|
||||||
let seeds, mappings = parse s
|
|
||||||
|
|
||||||
let mutable intervalStarts = ResizeArray ()
|
|
||||||
let mutable intervalEnds = ResizeArray ()
|
|
||||||
|
|
||||||
for i = 0 to (seeds.Length - 1) / 2 do
|
|
||||||
intervalStarts.Add seeds.[2 * i]
|
|
||||||
intervalEnds.Add (seeds.[2 * i + 1] + seeds.[2 * i] - 1ul)
|
|
||||||
|
|
||||||
let mutable nextIntervalStarts = ResizeArray ()
|
|
||||||
let mutable nextIntervalEnds = ResizeArray ()
|
|
||||||
|
|
||||||
for mapLayer in mappings do
|
|
||||||
let mutable i = 0
|
|
||||||
|
|
||||||
while i < intervalStarts.Count do
|
|
||||||
// split interval according to every map
|
|
||||||
let mutable allMoved = false
|
|
||||||
let mutable currentRange = 0
|
|
||||||
|
|
||||||
while not allMoved && currentRange < mapLayer.Count do
|
|
||||||
let range = mapLayer.[currentRange]
|
|
||||||
// range is e.g. 50 98 2, i.e. "98-99 goes to 50-51"
|
|
||||||
match split nextIntervalStarts nextIntervalEnds intervalStarts.[i] intervalEnds.[i] range with
|
|
||||||
| ValueNone -> allMoved <- true
|
|
||||||
| ValueSome (start, finish, v) ->
|
|
||||||
intervalStarts.[i] <- start
|
|
||||||
intervalEnds.[i] <- finish
|
|
||||||
|
|
||||||
match v with
|
|
||||||
| ValueNone -> ()
|
|
||||||
| ValueSome (start, finish) ->
|
|
||||||
intervalStarts.Add start
|
|
||||||
intervalEnds.Add finish
|
|
||||||
|
|
||||||
currentRange <- currentRange + 1
|
|
||||||
|
|
||||||
if not allMoved then
|
|
||||||
nextIntervalStarts.Add intervalStarts.[i]
|
|
||||||
nextIntervalEnds.Add intervalEnds.[i]
|
|
||||||
|
|
||||||
i <- i + 1
|
|
||||||
|
|
||||||
let oldIntervals = intervalStarts
|
|
||||||
oldIntervals.Clear ()
|
|
||||||
intervalStarts <- nextIntervalStarts
|
|
||||||
nextIntervalStarts <- oldIntervals
|
|
||||||
|
|
||||||
let oldIntervals = intervalEnds
|
|
||||||
oldIntervals.Clear ()
|
|
||||||
intervalEnds <- nextIntervalEnds
|
|
||||||
nextIntervalEnds <- oldIntervals
|
|
||||||
|
|
||||||
|
|
||||||
// SIMD go brrr
|
|
||||||
System.Linq.Enumerable.Min intervalStarts
|
|
@@ -1,86 +0,0 @@
|
|||||||
namespace AdventOfCode2023
|
|
||||||
|
|
||||||
open System
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module Day6 =
|
|
||||||
|
|
||||||
let parse (s : string) =
|
|
||||||
use mutable lines = StringSplitEnumerator.make '\n' s
|
|
||||||
|
|
||||||
let times =
|
|
||||||
lines.MoveNext () |> ignore
|
|
||||||
use mutable line = StringSplitEnumerator.make' ' ' lines.Current
|
|
||||||
StringSplitEnumerator.chomp "Time:" &line
|
|
||||||
line.MoveNext () |> ignore
|
|
||||||
let times = ResizeArray ()
|
|
||||||
|
|
||||||
while line.MoveNext () do
|
|
||||||
if not line.Current.IsEmpty then
|
|
||||||
times.Add (UInt64.Parse line.Current)
|
|
||||||
|
|
||||||
times
|
|
||||||
|
|
||||||
let distance =
|
|
||||||
lines.MoveNext () |> ignore
|
|
||||||
use mutable line = StringSplitEnumerator.make' ' ' lines.Current
|
|
||||||
StringSplitEnumerator.chomp "Distance:" &line
|
|
||||||
line.MoveNext () |> ignore
|
|
||||||
let distance = ResizeArray ()
|
|
||||||
|
|
||||||
while line.MoveNext () do
|
|
||||||
if not line.Current.IsEmpty then
|
|
||||||
distance.Add (UInt64.Parse line.Current)
|
|
||||||
|
|
||||||
distance
|
|
||||||
|
|
||||||
times, distance
|
|
||||||
|
|
||||||
let furthest (distance : uint64) (toBeat : uint64) =
|
|
||||||
// Count i in [1 .. distance - 1] such that (distance - i) * i > toBeat
|
|
||||||
// i.e. such that distance * i - i * i > toBeat
|
|
||||||
// -i^2 + distance * i - toBeat = 0 when:
|
|
||||||
// i = (distance +- sqrt(distance^2 - 4 * toBeat)) / 2
|
|
||||||
let distFloat = float distance
|
|
||||||
let inside = sqrt (distFloat * distFloat - 4.0 * float toBeat)
|
|
||||||
let limit1 = (distFloat + inside) / 2.0
|
|
||||||
let limit2 = (distFloat - sqrt (distFloat * distFloat - 4.0 * float toBeat)) / 2.0
|
|
||||||
// round limit2 up and limit1 down
|
|
||||||
let limit1 = uint64 (floor limit1)
|
|
||||||
let limit2 = uint64 (ceil limit2)
|
|
||||||
// cope with edge case of an exact square
|
|
||||||
if (uint64 inside) * (uint64 inside) = uint64 distance * uint64 distance - 4uL * uint64 toBeat then
|
|
||||||
limit1 - limit2 - 1uL
|
|
||||||
else
|
|
||||||
limit1 - limit2 + 1uL
|
|
||||||
|
|
||||||
let part1 (s : string) =
|
|
||||||
let times, distance = parse s
|
|
||||||
let mutable answer = 1uL
|
|
||||||
|
|
||||||
for i = 0 to times.Count - 1 do
|
|
||||||
let time = times.[i]
|
|
||||||
let distance = distance.[i]
|
|
||||||
let winners = furthest time distance
|
|
||||||
answer <- answer * winners
|
|
||||||
|
|
||||||
answer
|
|
||||||
|
|
||||||
let concat (digits : ResizeArray<uint64>) : uint64 =
|
|
||||||
let mutable answer = 0uL
|
|
||||||
|
|
||||||
for digit in digits do
|
|
||||||
let mutable power = 10uL
|
|
||||||
|
|
||||||
while digit >= power do
|
|
||||||
power <- power * 10uL
|
|
||||||
|
|
||||||
answer <- answer * power + digit
|
|
||||||
|
|
||||||
answer
|
|
||||||
|
|
||||||
let part2 (s : string) =
|
|
||||||
let times, distance = parse s
|
|
||||||
let concatTime = concat times
|
|
||||||
let concatDist = concat distance
|
|
||||||
furthest concatTime concatDist
|
|
@@ -1,215 +0,0 @@
|
|||||||
namespace AdventOfCode2023
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Globalization
|
|
||||||
open AdventOfCode2023.ResizeArray
|
|
||||||
|
|
||||||
type Hand =
|
|
||||||
| Five = 6
|
|
||||||
| Four = 5
|
|
||||||
| FullHouse = 4
|
|
||||||
| Three = 3
|
|
||||||
| TwoPairs = 2
|
|
||||||
| Pair = 1
|
|
||||||
| High = 0
|
|
||||||
|
|
||||||
type HandContents =
|
|
||||||
{
|
|
||||||
First : byte
|
|
||||||
Second : byte
|
|
||||||
Third : byte
|
|
||||||
Fourth : byte
|
|
||||||
Fifth : byte
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module Day7 =
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let joker = 0uy
|
|
||||||
|
|
||||||
let inline toByte (adjustJoker : bool) (c : char) : byte =
|
|
||||||
if c <= '9' then byte c - byte '1'
|
|
||||||
elif c = 'T' then 9uy
|
|
||||||
elif c = 'J' then (if adjustJoker then joker else 10uy)
|
|
||||||
elif c = 'Q' then 11uy
|
|
||||||
elif c = 'K' then 12uy
|
|
||||||
elif c = 'A' then 13uy
|
|
||||||
else failwithf "could not parse: %c" c
|
|
||||||
|
|
||||||
let inline private updateState (tallies : ResizeArray<_>) newNum =
|
|
||||||
let mutable isAdded = false
|
|
||||||
|
|
||||||
for i = 0 to tallies.Count - 1 do
|
|
||||||
if fst tallies.[i] = newNum then
|
|
||||||
tallies.[i] <- (fst tallies.[i], snd tallies.[i] + 1)
|
|
||||||
isAdded <- true
|
|
||||||
|
|
||||||
if not isAdded then
|
|
||||||
tallies.Add (newNum, 1)
|
|
||||||
|
|
||||||
type RankedHand = uint32
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let fourteen = 14ul
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let fourteenFive = fourteen * fourteen * fourteen * fourteen * fourteen
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let fourteenFour = fourteen * fourteen * fourteen * fourteen
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let fourteenThree = fourteen * fourteen * fourteen
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let fourteenTwo = fourteen * fourteen
|
|
||||||
|
|
||||||
let toInt (hand : Hand) (contents : HandContents) : RankedHand =
|
|
||||||
uint32 hand * fourteenFive
|
|
||||||
+ uint32 contents.First * fourteenFour
|
|
||||||
+ uint32 contents.Second * fourteenThree
|
|
||||||
+ uint32 contents.Third * fourteenTwo
|
|
||||||
+ uint32 contents.Fourth * fourteen
|
|
||||||
+ uint32 contents.Fifth
|
|
||||||
|
|
||||||
let parseHand (tallyBuffer : ResizeArray<_>) (adjustJoker : bool) (s : ReadOnlySpan<char>) : RankedHand =
|
|
||||||
let contents =
|
|
||||||
{
|
|
||||||
First = toByte adjustJoker s.[0]
|
|
||||||
Second = toByte adjustJoker s.[1]
|
|
||||||
Third = toByte adjustJoker s.[2]
|
|
||||||
Fourth = toByte adjustJoker s.[3]
|
|
||||||
Fifth = toByte adjustJoker s.[4]
|
|
||||||
}
|
|
||||||
|
|
||||||
tallyBuffer.Clear ()
|
|
||||||
tallyBuffer.Add (contents.First, 1)
|
|
||||||
updateState tallyBuffer contents.Second
|
|
||||||
updateState tallyBuffer contents.Third
|
|
||||||
updateState tallyBuffer contents.Fourth
|
|
||||||
updateState tallyBuffer contents.Fifth
|
|
||||||
|
|
||||||
let jokerCount, jokerPos =
|
|
||||||
if not adjustJoker then
|
|
||||||
0, -1
|
|
||||||
else
|
|
||||||
let mutable jokerCount = 0
|
|
||||||
let mutable jokerPos = 0
|
|
||||||
|
|
||||||
while jokerPos < tallyBuffer.Count && jokerCount = 0 do
|
|
||||||
let card, tally = tallyBuffer.[jokerPos]
|
|
||||||
|
|
||||||
if card = joker then
|
|
||||||
jokerCount <- tally
|
|
||||||
else
|
|
||||||
jokerPos <- jokerPos + 1
|
|
||||||
|
|
||||||
jokerCount, jokerPos
|
|
||||||
|
|
||||||
let hand =
|
|
||||||
if jokerCount > 0 then
|
|
||||||
match tallyBuffer.Count with
|
|
||||||
| 1 ->
|
|
||||||
// Five jokers
|
|
||||||
Hand.Five
|
|
||||||
| 2 ->
|
|
||||||
// Jokers plus one other card type
|
|
||||||
Hand.Five
|
|
||||||
| 3 ->
|
|
||||||
// Jokers plus two other card types. Either full house, or four of a kind
|
|
||||||
if jokerCount >= 2 then
|
|
||||||
// JJABB or JJJAB
|
|
||||||
Hand.Four
|
|
||||||
else if
|
|
||||||
// JAABB or JAAAB
|
|
||||||
jokerPos <> 0
|
|
||||||
then
|
|
||||||
if snd tallyBuffer.[0] = 2 then
|
|
||||||
Hand.FullHouse
|
|
||||||
else
|
|
||||||
Hand.Four
|
|
||||||
else if snd tallyBuffer.[1] = 2 then
|
|
||||||
Hand.FullHouse
|
|
||||||
else
|
|
||||||
Hand.Four
|
|
||||||
| 4 ->
|
|
||||||
// Jokers plus three other card types, exactly one of which therefore is a two-of.
|
|
||||||
Hand.Three
|
|
||||||
| 5 ->
|
|
||||||
// Five different cards, one of which is a joker.
|
|
||||||
Hand.Pair
|
|
||||||
| _ -> failwith "bad tallyBuffer"
|
|
||||||
elif tallyBuffer.Count = 1 then
|
|
||||||
Hand.Five
|
|
||||||
elif tallyBuffer.Count = 2 then
|
|
||||||
// AAAAB or AAABB
|
|
||||||
if snd tallyBuffer.[0] = 3 || snd tallyBuffer.[0] = 2 then
|
|
||||||
Hand.FullHouse
|
|
||||||
else
|
|
||||||
Hand.Four
|
|
||||||
elif tallyBuffer.Count = 3 then
|
|
||||||
// AAABC or AABBC
|
|
||||||
if snd tallyBuffer.[0] = 3 then Hand.Three
|
|
||||||
elif snd tallyBuffer.[0] = 2 then Hand.TwoPairs
|
|
||||||
elif snd tallyBuffer.[1] = 3 then Hand.Three
|
|
||||||
elif snd tallyBuffer.[1] = 2 then Hand.TwoPairs
|
|
||||||
else Hand.Three
|
|
||||||
elif tallyBuffer.Count = 4 then
|
|
||||||
Hand.Pair
|
|
||||||
else
|
|
||||||
Hand.High
|
|
||||||
|
|
||||||
toInt hand contents
|
|
||||||
|
|
||||||
type RankedHandAndBid = uint32
|
|
||||||
|
|
||||||
[<Literal>]
|
|
||||||
let bidSeparator = 1001ul
|
|
||||||
|
|
||||||
let inline toRankedHandAndBid (r : RankedHand) (bid : uint32) : RankedHandAndBid = bidSeparator * r + bid
|
|
||||||
|
|
||||||
let inline getBid (r : RankedHandAndBid) : uint32 = uint32 (r % bidSeparator)
|
|
||||||
|
|
||||||
let parse (adjustJoker : bool) (s : string) : ResizeArray<RankedHandAndBid> =
|
|
||||||
use mutable lines = StringSplitEnumerator.make '\n' s
|
|
||||||
let result = ResizeArray.create 4
|
|
||||||
let tallies = ResizeArray.create 5
|
|
||||||
|
|
||||||
while lines.MoveNext () do
|
|
||||||
if not lines.Current.IsEmpty then
|
|
||||||
use mutable line = StringSplitEnumerator.make' ' ' lines.Current
|
|
||||||
line.MoveNext () |> ignore
|
|
||||||
let rankedHand = parseHand tallies adjustJoker line.Current
|
|
||||||
line.MoveNext () |> ignore
|
|
||||||
|
|
||||||
let bid =
|
|
||||||
UInt32.Parse (line.Current, NumberStyles.Integer, CultureInfo.InvariantCulture)
|
|
||||||
|
|
||||||
result.Add (toRankedHandAndBid rankedHand bid)
|
|
||||||
|
|
||||||
result
|
|
||||||
|
|
||||||
let part1 (s : string) =
|
|
||||||
let arr = parse false s
|
|
||||||
|
|
||||||
arr.Sort ()
|
|
||||||
|
|
||||||
let mutable answer = 0ul
|
|
||||||
|
|
||||||
for i = 0 to arr.Count - 1 do
|
|
||||||
answer <- answer + getBid arr.[i] * (uint32 i + 1ul)
|
|
||||||
|
|
||||||
answer
|
|
||||||
|
|
||||||
let part2 (s : string) =
|
|
||||||
let arr = parse true s
|
|
||||||
|
|
||||||
arr.Sort ()
|
|
||||||
|
|
||||||
let mutable answer = 0ul
|
|
||||||
|
|
||||||
for i = 0 to arr.Count - 1 do
|
|
||||||
answer <- answer + getBid arr.[i] * (uint32 i + 1ul)
|
|
||||||
|
|
||||||
answer
|
|
@@ -95,15 +95,3 @@ module StringSplitEnumerator =
|
|||||||
failwith "expected an int, got nothing"
|
failwith "expected an int, got nothing"
|
||||||
|
|
||||||
Int32.Parse e.Current
|
Int32.Parse e.Current
|
||||||
|
|
||||||
let consumeU32 (e : byref<StringSplitEnumerator>) : uint32 =
|
|
||||||
if not (e.MoveNext ()) then
|
|
||||||
failwith "expected an int, got nothing"
|
|
||||||
|
|
||||||
UInt32.Parse e.Current
|
|
||||||
|
|
||||||
let consumeU64 (e : byref<StringSplitEnumerator>) : uint64 =
|
|
||||||
if not (e.MoveNext ()) then
|
|
||||||
failwith "expected an int, got nothing"
|
|
||||||
|
|
||||||
UInt64.Parse e.Current
|
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
namespace AdventOfCode2023.ResizeArray
|
|
||||||
|
|
||||||
open System
|
|
||||||
|
|
||||||
type ResizeArray<'T> =
|
|
||||||
private
|
|
||||||
{
|
|
||||||
mutable Array : 'T array
|
|
||||||
mutable Length : int
|
|
||||||
}
|
|
||||||
|
|
||||||
member this.Count = this.Length
|
|
||||||
member this.Clear () = this.Length <- 0
|
|
||||||
|
|
||||||
member this.Add (t : 'T) =
|
|
||||||
if this.Length < this.Array.Length then
|
|
||||||
this.Array.[this.Length] <- t
|
|
||||||
else
|
|
||||||
let newLength = this.Length * 2
|
|
||||||
let newArray = Array.zeroCreate<'T> newLength
|
|
||||||
Array.blit this.Array 0 newArray 0 this.Length
|
|
||||||
newArray.[this.Length] <- t
|
|
||||||
this.Array <- newArray
|
|
||||||
|
|
||||||
this.Length <- this.Length + 1
|
|
||||||
|
|
||||||
member this.Item
|
|
||||||
with get (i : int) = this.Array.[i]
|
|
||||||
and set (i : int) (t : 'T) = this.Array.[i] <- t
|
|
||||||
|
|
||||||
member this.Sort () =
|
|
||||||
Span(this.Array).Slice(0, this.Count).Sort ()
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module ResizeArray =
|
|
||||||
let create<'T> (capacity : int) =
|
|
||||||
{
|
|
||||||
Array = Array.zeroCreate<'T> capacity
|
|
||||||
Length = 0
|
|
||||||
}
|
|
@@ -1,28 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<PublishAot>true</PublishAot>
|
<PublishAot>true</PublishAot>
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
|
||||||
<UseSystemResourceKeys>true</UseSystemResourceKeys>
|
|
||||||
|
|
||||||
<IlcOptimizationPreference>Speed</IlcOptimizationPreference>
|
|
||||||
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
|
|
||||||
|
|
||||||
<DebuggerSupport>false</DebuggerSupport>
|
|
||||||
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
|
|
||||||
<EventSourceSupport>false</EventSourceSupport>
|
|
||||||
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
|
|
||||||
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Program.fs"/>
|
<Compile Include="Program.fs"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AdventOfCode2023.FSharp.Lib\AdventOfCode2023.FSharp.Lib.fsproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -1,168 +1,7 @@
|
|||||||
namespace AdventOfCode2023
|
namespace AdventOfCode2023
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
#else
|
|
||||||
#nowarn "9"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Diagnostics
|
|
||||||
open System.IO
|
|
||||||
|
|
||||||
module Program =
|
module Program =
|
||||||
|
|
||||||
let inline toUs (ticks : int64) =
|
|
||||||
1_000_000.0 * float ticks / float Stopwatch.Frequency
|
|
||||||
|
|
||||||
[<EntryPoint>]
|
[<EntryPoint>]
|
||||||
let main argv =
|
let main argv =
|
||||||
let endToEnd = Stopwatch.StartNew ()
|
|
||||||
endToEnd.Restart ()
|
|
||||||
|
|
||||||
let dir = DirectoryInfo argv.[0]
|
|
||||||
|
|
||||||
let sw = Stopwatch.StartNew ()
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 1====="
|
|
||||||
|
|
||||||
do
|
|
||||||
sw.Restart ()
|
|
||||||
|
|
||||||
let input =
|
|
||||||
try
|
|
||||||
Path.Combine (dir.FullName, "day1part1.txt") |> File.ReadAllText
|
|
||||||
with :? FileNotFoundException ->
|
|
||||||
Path.Combine (dir.FullName, "day1.txt") |> File.ReadAllText
|
|
||||||
|
|
||||||
let part1 = Day1.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let input = Path.Combine (dir.FullName, "day1.txt") |> File.ReadAllText
|
|
||||||
let part2 = Day1.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 2====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day2.txt") |> File.ReadAllText
|
|
||||||
sw.Restart ()
|
|
||||||
let part1 = Day2.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day2.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 3====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day3.txt") |> File.ReadAllBytes
|
|
||||||
|
|
||||||
sw.Restart ()
|
|
||||||
let resultArr, len, lineCount = Day3.parse input
|
|
||||||
sw.Stop ()
|
|
||||||
|
|
||||||
Console.Error.WriteLine (
|
|
||||||
(1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString ()
|
|
||||||
+ "ms parse"
|
|
||||||
)
|
|
||||||
#if DEBUG
|
|
||||||
let contents =
|
|
||||||
{
|
|
||||||
Elements = Array.take len resultArr
|
|
||||||
Width = len / lineCount
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
use ptr = fixed resultArr
|
|
||||||
|
|
||||||
let contents =
|
|
||||||
{
|
|
||||||
Elements = ptr
|
|
||||||
Length = len
|
|
||||||
Width = len / lineCount
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
let part1 = Day3.part1 contents
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day3.part2 contents
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 4====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day4.txt") |> File.ReadAllText
|
|
||||||
sw.Restart ()
|
|
||||||
let part1 = Day4.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day4.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 5====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day5.txt") |> File.ReadAllText
|
|
||||||
sw.Restart ()
|
|
||||||
let part1 = Day5.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day5.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 6====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day6.txt") |> File.ReadAllText
|
|
||||||
sw.Restart ()
|
|
||||||
let part1 = Day6.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day6.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
Console.WriteLine "=====Day 7====="
|
|
||||||
|
|
||||||
do
|
|
||||||
let input = Path.Combine (dir.FullName, "day7.txt") |> File.ReadAllText
|
|
||||||
sw.Restart ()
|
|
||||||
let part1 = Day7.part1 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part1.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
sw.Restart ()
|
|
||||||
let part2 = Day7.part2 input
|
|
||||||
sw.Stop ()
|
|
||||||
Console.WriteLine (part2.ToString ())
|
|
||||||
Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms")
|
|
||||||
|
|
||||||
endToEnd.Stop ()
|
|
||||||
|
|
||||||
Console.Error.WriteLine (
|
|
||||||
(1_000.0 * float endToEnd.ElapsedTicks / float Stopwatch.Frequency).ToString ()
|
|
||||||
+ "ms total"
|
|
||||||
)
|
|
||||||
|
|
||||||
0
|
0
|
||||||
|
@@ -12,18 +12,10 @@
|
|||||||
<Compile Include="TestDay1.fs" />
|
<Compile Include="TestDay1.fs" />
|
||||||
<Compile Include="TestDay2.fs" />
|
<Compile Include="TestDay2.fs" />
|
||||||
<Compile Include="TestDay3.fs" />
|
<Compile Include="TestDay3.fs" />
|
||||||
<Compile Include="TestDay4.fs" />
|
|
||||||
<Compile Include="TestDay5.fs" />
|
|
||||||
<Compile Include="TestDay6.fs" />
|
|
||||||
<Compile Include="TestDay7.fs" />
|
|
||||||
<EmbeddedResource Include="samples\day1.txt" />
|
<EmbeddedResource Include="samples\day1.txt" />
|
||||||
<EmbeddedResource Include="samples\day1part1.txt" />
|
<EmbeddedResource Include="samples\day1part1.txt" />
|
||||||
<EmbeddedResource Include="samples\day2.txt" />
|
<EmbeddedResource Include="samples\day2.txt" />
|
||||||
<EmbeddedResource Include="samples\day3.txt" />
|
<EmbeddedResource Include="samples\day3.txt" />
|
||||||
<EmbeddedResource Include="samples\day4.txt" />
|
|
||||||
<EmbeddedResource Include="samples\day5.txt" />
|
|
||||||
<EmbeddedResource Include="samples\day6.txt" />
|
|
||||||
<EmbeddedResource Include="samples\day7.txt" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
namespace AdventOfCode2023.Test
|
|
||||||
|
|
||||||
open AdventOfCode2023
|
|
||||||
open NUnit.Framework
|
|
||||||
open FsUnitTyped
|
|
||||||
open System.IO
|
|
||||||
|
|
||||||
[<TestFixture>]
|
|
||||||
module TestDay4 =
|
|
||||||
|
|
||||||
let sample = Assembly.getEmbeddedResource typeof<Dummy>.Assembly "day4.txt"
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Sample () = sample |> Day4.part1 |> shouldEqual 13
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Sample () = sample |> Day4.part2 |> shouldEqual 30
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day4.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day4.part1 s |> shouldEqual 27454
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day4.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day4.part2 s |> shouldEqual 6857330
|
|
@@ -1,46 +0,0 @@
|
|||||||
namespace AdventOfCode2023.Test
|
|
||||||
|
|
||||||
open System
|
|
||||||
open AdventOfCode2023
|
|
||||||
open NUnit.Framework
|
|
||||||
open FsUnitTyped
|
|
||||||
open System.IO
|
|
||||||
|
|
||||||
[<TestFixture>]
|
|
||||||
module TestDay5 =
|
|
||||||
|
|
||||||
let sample = Assembly.getEmbeddedResource typeof<Dummy>.Assembly "day5.txt"
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Sample () =
|
|
||||||
sample |> Day5.part1 |> shouldEqual 35ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Sample () =
|
|
||||||
sample |> Day5.part2 |> shouldEqual 46ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day5.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day5.part1 s |> shouldEqual 806029445ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day5.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day5.part2 s |> shouldEqual 59370572ul
|
|
@@ -1,46 +0,0 @@
|
|||||||
namespace AdventOfCode2023.Test
|
|
||||||
|
|
||||||
open System
|
|
||||||
open AdventOfCode2023
|
|
||||||
open NUnit.Framework
|
|
||||||
open FsUnitTyped
|
|
||||||
open System.IO
|
|
||||||
|
|
||||||
[<TestFixture>]
|
|
||||||
module TestDay6 =
|
|
||||||
|
|
||||||
let sample = Assembly.getEmbeddedResource typeof<Dummy>.Assembly "day6.txt"
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Sample () =
|
|
||||||
sample |> Day6.part1 |> shouldEqual 288uL
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Sample () =
|
|
||||||
sample |> Day6.part2 |> shouldEqual 71503uL
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day6.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day6.part1 s |> shouldEqual 32076uL
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day6.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day6.part2 s |> shouldEqual 34278221uL
|
|
@@ -1,46 +0,0 @@
|
|||||||
namespace AdventOfCode2023.Test
|
|
||||||
|
|
||||||
open System
|
|
||||||
open AdventOfCode2023
|
|
||||||
open NUnit.Framework
|
|
||||||
open FsUnitTyped
|
|
||||||
open System.IO
|
|
||||||
|
|
||||||
[<TestFixture>]
|
|
||||||
module TestDay7 =
|
|
||||||
|
|
||||||
let sample = Assembly.getEmbeddedResource typeof<Dummy>.Assembly "day7.txt"
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Sample () =
|
|
||||||
sample |> Day7.part1 |> shouldEqual 6440ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Sample () =
|
|
||||||
sample |> Day7.part2 |> shouldEqual 5905ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part1Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day7.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day7.part1 s |> shouldEqual 250058342ul
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let part2Actual () =
|
|
||||||
let s =
|
|
||||||
try
|
|
||||||
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day7.txt"))
|
|
||||||
with
|
|
||||||
| :? DirectoryNotFoundException
|
|
||||||
| :? FileNotFoundException ->
|
|
||||||
Assert.Inconclusive ()
|
|
||||||
failwith "unreachable"
|
|
||||||
|
|
||||||
Day7.part2 s |> shouldEqual 250506580ul
|
|
@@ -1,6 +0,0 @@
|
|||||||
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
|
||||||
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
|
||||||
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
|
||||||
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
|
||||||
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
|
||||||
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
|
@@ -1,33 +0,0 @@
|
|||||||
seeds: 79 14 55 13
|
|
||||||
|
|
||||||
seed-to-soil map:
|
|
||||||
50 98 2
|
|
||||||
52 50 48
|
|
||||||
|
|
||||||
soil-to-fertilizer map:
|
|
||||||
0 15 37
|
|
||||||
37 52 2
|
|
||||||
39 0 15
|
|
||||||
|
|
||||||
fertilizer-to-water map:
|
|
||||||
49 53 8
|
|
||||||
0 11 42
|
|
||||||
42 0 7
|
|
||||||
57 7 4
|
|
||||||
|
|
||||||
water-to-light map:
|
|
||||||
88 18 7
|
|
||||||
18 25 70
|
|
||||||
|
|
||||||
light-to-temperature map:
|
|
||||||
45 77 23
|
|
||||||
81 45 19
|
|
||||||
68 64 13
|
|
||||||
|
|
||||||
temperature-to-humidity map:
|
|
||||||
0 69 1
|
|
||||||
1 0 69
|
|
||||||
|
|
||||||
humidity-to-location map:
|
|
||||||
60 56 37
|
|
||||||
56 93 4
|
|
@@ -1,2 +0,0 @@
|
|||||||
Time: 7 15 30
|
|
||||||
Distance: 9 40 200
|
|
@@ -1,5 +0,0 @@
|
|||||||
32T3K 765
|
|
||||||
T55J5 684
|
|
||||||
KK677 28
|
|
||||||
KTJJT 220
|
|
||||||
QQQJA 483
|
|
Reference in New Issue
Block a user