Add day 3

This commit is contained in:
Smaug123
2023-12-03 14:35:47 +00:00
parent 01d66b8814
commit 3a8061e28d
11 changed files with 509 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Arr2D.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,133 @@
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