aboutsummaryrefslogtreecommitdiff
path: root/aoc-2022-dotnet
diff options
context:
space:
mode:
Diffstat (limited to 'aoc-2022-dotnet')
-rw-r--r--aoc-2022-dotnet/Common/Util.fs4
-rw-r--r--aoc-2022-dotnet/Common/Vec2.fs28
-rw-r--r--aoc-2022-dotnet/Day14/Program.fs8
-rw-r--r--aoc-2022-dotnet/Day15/Program.fs12
-rw-r--r--aoc-2022-dotnet/Day24/Day24.fsproj22
-rw-r--r--aoc-2022-dotnet/Day24/Program.fs107
-rw-r--r--aoc-2022-dotnet/README.md6
-rw-r--r--aoc-2022-dotnet/aoc-2022-dotnet.sln8
8 files changed, 173 insertions, 22 deletions
diff --git a/aoc-2022-dotnet/Common/Util.fs b/aoc-2022-dotnet/Common/Util.fs
index ac5d981..ad177db 100644
--- a/aoc-2022-dotnet/Common/Util.fs
+++ b/aoc-2022-dotnet/Common/Util.fs
@@ -4,6 +4,7 @@ module Util =
open System.Globalization
open FParsec
open FSharpPlus
+ open FSharpPlus.Math.Generic
let parse parser input =
match run parser input with
@@ -16,6 +17,9 @@ module Util =
let charToInt = CharUnicodeInfo.GetDigitValue
+ let wrapInRangeInc lower upper x =
+ lower + remE (x - lower) (upper - lower + 1)
+
let cutInHalf xs =
let half = Seq.length xs / 2
[ Seq.take half xs; Seq.skip half xs ]
diff --git a/aoc-2022-dotnet/Common/Vec2.fs b/aoc-2022-dotnet/Common/Vec2.fs
index 438135b..8183083 100644
--- a/aoc-2022-dotnet/Common/Vec2.fs
+++ b/aoc-2022-dotnet/Common/Vec2.fs
@@ -4,10 +4,14 @@
type Vec2 =
| Vec2 of int * int
- static member inline x(Vec2 (x, _)) = x
- static member inline y(Vec2 (_, y)) = y
+ static member inline getX(Vec2 (x, _)) = x
+ static member inline getY(Vec2 (_, y)) = y
+ member inline this.x = Vec2.getX this
+ member inline this.y = Vec2.getY this
static member zero = Vec2(0, 0)
+ static member ones = Vec2(1, 1)
+
static member up = Vec2(0, 1)
static member right = Vec2(1, 0)
static member down = Vec2(0, -1)
@@ -44,6 +48,8 @@ type Vec2 =
Vec2.down
Vec2.left ]
+ static member directions5 = Vec2.directions4 |> Set.add Vec2.zero
+
static member directions8 =
Set [ Vec2.up
Vec2.upRight
@@ -54,19 +60,20 @@ type Vec2 =
Vec2.left
Vec2.upLeft ]
- static member inline private _map f (Vec2 (x, y)) = Vec2(f x, f y)
+ static member inline mapX f (Vec2 (x, y)) = Vec2(f x, y)
+ static member inline mapY f (Vec2 (x, y)) = Vec2(x, f y)
+ static member inline private _map f = Vec2.mapX f >> Vec2.mapY f
static member inline private _combine fn (Vec2 (x1, y1)) (Vec2 (x2, y2)) = Vec2(fn x1 x2, fn y1 y2)
static member inline (~-)(v) = Vec2._map (~-) v
static member inline (+)(v1, v2) = Vec2._combine (+) v1 v2
static member inline (-)(v1, v2) = Vec2._combine (-) v1 v2
static member inline (*)(v1, k) = Vec2._map ((*) k) v1
-
static member inline dot (Vec2 (x1, y1)) (Vec2 (x2, y2)) = x1 * x2 + y1 * y2
static member inline cross (Vec2 (x1, y1)) (Vec2 (x2, y2)) = x1 * y2 - y1 * x2
static member inline sign = Vec2._map sign
- static member inline lengthSquared(Vec2 (x, y)) = x * x + y * y
- static member mahattanDist (Vec2 (x1, y1)) (Vec2 (x2, y2)) = abs (x2 - x1) + abs (y2 - y1)
+ static member inline lengthSquared v = Vec2.dot v v
+ static member manhattanDist (Vec2 (x1, y1)) (Vec2 (x2, y2)) = abs (x2 - x1) + abs (y2 - y1)
static member chebyshevDist (Vec2 (x1, y1)) (Vec2 (x2, y2)) = max (abs <| x2 - x1) (abs <| y2 - y1)
static member inline private _nb d v = Set.map ((+) v) d
@@ -75,8 +82,17 @@ type Vec2 =
static member neighboursDown = Vec2._nb Vec2.directionsDown
static member neighboursLeft = Vec2._nb Vec2.directionsLeft
static member neighbours4 = Vec2._nb Vec2.directions4
+ static member neighbours5 = Vec2._nb Vec2.directions5
static member neighbours8 = Vec2._nb Vec2.directions8
+ static member dirFromChar =
+ function
+ | '^' -> Vec2.up
+ | '>' -> Vec2.right
+ | 'v' -> Vec2.down
+ | '<' -> Vec2.left
+ | c -> failwithf "Invalid direction: %c" c
+
static member boundingRectangle vs =
(Seq.reduce (Vec2._combine min) vs, Seq.reduce (Vec2._combine max) vs)
diff --git a/aoc-2022-dotnet/Day14/Program.fs b/aoc-2022-dotnet/Day14/Program.fs
index b3ff4f9..018664a 100644
--- a/aoc-2022-dotnet/Day14/Program.fs
+++ b/aoc-2022-dotnet/Day14/Program.fs
@@ -28,11 +28,11 @@ let buildCaveScan =
let solution1 input =
let initialCaveScan = buildCaveScan input
- let voidY = initialCaveScan |> Seq.map Vec2.y |> Seq.min
+ let voidY = initialCaveScan |> Seq.map Vec2.getY |> Seq.min
let settleNewUnit caveScan =
let rec fall pos =
- if Vec2.y pos <= voidY then
+ if Vec2.getY pos <= voidY then
None
else
sandMoveOffsets
@@ -55,12 +55,12 @@ let solution1 input =
let solution2 input =
let caveScan = buildCaveScan input
- let floorY = caveScan |> Seq.map Vec2.y |> Seq.min |> (+) -2
+ let floorY = caveScan |> Seq.map Vec2.getY |> Seq.min |> (+) -2
let neighbours pos =
sandMoveOffsets
|> List.map ((+) pos)
- |> List.filter (fun pos -> Util.notIn caveScan pos && Vec2.y pos <> floorY)
+ |> List.filter (fun pos -> Util.notIn caveScan pos && Vec2.getY pos <> floorY)
let rec dfs vis =
function
diff --git a/aoc-2022-dotnet/Day15/Program.fs b/aoc-2022-dotnet/Day15/Program.fs
index e158346..427c411 100644
--- a/aoc-2022-dotnet/Day15/Program.fs
+++ b/aoc-2022-dotnet/Day15/Program.fs
@@ -53,15 +53,15 @@ let sensorList =
let (sensorPos, beaconPos) = Util.parse pline line
{ Pos = sensorPos
- Radius = Vec2.mahattanDist sensorPos beaconPos
+ Radius = Vec2.manhattanDist sensorPos beaconPos
NearestBeacon = beaconPos }
Seq.map parseSensor >> List.ofSeq
let rowCoverages y sensors =
let coverage ({ Radius = radius; Pos = pos }) =
- let offset = radius - abs (Vec2.y pos - y)
- (Vec2.x pos - offset, Vec2.x pos + offset)
+ let offset = radius - abs (pos.y - y)
+ (pos.x - offset, pos.x + offset)
sensors
|> Seq.map coverage
@@ -72,11 +72,7 @@ let solution1 y input =
let beaconsInRow =
sensors
- |> Seq.choose (fun ({ NearestBeacon = b }) ->
- if Vec2.y b = y then
- Some(Vec2.x b)
- else
- None)
+ |> Seq.choose (fun ({ NearestBeacon = b }) -> if b.y = y then Some(b.x) else None)
|> Util.countDistinct
sensors
diff --git a/aoc-2022-dotnet/Day24/Day24.fsproj b/aoc-2022-dotnet/Day24/Day24.fsproj
new file mode 100644
index 0000000..b9a3919
--- /dev/null
+++ b/aoc-2022-dotnet/Day24/Day24.fsproj
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net7.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Content Include="test.txt">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <Content Include="input.txt">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <Compile Include="Program.fs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Common\Common.fsproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/aoc-2022-dotnet/Day24/Program.fs b/aoc-2022-dotnet/Day24/Program.fs
new file mode 100644
index 0000000..0446c06
--- /dev/null
+++ b/aoc-2022-dotnet/Day24/Program.fs
@@ -0,0 +1,107 @@
+module Day24
+
+open System.IO
+open FSharpPlus
+open Common
+
+type Valley =
+ { MaxX: int
+ MaxY: int
+ ExpeditionSuperposition: Set<Vec2>
+ Walls: Set<Vec2>
+ Blizzards: Map<Vec2, Set<Vec2>> }
+
+ static member private empty =
+ { MaxX = 0
+ MaxY = 0
+ ExpeditionSuperposition = Set.empty
+ Walls = Set.empty
+ Blizzards =
+ Vec2.directions4
+ |> Set.map (fun d -> d, Set.empty)
+ |> Map.ofSeq }
+
+ static member private withExpeditionAt target valley =
+ { valley with ExpeditionSuperposition = Set.singleton <| target valley }
+
+ static member private hasReached target valley =
+ Set.contains (target valley) valley.ExpeditionSuperposition
+
+ static member startOf({ MaxY = maxY }) = Vec2(1, maxY)
+ static member endOf({ MaxX = maxX }) = Vec2(maxX - 1, 0)
+
+ static member parse input =
+ input
+ |> Seq.rev
+ |> array2D
+ |> Array2D.mapi (fun r c char -> Vec2(c, r), char)
+ |> Seq.cast
+ |> Seq.fold
+ (fun valley (pos, char) ->
+ match char with
+ | '.' -> valley
+ | '#' ->
+ { valley with
+ Walls = valley.Walls.Add(pos)
+ MaxX = max valley.MaxX pos.x
+ MaxY = max valley.MaxY pos.y }
+ | '^'
+ | '>'
+ | 'v'
+ | '<' ->
+ { valley with Blizzards = valley.Blizzards.Change(Vec2.dirFromChar char, Option.map <| Set.add pos) }
+ | c -> failwithf "Invalid valley character: %c" c)
+ Valley.empty
+ |> Valley.withExpeditionAt Valley.startOf
+
+ member private valley.moveBlizzard offset pos =
+ pos + offset
+ |> Vec2.mapX (Util.wrapInRangeInc 1 (valley.MaxX - 1))
+ |> Vec2.mapY (Util.wrapInRangeInc 1 (valley.MaxY - 1))
+
+ static member private step valley =
+ let valley =
+ { valley with Blizzards = Map.map (fun dir -> Set.map <| valley.moveBlizzard dir) valley.Blizzards }
+
+ { valley with
+ ExpeditionSuperposition =
+ valley.ExpeditionSuperposition
+ |> Seq.collect Vec2.neighbours5
+ |> Seq.filter (fun pos ->
+ pos.y >= 0
+ && pos.y <= valley.MaxY
+ && Util.notIn valley.Walls pos
+ && Seq.forall (not << Set.contains pos) valley.Blizzards.Values)
+ |> Set }
+
+ static member moveTo target valley =
+ Some(valley, 0)
+ |> Seq.unfold (
+ Option.map
+ <| fun (v, m) ->
+ (v, m),
+ match Valley.hasReached target v with
+ | true -> None
+ | false -> Some(Valley.step v, m + 1)
+ )
+ |> Seq.last
+ |> mapItem1 (Valley.withExpeditionAt target)
+
+let solution1 = Valley.parse >> Valley.moveTo Valley.endOf >> snd
+
+let solution2 input =
+ let valley = Valley.parse input
+
+ let (valley, minutes1) = Valley.moveTo Valley.endOf valley
+ let (valley, minutes2) = Valley.moveTo Valley.startOf valley
+ let (_, minutes3) = Valley.moveTo Valley.endOf valley
+
+ minutes1 + minutes2 + minutes3
+
+let test = File.ReadLines("test.txt")
+assert (solution1 test = 18)
+assert (solution2 test = 54)
+
+let input = File.ReadLines("input.txt")
+printfn "%d" <| solution1 input
+printfn "%d" <| solution2 input
diff --git a/aoc-2022-dotnet/README.md b/aoc-2022-dotnet/README.md
index 5bb91e0..34d872f 100644
--- a/aoc-2022-dotnet/README.md
+++ b/aoc-2022-dotnet/README.md
@@ -1,6 +1,6 @@
# Advent of Code 2022 in F#
![F#](https://img.shields.io/badge/F%23-grey?logo=.NET)
-![Stars](https://img.shields.io/badge/🌟%20stars-43/50-orange)
+![Stars](https://img.shields.io/badge/🌟%20stars-45/50-orange)
| Day | Problem Name | Part 1 | Part 2 | Practiced Concepts |
| :----: | ---------------------------------------------------------------- | :----: | :----: | ---------------------------------------------------- |
@@ -23,9 +23,9 @@
| **17** | [Pyroclastic Flow](https://adventofcode.com/2022/day/17) | :star: | :star: | cycle detection, bitwise operators, `hash` |
| **18** | [Boiling Boulders](https://adventofcode.com/2022/day/18) | :star: | :star: | functional DFS, 3D vectors, position bound checking |
| **19** | [Not Enough Minerals](https://adventofcode.com/2022/day/19) | | | |
-| **20** | [Grove Positioning System](https://adventofcode.com/2022/day/20) | :star: | :star: | linked lists, modulo & remainder |
+| **20** | [Grove Positioning System](https://adventofcode.com/2022/day/20) | :star: | :star: | linked lists, remainder |
| **21** | [Monkey Math](https://adventofcode.com/2022/day/21) | :star: | :star: | topological sort, ADTs, complex numbers |
| **22** | [Monkey Map](https://adventofcode.com/2022/day/22) | | | |
| **23** | [Unstable Diffusion](https://adventofcode.com/2022/day/23) | :star: | :star: | `Set`, 2D vectors, `Seq.unfold`, composition |
-| **24** | [Blizzard Basin](https://adventofcode.com/2022/day/24) | | | |
+| **24** | [Blizzard Basin](https://adventofcode.com/2022/day/24) | :star: | :star: | `Set`, 2D vectors, remainder, records |
| **25** | [Full of Hot Air](https://adventofcode.com/2022/day/25) | :star: | | positional numeral systems, recursion |
diff --git a/aoc-2022-dotnet/aoc-2022-dotnet.sln b/aoc-2022-dotnet/aoc-2022-dotnet.sln
index 3b78609..a233bf2 100644
--- a/aoc-2022-dotnet/aoc-2022-dotnet.sln
+++ b/aoc-2022-dotnet/aoc-2022-dotnet.sln
@@ -56,7 +56,9 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Day21", "Day21\Day21.fsproj
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Day25", "Day25\Day25.fsproj", "{3CBC73E3-821C-45FC-85F8-877C29810E67}"
EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Day23", "Day23\Day23.fsproj", "{E6AEEB69-7669-4026-AC28-94D7B4467838}"
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Day23", "Day23\Day23.fsproj", "{E6AEEB69-7669-4026-AC28-94D7B4467838}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Day24", "Day24\Day24.fsproj", "{253313A4-C9F8-4A83-89A5-8FCB426004E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -160,6 +162,10 @@ Global
{E6AEEB69-7669-4026-AC28-94D7B4467838}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6AEEB69-7669-4026-AC28-94D7B4467838}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6AEEB69-7669-4026-AC28-94D7B4467838}.Release|Any CPU.Build.0 = Release|Any CPU
+ {253313A4-C9F8-4A83-89A5-8FCB426004E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {253313A4-C9F8-4A83-89A5-8FCB426004E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {253313A4-C9F8-4A83-89A5-8FCB426004E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {253313A4-C9F8-4A83-89A5-8FCB426004E0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE