Compare commits

...

3 Commits

Author SHA1 Message Date
Smaug123
1f505a7cce Day 1 in F# 2023-12-03 16:56:38 +00:00
Smaug123
a637f79bf1 Format and tests 2023-12-03 16:31:46 +00:00
Smaug123
3a8061e28d Add day 3 2023-12-03 14:35:47 +00:00
17 changed files with 955 additions and 0 deletions

12
.config/dotnet-tools.json Normal file
View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.2.3",
"commands": [
"fantomas"
]
}
}
}

41
.editorconfig Normal file
View File

@@ -0,0 +1,41 @@
root=true
[*]
charset=utf-8
end_of_line=crlf
trim_trailing_whitespace=true
insert_final_newline=true
indent_style=space
indent_size=4
# ReSharper properties
resharper_xml_indent_size=2
resharper_xml_max_line_length=100
resharper_xml_tab_width=2
[*.{csproj,fsproj,sqlproj,targets,props,ts,tsx,css,json}]
indent_style=space
indent_size=2
[*.{fs,fsi}]
fsharp_bar_before_discriminated_union_declaration=true
fsharp_space_before_uppercase_invocation=true
fsharp_space_before_class_constructor=true
fsharp_space_before_member=true
fsharp_space_before_colon=true
fsharp_space_before_semicolon=true
fsharp_multiline_bracket_style=aligned
fsharp_newline_between_type_definition_and_members=true
fsharp_align_function_signature_to_indentation=true
fsharp_alternative_long_member_definitions=true
fsharp_multi_line_lambda_closing_newline=true
fsharp_experimental_keep_indent_in_branch=true
fsharp_max_value_binding_width=80
fsharp_max_record_width=0
max_line_length=120
end_of_line=lf
[*.{appxmanifest,build,dtd,nuspec,xaml,xamlx,xoml,xsd}]
indent_style=space
indent_size=2
tab_width=2

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.txt text eol=lf

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
bin/
obj/
riderModule.iml
_ReSharper.Caches/
.idea/
*.user
*.DotSettings
.DS_Store
result
.profile*
inputs/

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Arr2D.fs" />
<Compile Include="EfficientString.fs" />
<Compile Include="Day1.fs" />
<Compile Include="Day3.fs" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,102 @@
namespace AdventOfCode2023
#if DEBUG
#else
#nowarn "9"
#endif
open Microsoft.FSharp.NativeInterop
[<Struct>]
#if DEBUG
type Arr2D<'a> =
{
Elements : 'a array
Width : int
}
member this.Height = this.Elements.Length / this.Width
#else
type Arr2D<'a when 'a : unmanaged> =
{
Elements : nativeptr<'a>
Length : int
Width : int
}
member this.Height = this.Length / this.Width
#endif
[<RequireQualifiedAccess>]
module Arr2D =
/// It's faster to iterate forward over the first argument, `x`.
let inline get (arr : Arr2D<'a>) (x : int) (y : int) : 'a =
#if DEBUG
arr.Elements.[y * arr.Width + x]
#else
NativePtr.get arr.Elements (y * arr.Width + x)
#endif
let inline set (arr : Arr2D<'a>) (x : int) (y : int) (newVal : 'a) : unit =
#if DEBUG
arr.Elements.[y * arr.Width + x] <- newVal
#else
NativePtr.write (NativePtr.add arr.Elements (y * arr.Width + x)) newVal
#endif
#if DEBUG
let create (width : int) (height : int) (value : 'a) : Arr2D<'a> =
let arr = Array.create (width * height) value
{
Width = width
Elements = arr
}
#else
/// The input array must be at least of size width * height
let create (arr : nativeptr<'a>) (width : int) (height : int) (value : 'a) : Arr2D<'a> =
{
Width = width
Elements = arr
Length = width * height
}
#endif
[<RequiresExplicitTypeArguments>]
#if DEBUG
let zeroCreate<'a when 'a : unmanaged> (width : int) (height : int) : Arr2D<'a> =
{
Elements = Array.zeroCreate (width * height)
Width = width
}
#else
let zeroCreate<'a when 'a : unmanaged> (elts : nativeptr<'a>) (width : int) (height : int) : Arr2D<'a> =
{
Elements = elts
Width = width
Length = width * height
}
#endif
/// The closure is given x and then y.
#if DEBUG
let inline init (width : int) (height : int) (f : int -> int -> 'a) : Arr2D<'a> =
let result = zeroCreate<'a> width height
#else
let inline init (arr : nativeptr<'a>) (width : int) (height : int) (f : int -> int -> 'a) : Arr2D<'a> =
let result = zeroCreate<'a> arr width height
#endif
for y = 0 to height - 1 do
for x = 0 to width - 1 do
set result x y (f x y)
result
let inline clear (a : Arr2D<'a>) : unit =
#if DEBUG
System.Array.Clear a.Elements
#else
NativePtr.initBlock a.Elements 0uy (uint32 sizeof<'a> * uint32 a.Length)
#endif

View File

@@ -0,0 +1,97 @@
namespace AdventOfCode2023
open System
[<RequireQualifiedAccess>]
module Day1 =
let firstDigit (s : ReadOnlySpan<char>) =
let mutable pos = 0
while '0' > s.[pos] || s.[pos] > '9' do
pos <- pos + 1
byte s.[pos] - byte '0'
// No surrogate pairs please!
let lastDigit (s : ReadOnlySpan<char>) =
let mutable pos = s.Length - 1
while '0' > s.[pos] || s.[pos] > '9' do
pos <- pos - 1
byte s.[pos] - byte '0'
let part1 (s : string) =
use enum = StringSplitEnumerator.make '\n' s
let mutable total = 0
for line in enum do
if not line.IsEmpty then
let firstDigit = firstDigit line
let lastDigit = lastDigit line
total <- total + int (lastDigit + 10uy * firstDigit)
total
let table =
[|
"one", 1uy
"two", 2uy
"three", 3uy
"four", 4uy
"five", 5uy
"six", 6uy
"seven", 7uy
"eight", 8uy
"nine", 9uy
|]
let firstDigitIncSpelled (s : ReadOnlySpan<char>) =
let mutable pos = 0
let mutable answer = 255uy
while answer = 255uy do
if s.[pos] >= '0' && s.[pos] <= '9' then
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
let lastDigitIncSpelled (s : ReadOnlySpan<char>) =
let mutable pos = s.Length - 1
let mutable answer = 255uy
while answer = 255uy do
if s.[pos] >= '0' && s.[pos] <= '9' then
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
let part2 (s : string) =
use enum = StringSplitEnumerator.make '\n' s
let mutable total = 0
for line in enum do
if not line.IsEmpty then
total <- total + int (10uy * firstDigitIncSpelled line + lastDigitIncSpelled line)
total

View File

@@ -0,0 +1,153 @@
namespace AdventOfCode2023
open System.Collections.Generic
[<RequireQualifiedAccess>]
module Day3 =
let inline private isSymbol (i : byte) = i > 200uy
let inline private isGear (i : byte) = i = 255uy
/// Returns the parsed board as a buffer, the length of the buffer (there may be garbage at the end), and
/// the number of lines the resulting 2D array has.
let parse (fileContents : byte[]) =
let mutable lineCount = 0
let mutable len = 0
let resultArr = Array.zeroCreate fileContents.Length
for b in fileContents do
if b = byte '.' then
resultArr.[len] <- 100uy
len <- len + 1
elif b = byte '*' then
resultArr.[len] <- 255uy
len <- len + 1
elif byte '0' <= b && b <= byte '9' then
resultArr.[len] <- b - byte '0'
len <- len + 1
elif b = 10uy then
lineCount <- lineCount + 1
else
resultArr.[len] <- 254uy
len <- len + 1
resultArr, len, lineCount
let part1 (contents : Arr2D<byte>) =
let lineLength = contents.Width
let isNearSymbol (row : int) (numStart : int) (curCol : int) : bool =
let mutable isNearSymbol = false
if row > 0 then
for col = max (numStart - 1) 0 to min curCol (lineLength - 1) do
if isSymbol (Arr2D.get contents col (row - 1)) then
isNearSymbol <- true
if row < contents.Height - 1 then
for col = max (numStart - 1) 0 to min curCol (lineLength - 1) do
if isSymbol (Arr2D.get contents col (row + 1)) then
isNearSymbol <- true
if
(numStart > 0 && isSymbol (Arr2D.get contents (numStart - 1) row))
|| (curCol < lineLength && isSymbol (Arr2D.get contents curCol row))
then
isNearSymbol <- true
isNearSymbol
let mutable total = 0
for row = 0 to contents.Height - 1 do
let mutable currNum = 0
let mutable numStart = -1
for col = 0 to lineLength - 1 do
if Arr2D.get contents col row < 10uy then
if numStart = -1 then
numStart <- col
currNum <- currNum * 10 + int (Arr2D.get contents col row)
elif numStart > -1 then
if isNearSymbol row numStart col then
total <- total + currNum
currNum <- 0
numStart <- -1
if numStart >= 0 then
if isNearSymbol row numStart lineLength then
total <- total + currNum
currNum <- 0
numStart <- -1
total
let part2 (contents : Arr2D<byte>) =
let lineLength = contents.Width
let isNearGear (row : int) (numStart : int) (curCol : int) : (int * int) IReadOnlyList =
let gearsNear = ResizeArray ()
if row > 0 then
for col = max (numStart - 1) 0 to min curCol (lineLength - 1) do
if isGear (Arr2D.get contents col (row - 1)) then
gearsNear.Add (row - 1, col)
if row < lineLength - 1 then
for col = max (numStart - 1) 0 to min curCol (lineLength - 1) do
if isGear (Arr2D.get contents col (row + 1)) then
gearsNear.Add (row + 1, col)
if (numStart > 0 && isGear (Arr2D.get contents (numStart - 1) row)) then
gearsNear.Add (row, numStart - 1)
if (curCol < lineLength && isGear (Arr2D.get contents curCol row)) then
gearsNear.Add (row, curCol)
gearsNear
let gears = Dictionary<int * int, ResizeArray<int>> ()
let addGear (gearPos : int * int) (num : int) =
match gears.TryGetValue gearPos with
| false, _ ->
let arr = ResizeArray ()
arr.Add num
gears.Add (gearPos, arr)
| true, arr when arr.Count < 3 -> arr.Add num
| _ -> ()
for row = 0 to contents.Height - 1 do
let mutable currNum = 0
let mutable numStart = -1
for col = 0 to lineLength - 1 do
if Arr2D.get contents col row < 10uy then
if numStart = -1 then
numStart <- col
currNum <- currNum * 10 + int (Arr2D.get contents col row)
elif numStart > -1 then
for gearPos in isNearGear row numStart col do
addGear gearPos currNum
currNum <- 0
numStart <- -1
if numStart >= 0 then
for gearPos in isNearGear row numStart lineLength do
addGear gearPos currNum
currNum <- 0
numStart <- -1
let mutable answer = 0
for KeyValue (_gearPos, gears) in gears do
if gears.Count = 2 then
answer <- answer + gears.[0] * gears.[1]
answer

View File

@@ -0,0 +1,97 @@
namespace AdventOfCode2023
open System
open System.Globalization
open System.Runtime.CompilerServices
type EfficientString = System.ReadOnlySpan<char>
[<RequireQualifiedAccess>]
module EfficientString =
let inline isEmpty (s : EfficientString) : bool = s.IsEmpty
let inline ofString (s : string) : EfficientString = s.AsSpan ()
let inline toString (s : EfficientString) : string = s.ToString ()
let inline trimStart (s : EfficientString) : EfficientString = s.TrimStart ()
let inline slice (start : int) (length : int) (s : EfficientString) : EfficientString = s.Slice (start, length)
let inline equals (a : string) (other : EfficientString) : bool =
MemoryExtensions.Equals (other, a.AsSpan (), StringComparison.Ordinal)
/// Mutates the input to drop up to the first instance of the input char,
/// and returns what was dropped.
/// If the char is not present, deletes the input.
let takeUntil<'a> (c : char) (s : EfficientString byref) : EfficientString =
let first = s.IndexOf c
if first < 0 then
let toRet = s
s <- EfficientString.Empty
toRet
else
let toRet = slice 0 first s
s <- slice (first + 1) (s.Length - first - 1) s
toRet
[<Struct>]
[<IsByRefLike>]
type StringSplitEnumerator =
internal
{
Original : EfficientString
mutable Remaining : EfficientString
mutable InternalCurrent : EfficientString
SplitOn : char
}
interface IDisposable with
member this.Dispose () = ()
member this.Current : EfficientString = this.InternalCurrent
member this.MoveNext () =
if this.Remaining.Length = 0 then
false
else
this.InternalCurrent <- EfficientString.takeUntil this.SplitOn &this.Remaining
true
member this.GetEnumerator () = this
[<RequireQualifiedAccess>]
module StringSplitEnumerator =
let make (splitChar : char) (s : string) : StringSplitEnumerator =
{
Original = EfficientString.ofString s
Remaining = EfficientString.ofString s
InternalCurrent = EfficientString.Empty
SplitOn = splitChar
}
let make' (splitChar : char) (s : ReadOnlySpan<char>) : StringSplitEnumerator =
{
Original = s
Remaining = s
InternalCurrent = EfficientString.Empty
SplitOn = splitChar
}
let chomp (s : string) (e : byref<StringSplitEnumerator>) : unit =
#if DEBUG
if not (e.MoveNext ()) || not (EfficientString.equals s e.Current) then
failwithf "expected '%s', got '%s'" s (e.Current.ToString ())
#else
e.MoveNext () |> ignore
#endif
let consumeInt (e : byref<StringSplitEnumerator>) : int =
if not (e.MoveNext ()) then
failwith "expected an int, got nothing"
Int32.Parse e.Current

View File

@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AdventOfCode2023.FSharp", "AdventOfCode2023.FSharp\AdventOfCode2023.FSharp.fsproj", "{E2EC7715-E2C9-4671-AFBD-84D740B604FE}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Test", "Test\Test.fsproj", "{AC9C7858-2F5D-4DE1-8E13-0A87E1EA8598}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AdventOfCode2023.FSharp.Lib", "AdventOfCode2023.FSharp.Lib\AdventOfCode2023.FSharp.Lib.fsproj", "{95CE0568-3D1A-4060-BB54-52460FB1E399}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E2EC7715-E2C9-4671-AFBD-84D740B604FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2EC7715-E2C9-4671-AFBD-84D740B604FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2EC7715-E2C9-4671-AFBD-84D740B604FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2EC7715-E2C9-4671-AFBD-84D740B604FE}.Release|Any CPU.Build.0 = Release|Any CPU
{AC9C7858-2F5D-4DE1-8E13-0A87E1EA8598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC9C7858-2F5D-4DE1-8E13-0A87E1EA8598}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC9C7858-2F5D-4DE1-8E13-0A87E1EA8598}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC9C7858-2F5D-4DE1-8E13-0A87E1EA8598}.Release|Any CPU.Build.0 = Release|Any CPU
{95CE0568-3D1A-4060-BB54-52460FB1E399}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95CE0568-3D1A-4060-BB54-52460FB1E399}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95CE0568-3D1A-4060-BB54-52460FB1E399}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95CE0568-3D1A-4060-BB54-52460FB1E399}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<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>
<ItemGroup>
<Compile Include="Program.fs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AdventOfCode2023.FSharp.Lib\AdventOfCode2023.FSharp.Lib.fsproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,66 @@
namespace AdventOfCode2023
#if DEBUG
#else
#nowarn "9"
#endif
open System.Diagnostics
open System.IO
module Program =
let inline toUs (ticks : int64) =
1_000_000.0 * float ticks / float Stopwatch.Frequency
[<EntryPoint>]
let main argv =
let endToEnd = Stopwatch.StartNew ()
endToEnd.Restart ()
let sw = Stopwatch.StartNew ()
sw.Restart ()
let contents = File.ReadAllBytes argv.[0]
sw.Stop ()
System.Console.Error.WriteLine ("Reading file (us): " + (toUs sw.ElapsedTicks).ToString ())
sw.Restart ()
let resultArr, len, lineCount = Day3.parse contents
sw.Stop ()
System.Console.Error.WriteLine ("Populating array (us): " + (toUs sw.ElapsedTicks).ToString ())
#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
// |> Array.map (fun s -> Array.init s.Length (fun i -> if s.[i] = '.' then 100uy elif s.[i] = '*' then 255uy elif '0' <= s.[i] && s.[i] <= '9' then byte s.[i] - byte '0' else 254uy))
sw.Restart ()
let part1 = Day3.part1 contents
sw.Stop ()
System.Console.Error.WriteLine ("Part 1 (us): " + (toUs sw.ElapsedTicks).ToString ())
System.Console.WriteLine (part1.ToString ())
sw.Restart ()
let part2 = Day3.part2 contents
sw.Stop ()
System.Console.Error.WriteLine ("Part 2 (us): " + (toUs sw.ElapsedTicks).ToString ())
System.Console.WriteLine (part2.ToString ())
endToEnd.Stop ()
System.Console.Error.WriteLine ("Total (us): " + (toUs endToEnd.ElapsedTicks).ToString ())
0

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<Compile Include="TestDay1.fs" />
<Compile Include="TestDay3.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FsUnit" Version="5.6.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
<PackageReference Include="NUnit.Analyzers" Version="3.6.1"/>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AdventOfCode2023.FSharp.Lib\AdventOfCode2023.FSharp.Lib.fsproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,56 @@
namespace AdventOfCode2023.Test
open AdventOfCode2023
open NUnit.Framework
open FsUnitTyped
open System.IO
[<TestFixture>]
module TestDay1 =
let sample1 =
"""1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet
"""
[<Test>]
let part1Sample () =
sample1 |> Day1.part1 |> shouldEqual 142
let sample2 =
"""two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen
"""
[<Test>]
let part2Sample () =
sample2 |> Day1.part2 |> shouldEqual 281
[<Test>]
let part1Actual () =
let s =
try
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day1.txt"))
with :? FileNotFoundException ->
Assert.Inconclusive ()
failwith "unreachable"
Day1.part1 s |> shouldEqual 54304
[<Test>]
let part2Actual () =
let s =
try
File.ReadAllText (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day1.txt"))
with :? FileNotFoundException ->
Assert.Inconclusive ()
failwith "unreachable"
Day1.part2 s |> shouldEqual 54418

View File

@@ -0,0 +1,133 @@
namespace AdventOfCode2023.Test
#if DEBUG
#else
#nowarn "9"
#endif
open AdventOfCode2023
open NUnit.Framework
open FsUnitTyped
open System.IO
[<TestFixture>]
module TestDay3 =
let sample =
"""467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
"""
[<Test>]
let part1Sample () =
let arr, len, rows = sample.ToCharArray () |> Array.map byte |> Day3.parse
#if DEBUG
let arr =
{
Elements = arr
Width = len / rows
}
#else
use arr = fixed arr
let arr =
{
Elements = arr
Length = len
Width = len / rows
}
#endif
arr |> Day3.part1 |> shouldEqual 4361
[<Test>]
let part2Sample () =
let arr, len, rows = sample.ToCharArray () |> Array.map byte |> Day3.parse
#if DEBUG
let arr =
{
Elements = arr
Width = len / rows
}
#else
use arr = fixed arr
let arr =
{
Elements = arr
Length = len
Width = len / rows
}
#endif
arr |> Day3.part2 |> shouldEqual 467835
[<Test>]
let part1Actual () =
let bytes =
try
File.ReadAllBytes (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day3.txt"))
with :? FileNotFoundException ->
Assert.Inconclusive ()
failwith "unreachable"
let arr, len, rows = Day3.parse bytes
#if DEBUG
let arr =
{
Elements = arr
Width = len / rows
}
#else
use arr = fixed arr
let arr =
{
Elements = arr
Length = len
Width = len / rows
}
#endif
Day3.part1 arr |> shouldEqual 540131
[<Test>]
let part2Actual () =
let bytes =
try
File.ReadAllBytes (Path.Combine (__SOURCE_DIRECTORY__, "../../inputs/day3.txt"))
with :? FileNotFoundException ->
Assert.Inconclusive ()
failwith "unreachable"
let arr, len, rows = Day3.parse bytes
#if DEBUG
let arr =
{
Elements = arr
Width = len / rows
}
#else
use arr = fixed arr
let arr =
{
Elements = arr
Length = len
Width = len / rows
}
#endif
Day3.part2 arr |> shouldEqual 86879020

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1701253981,
"narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

26
flake.nix Normal file
View File

@@ -0,0 +1,26 @@
{
description = "Advent of Code 2023";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = {
self,
nixpkgs,
flake-utils,
}: flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
devShells = { default = pkgs.mkShell {
buildInputs = with pkgs; [
(with dotnetCorePackages;
combinePackages [
dotnet-sdk_8
dotnetPackages.Nuget
])
] ++ [pkgs.swift darwin.apple_sdk.frameworks.Foundation darwin.apple_sdk.frameworks.CryptoKit darwin.apple_sdk.frameworks.GSS pkgs.zlib pkgs.zlib.dev pkgs.openssl pkgs.icu];
};};
}
);
}