From 3a8061e28d3bd88b10da33488949d24f861c01c5 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Sun, 3 Dec 2023 14:35:47 +0000 Subject: [PATCH] Add day 3 --- .gitignore | 12 ++ .../AdventOfCode2023.FSharp.Lib.fsproj | 13 ++ .../AdventOfCode2023.FSharp.Lib/Arr2D.fs | 102 ++++++++++++++ .../AdventOfCode2023.FSharp.Lib/Day3.fs | 133 ++++++++++++++++++ .../AdventOfCode2023.FSharp.sln | 28 ++++ .../AdventOfCode2023.FSharp.fsproj | 28 ++++ .../AdventOfCode2023.FSharp/Program.fs | 65 +++++++++ AdventOfCode2023.FSharp/Test/Test.fsproj | 23 +++ AdventOfCode2023.FSharp/Test/TestDay3.fs | 18 +++ flake.lock | 61 ++++++++ flake.nix | 26 ++++ 11 files changed, 509 insertions(+) create mode 100644 .gitignore create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arr2D.fs create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day3.fs create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.sln create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj create mode 100644 AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs create mode 100644 AdventOfCode2023.FSharp/Test/Test.fsproj create mode 100644 AdventOfCode2023.FSharp/Test/TestDay3.fs create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..104abff --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +bin/ +obj/ +riderModule.iml +_ReSharper.Caches/ +.idea/ +*.user +*.DotSettings +.DS_Store +result +.profile* + +inputs/ diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj new file mode 100644 index 0000000..e2186c7 --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/AdventOfCode2023.FSharp.Lib.fsproj @@ -0,0 +1,13 @@ + + + + net8.0 + true + + + + + + + + diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arr2D.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arr2D.fs new file mode 100644 index 0000000..a29387f --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Arr2D.fs @@ -0,0 +1,102 @@ +namespace AdventOfCode2023 + +#if DEBUG +#else +#nowarn "9" +#endif + +open Microsoft.FSharp.NativeInterop + +[] +#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 + +[] +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 + + [] +#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 \ No newline at end of file diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day3.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day3.fs new file mode 100644 index 0000000..1879e45 --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day3.fs @@ -0,0 +1,133 @@ +namespace AdventOfCode2023 + +open System.Collections.Generic + +[] +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) = + 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) = + 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> () + 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 + diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.sln b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.sln new file mode 100644 index 0000000..f6ffa4f --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.sln @@ -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 diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj new file mode 100644 index 0000000..c59ca02 --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.fsproj @@ -0,0 +1,28 @@ + + + + Exe + net8.0 + true + true + true + + Speed + false + + false + false + false + false + false + + + + + + + + + + + diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs new file mode 100644 index 0000000..6e4affa --- /dev/null +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs @@ -0,0 +1,65 @@ +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 + + [] + 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 \ No newline at end of file diff --git a/AdventOfCode2023.FSharp/Test/Test.fsproj b/AdventOfCode2023.FSharp/Test/Test.fsproj new file mode 100644 index 0000000..48d6af2 --- /dev/null +++ b/AdventOfCode2023.FSharp/Test/Test.fsproj @@ -0,0 +1,23 @@ + + + + net8.0 + + false + true + TestProject1 + + + + + + + + + + + + + + + diff --git a/AdventOfCode2023.FSharp/Test/TestDay3.fs b/AdventOfCode2023.FSharp/Test/TestDay3.fs new file mode 100644 index 0000000..efa83d7 --- /dev/null +++ b/AdventOfCode2023.FSharp/Test/TestDay3.fs @@ -0,0 +1,18 @@ +namespace Test + +open NUnit.Framework + +[] +module TestDay3 = + + [] + let day1Sample () = + () + + [] + let day1Actual () = + 540131 + + [] + let day2Actual () = + 86879020 \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..aa98498 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..14643f8 --- /dev/null +++ b/flake.nix @@ -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]; + };}; + } + ); +}