├── .gitignore
├── Aoc
├── Aoc.csproj
├── Program.cs
├── ScratchPad.cs
└── appsettings.json
├── Automation
├── Automation.csproj
├── Client
│ └── AocHttpClient.cs
├── Input
│ └── InputProvider.cs
├── Readme
│ ├── FavouriteTable.cs
│ ├── FavouriteTableBuilder.cs
│ ├── FavouriteTableFormatter.cs
│ └── ReadmeUtils.cs
└── Runner
│ └── SolutionRunner.cs
├── LICENSE
├── README.md
├── Solutions
├── Attributes
│ ├── Difficulty.cs
│ ├── InputSpecificSolutionAttribute.cs
│ ├── PuzzleInfoAttribute.cs
│ └── Topics.cs
├── Common
│ ├── NoSolutionException.cs
│ └── SolutionBase.cs
├── Solutions.csproj
├── Y2015
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ ├── Box.cs
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ └── Solution.cs
│ ├── D12
│ │ └── Solution.cs
│ ├── D13
│ │ └── Solution.cs
│ ├── D14
│ │ └── Solution.cs
│ ├── D15
│ │ └── Solution.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ ├── Cup.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ └── Solution.cs
│ ├── D20
│ │ └── Solution.cs
│ ├── D21
│ │ ├── CombatResult.cs
│ │ ├── Gear.cs
│ │ ├── Resolution.cs
│ │ ├── Shop.cs
│ │ ├── Sim.cs
│ │ ├── Solution.cs
│ │ └── Unit.cs
│ ├── D22
│ │ ├── GameData.cs
│ │ ├── Solution.cs
│ │ ├── Spell.cs
│ │ ├── State.cs
│ │ └── Units.cs
│ ├── D23
│ │ ├── Solution.cs
│ │ └── Vm.cs
│ ├── D24
│ │ ├── GroupComparer.cs
│ │ └── Solution.cs
│ └── D25
│ │ └── Solution.cs
├── Y2016
│ ├── Common
│ │ └── Vm.cs
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ ├── Encoding.cs
│ │ └── Solution.cs
│ ├── D07
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ ├── Node.cs
│ │ └── Solution.cs
│ ├── D11
│ │ ├── Device.cs
│ │ ├── DeviceType.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D12
│ │ └── Solution.cs
│ ├── D13
│ │ └── Solution.cs
│ ├── D14
│ │ ├── HashSequence.cs
│ │ └── Solution.cs
│ ├── D15
│ │ ├── Disk.cs
│ │ └── Solution.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ ├── PathCriteria.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ └── Solution.cs
│ ├── D20
│ │ ├── Enums.cs
│ │ └── Solution.cs
│ ├── D21
│ │ └── Solution.cs
│ ├── D22
│ │ ├── PosComparer.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D23
│ │ └── Solution.cs
│ ├── D24
│ │ └── Solution.cs
│ └── D25
│ │ └── Solution.cs
├── Y2017
│ ├── Common
│ │ ├── KnotHash.cs
│ │ └── Vm.cs
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ ├── Solution.cs
│ │ └── Spiral.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ ├── MemoryBankState.cs
│ │ └── Solution.cs
│ ├── D07
│ │ ├── Solution.cs
│ │ └── Tower.cs
│ ├── D08
│ │ ├── Instruction.cs
│ │ ├── Scope.cs
│ │ └── Solution.cs
│ ├── D09
│ │ ├── Scope.cs
│ │ └── Solution.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ └── Solution.cs
│ ├── D12
│ │ └── Solution.cs
│ ├── D13
│ │ ├── Scanner.cs
│ │ └── Solution.cs
│ ├── D14
│ │ └── Solution.cs
│ ├── D15
│ │ └── Solution.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ └── Solution.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ └── Solution.cs
│ ├── D20
│ │ ├── Records.cs
│ │ └── Solution.cs
│ ├── D21
│ │ ├── Pattern.cs
│ │ └── Solution.cs
│ ├── D22
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D23
│ │ ├── Solution.cs
│ │ └── asm.txt
│ ├── D24
│ │ ├── AdapterHelper.cs
│ │ ├── Comparers.cs
│ │ ├── Records.cs
│ │ └── Solution.cs
│ └── D25
│ │ ├── Solution.cs
│ │ └── TuringMachine.cs
├── Y2018
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ └── Solution.cs
│ ├── D12
│ │ ├── Input.cs
│ │ └── Solution.cs
│ ├── D13
│ │ ├── CrashResponse.cs
│ │ ├── Solution.cs
│ │ ├── State.cs
│ │ ├── TickOrderComparer.cs
│ │ └── Track.cs
│ ├── D14
│ │ └── Solution.cs
│ ├── D15
│ │ ├── CombatResult.cs
│ │ ├── GameData.cs
│ │ ├── GameState.cs
│ │ ├── Pathfinding.cs
│ │ ├── Sim.cs
│ │ ├── Solution.cs
│ │ ├── SquareComparer.cs
│ │ ├── Unit.cs
│ │ └── UnitFactory.cs
│ ├── D16
│ │ ├── Cpu.cs
│ │ ├── Observation.cs
│ │ └── Solution.cs
│ ├── D17
│ │ ├── Materials.cs
│ │ ├── Reservoir.cs
│ │ └── Solution.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ ├── Cpu.cs
│ │ ├── Solution.cs
│ │ └── asm.txt
│ ├── D20
│ │ ├── MapChars.cs
│ │ └── Solution.cs
│ ├── D21
│ │ ├── Cpu.cs
│ │ ├── Solution.cs
│ │ └── asm.txt
│ ├── D22
│ │ ├── Cave.cs
│ │ ├── Enums.cs
│ │ ├── Records.cs
│ │ └── Solution.cs
│ ├── D23
│ │ ├── SearchRanking.cs
│ │ └── Solution.cs
│ ├── D24
│ │ ├── Comparers.cs
│ │ ├── Enums.cs
│ │ ├── Group.cs
│ │ ├── Input.cs
│ │ ├── Records.cs
│ │ ├── Sim.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ └── D25
│ │ └── Solution.cs
├── Y2019
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ └── Solution.cs
│ ├── D12
│ │ ├── Moon.cs
│ │ ├── Solution.cs
│ │ ├── State.cs
│ │ └── StateComp.cs
│ ├── D13
│ │ ├── GameObject.cs
│ │ ├── Joystick.cs
│ │ ├── Screen.cs
│ │ └── Solution.cs
│ ├── D14
│ │ ├── Reaction.cs
│ │ ├── Solution.cs
│ │ └── Term.cs
│ ├── D15
│ │ ├── Solution.cs
│ │ └── Tile.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ ├── Records.cs
│ │ ├── RoutineBuilder.cs
│ │ └── Solution.cs
│ ├── D18
│ │ ├── Field.cs
│ │ ├── PathFinder.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D19
│ │ └── Solution.cs
│ ├── D20
│ │ ├── Enums.cs
│ │ ├── PortalKey.cs
│ │ ├── PortalMap.cs
│ │ ├── Records.cs
│ │ └── Solution.cs
│ ├── D21
│ │ ├── Solution.cs
│ │ └── Springdroid.cs
│ ├── D22
│ │ └── Solution.cs
│ ├── D23
│ │ ├── Computer.cs
│ │ ├── Nat.cs
│ │ ├── Network.cs
│ │ ├── NetworkAwaiter.cs
│ │ ├── Packet.cs
│ │ └── Solution.cs
│ ├── D24
│ │ ├── GridType.cs
│ │ └── Solution.cs
│ ├── D25
│ │ ├── Cheats.cs
│ │ └── Solution.cs
│ └── IntCode
│ │ ├── Instruction.cs
│ │ ├── IntCodeSolution.cs
│ │ ├── IntCodeVm.cs
│ │ ├── IntCodeVmFactory.cs
│ │ ├── IntCodeVmOperations.cs
│ │ ├── OpCode.cs
│ │ └── ParameterMode.cs
├── Y2020
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ ├── BagContent.cs
│ │ └── Solution.cs
│ ├── D08
│ │ ├── Machine.cs
│ │ └── Solution.cs
│ ├── D09
│ │ ├── Solution.cs
│ │ └── Sum.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ ├── Concern.cs
│ │ ├── SeatMap.cs
│ │ └── Solution.cs
│ ├── D12
│ │ └── Solution.cs
│ ├── D13
│ │ └── Solution.cs
│ ├── D14
│ │ ├── Machine.cs
│ │ ├── MaskFloating.cs
│ │ ├── MaskSimple.cs
│ │ └── Solution.cs
│ ├── D15
│ │ └── Solution.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ └── Solution.cs
│ ├── D18
│ │ ├── Operators.cs
│ │ └── Solution.cs
│ ├── D19
│ │ └── Solution.cs
│ ├── D20
│ │ ├── EdgeFingerprint.cs
│ │ ├── SeaMonster.cs
│ │ ├── Solution.cs
│ │ └── Tile.cs
│ ├── D21
│ │ ├── Food.cs
│ │ └── Solution.cs
│ ├── D22
│ │ ├── Deck.cs
│ │ └── Solution.cs
│ ├── D23
│ │ └── Solution.cs
│ ├── D24
│ │ └── Solution.cs
│ └── D25
│ │ └── Solution.cs
├── Y2021
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ ├── BitCriteria.cs
│ │ └── Solution.cs
│ ├── D04
│ │ ├── BingoCard.cs
│ │ ├── BingoData.cs
│ │ └── Solution.cs
│ ├── D05
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ └── Solution.cs
│ ├── D08
│ │ ├── DisplayObservation.cs
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ ├── Solution.cs
│ │ └── SyntaxChecker.cs
│ ├── D11
│ │ ├── OctopusGrid.cs
│ │ └── Solution.cs
│ ├── D12
│ │ ├── PathFinder.cs
│ │ └── Solution.cs
│ ├── D13
│ │ ├── FoldType.cs
│ │ ├── Origami.cs
│ │ └── Solution.cs
│ ├── D14
│ │ ├── Rule.cs
│ │ └── Solution.cs
│ ├── D15
│ │ └── Solution.cs
│ ├── D16
│ │ ├── Operator.cs
│ │ ├── Packet.cs
│ │ ├── Section.cs
│ │ └── Solution.cs
│ ├── D17
│ │ └── Solution.cs
│ ├── D18
│ │ ├── Element.cs
│ │ ├── SnailfishParser.cs
│ │ └── Solution.cs
│ ├── D19
│ │ ├── Map.cs
│ │ ├── Records.cs
│ │ └── Solution.cs
│ ├── D20
│ │ └── Solution.cs
│ ├── D21
│ │ ├── DeterministicDie.cs
│ │ ├── Records.cs
│ │ └── Solution.cs
│ ├── D22
│ │ └── Solution.cs
│ ├── D23
│ │ ├── Field.cs
│ │ ├── Input.cs
│ │ ├── Move.cs
│ │ ├── SideRoom.cs
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D24
│ │ ├── Alu.cs
│ │ └── Solution.cs
│ └── D25
│ │ └── Solution.cs
├── Y2022
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ ├── RockPaperScissorsChoice.cs
│ │ ├── RockPaperScissorsHelper.cs
│ │ ├── RockPaperScissorsResult.cs
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ ├── CraneInstruction.cs
│ │ ├── CraneOperator.cs
│ │ ├── CranePlan.cs
│ │ ├── PickupMode.cs
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ ├── Command.cs
│ │ ├── ConsoleParser.cs
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ ├── Cpu.cs
│ │ └── Solution.cs
│ ├── D11
│ │ ├── Monkey.cs
│ │ ├── MonkeyData.cs
│ │ ├── Operator.cs
│ │ └── Solution.cs
│ ├── D12
│ │ └── Solution.cs
│ ├── D13
│ │ ├── ComparisonResult.cs
│ │ ├── IntegerPacketElement.cs
│ │ ├── ListPacketElement.cs
│ │ ├── PacketComparator.cs
│ │ ├── PacketElement.cs
│ │ ├── PacketPair.cs
│ │ ├── PacketParser.cs
│ │ └── Solution.cs
│ ├── D14
│ │ └── Solution.cs
│ ├── D15
│ │ ├── Reporting.cs
│ │ └── Solution.cs
│ ├── D16
│ │ ├── Solution.cs
│ │ ├── Strategy.cs
│ │ ├── StrategyFinder.cs
│ │ └── ValveData.cs
│ ├── D17
│ │ ├── JetPattern.cs
│ │ ├── RockSource.cs
│ │ ├── Rocks.cs
│ │ └── Solution.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ ├── Blueprint.cs
│ │ ├── Inventory.cs
│ │ ├── Materials.cs
│ │ └── Solution.cs
│ ├── D20
│ │ └── Solution.cs
│ ├── D21
│ │ ├── AlgebraicOperation.cs
│ │ ├── Expression.cs
│ │ ├── ExpressionFactory.cs
│ │ ├── Operator.cs
│ │ └── Solution.cs
│ ├── D22
│ │ ├── Instruction.cs
│ │ ├── MapData.cs
│ │ ├── MoveMode.cs
│ │ ├── Solution.cs
│ │ └── Square.cs
│ ├── D23
│ │ ├── MovePreferences.cs
│ │ └── Solution.cs
│ ├── D24
│ │ ├── Blizzards.cs
│ │ ├── Solution.cs
│ │ ├── Storm.cs
│ │ └── Terrain.cs
│ └── D25
│ │ └── Solution.cs
├── Y2023
│ ├── D01
│ │ └── Solution.cs
│ ├── D02
│ │ └── Solution.cs
│ ├── D03
│ │ └── Solution.cs
│ ├── D04
│ │ └── Solution.cs
│ ├── D05
│ │ ├── Almanac.cs
│ │ ├── MapEntry.cs
│ │ ├── MapTable.cs
│ │ └── Solution.cs
│ ├── D06
│ │ └── Solution.cs
│ ├── D07
│ │ ├── Deck.cs
│ │ ├── Hand.cs
│ │ └── Solution.cs
│ ├── D08
│ │ └── Solution.cs
│ ├── D09
│ │ └── Solution.cs
│ ├── D10
│ │ └── Solution.cs
│ ├── D11
│ │ └── Solution.cs
│ ├── D12
│ │ ├── Solution.cs
│ │ └── State.cs
│ ├── D13
│ │ └── Solution.cs
│ ├── D14
│ │ └── Solution.cs
│ ├── D15
│ │ ├── Box.cs
│ │ └── Solution.cs
│ ├── D16
│ │ └── Solution.cs
│ ├── D17
│ │ └── Solution.cs
│ ├── D18
│ │ └── Solution.cs
│ ├── D19
│ │ ├── Rule.cs
│ │ └── Solution.cs
│ ├── D20
│ │ ├── ModuleType.cs
│ │ ├── Network.cs
│ │ ├── Solution.cs
│ │ └── Trace.cs
│ ├── D21
│ │ └── Solution.cs
│ ├── D22
│ │ ├── Brick.cs
│ │ └── Solution.cs
│ ├── D23
│ │ └── Solution.cs
│ ├── D24
│ │ ├── Aabb2.cs
│ │ ├── Ray3.cs
│ │ ├── Solution.cs
│ │ └── Vec3.cs
│ └── D25
│ │ └── Solution.cs
└── Y2024
│ ├── D01
│ └── Solution.cs
│ ├── D02
│ └── Solution.cs
│ ├── D03
│ └── Solution.cs
│ ├── D04
│ └── Solution.cs
│ ├── D05
│ └── Solution.cs
│ ├── D06
│ └── Solution.cs
│ ├── D07
│ └── Solution.cs
│ ├── D08
│ └── Solution.cs
│ ├── D09
│ ├── Disk.cs
│ └── Solution.cs
│ ├── D10
│ └── Solution.cs
│ ├── D11
│ └── Solution.cs
│ ├── D12
│ └── Solution.cs
│ ├── D13
│ └── Solution.cs
│ ├── D14
│ └── Solution.cs
│ ├── D15
│ └── Solution.cs
│ ├── D16
│ └── Solution.cs
│ ├── D17
│ ├── Solution.cs
│ ├── Vm.cs
│ └── asm.text
│ ├── D18
│ └── Solution.cs
│ ├── D19
│ └── Solution.cs
│ ├── D20
│ └── Solution.cs
│ ├── D21
│ ├── Pad.cs
│ └── Solution.cs
│ ├── D22
│ └── Solution.cs
│ ├── D23
│ └── Solution.cs
│ ├── D24
│ └── Solution.cs
│ └── D25
│ └── Solution.cs
├── Utilities.Tests
├── Collections
│ ├── CircularBuffer.Tests.cs
│ ├── CircularLinkedList.Tests.cs
│ ├── DefaultDict.Tests.cs
│ └── DisjointSet.Tests.cs
├── Extensions
│ ├── NumberExtensions.Tests.cs
│ ├── RegexExtensions.Tests.cs
│ └── StringExtensions.Tests.cs
├── Geometry
│ └── Euclidean
│ │ ├── Grid2D.Factory.Tests.cs
│ │ ├── Grid2D.Tests.cs
│ │ ├── Grid2D.Transforms.Tests.cs
│ │ └── Rot3D.Tests.cs
├── Language
│ └── ContextFree
│ │ └── CykParser.Tests.cs
├── Numerics
│ ├── LinearSolver.Tests.cs
│ ├── Numerics.Tests.cs
│ └── Range.Tests.cs
└── Utilities.Tests.csproj
├── Utilities
├── Collections
│ ├── CircularBuffer.cs
│ ├── CircularLinkedList.cs
│ ├── CircularLinkedListNode.cs
│ ├── DefaultDict.cs
│ ├── DisjointSet.cs
│ └── DisjointSetNode.cs
├── Extensions
│ ├── CollectionExtensions.cs
│ ├── NumberExtensions.cs
│ ├── RegexExtensions.cs
│ └── StringExtensions.cs
├── Geometry
│ ├── Euclidean
│ │ ├── Aabb2D.cs
│ │ ├── Aabb3D.cs
│ │ ├── Aabb4D.cs
│ │ ├── Axis.cs
│ │ ├── Degrees.cs
│ │ ├── Grid2D.Factory.cs
│ │ ├── Grid2D.Transforms.cs
│ │ ├── Grid2D.cs
│ │ ├── Metric.cs
│ │ ├── Octree.cs
│ │ ├── Origin.cs
│ │ ├── Pose2D.cs
│ │ ├── Quaternion.cs
│ │ ├── Rot3D.cs
│ │ ├── Vec2D.cs
│ │ ├── Vec3D.cs
│ │ ├── Vec4D.cs
│ │ └── VecThrowHelper.cs
│ └── Hexagonal
│ │ └── Hex.cs
├── Graph
│ ├── BinaryTree.cs
│ ├── BinaryTreeNode.cs
│ ├── BinaryTreePrinter.cs
│ ├── DirectedGraph.cs
│ └── GraphHelper.cs
├── Hashing
│ └── Md5Provider.cs
├── Language
│ └── ContextFree
│ │ ├── CnfConverter.cs
│ │ ├── CykParser.cs
│ │ ├── Grammar.Utilities.cs
│ │ ├── Grammar.cs
│ │ └── Production.cs
├── Numerics
│ ├── LinearSolver.cs
│ ├── Numerics.cs
│ └── Range.cs
└── Utilities.csproj
├── advent-of-code.sln
├── advent-of-code.sln.DotSettings
└── global.json
/Aoc/Aoc.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 | Always
14 |
15 |
16 | Always
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Aoc/ScratchPad.cs:
--------------------------------------------------------------------------------
1 | namespace Aoc;
2 |
3 | public static class ScratchPad
4 | {
5 | public static void Execute()
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/Aoc/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "UserSession": "",
3 | "InputCachePath": "Inputs"
4 | }
--------------------------------------------------------------------------------
/Automation/Automation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Automation/Readme/FavouriteTable.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Attributes;
2 |
3 | namespace Automation.Readme;
4 |
5 | public sealed class FavouriteTable(int year, IEnumerable entries)
6 | {
7 | public readonly record struct Entry(string Title, int Year, int Day, Topics Topics, Difficulty Difficulty);
8 |
9 | public int Year { get; } = year;
10 | public IReadOnlyList Entries { get; } = new List(entries);
11 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Trevor Barker
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Solutions/Attributes/Difficulty.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Attributes;
2 |
3 | public enum Difficulty
4 | {
5 | Easy,
6 | Medium,
7 | Hard
8 | }
--------------------------------------------------------------------------------
/Solutions/Attributes/InputSpecificSolutionAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Attributes;
2 |
3 | [AttributeUsage(validOn: AttributeTargets.Class)]
4 | public sealed class InputSpecificSolutionAttribute(string message) : Attribute
5 | {
6 | private const string DefaultMessage =
7 | "This solution implementation is input specific, and may not work on all inputs";
8 |
9 | public string Message { get; } = message;
10 |
11 | public InputSpecificSolutionAttribute() : this(DefaultMessage)
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Attributes/PuzzleInfoAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Attributes;
2 |
3 | [AttributeUsage(validOn: AttributeTargets.Class)]
4 | public sealed class PuzzleInfoAttribute(string title, Topics topics, Difficulty difficulty, bool favourite = false)
5 | : Attribute
6 | {
7 | public string Title { get; } = title;
8 | public Topics Topics { get; } = topics;
9 | public Difficulty Difficulty { get; } = difficulty;
10 | public bool Favourite { get; } = favourite;
11 | }
--------------------------------------------------------------------------------
/Solutions/Attributes/Topics.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Attributes;
2 |
3 | [Flags]
4 | public enum Topics
5 | {
6 | None = 0,
7 | Vectors = 1 << 0,
8 | Graphs = 1 << 1,
9 | Recursion = 1 << 2,
10 | StringParsing = 1 << 3,
11 | RegularExpressions = 1 << 4,
12 | Assembly = 1 << 5,
13 | Math = 1 << 6,
14 | IntCode = 1 << 7,
15 | BitwiseOperations = 1 << 8,
16 | FormalLanguage = 1 << 9,
17 | Hashing = 1 << 10,
18 | Simulation = 1 << 11
19 | }
--------------------------------------------------------------------------------
/Solutions/Common/NoSolutionException.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Common;
2 |
3 | public sealed class NoSolutionException : Exception
4 | {
5 | private const string NoSolutionErrorText = "No solution exists";
6 |
7 | public NoSolutionException() : base(NoSolutionErrorText)
8 | {
9 | }
10 |
11 | public NoSolutionException(string message) : base(message)
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Solutions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Solutions/Y2015/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D01;
2 |
3 | [PuzzleInfo("Not Quite Lisp", Topics.StringParsing, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var instructions = GetInputText();
9 | return part switch
10 | {
11 | 1 => FollowInstructions(instructions, basement: false),
12 | 2 => FollowInstructions(instructions, basement: true),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int FollowInstructions(string instructions, bool basement)
18 | {
19 | var floor = 0;
20 | var index = 0;
21 |
22 | while (index < instructions.Length)
23 | {
24 | floor += instructions[index++] == '(' ? 1 : -1;
25 |
26 | if (basement && floor == -1)
27 | {
28 | return index;
29 | }
30 | }
31 |
32 | return floor;
33 | }
34 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D02/Box.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D02;
2 |
3 | public readonly struct Box(long l, long w, long h)
4 | {
5 | private long Lw => l * w;
6 | private long Wh => w * h;
7 | private long Lh => l * h;
8 | private long MinFace => new[] { Lw, Wh, Lh }.Min();
9 | private long MinPerimeter => new[] { 2 * l + 2 * w, 2 * w + 2 * h, 2 * l + 2 * h }.Min();
10 | private long Volume => l * w * h;
11 |
12 | public long PaperReq => 2L * Lw + 2L * Wh + 2L * Lh + MinFace;
13 | public long RibbonReq => MinPerimeter + Volume;
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D02/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2015.D02;
4 |
5 | [PuzzleInfo("I Was Told There Would Be No Math", Topics.Math, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | var boxes = input.Select(ParseBox);
12 |
13 | return part switch
14 | {
15 | 1 => boxes.Sum(box => box.PaperReq),
16 | 2 => boxes.Sum(box => box.RibbonReq),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static Box ParseBox(string line)
22 | {
23 | var dims = line.ParseLongs();
24 | return new Box(
25 | l: dims[0],
26 | w: dims[1],
27 | h: dims[2]);
28 | }
29 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D03/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2015.D03;
4 |
5 | [PuzzleInfo("Perfectly Spherical Houses in a Vacuum", Topics.Vectors, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | private static readonly Dictionary Map = new()
9 | {
10 | { '^', Vec2D.Up },
11 | { 'v', Vec2D.Down },
12 | { '<', Vec2D.Left },
13 | { '>', Vec2D.Right }
14 | };
15 |
16 | public override object Run(int part)
17 | {
18 | var steps = GetInputText();
19 | return part switch
20 | {
21 | 1 => CountDistinctAlone(steps),
22 | 2 => CountDistinctPair(steps),
23 | _ => PuzzleNotSolvedString
24 | };
25 | }
26 |
27 | private static int CountDistinctAlone(string steps)
28 | {
29 | var pos = Vec2D.Zero;
30 | var set = new HashSet(collection: [Vec2D.Zero]);
31 |
32 | foreach (var step in steps)
33 | {
34 | pos += Map[step];
35 | set.Add(pos);
36 | }
37 |
38 | return set.Count;
39 | }
40 |
41 | private static int CountDistinctPair(string steps)
42 | {
43 | var set = new HashSet(collection: [Vec2D.Zero]);
44 | var agents = new Dictionary
45 | {
46 | { 0, Vec2D.Zero },
47 | { 1, Vec2D.Zero }
48 | };
49 |
50 | for (var i = 0; i < steps.Length; i++)
51 | {
52 | agents[i % 2] += Map[steps[i]];
53 | set.Add(agents[i % 2]);
54 | }
55 |
56 | return set.Count;
57 | }
58 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D04/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Hashing;
2 |
3 | namespace Solutions.Y2015.D04;
4 |
5 | [PuzzleInfo("The Ideal Stocking Stuffer", Topics.Hashing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var key = GetInputText();
11 | return part switch
12 | {
13 | 1 => FindHashSeed(key, zeroes: 5),
14 | 2 => FindHashSeed(key, zeroes: 6),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int FindHashSeed(string key, int zeroes)
20 | {
21 | using var provider = new Md5Provider();
22 | var hash = string.Empty;
23 | var target = new string(c: '0', zeroes);
24 | var value = 0;
25 |
26 | while (!hash.StartsWith(target))
27 | {
28 | hash = provider.GetHashHex($"{key}{++value}");
29 | }
30 |
31 | return value;
32 | }
33 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D05/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Solutions.Y2015.D05;
4 |
5 | [PuzzleInfo("Doesn't He Have Intern-Elves For This?", Topics.RegularExpressions, Difficulty.Medium, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | private static readonly Regex NiceA = new(pattern:@"^(?=.*(.)\1)(?!.*(ab|cd|pq|xy))(.*[aeiou]){3,}");
9 | private static readonly Regex NiceB = new(pattern:@"^(?=.*(.)(.).*\1\2).*(.).\3");
10 |
11 | public override object Run(int part)
12 | {
13 | var strings = GetInputLines();
14 | return part switch
15 | {
16 | 1 => strings.Count(NiceA.IsMatch),
17 | 2 => strings.Count(NiceB.IsMatch),
18 | _ => PuzzleNotSolvedString
19 | };
20 | }
21 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D08/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Solutions.Y2015.D08;
4 |
5 | [PuzzleInfo("Matchsticks", Topics.RegularExpressions, Difficulty.Medium, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var strings = GetInputLines();
11 | return part switch
12 | {
13 | 1 => strings.Sum(raw => raw.Length - ToInMemory(raw).Length),
14 | 2 => strings.Sum(raw => ToEscaped(raw).Length - raw.Length),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static string ToInMemory(string str)
20 | {
21 | str = Regex.Replace(str, pattern: @"^""", replacement: "");
22 | str = Regex.Replace(str, pattern: @"""$", replacement: "");
23 | str = Regex.Replace(str, pattern: @"\\\\", replacement: "\\");
24 | str = Regex.Replace(str, pattern: @"\\""", replacement: "\"");
25 | str = Regex.Replace(str, pattern: @"\\x[a-f0-9][a-f0-9]", replacement: "U");
26 |
27 | return str;
28 | }
29 |
30 | private static string ToEscaped(string str)
31 | {
32 | str = Regex.Replace(str, pattern: @"\\\\(?!\"")", replacement: "\\\\\\\\");
33 | str = Regex.Replace(str, pattern: @"\\""", replacement: "\\\\\\\"");
34 | str = Regex.Replace(str, pattern: @"\\x[a-f0-9][a-f0-9]", replacement: "\\\\xUU");
35 | str = Regex.Replace(str, pattern: @"^""", replacement: "\"\\\"");
36 | str = Regex.Replace(str, pattern: @"""$", replacement: "\"\\\"");
37 |
38 | return str;
39 | }
40 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D10/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2015.D10;
4 |
5 | [PuzzleInfo("Elves Look, Elves Say", Topics.None, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputText();
11 | var sequence = input
12 | .Select(StringExtensions.AsDigit)
13 | .ToList();
14 |
15 | return part switch
16 | {
17 | 1 => LookAndSay(sequence, rounds: 40),
18 | 2 => LookAndSay(sequence, rounds: 50),
19 | _ => PuzzleNotSolvedString
20 | };
21 | }
22 |
23 | private static int LookAndSay(List sequence, int rounds)
24 | {
25 | for (var i = 0; i < rounds; i++)
26 | {
27 | sequence = LookAndSay(sequence);
28 | }
29 |
30 | return sequence.Count;
31 | }
32 |
33 | private static List LookAndSay(List sequence)
34 | {
35 | var next = new List();
36 | var prev = sequence[0];
37 | var count = 1;
38 |
39 | for (var i = 1; i < sequence.Count; i++)
40 | {
41 | if (sequence[i] == prev)
42 | {
43 | count++;
44 | continue;
45 | }
46 |
47 | next.Add(count);
48 | next.Add(prev);
49 | prev = sequence[i];
50 | count = 1;
51 | }
52 |
53 | next.Add(count);
54 | next.Add(prev);
55 |
56 | return next;
57 | }
58 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D17/Cup.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D17;
2 |
3 | public readonly record struct Cup(int Id, int Size);
--------------------------------------------------------------------------------
/Solutions/Y2015/D17/State.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2015.D17;
4 |
5 | public readonly struct State : IEquatable
6 | {
7 | private readonly string _key;
8 |
9 | public HashSet Unused { get; }
10 | public int NumUsed { get; }
11 | public int TotalVolume { get; }
12 |
13 | public State(HashSet unused, int numUsed, int totalVolume)
14 | {
15 | _key = string.Join(',', unused.OrderBy(c => c.Id));
16 | Unused = unused;
17 | NumUsed = numUsed;
18 | TotalVolume = totalVolume;
19 | }
20 |
21 | public State AfterUsing(Cup cup)
22 | {
23 | return new State(
24 | unused: Unused.Except(cup).ToHashSet(),
25 | numUsed: NumUsed + 1,
26 | totalVolume: TotalVolume + cup.Size);
27 | }
28 |
29 | public bool Equals(State other)
30 | {
31 | return _key == other._key;
32 | }
33 |
34 | public override bool Equals(object? obj)
35 | {
36 | return obj is State other && Equals(other);
37 | }
38 |
39 | public override int GetHashCode()
40 | {
41 | return _key.GetHashCode();
42 | }
43 |
44 | public static bool operator ==(State left, State right)
45 | {
46 | return left.Equals(right);
47 | }
48 |
49 | public static bool operator !=(State left, State right)
50 | {
51 | return !left.Equals(right);
52 | }
53 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D20/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D20;
2 |
3 | [PuzzleInfo("Infinite Elves and Infinite Houses", Topics.Math, Difficulty.Medium, favourite: true)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var input = GetInputText();
9 | var threshold = int.Parse(input);
10 |
11 | return part switch
12 | {
13 | 1 => FindFirstHouse(threshold, factor: 10, limit: int.MaxValue),
14 | 2 => FindFirstHouse(threshold, factor: 11, limit: 50),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int FindFirstHouse(int threshold, int factor, int limit)
20 | {
21 | // Compute an upperbound of houses to compute, then cache the number of presents for all
22 | // houses from 1 to upperbound. Use sieve-like method to compute sum of divisors:
23 | //
24 | var upperBound = threshold / factor + 1;
25 | var presentCounts = new int[upperBound + 1];
26 |
27 | for (var i = 1; i <= upperBound; i++)
28 | {
29 | var delivered = 0;
30 | for (var j = i; j <= upperBound && delivered < limit; j += i)
31 | {
32 | presentCounts[j] += factor * i;
33 | delivered++;
34 | }
35 | }
36 |
37 | for (var i = 1; i < presentCounts.Length; i++)
38 | {
39 | if (presentCounts[i] >= threshold)
40 | {
41 | return i;
42 | }
43 | }
44 |
45 | throw new NoSolutionException();
46 | }
47 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/CombatResult.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public readonly record struct CombatResult(Resolution Resolution, Unit Player);
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/Gear.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public readonly record struct Gear(int Cost, int Damage, int Armor);
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/Resolution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public enum Resolution
4 | {
5 | Win,
6 | Lose
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/Shop.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public static class Shop
4 | {
5 | public static readonly HashSet Weapons =
6 | [
7 | new Gear(Cost: 8, Damage: 4, Armor: 0),
8 | new Gear(Cost: 10, Damage: 5, Armor: 0),
9 | new Gear(Cost: 25, Damage: 6, Armor: 0),
10 | new Gear(Cost: 40, Damage: 7, Armor: 0),
11 | new Gear(Cost: 74, Damage: 8, Armor: 0)
12 | ];
13 |
14 | public static readonly HashSet Armor =
15 | [
16 | new Gear(Cost: 13, Damage: 0, Armor: 1),
17 | new Gear(Cost: 31, Damage: 0, Armor: 2),
18 | new Gear(Cost: 53, Damage: 0, Armor: 3),
19 | new Gear(Cost: 75, Damage: 0, Armor: 4),
20 | new Gear(Cost: 102, Damage: 0, Armor: 5)
21 | ];
22 |
23 | public static readonly HashSet Rings =
24 | [
25 | new Gear(Cost: 25, Damage: 1, Armor: 0),
26 | new Gear(Cost: 50, Damage: 2, Armor: 0),
27 | new Gear(Cost: 100, Damage: 3, Armor: 0),
28 | new Gear(Cost: 20, Damage: 0, Armor: 1),
29 | new Gear(Cost: 40, Damage: 0, Armor: 2),
30 | new Gear(Cost: 80, Damage: 0, Armor: 3)
31 | ];
32 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/Sim.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public static class Sim
4 | {
5 | public static CombatResult Run(Unit player, Unit enemy)
6 | {
7 | while (!player.Dead && !enemy.Dead)
8 | {
9 | enemy = enemy.InflictDamage(Math.Max(1, player.Damage - enemy.Armor));
10 |
11 | if (!enemy.Dead)
12 | {
13 | player = player.InflictDamage(Math.Max(1, enemy.Damage - player.Armor));
14 | }
15 | }
16 |
17 | return new CombatResult(
18 | Resolution: player.Dead ? Resolution.Lose : Resolution.Win,
19 | Player: player);
20 | }
21 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D21/Unit.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D21;
2 |
3 | public readonly struct Unit
4 | {
5 | public int Damage { get; }
6 | public int Armor { get; }
7 | public int GearCost { get; }
8 | public int Hp { get; }
9 |
10 | public bool Dead => Hp <= 0;
11 |
12 | private Unit(int damage, int armor, int gearCost, int hp)
13 | {
14 | Damage = damage;
15 | Armor = armor;
16 | GearCost = gearCost;
17 | Hp = hp;
18 | }
19 |
20 | public Unit InflictDamage(int damage)
21 | {
22 | return new Unit(
23 | damage: Damage,
24 | armor: Armor,
25 | gearCost: GearCost,
26 | hp: Math.Max(0, Hp - damage));
27 | }
28 |
29 | public static Unit Spawn(ICollection gear, int hp)
30 | {
31 | return new Unit(
32 | damage: gear.Sum(g => g.Damage),
33 | armor: gear.Sum(g => g.Armor),
34 | gearCost: gear.Sum(g => g.Cost),
35 | hp: hp);
36 | }
37 |
38 | public static Unit Spawn(int damage, int armor, int hp)
39 | {
40 | return new Unit(damage, armor, gearCost: 0, hp);
41 | }
42 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D22/GameData.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D22;
2 |
3 | public static class GameData
4 | {
5 | public static readonly HashSet Spells =
6 | [
7 | Spell.MagicMissile,
8 | Spell.Drain,
9 | Spell.Shield,
10 | Spell.Poison,
11 | Spell.Recharge
12 | ];
13 |
14 | public static readonly Dictionary SpellCosts = new()
15 | {
16 | { Spell.MagicMissile, 53 },
17 | { Spell.Drain, 73 },
18 | { Spell.Shield, 113 },
19 | { Spell.Poison, 173 },
20 | { Spell.Recharge, 229 }
21 | };
22 |
23 | public static readonly Dictionary SpellDurations = new()
24 | {
25 | { Spell.MagicMissile, 0 },
26 | { Spell.Drain, 0 },
27 | { Spell.Shield, 6 },
28 | { Spell.Poison, 6 },
29 | { Spell.Recharge, 5 }
30 | };
31 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D22/Spell.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D22;
2 |
3 | public enum Spell
4 | {
5 | MagicMissile,
6 | Drain,
7 | Shield,
8 | Poison,
9 | Recharge
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D22/Units.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D22;
2 |
3 | public readonly record struct Boss(int Hp, int Dmg);
4 | public readonly record struct Wizard(int Hp, int Mana, int Armor);
--------------------------------------------------------------------------------
/Solutions/Y2015/D23/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D23;
2 |
3 | [PuzzleInfo("Opening the Turing Lock", Topics.Assembly|Topics.Simulation, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | return part switch
9 | {
10 | 1 => Emulate(a: 0L),
11 | 2 => Emulate(a: 1L),
12 | _ => PuzzleNotSolvedString
13 | };
14 | }
15 |
16 | private long Emulate(long a)
17 | {
18 | var program = GetInputLines();
19 | var vm = new Vm { ['a'] = a };
20 |
21 | vm.Run(program);
22 | return vm['b'];
23 | }
24 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D24/GroupComparer.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2015.D24;
2 |
3 | public sealed class GroupComparer : IComparer?>
4 | {
5 | public static GroupComparer Instance { get; } = new();
6 |
7 | private GroupComparer()
8 | {
9 | }
10 |
11 | public int Compare(HashSet? x, HashSet? y)
12 | {
13 | if (x!.Count != y!.Count)
14 | {
15 | return x.Count.CompareTo(y.Count);
16 | }
17 |
18 | var xS = x.Aggregate(seed: 1L, (i, j) => i * j);
19 | var yS = y.Aggregate(seed: 1L, (i, j) => i * j);
20 |
21 | return xS.CompareTo(yS);
22 | }
23 | }
--------------------------------------------------------------------------------
/Solutions/Y2015/D25/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2015.D25;
4 |
5 | [PuzzleInfo("Let It Snow", Topics.Math, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override int Parts => 1;
9 |
10 | public override object Run(int part)
11 | {
12 | return part switch
13 | {
14 | 1 => GetCode(),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private ulong GetCode()
20 | {
21 | var input = GetInputText();
22 | var numbers = input.ParseInts();
23 | var tx = (ulong)numbers[1];
24 | var ty = (ulong)numbers[0];
25 |
26 | var x = 1UL;
27 | var y = 1UL;
28 | var code = 20151125UL;
29 |
30 | while (true)
31 | {
32 | x += 1;
33 | y -= 1;
34 |
35 | if (y == 0)
36 | {
37 | y = x;
38 | x = 1;
39 | }
40 |
41 | code = GetNextCode(code);
42 | if (x == tx && y == ty)
43 | {
44 | break;
45 | }
46 | }
47 |
48 | return code;
49 | }
50 |
51 | private static ulong GetNextCode(ulong prev)
52 | {
53 | return 252533UL * prev % 33554393UL;
54 | }
55 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 | using Utilities.Geometry.Euclidean;
3 |
4 | namespace Solutions.Y2016.D01;
5 |
6 | [PuzzleInfo("No Time for a Taxicab", Topics.Vectors, Difficulty.Easy)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override object Run(int part)
10 | {
11 | var input = GetInputText();
12 | var steps = input.Split(separator: ", ", StringSplitOptions.RemoveEmptyEntries);
13 |
14 | return part switch
15 | {
16 | 1 => GetMinDistance(steps, haltOnRepeat: false),
17 | 2 => GetMinDistance(steps, haltOnRepeat: true),
18 | _ => PuzzleNotSolvedString
19 | };
20 | }
21 |
22 | private static int GetMinDistance(IEnumerable steps, bool haltOnRepeat)
23 | {
24 | var pose = new Pose2D(Pos: Vec2D.Zero, Face: Vec2D.Up);
25 | var visited = new HashSet();
26 |
27 | foreach (var step in steps)
28 | {
29 | pose = step[0] switch
30 | {
31 | 'L' => pose.Turn(Rot3D.P90Z),
32 | 'R' => pose.Turn(Rot3D.N90Z),
33 | _ => pose
34 | };
35 |
36 | for (var i = 0; i < step.ParseInt(); i++)
37 | {
38 | pose = pose.Step();
39 | if (haltOnRepeat && !visited.Add(pose.Pos))
40 | {
41 | return pose.Pos.Magnitude(metric: Metric.Taxicab);
42 | }
43 | }
44 | }
45 |
46 | return pose.Pos.Magnitude(metric: Metric.Taxicab);
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D03/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2016.D03;
4 |
5 | [PuzzleInfo("Square With Three Sides", Topics.Math, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => CountValidHorizontal(),
13 | 2 => CountValidVertical(),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private int CountValidHorizontal()
19 | {
20 | var input = GetInputLines();
21 | return input
22 | .Select(StringExtensions.ParseInts)
23 | .Count(IsValidTriangle);
24 | }
25 |
26 | private int CountValidVertical()
27 | {
28 | var count = 0;
29 | var numbers = GetInputLines()
30 | .Select(line => line.ParseInts())
31 | .ToList();
32 |
33 | var lengths = new int[3];
34 | for (var i = 0; i < numbers.Count; i += 3)
35 | for (var c = 0; c < 3; c++)
36 | {
37 | lengths[0] = numbers[i + 0][c];
38 | lengths[1] = numbers[i + 1][c];
39 | lengths[2] = numbers[i + 2][c];
40 |
41 | if (IsValidTriangle(lengths))
42 | {
43 | count++;
44 | }
45 | }
46 |
47 | return count;
48 | }
49 |
50 | private static bool IsValidTriangle(IList lengths)
51 | {
52 | return
53 | lengths[0] + lengths[1] > lengths[2] &&
54 | lengths[0] + lengths[2] > lengths[1] &&
55 | lengths[1] + lengths[2] > lengths[0];
56 | }
57 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D06/Encoding.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D06;
2 |
3 | public enum Encoding
4 | {
5 | MostCommon,
6 | LeastCommon
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D06/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using Utilities.Collections;
3 |
4 | namespace Solutions.Y2016.D06;
5 |
6 | [PuzzleInfo("Signals and Noise", Topics.StringParsing, Difficulty.Medium)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override object Run(int part)
10 | {
11 | return part switch
12 | {
13 | 1 => ParseMessage(encoding: Encoding.MostCommon),
14 | 2 => ParseMessage(encoding: Encoding.LeastCommon),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private string ParseMessage(Encoding encoding)
20 | {
21 | var message = new StringBuilder();
22 | var messages = GetInputLines();
23 | var columnCounts = new DefaultDict>(
24 | defaultSelector: _ => new DefaultDict(defaultValue: 0));
25 |
26 | for (var j = 0; j < messages[0].Length; j++)
27 | for (var i = 0; i < messages.Length; i++)
28 | {
29 | columnCounts[j][messages[i][j]]++;
30 | }
31 |
32 | for (var i = 0; i < messages[0].Length; i++)
33 | {
34 | var counts = columnCounts[i];
35 | var letter = encoding switch
36 | {
37 | Encoding.MostCommon => counts.Keys.MaxBy(c => counts[c]),
38 | Encoding.LeastCommon => counts.Keys.MinBy(c => counts[c]),
39 | _ => throw new NoSolutionException()
40 | };
41 |
42 | message.Append(letter);
43 | }
44 |
45 | return message.ToString();
46 | }
47 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D10/Node.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D10;
2 |
3 | public sealed class Node
4 | {
5 | public string Id { get; }
6 | public string? Low { get; init; }
7 | public string? High { get; init; }
8 | public HashSet Values { get; } = new(capacity: 2);
9 |
10 | public bool Ready => Values.Count == 2;
11 |
12 | public Node(string id)
13 | {
14 | Id = id;
15 | }
16 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D11/Device.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D11;
2 |
3 | public readonly record struct Device(DeviceType Type, string Element, int Floor);
--------------------------------------------------------------------------------
/Solutions/Y2016/D11/DeviceType.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D11;
2 |
3 | public enum DeviceType
4 | {
5 | Microchip,
6 | Generator
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D12/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2016.Common;
2 |
3 | namespace Solutions.Y2016.D12;
4 |
5 | [PuzzleInfo("Leonardo's Monorail", Topics.Assembly|Topics.Simulation, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | var tokens = input.Select(line => line.Split(' ')).ToList();
12 |
13 | return part switch
14 | {
15 | 1 => RunProgram(tokens, c: 0),
16 | 2 => RunProgram(tokens, c: 1),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static long RunProgram(IList program, long c)
22 | {
23 | var vm = new Vm { ["c"] = c };
24 | vm.Run(program);
25 | return vm["a"];
26 | }
27 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D14/HashSequence.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Hashing;
2 |
3 | namespace Solutions.Y2016.D14;
4 |
5 | public sealed class HashSequence : IDisposable
6 | {
7 | private readonly string _salt;
8 | private readonly int _stretches;
9 | private readonly List _hashes = [];
10 | private readonly Md5Provider _md5Provider = new();
11 |
12 | public string this[int i] => GetHashInternal(i);
13 |
14 | public HashSequence(string salt, int stretches, int count)
15 | {
16 | _salt = salt;
17 | _stretches = stretches;
18 | EnsureContains(count);
19 | }
20 |
21 | private string GetHashInternal(int i)
22 | {
23 | EnsureContains(count: i + 1);
24 | return _hashes[i];
25 | }
26 |
27 | private void EnsureContains(int count)
28 | {
29 | while(_hashes.Count < count)
30 | {
31 | var input = $"{_salt}{_hashes.Count}";
32 | var hash = _md5Provider.GetHashHex(input);
33 |
34 | for (var i = 0; i < _stretches; i++)
35 | {
36 | hash = _md5Provider.GetHashHex(hash);
37 | }
38 |
39 | _hashes.Add(hash);
40 | }
41 | }
42 |
43 | public void Dispose()
44 | {
45 | _md5Provider.Dispose();
46 | }
47 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D14/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Solutions.Y2016.D14;
4 |
5 | [PuzzleInfo("One-Time Pad", Topics.Hashing, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | private static readonly Regex CandidateRegex = new(@"(?[a-z0-9])\1\1");
9 |
10 | public override object Run(int part)
11 | {
12 | var salt = GetInputText();
13 | return part switch
14 | {
15 | 1 => FindKeys(salt, stretches: 0000, count: 64),
16 | 2 => FindKeys(salt, stretches: 2016, count: 64),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int FindKeys(string salt, int stretches, int count)
22 | {
23 | using var hashes = new HashSequence(salt, stretches, count: 1000);
24 | var indices = new List();
25 |
26 | for (var i = 0; indices.Count < count; i++)
27 | {
28 | var candidate = hashes[i];
29 | var match = CandidateRegex.Match(candidate);
30 |
31 | if (!match.Success)
32 | {
33 | continue;
34 | }
35 |
36 | var repeat = match.Groups["C"].Value.Single();
37 | var need = new string(repeat, count: 5);
38 |
39 | for (var j = 1; j <= 1000; j++)
40 | {
41 | if (hashes[i + j].Contains(need))
42 | {
43 | indices.Add(i);
44 | break;
45 | }
46 | }
47 | }
48 |
49 | return indices.Last();
50 | }
51 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D15/Disk.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D15;
2 |
3 | public readonly record struct Disk(int Depth, int Positions, int Initial);
--------------------------------------------------------------------------------
/Solutions/Y2016/D16/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D16;
2 |
3 | [PuzzleInfo("Dragon Checksum", Topics.BitwiseOperations, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var input = GetInputText();
9 | var data = input.Select(c => c == '1').ToList();
10 |
11 | return part switch
12 | {
13 | 1 => Validate(data, length: 272),
14 | 2 => Validate(data, length: 35651584),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static string Validate(List data, int length)
20 | {
21 | while (data.Count < length)
22 | {
23 | data = Generate(data);
24 | }
25 |
26 | var trimmed = data.Take(length).ToList();
27 | var checksum = Checksum(trimmed);
28 |
29 | while (checksum.Count % 2 == 0)
30 | {
31 | checksum = Checksum(checksum);
32 | }
33 |
34 | return string.Concat(checksum.Select(b => b ? '1' : '0'));
35 | }
36 |
37 | private static List Generate(IList data)
38 | {
39 | var b = data
40 | .Reverse()
41 | .Select(bit => !bit);
42 |
43 | return data
44 | .Append(false)
45 | .Concat(b)
46 | .ToList();
47 | }
48 |
49 | private static List Checksum(List data)
50 | {
51 | var checksum = new List();
52 | for (var i = 0; i < data.Count; i += 2)
53 | {
54 | checksum.Add(!(data[i] ^ data[i + 1]));
55 | }
56 |
57 | return checksum;
58 | }
59 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D17/PathCriteria.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D17;
2 |
3 | public enum PathCriteria
4 | {
5 | Shortest,
6 | Longest
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D17/State.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2016.D17;
4 |
5 | public readonly record struct State(Vec2D Pos, string Path);
--------------------------------------------------------------------------------
/Solutions/Y2016/D19/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 |
3 | namespace Solutions.Y2016.D19;
4 |
5 | [PuzzleInfo("An Elephant Named Joseph", Topics.Math, Difficulty.Hard, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputText();
11 | var count = int.Parse(input);
12 |
13 | return part switch
14 | {
15 | 1 => FindWinnerAdjacent(count),
16 | 2 => FindWinnerAcross(count),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int FindWinnerAdjacent(int count)
22 | {
23 | var members = new CircularLinkedList(Enumerable.Range(1, count));
24 | var current = members.Head;
25 |
26 | while (members.Count > 1)
27 | {
28 | members.Remove(current!.Next!);
29 | current = current.Next;
30 | }
31 |
32 | return current!.Value;
33 | }
34 |
35 | private static int FindWinnerAcross(int count)
36 | {
37 | var members = new CircularLinkedList(Enumerable.Range(1, count));
38 | var across = members.Head;
39 |
40 | for (var i = 0; i < count / 2; i++)
41 | {
42 | across = across!.Next;
43 | }
44 |
45 | while (members.Count > 1)
46 | {
47 | var tmp = members.Count % 2 == 1
48 | ? across!.Next!.Next
49 | : across!.Next;
50 |
51 | members.Remove(across);
52 | across = tmp;
53 | }
54 |
55 | return across!.Value;
56 | }
57 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D20/Enums.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2016.D20;
2 |
3 | public enum EndpointType
4 | {
5 | Start,
6 | End
7 | }
8 |
9 | public enum Target
10 | {
11 | LowestValid,
12 | TotalAllowed
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2016/D22/PosComparer.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2016.D22;
4 |
5 | public sealed class PosComparer : IComparer
6 | {
7 | public int Compare(Vec2D x, Vec2D y)
8 | {
9 | var xComparison = x.X.CompareTo(y.X);
10 | return xComparison != 0
11 | ? xComparison : x.Y.CompareTo(y.Y);
12 | }
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2017.D01;
4 |
5 | [PuzzleInfo("Inverse Captcha", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var stream = GetInputText();
11 | return part switch
12 | {
13 | 1 => SumDigits(stream, steps: 1),
14 | 2 => SumDigits(stream, steps: stream.Length / 2),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int SumDigits(string stream, int steps)
20 | {
21 | return stream
22 | .Where((chr, i) => chr == stream[(i + steps) % stream.Length])
23 | .Sum(StringExtensions.AsDigit);
24 | }
25 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D02/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2017.D02;
4 |
5 | [PuzzleInfo("Corruption Checksum", Topics.Math, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var table = ParseInputLines(parseFunc: StringExtensions.ParseInts);
11 | return part switch
12 | {
13 | 1 => GetChecksum(table),
14 | 2 => GetDivisorSum(table),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int GetChecksum(IEnumerable> table)
20 | {
21 | return table.Sum(row => row.Max() - row.Min());
22 | }
23 |
24 | private static int GetDivisorSum(IEnumerable> table)
25 | {
26 | return table.Sum(row =>
27 | {
28 | for (var i = 0; i < row.Count; i++)
29 | for (var j = 0; j < row.Count; j++)
30 | {
31 | if (i == j)
32 | {
33 | continue;
34 | }
35 |
36 | if (row[i] % row[j] == 0)
37 | {
38 | return row[i] / row[j];
39 | }
40 | }
41 |
42 | throw new NoSolutionException();
43 | });
44 | }
45 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D03/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2017.D03;
4 |
5 | [PuzzleInfo("Spiral Memory", Topics.Vectors, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputText();
11 | var value = int.Parse(input);
12 |
13 | return part switch
14 | {
15 | 1 => GetDistanceToOrigin(value),
16 | 2 => GetFirstLargerValue(value),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int GetDistanceToOrigin(int square)
22 | {
23 | var memory = new Spiral();
24 | memory.Build(
25 | valueFunc: spiral => spiral.LastVal + 1,
26 | stopFunc: spiral => spiral.LastVal == square);
27 |
28 | return memory.LastPos.Magnitude(Metric.Taxicab);
29 | }
30 |
31 | private static int GetFirstLargerValue(int threshold)
32 | {
33 | var memory = new Spiral();
34 | memory.Build(
35 | valueFunc: spiral => spiral.NextPos.GetAdjacentSet(Metric.Chebyshev).Sum(pos => spiral[pos]),
36 | stopFunc: spiral => spiral.LastVal > threshold);
37 |
38 | return memory.LastVal;
39 | }
40 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D03/Spiral.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 | using Utilities.Geometry.Euclidean;
3 |
4 | namespace Solutions.Y2017.D03;
5 |
6 | public sealed class Spiral
7 | {
8 | private readonly DefaultDict _memory;
9 | private Vec2D _dir = Vec2D.Right;
10 |
11 | public int LastVal { get; private set; }
12 | public Vec2D LastPos { get; private set; }
13 | public Vec2D NextPos => LastPos + _dir;
14 |
15 | public int this[Vec2D pos]
16 | {
17 | get => _memory[pos];
18 | private set => StoreValue(pos, value);
19 | }
20 |
21 | public Spiral()
22 | {
23 | _memory = new DefaultDict(defaultValue: 0);
24 | StoreValue(pos: Vec2D.Zero, value: 1);
25 | }
26 |
27 | public void Build(Func valueFunc, Predicate stopFunc)
28 | {
29 | var stepsBeforeTurn = 1;
30 | var stepsSinceLastTurn = 0;
31 | var turnsSinceStepIncrement = 0;
32 |
33 | while (!stopFunc(this))
34 | {
35 | if (stepsSinceLastTurn == stepsBeforeTurn)
36 | {
37 | _dir = Rot3D.P90Z.Transform(_dir);
38 | stepsSinceLastTurn = 0;
39 | turnsSinceStepIncrement++;
40 | }
41 |
42 | if (turnsSinceStepIncrement == 2)
43 | {
44 | stepsBeforeTurn++;
45 | turnsSinceStepIncrement = 0;
46 | }
47 |
48 | stepsSinceLastTurn++;
49 | this[LastPos + _dir] = valueFunc(this);
50 | }
51 | }
52 |
53 | private void StoreValue(Vec2D pos, int value)
54 | {
55 | _memory[pos] = value;
56 | LastVal = value;
57 | LastPos = pos;
58 | }
59 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D04/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D04;
2 |
3 | using Passphrase = IList;
4 |
5 | [PuzzleInfo("High-Entropy Passphrases", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var passphrases = ParseInputLines(parseFunc: ParsePassphrase);
11 | return part switch
12 | {
13 | 1 => CountValidDistinctWords(passphrases),
14 | 2 => CountValidNoAnagrams(passphrases),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int CountValidDistinctWords(IEnumerable passphrases)
20 | {
21 | return passphrases.Count(passphrase => new HashSet(passphrase).Count == passphrase.Count);
22 | }
23 |
24 | private static int CountValidNoAnagrams(IEnumerable passphrases)
25 | {
26 | return passphrases.Count(passphrase =>
27 | {
28 | var sortedWords = passphrase.Select(word => string.Concat(word.Order()));
29 | var sortedSet = new HashSet(sortedWords);
30 |
31 | return sortedSet.Count == passphrase.Count;
32 | });
33 | }
34 |
35 | private static Passphrase ParsePassphrase(string line)
36 | {
37 | return line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
38 | }
39 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D05/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D05;
2 |
3 | [PuzzleInfo("A Maze of Twisty Trampolines, All Alike", Topics.Simulation, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var offsets = ParseInputLines(parseFunc: int.Parse);
9 | return part switch
10 | {
11 | 1 => CountSteps(offsets, offsetModifier: offset => offset + 1),
12 | 2 => CountSteps(offsets, offsetModifier: offset => offset >= 3 ? offset - 1 : offset + 1),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int CountSteps(int[] offsets, Func offsetModifier)
18 | {
19 | var ip = 0;
20 | var steps = 0;
21 |
22 | while (ip >= 0 && ip < offsets.Length)
23 | {
24 | var jumpTo = ip + offsets[ip];
25 | offsets[ip] = offsetModifier(offsets[ip]);
26 | steps++;
27 | ip = jumpTo;
28 | }
29 |
30 | return steps;
31 | }
32 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D06/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2017.D06;
4 |
5 | [PuzzleInfo("Memory Reallocation", Topics.BitwiseOperations, Difficulty.Easy, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputText();
11 | var state = ParseState(input);
12 |
13 | return part switch
14 | {
15 | 1 => GetCycle(state),
16 | 2 => GetLoopLength(state),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int GetCycle(MemoryBankState state)
22 | {
23 | var seen = new HashSet();
24 | while (seen.Add(state))
25 | {
26 | state = state.Reallocate();
27 | }
28 |
29 | return seen.Count;
30 | }
31 |
32 | private static int GetLoopLength(MemoryBankState state)
33 | {
34 | var seen = new HashSet();
35 | while (seen.Add(state))
36 | {
37 | state = state.Reallocate();
38 | }
39 |
40 | seen.Clear();
41 | while (seen.Add(state))
42 | {
43 | state = state.Reallocate();
44 | }
45 |
46 | return seen.Count;
47 | }
48 |
49 | private static MemoryBankState ParseState(string input)
50 | {
51 | var ulongs = input
52 | .ParseLongs()
53 | .Select(l => (ulong)l)
54 | .ToList();
55 |
56 | return new MemoryBankState(banks: ulongs);
57 | }
58 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D08/Instruction.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D08;
2 |
3 | public readonly record struct Instruction(string DestReg, string DestOp, int DestArg, string SrcReg, string CheckOp, int CheckArg);
--------------------------------------------------------------------------------
/Solutions/Y2017/D08/Scope.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D08;
2 |
3 | public enum Scope
4 | {
5 | Halted = 0,
6 | Lifetime
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D09/Scope.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D09;
2 |
3 | public enum Scope
4 | {
5 | Group = 0,
6 | Garbage
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D10/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2017.Common;
2 | using Utilities.Extensions;
3 |
4 | namespace Solutions.Y2017.D10;
5 |
6 | [PuzzleInfo("Knot Hash", Topics.Hashing, Difficulty.Medium)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override object Run(int part)
10 | {
11 | var input = GetInputText();
12 | return part switch
13 | {
14 | 1 => HashSimple(input),
15 | 2 => HashFull(input),
16 | _ => PuzzleNotSolvedString
17 | };
18 | }
19 |
20 | private static int HashSimple(string input)
21 | {
22 | var lengths = GetByteArray(input.ParseInts());
23 | var ring = GetByteArray(Enumerable.Range(0, 256));
24 | var result = KnotHash.TieKnots(ring, lengths, rounds: 1);
25 |
26 | return result[0] * result[1];
27 | }
28 |
29 | private static string HashFull(string input)
30 | {
31 | return KnotHash.ComputeHash(input);
32 | }
33 |
34 | private static byte[] GetByteArray(IEnumerable ints)
35 | {
36 | return ints.Select(n => (byte)n).ToArray();
37 | }
38 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D11/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Hexagonal;
2 |
3 | namespace Solutions.Y2017.D11;
4 |
5 | [PuzzleInfo("Hex Ed", Topics.Math, Difficulty.Easy, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | private static readonly Dictionary Directions = new()
9 | {
10 | { "n", Hex.Directions[Hex.Flat.N] },
11 | { "ne", Hex.Directions[Hex.Flat.Ne] },
12 | { "se", Hex.Directions[Hex.Flat.Se] },
13 | { "s", Hex.Directions[Hex.Flat.S] },
14 | { "sw", Hex.Directions[Hex.Flat.Sw] },
15 | { "nw", Hex.Directions[Hex.Flat.Nw] }
16 | };
17 |
18 | public override object Run(int part)
19 | {
20 | var input = GetInputText();
21 | var steps = input.Split(separator: ',');
22 |
23 | return part switch
24 | {
25 | 1 => GetEndDistance(steps),
26 | 2 => GetMaxDistance(steps),
27 | _ => PuzzleNotSolvedString
28 | };
29 | }
30 |
31 | private static int GetEndDistance(IEnumerable steps)
32 | {
33 | var end = steps.Aggregate(
34 | seed: Hex.Zero,
35 | func: (pos, step) => pos + Directions[step]);
36 |
37 | return end.Magnitude;
38 | }
39 |
40 | private static int GetMaxDistance(IEnumerable steps)
41 | {
42 | var pos = Hex.Zero;
43 | var set = new HashSet();
44 |
45 | foreach (var step in steps)
46 | {
47 | pos += Directions[step];
48 | set.Add(pos);
49 | }
50 |
51 | return set.Max(p => p.Magnitude);
52 | }
53 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D13/Scanner.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D13;
2 |
3 | public readonly struct Scanner(int depth, int range)
4 | {
5 | public int Depth { get; } = depth;
6 | public int Range { get; } = range;
7 |
8 | public int Severity => Depth * Range;
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D13/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2017.D13;
4 |
5 | [PuzzleInfo("Packet Scanners", Topics.Math, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | var scanners = new List(input.Select(ParseScanner));
12 |
13 | return part switch
14 | {
15 | 1 => GetSeverity(scanners, delay: 0),
16 | 2 => FindDelay(scanners),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int FindDelay(IList scanners)
22 | {
23 | var delay = 0;
24 | while (scanners.Any(s => IsCaught(scanner: s, time: s.Depth + delay)))
25 | {
26 | delay++;
27 | }
28 | return delay;
29 | }
30 |
31 | private static int GetSeverity(IEnumerable scanners, int delay)
32 | {
33 | return scanners
34 | .Where(s => IsCaught(scanner: s, time: s.Depth + delay))
35 | .Sum(s => s.Severity);
36 | }
37 |
38 | private static bool IsCaught(Scanner scanner, int time)
39 | {
40 | return time % (scanner.Range - 1) == 0 && time / (scanner.Range - 1) % 2 == 0;
41 | }
42 |
43 | private static Scanner ParseScanner(string line)
44 | {
45 | var numbers = line.ParseInts();
46 | return new Scanner(
47 | depth: numbers[0],
48 | range: numbers[1]);
49 | }
50 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D20/Records.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2017.D20;
4 |
5 | public readonly record struct Particle(Vec3D Pos, Vec3D Vel, Vec3D Acc);
6 | public readonly record struct Collision(int Tick, Vec3D Pos);
--------------------------------------------------------------------------------
/Solutions/Y2017/D22/State.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D22;
2 |
3 | public enum State
4 | {
5 | Clean = 0,
6 | Weakened,
7 | Infected,
8 | Flagged
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D24/AdapterHelper.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 |
3 | namespace Solutions.Y2017.D24;
4 |
5 | public sealed class AdapterHelper
6 | {
7 | private readonly Dictionary _adapters = new();
8 | private readonly DefaultDict> _compatibilities = new(defaultSelector: _ => []);
9 |
10 | public IReadOnlySet GetCompatibilities(int port, HashSet used)
11 | {
12 | return _compatibilities[port]
13 | .Where(c => !used.Contains(c.ViaAdapter))
14 | .ToHashSet();
15 | }
16 |
17 | public int GetStrength(IEnumerable used)
18 | {
19 | return used.Sum(key => _adapters[key].Port1 + _adapters[key].Port2);
20 | }
21 |
22 | public void RegisterAdapter(Adapter adapter)
23 | {
24 | var compatibility1 = new Compatibility(ResultingPort: adapter.Port1, ViaAdapter: adapter.Key);
25 | var compatibility2 = new Compatibility(ResultingPort: adapter.Port2, ViaAdapter: adapter.Key);
26 |
27 | _adapters[adapter.Key] = adapter;
28 | _compatibilities[adapter.Port1].Add(compatibility2);
29 | _compatibilities[adapter.Port2].Add(compatibility1);
30 | }
31 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D24/Comparers.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D24;
2 |
3 | public sealed class StrengthComparer : IComparer
4 | {
5 | public int Compare(Bridge x, Bridge y)
6 | {
7 | return x.Strength.CompareTo(y.Strength);
8 | }
9 | }
10 |
11 | public sealed class LengthComparer : IComparer
12 | {
13 | public int Compare(Bridge x, Bridge y)
14 | {
15 | return x.Length != y.Length
16 | ? x.Length.CompareTo(y.Length)
17 | : x.Strength.CompareTo(y.Length);
18 | }
19 | }
--------------------------------------------------------------------------------
/Solutions/Y2017/D24/Records.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D24;
2 |
3 | public readonly record struct Adapter(string Key, int Port1, int Port2);
4 | public readonly record struct Bridge(int Strength, int Length);
5 | public readonly record struct Compatibility(int ResultingPort, string ViaAdapter);
--------------------------------------------------------------------------------
/Solutions/Y2017/D25/TuringMachine.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2017.D25;
2 |
3 | public sealed class TuringMachine(IReadOnlyDictionary ruleTable)
4 | {
5 | public readonly record struct Transition(bool Write, int Move, char Next);
6 | public readonly record struct State(Transition False, Transition True);
7 |
8 | private readonly Dictionary _tape = new();
9 | private int _cursor;
10 |
11 | public int Run(char state, int steps)
12 | {
13 | _tape.Clear();
14 | _cursor = 0;
15 |
16 | for (var i = 0; i < steps; i++)
17 | {
18 | var rule = ruleTable[state];
19 | var transition = _tape.TryGetValue(_cursor, out var value) && value
20 | ? rule.True
21 | : rule.False;
22 |
23 | _tape[_cursor] = transition.Write;
24 | _cursor += transition.Move;
25 |
26 | state = transition.Next;
27 | }
28 |
29 | return _tape.Values.Count(b => b);
30 | }
31 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D01;
2 |
3 | [PuzzleInfo("Chronal Calibration", Topics.Math, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var numbers = ParseInputLines(int.Parse);
9 | return part switch
10 | {
11 | 1 => numbers.Sum(),
12 | 2 => GetFirstRepeatedFrequency(numbers),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int GetFirstRepeatedFrequency(int[] numbers)
18 | {
19 | var i = 0;
20 | var freq = 0;
21 | var seen = new HashSet();
22 |
23 | while (seen.Add(freq))
24 | {
25 | freq += numbers[i];
26 | i = (i + 1) % numbers.Length;
27 | }
28 |
29 | return freq;
30 | }
31 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D02/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D02;
2 |
3 | [PuzzleInfo("Inventory Management System", Topics.StringParsing, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | return part switch
9 | {
10 | 1 => GetChecksum(ids: GetInputLines(), c1: 2, c2: 3),
11 | 2 => GetCommonCorrectLetters(ids: GetInputLines()),
12 | _ => PuzzleNotSolvedString
13 | };
14 | }
15 |
16 | private static int GetChecksum(IList ids, int c1, int c2)
17 | {
18 | return
19 | ids.Count(id => HasExactCountOfAnyLetter(id, c1)) *
20 | ids.Count(id => HasExactCountOfAnyLetter(id, c2));
21 | }
22 |
23 | private static string GetCommonCorrectLetters(string[] ids)
24 | {
25 | var numIds = ids.Length;
26 | var idLength = ids[0].Length;
27 |
28 | for (var i = 0; i < numIds; i++)
29 | for (var j = 0; j < numIds; j++)
30 | {
31 | var z = ZipCommon(ids[i], ids[j]);
32 | if (z.Length == idLength - 1)
33 | {
34 | return z;
35 | }
36 | }
37 |
38 | throw new NoSolutionException();
39 | }
40 |
41 | private static string ZipCommon(string a, string b)
42 | {
43 | return string.Join(string.Empty, a.Zip(b, (c1, c2) => c1 == c2 ? c1.ToString() : string.Empty));
44 | }
45 |
46 | private static bool HasExactCountOfAnyLetter(string id, int count)
47 | {
48 | return id.Any(c => id.Count(e => e == c) == count);
49 | }
50 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D03/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 | using Utilities.Extensions;
3 | using Utilities.Geometry.Euclidean;
4 |
5 | namespace Solutions.Y2018.D03;
6 |
7 | [PuzzleInfo("No Matter How You Slice It", Topics.Vectors, Difficulty.Easy)]
8 | public sealed class Solution : SolutionBase
9 | {
10 | public override object Run(int part)
11 | {
12 | var claims = ParseInputLines(parseFunc: ParseClaim);
13 | return part switch
14 | {
15 | 1 => CountClaimOverlaps(claims.Select(c => c.Aabb)),
16 | 2 => GetNonOverlappedClaim(claims.ToList()),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int CountClaimOverlaps(IEnumerable claimAabbs)
22 | {
23 | var map = new DefaultDict(defaultValue: 0);
24 |
25 | foreach (var aabb in claimAabbs)
26 | foreach (var position in aabb)
27 | {
28 | map[position]++;
29 | }
30 |
31 | return map.Keys.Count(c => map[c] > 1);
32 | }
33 |
34 | private static int GetNonOverlappedClaim(IList<(int Id, Aabb2D aabb2D)> claims)
35 | {
36 | return claims
37 | .Single(claim => claims.Count(other => Aabb2D.Overlap(a: claim.aabb2D, b: other.aabb2D, out _)) == 1).Id;
38 | }
39 |
40 | private static (int Id, Aabb2D Aabb) ParseClaim(string line)
41 | {
42 | var numbers = line.ParseInts();
43 | var id = numbers[0];
44 | var aabb = new Aabb2D(
45 | xMin: numbers[1],
46 | xMax: numbers[1] + numbers[3] - 1,
47 | yMin: numbers[2],
48 | yMax: numbers[2] + numbers[4] - 1);
49 |
50 | return (id, aabb);
51 | }
52 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D12/Input.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D12;
2 |
3 | public static class Input
4 | {
5 | public static void Parse(IList input, out HashSet state, out Dictionary rules)
6 | {
7 | state = new HashSet();
8 | rules = new Dictionary();
9 |
10 | for (var i = 2; i < input.Count; i++)
11 | {
12 | var mask = 0U;
13 | for (var j = 0; j < 5; j++)
14 | {
15 | if (input[i][j] == '#')
16 | {
17 | mask |= 1U << j;
18 | }
19 | }
20 |
21 | rules.Add(mask, input[i].Last() == '#');
22 | }
23 |
24 | var initialStr = input[0]
25 | .Where(c => c is '#' or '.')
26 | .ToList();
27 |
28 | for (var i = 0; i < initialStr.Count; i++)
29 | {
30 | if (initialStr[i] == '#')
31 | {
32 | state.Add(i);
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D13/CrashResponse.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D13;
2 |
3 | public enum CrashResponse
4 | {
5 | Halt = 0,
6 | Remove
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D13/TickOrderComparer.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D13;
4 |
5 | public sealed class TickOrderComparer : IComparer
6 | {
7 | public int Compare(Vec2D a, Vec2D b)
8 | {
9 | var yComparison = a.Y.CompareTo(b.Y);
10 | return yComparison != 0
11 | ? yComparison
12 | : a.X.CompareTo(b.X);
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D13/Track.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D13;
4 |
5 | public static class Track
6 | {
7 | public const char Junction = '+';
8 | public const char Left = '\\';
9 | public const char Right = '/';
10 |
11 | public static readonly Dictionary CartFacings = new()
12 | {
13 | { '<', Vec2D.Left },
14 | { '>', Vec2D.Right },
15 | { '^', Vec2D.Down },
16 | { 'v', Vec2D.Up }
17 | };
18 |
19 | public static readonly Dictionary TurnChoices = new()
20 | {
21 | { 0, Rot3D.N90Z },
22 | { 1, Rot3D.Zero },
23 | { 2, Rot3D.P90Z }
24 | };
25 |
26 | public static Vec2D TurnForCorner(char corner, Vec2D face)
27 | {
28 | var eastOrWest = face == Vec2D.Left || face == Vec2D.Right;
29 | var rot = corner switch
30 | {
31 | Left => eastOrWest ? Rot3D.P90Z : Rot3D.N90Z,
32 | Right => eastOrWest ? Rot3D.N90Z : Rot3D.P90Z,
33 | _ => throw new ArgumentOutOfRangeException(nameof(corner))
34 | };
35 |
36 | return rot.Transform(face);
37 | }
38 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/CombatResult.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D15;
2 |
3 | public readonly struct CombatResult
4 | {
5 | public char WinningTeam { get; }
6 | public int CompletedRounds { get; }
7 | public int HpSum { get; }
8 | public IReadOnlyDictionary Casualties { get; }
9 |
10 | public int Score => CompletedRounds * HpSum;
11 |
12 | public static CombatResult FromState(GameState state)
13 | {
14 | return new CombatResult(
15 | winningTeam: state.Units.Values.First(unit => !unit.Dead).Team,
16 | completedRounds: state.Tick,
17 | hpSum: state.Units.Values.Sum(unit => unit.Hp),
18 | casualties: state.Casualties);
19 | }
20 |
21 | public void Print()
22 | {
23 | var casualtiesSummary = string.Join(' ', Casualties.Select(kvp => $"[{kvp.Key}={kvp.Value}]"));
24 |
25 | Console.WriteLine("COMBAT RESULT: ");
26 | Console.WriteLine($"Winning team: {WinningTeam}");
27 | Console.WriteLine($"Completed combat rounds: {CompletedRounds}");
28 | Console.WriteLine($"Remaining HP sum: {HpSum}");
29 | Console.WriteLine($"Casualties: {casualtiesSummary}");
30 | }
31 |
32 | private CombatResult(char winningTeam, int completedRounds, int hpSum, IReadOnlyDictionary casualties)
33 | {
34 | WinningTeam = winningTeam;
35 | CompletedRounds = completedRounds;
36 | HpSum = hpSum;
37 | Casualties = casualties;
38 | }
39 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/GameData.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D15;
2 |
3 | public static class GameData
4 | {
5 | public const char Empty = '.';
6 | public const char Goblin = 'G';
7 | public const char Elf = 'E';
8 |
9 | public const int Hp = 200;
10 | public const int Dmg = 3;
11 |
12 | public static readonly SquareComparer SquareComparer = new();
13 | public static readonly Dictionary EnemyMap = new()
14 | {
15 | { Elf, Goblin },
16 | { Goblin, Elf }
17 | };
18 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D15;
2 |
3 | [PuzzleInfo("Beverage Bandits", Topics.Simulation, Difficulty.Hard)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var input = GetInputLines();
9 | return part switch
10 | {
11 | 1 => PlayDefaultGame(input, print: LogsEnabled),
12 | 2 => PlayBuffedGame(input, print: LogsEnabled),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int PlayDefaultGame(IList input, bool print)
18 | {
19 | var emptyBuffs = new Dictionary();
20 | var state = GameState.Create(input, teamDmgBuffs: emptyBuffs);
21 | var result = Sim.Run(state, print);
22 |
23 | return result.Score;
24 | }
25 |
26 | private static int PlayBuffedGame(IList input, bool print)
27 | {
28 | var score = 0;
29 | var dmgBuff = 0;
30 | var desiredResult = false;
31 |
32 | while (!desiredResult)
33 | {
34 | var buffs = new Dictionary { { GameData.Elf, dmgBuff++ } };
35 | var state = GameState.Create(input, buffs);
36 | var result = Sim.Run(state, print);
37 |
38 | score = result.Score;
39 | desiredResult =
40 | result.WinningTeam == GameData.Elf &&
41 | result.Casualties[GameData.Elf] == 0;
42 | }
43 |
44 | return score;
45 | }
46 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/SquareComparer.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D15;
4 |
5 | public sealed class SquareComparer : IComparer
6 | {
7 | public int Compare(Vec2D a, Vec2D b)
8 | {
9 | var yComparison = a.Y.CompareTo(b.Y);
10 | return yComparison != 0
11 | ? yComparison
12 | : a.X.CompareTo(b.X);
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/Unit.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D15;
4 |
5 | public sealed class Unit
6 | {
7 | public int Id { get; }
8 | public char Team { get; }
9 | public int Dmg { get; }
10 | public int Hp { get; private set; }
11 | public Vec2D Pos { get; set; }
12 |
13 | public bool Dead => Hp <= 0;
14 |
15 | public Unit(int id, char team, int dmg, int hp, Vec2D pos)
16 | {
17 | Id = id;
18 | Team = team;
19 | Dmg = dmg;
20 | Hp = hp;
21 | Pos = pos;
22 | }
23 |
24 | public void InflictDamage(int dmg)
25 | {
26 | Hp = Math.Max(0, Hp - dmg);
27 | }
28 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D15/UnitFactory.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D15;
4 |
5 | public static class UnitFactory
6 | {
7 | public static Unit Build(int id, char team, int dmgBuff, Vec2D pos)
8 | {
9 | return new Unit(
10 | id: id,
11 | team: team,
12 | pos: pos,
13 | dmg: GameData.Dmg + dmgBuff,
14 | hp: GameData.Hp);
15 | }
16 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D16/Observation.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D16;
2 |
3 | public readonly record struct Observation(IList Before, IList Instr, IList After);
--------------------------------------------------------------------------------
/Solutions/Y2018/D17/Materials.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D17;
2 |
3 | public static class Terrain
4 | {
5 | public const char Spring = '+';
6 | public const char Empty = '.';
7 | public const char Clay = '#';
8 | }
9 |
10 | public static class Water
11 | {
12 | public const char Flowing = '|';
13 | public const char Settled = '~';
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D19/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2018.D19;
4 |
5 | [PuzzleInfo("Go With The Flow", Topics.Assembly|Topics.Simulation, Difficulty.Hard)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | var ipAdr = input[0].ParseInt();
12 | var program = input[1..].Select(ParseInstruction).ToArray();
13 |
14 | return part switch
15 | {
16 | 1 => Execute(ipAdr, program, r0: 0, enableOptimizations: true),
17 | 2 => Execute(ipAdr, program, r0: 1, enableOptimizations: true),
18 | _ => PuzzleNotSolvedString
19 | };
20 | }
21 |
22 | private static int Execute(int ipAdr, IList program, int r0, bool enableOptimizations)
23 | {
24 | var cpu = new Cpu(ipAdr) { [0] = r0 };
25 | var ec = cpu.Run(program, enableOptimizations);
26 |
27 | return ec;
28 | }
29 |
30 | private static Cpu.Instruction ParseInstruction(string line)
31 | {
32 | var elements = line.Split(' ');
33 | return new Cpu.Instruction(
34 | Opcode: elements[0],
35 | A: int.Parse(elements[1]),
36 | B: int.Parse(elements[2]),
37 | C: int.Parse(elements[3]));
38 | }
39 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D20/MapChars.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D20;
4 |
5 | public static class MapChars
6 | {
7 | public const char Start = 'X';
8 | public const char Empty = '.';
9 |
10 | public static readonly Dictionary DoorChars = new()
11 | {
12 | { Vec2D.Up, '-' },
13 | { Vec2D.Down, '-' },
14 | { Vec2D.Left, '|' },
15 | { Vec2D.Right, '|' }
16 | };
17 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D22/Enums.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D22;
2 |
3 | public enum RegionType
4 | {
5 | Rocky = 0,
6 | Wet,
7 | Narrow
8 | }
9 |
10 | public enum EquippedTool
11 | {
12 | None = 0,
13 | ClimbingGear,
14 | Torch
15 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D22/Records.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2018.D22;
4 |
5 | public readonly record struct Scan(int Depth, Vec2D Mouth, Vec2D Target);
6 | public readonly record struct Region(long Erosion, RegionType Type);
7 | public readonly record struct State(Vec2D Pos, EquippedTool Tool);
8 | public readonly record struct Transition(State NextState, int Cost);
--------------------------------------------------------------------------------
/Solutions/Y2018/D23/SearchRanking.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D23;
2 |
3 | public readonly struct SearchRanking(long inRange, long distance, long volume) : IComparable
4 | {
5 | private readonly long _inRange = inRange;
6 | private readonly long _distance = distance;
7 | private readonly long _volume = volume;
8 |
9 | public int CompareTo(SearchRanking other)
10 | {
11 | // If one region intersects the range of more bots than the other, rank it higher
12 | //
13 | var inRangeComparison = _inRange.CompareTo(other._inRange);
14 | if (inRangeComparison != 0)
15 | {
16 | return -1 * inRangeComparison;
17 | }
18 |
19 | // Otherwise, if one region is close to the origin, rank it higher
20 | //
21 | var distanceComparison = _distance.CompareTo(other._distance);
22 | if (distanceComparison != 0)
23 | {
24 | return distanceComparison;
25 | }
26 |
27 | // Finally, rank the smaller region (by volume) higher
28 | //
29 | return _volume.CompareTo(other._volume);
30 | }
31 |
32 | public override string ToString()
33 | {
34 | return $"[Bots in range: {_inRange}] [Distance: {_distance}] [Volume: {_volume}]";
35 | }
36 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D24/Comparers.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D24;
2 |
3 | public sealed class TargetPriorityComparer : IComparer
4 | {
5 | public int Compare(Group x, Group y)
6 | {
7 | return x.EffectivePower != y.EffectivePower
8 | ? x.EffectivePower.CompareTo(y.EffectivePower)
9 | : x.Units.Initiative.CompareTo(y.Units.Initiative);
10 | }
11 | }
12 |
13 | public sealed class AttackPriorityComparer : IComparer
14 | {
15 | public int Compare(Group x, Group y)
16 | {
17 | return x.Units.Initiative.CompareTo(y.Units.Initiative);
18 | }
19 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D24/Enums.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D24;
2 |
3 | public enum Team
4 | {
5 | ImmuneSystem = 0,
6 | Infection
7 | }
8 |
9 | public enum Resolution
10 | {
11 | Draw = 0,
12 | Winner
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D24/Group.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D24;
2 |
3 | using GroupKey = ValueTuple;
4 |
5 | public readonly struct Group
6 | {
7 | public Team Team { get; }
8 | public int Id { get; }
9 | public int Size { get; }
10 | public Unit Units { get; }
11 |
12 | public GroupKey Ck => (Team, Id);
13 | public int EffectivePower => Size * Units.Attack.Damage;
14 | public bool Dead => Size <= 0;
15 |
16 | public Group(Team team, int id, Unit units, int size)
17 | {
18 | Team = team;
19 | Id = id;
20 | Size = size;
21 | Units = units;
22 | }
23 |
24 | public override string ToString()
25 | {
26 | return $"{Team} Group {Id}";
27 | }
28 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D24/Records.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D24;
2 |
3 | public readonly record struct Attack(string Type, int Damage);
4 | public readonly record struct Unit(int Hp, IReadOnlySet Weaknesses, IReadOnlySet Immunities, Attack Attack, int Initiative);
5 | public readonly record struct Result(Resolution Resolution, Team Winner, int Score);
--------------------------------------------------------------------------------
/Solutions/Y2018/D24/State.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2018.D24;
2 |
3 | using GroupKey = ValueTuple;
4 |
5 | public sealed class State
6 | {
7 | private bool Drawn { get; }
8 | private Dictionary> MembersByTeam { get; } = new()
9 | {
10 | { Team.ImmuneSystem, new HashSet() },
11 | { Team.Infection, new HashSet() }
12 | };
13 |
14 | public Dictionary GroupsByCk { get; } = new();
15 |
16 | public IEnumerable GroupKeys => GroupsByCk.Keys;
17 | public bool Resolved => Drawn || MembersByTeam.Values.Any(members => members.Count == 0);
18 |
19 | public State(IEnumerable groups, bool drawn = false)
20 | {
21 | Drawn = drawn;
22 | RegisterGroups(groups);
23 | }
24 |
25 | public Result GetResult()
26 | {
27 | if (!Resolved)
28 | {
29 | throw new NoSolutionException();
30 | }
31 |
32 | return new Result(
33 | Resolution: Drawn ? Resolution.Draw : Resolution.Winner,
34 | Winner: Drawn ? default : GetWinningTeamInternal(),
35 | Score: GroupsByCk.Values.Sum(group => group.Size));
36 | }
37 |
38 | private Team GetWinningTeamInternal()
39 | {
40 | return MembersByTeam[Team.Infection].Count == 0
41 | ? Team.ImmuneSystem
42 | : Team.Infection;
43 | }
44 |
45 | private void RegisterGroups(IEnumerable groups)
46 | {
47 | foreach (var group in groups)
48 | {
49 | GroupsByCk[(group.Team, group.Id)] = group;
50 | MembersByTeam[group.Team].Add(group.Id);
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/Solutions/Y2018/D25/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 | using Utilities.Geometry.Euclidean;
3 |
4 | namespace Solutions.Y2018.D25;
5 |
6 | [PuzzleInfo("Four-Dimensional Adventure", Topics.Vectors, Difficulty.Medium)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override int Parts => 1;
10 |
11 | public override object Run(int part)
12 | {
13 | return part switch
14 | {
15 | 1 => CountConstellations(),
16 | _ => PuzzleNotSolvedString
17 | };
18 | }
19 |
20 | private int CountConstellations()
21 | {
22 | var points = ParseInputLines(parseFunc: Vec4D.Parse);
23 | var disjointSet = new DisjointSet();
24 | var adjacency = new Dictionary>();
25 |
26 | foreach (var point in points)
27 | {
28 | adjacency[point] = points.Where(p => Vec4D.Distance(
29 | a: p,
30 | b: point,
31 | metric: Metric.Taxicab) <= 3);
32 | }
33 |
34 | foreach (var (point, adjacencies) in adjacency)
35 | {
36 | disjointSet.MakeSet(point);
37 | foreach (var adjacent in adjacencies)
38 | {
39 | disjointSet.MakeSet(adjacent);
40 | disjointSet.Union(point, adjacent);
41 | }
42 | }
43 |
44 | return disjointSet.PartitionsCount;
45 | }
46 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D01;
2 |
3 | [PuzzleInfo("The Tyranny of the Rocket Equation", Topics.Math, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var masses = ParseInputLines(parseFunc: int.Parse);
9 | return part switch
10 | {
11 | 1 => masses.Sum(GetNaiveFuelRequirement),
12 | 2 => masses.Sum(GetIterativeFuelRequirement),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int GetNaiveFuelRequirement(int mass)
18 | {
19 | return mass / 3 - 2;
20 | }
21 |
22 | private static int GetIterativeFuelRequirement(int mass)
23 | {
24 | var total = 0;
25 | var fuel = GetNaiveFuelRequirement(mass);
26 |
27 | while (fuel > 0)
28 | {
29 | total += fuel;
30 | fuel = GetNaiveFuelRequirement(fuel);
31 | }
32 |
33 | return total;
34 | }
35 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D04/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Numerics;
2 |
3 | namespace Solutions.Y2019.D04;
4 |
5 | [PuzzleInfo("Secure Container", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputText();
11 | var range = Range.Parse(input);
12 |
13 | return part switch
14 | {
15 | 1 => CountValidPasswords(range, minRun: 2, maxRun: 6),
16 | 2 => CountValidPasswords(range, minRun: 2, maxRun: 2),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int CountValidPasswords(Range range, int minRun, int maxRun)
22 | {
23 | return Enumerable.Range(range.Min, range.Length).Count(p => IsValid(p, minRun, maxRun));
24 | }
25 |
26 | private static bool IsValid(int password, int minRun, int maxRun)
27 | {
28 | var chars = new List(password.ToString());
29 | var observedRuns = new HashSet();
30 | var curObservedRun = 1;
31 |
32 | for (var i = 1; i < chars.Count; i++)
33 | {
34 | if (chars[i] < chars[i - 1])
35 | {
36 | return false;
37 | }
38 |
39 | if (chars[i] == chars[i - 1])
40 | {
41 | curObservedRun++;
42 | }
43 | else
44 | {
45 | observedRuns.Add(curObservedRun);
46 | curObservedRun = 1;
47 | }
48 | }
49 |
50 | observedRuns.Add(curObservedRun);
51 | return observedRuns.Any(r => r >= minRun && r <= maxRun);
52 | }
53 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D05/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2019.IntCode;
2 |
3 | namespace Solutions.Y2019.D05;
4 |
5 | [PuzzleInfo("Sunny with a Chance of Asteroids", Topics.IntCode, Difficulty.Easy)]
6 | public sealed class Solution : IntCodeSolution
7 | {
8 | public override object Run(int part)
9 | {
10 | var program = LoadIntCodeProgram();
11 | return part switch
12 | {
13 | 1 => RunTestProgram(program, systemId: 1),
14 | 2 => RunTestProgram(program, systemId: 5),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static long RunTestProgram(IList program, int systemId)
20 | {
21 | var vm = IntCodeVm.Create(program, systemId);
22 | vm.Run();
23 | return vm.OutputBuffer.Last();
24 | }
25 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D09/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2019.IntCode;
2 |
3 | namespace Solutions.Y2019.D09;
4 |
5 | [PuzzleInfo("Sensor Boost", Topics.IntCode, Difficulty.Easy)]
6 | public sealed class Solution : IntCodeSolution
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => RunBoostProgram(input: 1L),
13 | 2 => RunBoostProgram(input: 2L),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private long RunBoostProgram(long input)
19 | {
20 | var vm = IntCodeVm.Create(LoadIntCodeProgram(), input);
21 | var ec = vm.Run();
22 |
23 | return ec == IntCodeVm.ExitCode.Halted
24 | ? vm.OutputBuffer.Dequeue()
25 | : throw new NoSolutionException(message: $"Invalid VM exit code [{ec}]");
26 | }
27 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D12/Moon.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D12;
2 |
3 | public enum Moon
4 | {
5 | Io,
6 | Europa,
7 | Ganymede,
8 | Callisto
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D12/State.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2019.D12;
4 |
5 | public readonly record struct State(Vec3D Pos, Vec3D Vel);
--------------------------------------------------------------------------------
/Solutions/Y2019/D12/StateComp.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2019.D12;
4 |
5 | public readonly struct StateComp(Axis component, State state) : IEquatable
6 | {
7 | private int Pos { get; } = state.Pos[component];
8 | private int Vel { get; } = state.Vel[component];
9 |
10 | public bool Equals(StateComp other)
11 | {
12 | return Pos == other.Pos && Vel == other.Vel;
13 | }
14 |
15 | public override bool Equals(object? obj)
16 | {
17 | return obj is StateComp other && Equals(other);
18 | }
19 |
20 | public override int GetHashCode()
21 | {
22 | return HashCode.Combine(Pos, Vel);
23 | }
24 |
25 | public static bool operator ==(StateComp left, StateComp right)
26 | {
27 | return left.Equals(right);
28 | }
29 |
30 | public static bool operator !=(StateComp left, StateComp right)
31 | {
32 | return !left.Equals(right);
33 | }
34 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D13/GameObject.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D13;
2 |
3 | public enum GameObject
4 | {
5 | Empty,
6 | Wall,
7 | Block,
8 | Paddle,
9 | Ball
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D13/Joystick.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D13;
2 |
3 | public static class Joystick
4 | {
5 | public const long Neutral = 0;
6 | public const long Left = -1;
7 | public const long Right = 1;
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D14/Reaction.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D14;
2 |
3 | public readonly struct Reaction(IEnumerable reactants, Term product)
4 | {
5 | public IReadOnlyList Reactants { get; } = new List(reactants);
6 | public Term Product { get; } = product;
7 |
8 | public override string ToString()
9 | {
10 | var reactantStrings = Reactants.Select(r => $"{r.Amount} {r.Substance}");
11 | return $"{string.Join(',', reactantStrings)} => {Product.Amount} {Product.Substance}";
12 | }
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D14/Term.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D14;
2 |
3 | public readonly record struct Term(long Amount, string Substance);
--------------------------------------------------------------------------------
/Solutions/Y2019/D15/Tile.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D15;
2 |
3 | public enum Tile
4 | {
5 | Wall = 0,
6 | Empty,
7 | Target
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D17/Records.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2019.D17;
4 |
5 | public readonly record struct State(IReadOnlySet Scaffolding, Pose2D Robot);
--------------------------------------------------------------------------------
/Solutions/Y2019/D18/PathFinder.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D18;
2 |
3 | public sealed class PathFinder(Field field)
4 | {
5 | public int Run(bool ignoreDoors)
6 | {
7 | var initialState = State.Initial(field.StartPos);
8 | var queue = new Queue(collection: [initialState]);
9 | var visited = new HashSet(collection: [initialState]);
10 |
11 | while (queue.Count > 0)
12 | {
13 | var state = queue.Dequeue();
14 | if (field.AllKeysFound(state))
15 | {
16 | return state.Steps;
17 | }
18 |
19 | foreach (var adj in field.GetAdj(state.Pos))
20 | {
21 | if (!ignoreDoors && field.CheckForDoorAt(adj, out var door) && !state.HasKey(char.ToLower(door)))
22 | {
23 | continue;
24 | }
25 |
26 | var next = field.CheckForKeyAt(adj, out var key) && !state.HasKey(key)
27 | ? state.AfterPickup(adj, key)
28 | : state.AfterStep(adj);
29 |
30 | if (visited.Add(next))
31 | {
32 | queue.Enqueue(next);
33 | }
34 | }
35 | }
36 |
37 | throw new NoSolutionException();
38 | }
39 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D18/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D18;
2 |
3 | [PuzzleInfo("Many-Worlds Interpretation", Topics.Graphs, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | return part switch
9 | {
10 | 1 => FindShortestPath(applyInputOverrides: false, ignoreDoors: false),
11 | 2 => FindShortestPath(applyInputOverrides: true, ignoreDoors: true),
12 | _ => PuzzleNotSolvedString
13 | };
14 | }
15 |
16 | private int FindShortestPath(bool applyInputOverrides, bool ignoreDoors)
17 | {
18 | var fields = Field.Parse(GetInputLines(), applyInputOverrides);
19 | return fields.Sum(f => new PathFinder(f).Run(ignoreDoors));
20 | }
21 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D19/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2019.IntCode;
2 | using Utilities.Geometry.Euclidean;
3 |
4 | namespace Solutions.Y2019.D19;
5 |
6 | [PuzzleInfo("Tractor Beam", Topics.IntCode, Difficulty.Medium)]
7 | public sealed class Solution : IntCodeSolution
8 | {
9 | public override object Run(int part)
10 | {
11 | return part switch
12 | {
13 | 1 => CountBeamPoints(searchDimension: 50),
14 | 2 => FindShip(shipSize: 100),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private long FindShip(int shipSize)
20 | {
21 | var program = LoadIntCodeProgram();
22 | var x = 0;
23 | var y = 0;
24 |
25 | while (!CheckPointInBeam(x + shipSize - 1, y, program))
26 | {
27 | y += 1;
28 | while (!CheckPointInBeam(x, y + shipSize - 1, program))
29 | {
30 | x += 1;
31 | }
32 | }
33 |
34 | return 10000L * x + y;
35 | }
36 |
37 | private int CountBeamPoints(int searchDimension)
38 | {
39 | var program = LoadIntCodeProgram();
40 | var searchArea = new Aabb2D(
41 | xMin: 0,
42 | xMax: searchDimension - 1,
43 | yMin: 0,
44 | yMax: searchDimension - 1);
45 |
46 | return searchArea.Sum(point => CheckPointInBeam(point.X, point.Y, program) ? 1 : 0);
47 | }
48 |
49 | private static bool CheckPointInBeam(int x, int y, IList program)
50 | {
51 | var inputs = new long[] { x, y };
52 | var vm = IntCodeVm.Create(program, inputs);
53 |
54 | vm.Run();
55 | return vm.OutputBuffer.Dequeue() > 0L;
56 | }
57 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D20/Enums.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D20;
2 |
3 | public enum MazeType
4 | {
5 | Static = 0,
6 | Recursive
7 | }
8 |
9 | public enum EntranceType
10 | {
11 | Inner = 0,
12 | Outer
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D20/PortalKey.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D20;
2 |
3 | public readonly struct PortalKey(char c1, char c2) : IEquatable
4 | {
5 | private readonly char _c1 = c1;
6 | private readonly char _c2 = c2;
7 |
8 | public bool Equals(PortalKey other)
9 | {
10 | return _c1 == other._c1 && _c2 == other._c2;
11 | }
12 |
13 | public override bool Equals(object? obj)
14 | {
15 | return obj is PortalKey other && Equals(other);
16 | }
17 |
18 | public override int GetHashCode()
19 | {
20 | return HashCode.Combine(_c1, _c2);
21 | }
22 |
23 | public static bool operator ==(PortalKey left, PortalKey right)
24 | {
25 | return left.Equals(right);
26 | }
27 |
28 | public static bool operator !=(PortalKey left, PortalKey right)
29 | {
30 | return !left.Equals(right);
31 | }
32 |
33 | public override string ToString()
34 | {
35 | return new string(new[] { _c1, _c2 });
36 | }
37 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D20/Records.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2019.D20;
4 |
5 | public readonly record struct State(Vec2D Pos, int Depth);
6 | public readonly record struct PortalEntrance(Vec2D Pos, EntranceType Type);
--------------------------------------------------------------------------------
/Solutions/Y2019/D21/Springdroid.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using Solutions.Y2019.IntCode;
3 |
4 | namespace Solutions.Y2019.D21;
5 |
6 | public static class Springdroid
7 | {
8 | private const int AsciiRange = 255;
9 |
10 | public static bool Run(IList firmware, IEnumerable program, out string output)
11 | {
12 | var input = Compile(program);
13 | var vm = IntCodeVm.Create(firmware, input);
14 |
15 | vm.Run();
16 |
17 | if (vm.OutputBuffer.Last() > AsciiRange)
18 | {
19 | output = vm.OutputBuffer.Last().ToString();
20 | return true;
21 | }
22 |
23 | output = ReadAsciiOutput(vm);
24 | return false;
25 | }
26 |
27 | private static IEnumerable Compile(IEnumerable script)
28 | {
29 | foreach (var instr in script)
30 | {
31 | foreach (var c in instr)
32 | {
33 | yield return c;
34 | }
35 |
36 | yield return '\n';
37 | }
38 | }
39 |
40 | private static string ReadAsciiOutput(IntCodeVm vm)
41 | {
42 | var sb = new StringBuilder();
43 | while (vm.OutputBuffer.Any())
44 | {
45 | sb.Append((char)vm.OutputBuffer.Dequeue());
46 | }
47 |
48 | return sb.ToString();
49 | }
50 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D23/Computer.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2019.IntCode;
2 |
3 | namespace Solutions.Y2019.D23;
4 |
5 | public sealed class Computer
6 | {
7 | private readonly int _id;
8 | private readonly IntCodeVm _vm;
9 | private readonly Queue _inputBuffer = new();
10 |
11 | public event EventHandler? PacketEmitted;
12 |
13 | public Computer(int id, IList firmware)
14 | {
15 | _id = id;
16 | _vm = IntCodeVm.Create(firmware, id);
17 | _vm.AwaitingInput += OnAwaitingInput;
18 | _vm.OutputEmitted += OnOutputEmitted;
19 | }
20 |
21 | public void Tick()
22 | {
23 | _vm.Run();
24 | }
25 |
26 | public void EnqueuePacket(Packet packet)
27 | {
28 | _inputBuffer.Enqueue(packet);
29 | }
30 |
31 | private void OnAwaitingInput(object? sender, EventArgs e)
32 | {
33 | if (_inputBuffer.Count == 0)
34 | {
35 | _vm.InputBuffer.Enqueue(item: -1L);
36 | return;
37 | }
38 |
39 | var packet = _inputBuffer.Dequeue();
40 | _vm.InputBuffer.Enqueue(packet.X);
41 | _vm.InputBuffer.Enqueue(packet.Y);
42 | }
43 |
44 | private void OnOutputEmitted(object? sender, EventArgs e)
45 | {
46 | if (_vm.OutputBuffer.Count < 3)
47 | {
48 | return;
49 | }
50 |
51 | RaisePacketEmitted(new Packet(
52 | RecipientId: (int)_vm.OutputBuffer.Dequeue(),
53 | X: _vm.OutputBuffer.Dequeue(),
54 | Y: _vm.OutputBuffer.Dequeue()));
55 | }
56 |
57 | private void RaisePacketEmitted(Packet payload)
58 | {
59 | PacketEmitted?.Invoke(this, new PacketEventArgs(_id, payload));
60 | }
61 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D23/Nat.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D23;
2 |
3 | public sealed class Nat
4 | {
5 | private Packet Packet { get; set; }
6 | public bool BufferInitialized { get; private set; }
7 |
8 | public void WriteBuffer(long x, long y)
9 | {
10 | Packet = new Packet(RecipientId: 0, x, y);
11 | BufferInitialized = true;
12 | }
13 |
14 | public Packet ReadBuffer()
15 | {
16 | return BufferInitialized ? Packet : throw new NoSolutionException();
17 | }
18 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D23/Packet.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D23;
2 |
3 | public readonly record struct Packet(int RecipientId, long X, long Y);
4 |
5 | public sealed class PacketEventArgs(int senderId, Packet payload) : EventArgs
6 | {
7 | public int SenderId { get; } = senderId;
8 | public Packet Payload { get; } = payload;
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D23/Solution.cs:
--------------------------------------------------------------------------------
1 | using Solutions.Y2019.IntCode;
2 |
3 | namespace Solutions.Y2019.D23;
4 |
5 | [PuzzleInfo("Category Six", Topics.IntCode, Difficulty.Hard)]
6 | public sealed class Solution : IntCodeSolution
7 | {
8 | public override object Run(int part)
9 | {
10 | var firmware = LoadIntCodeProgram();
11 | var networkAwaiter = new NetworkAwaiter(firmware);
12 |
13 | return part switch
14 | {
15 | 1 => networkAwaiter.WaitForMessage(targetRecipient: 255).Result,
16 | 2 => networkAwaiter.WaitForRepeatedNatMessage().Result,
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D24/GridType.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.D24;
2 |
3 | public enum GridType
4 | {
5 | Static = 0,
6 | Recursive
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/D25/Cheats.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Solutions.Y2019.D25;
4 |
5 | public static class Cheats
6 | {
7 | public const string TestInventoryCommand = "north";
8 | public static readonly Regex PasscodeRegex = new(@"(\d+)");
9 | public static readonly Queue AutomationCommands = new([
10 | "west",
11 | "take semiconductor",
12 | "west",
13 | "take planetoid",
14 | "west",
15 | "take food ration",
16 | "west",
17 | "take fixed point",
18 | "west",
19 | "take klein bottle",
20 | "east",
21 | "south",
22 | "west",
23 | "take weather machine",
24 | "east",
25 | "north",
26 | "east",
27 | "east",
28 | "south",
29 | "south",
30 | "south",
31 | "take pointer",
32 | "north",
33 | "north",
34 | "east",
35 | "take coin",
36 | "east",
37 | "north",
38 | "east",
39 | "inv"
40 | ]);
41 | public static readonly IReadOnlyList InventoryItems = new List
42 | {
43 | "semiconductor",
44 | "planetoid",
45 | "food ration",
46 | "fixed point",
47 | "klein bottle",
48 | "weather machine",
49 | "coin",
50 | "pointer"
51 | };
52 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/IntCode/Instruction.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.IntCode;
2 |
3 | internal readonly struct Instruction(OpCode opCode, IEnumerable paramModes)
4 | {
5 | private List ParamModes { get; } = [..paramModes];
6 | public OpCode OpCode { get; } = opCode;
7 |
8 | public ParameterMode GetParamMode(int paramIndex)
9 | {
10 | return paramIndex < ParamModes.Count
11 | ? ParamModes[paramIndex]
12 | : ParameterMode.Pos;
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/IntCode/IntCodeSolution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.IntCode;
2 |
3 | public abstract class IntCodeSolution : SolutionBase
4 | {
5 | protected IList LoadIntCodeProgram()
6 | {
7 | return new List(GetInputText().Split(separator: ',').Select(long.Parse));
8 | }
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/IntCode/IntCodeVmFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.IntCode;
2 |
3 | public partial class IntCodeVm
4 | {
5 | public static IntCodeVm Create(IList program)
6 | {
7 | return new IntCodeVm(program: program, inputs: []);
8 | }
9 |
10 | public static IntCodeVm Create(IList program, IEnumerable inputs)
11 | {
12 | return new IntCodeVm(program, inputs);
13 | }
14 |
15 | public static IntCodeVm Create(IList program, long input)
16 | {
17 | return new IntCodeVm(program, inputs: [input]);
18 | }
19 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/IntCode/OpCode.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.IntCode;
2 |
3 | internal enum OpCode
4 | {
5 | Add = 1,
6 | Mul = 2,
7 | Inp = 3,
8 | Out = 4,
9 | Jit = 5,
10 | Jif = 6,
11 | Lst = 7,
12 | Eql = 8,
13 | Rbo = 9,
14 | Hlt = 99
15 | }
--------------------------------------------------------------------------------
/Solutions/Y2019/IntCode/ParameterMode.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2019.IntCode;
2 |
3 | internal enum ParameterMode
4 | {
5 | Pos = 0,
6 | Imm = 1,
7 | Rel = 2
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D01;
2 |
3 | [PuzzleInfo("Report Repair", Topics.Math, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var numbers = ParseInputLines(parseFunc: int.Parse).ToHashSet();
9 | return part switch
10 | {
11 | 1 => GetSumPairProduct(targetSum: 2020, numbers),
12 | 2 => GetSumTripletProduct(targetSum: 2020, numbers),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int GetSumPairProduct(int targetSum, HashSet numbers)
18 | {
19 | foreach (var n1 in numbers)
20 | {
21 | var n2 = targetSum - n1;
22 | if (numbers.Contains(n2))
23 | {
24 | return n1 * n2;
25 | }
26 | }
27 |
28 | throw new NoSolutionException();
29 | }
30 |
31 | private static int GetSumTripletProduct(int targetSum, HashSet numbers)
32 | {
33 | foreach (var n1 in numbers)
34 | foreach (var n2 in numbers)
35 | {
36 | var n3 = targetSum - n1 - n2;
37 | if (numbers.Contains(n3))
38 | {
39 | return n1 * n2 * n3;
40 | }
41 | }
42 |
43 | throw new NoSolutionException();
44 | }
45 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D05/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D05;
2 |
3 | [PuzzleInfo("Binary Boarding", Topics.BitwiseOperations, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var boardingPasses = GetInputLines();
9 | return part switch
10 | {
11 | 1 => boardingPasses.Max(GetSeatId),
12 | 2 => FindMissingSeat(boardingPasses),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int FindMissingSeat(IEnumerable boardingPasses)
18 | {
19 | var orderedSeatIds = boardingPasses
20 | .Select(GetSeatId)
21 | .Order()
22 | .ToList();
23 |
24 | for (var i = 1; i < orderedSeatIds.Count; i++)
25 | {
26 | if (orderedSeatIds[i] != orderedSeatIds[i - 1] + 1)
27 | {
28 | return orderedSeatIds[i] - 1;
29 | }
30 | }
31 |
32 | throw new NoSolutionException();
33 | }
34 |
35 | private static int GetSeatId(string boardingPass)
36 | {
37 | const int numRows = 128;
38 | const int numCols = 8;
39 |
40 | var rowBits = (int)Math.Round(Math.Log2(numRows));
41 | var row = Convert.ToInt16(boardingPass[..rowBits]
42 | .Replace(oldChar: 'F', newChar: '0')
43 | .Replace(oldChar: 'B', newChar: '1'), fromBase: 2);
44 | var col = Convert.ToInt16(boardingPass[rowBits..]
45 | .Replace(oldChar: 'L', newChar: '0')
46 | .Replace(oldChar: 'R', newChar: '1'), fromBase: 2);
47 |
48 | return numCols * row + col;
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D06/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2020.D06;
4 |
5 | [PuzzleInfo("Custom Customs", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var lines = GetInputLines();
11 | var groupAnswers = lines.ChunkByNonEmpty();
12 |
13 | return part switch
14 | {
15 | 1 => groupAnswers.Sum(GetUniqueGroupAnswers),
16 | 2 => groupAnswers.Sum(GetUnanimousGroupAnswers),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static int GetUniqueGroupAnswers(IList groupAnswers)
22 | {
23 | return groupAnswers.SelectMany(g => g).Distinct().Count();
24 | }
25 |
26 | private static int GetUnanimousGroupAnswers(IList groupAnswers)
27 | {
28 | return groupAnswers.IntersectAll().Count;
29 | }
30 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D07/BagContent.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D07;
2 |
3 | public readonly record struct BagContent(int Count, string Colour);
--------------------------------------------------------------------------------
/Solutions/Y2020/D08/Machine.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D08;
2 |
3 | public sealed class Machine
4 | {
5 | public const string Nop = "nop";
6 | public const string Jmp = "jmp";
7 | private const string Acc = "acc";
8 |
9 | public readonly record struct Result(int Acc, bool Looped);
10 |
11 | public static Result Run(List<(string Op, int Arg)> instructions)
12 | {
13 | var looped = false;
14 | var acc = 0;
15 | var pc = 0;
16 | var pcValueSet = new HashSet([0]);
17 |
18 | while (pc < instructions.Count)
19 | {
20 | var (op, arg) = instructions[pc];
21 | switch (op)
22 | {
23 | case Nop:
24 | pc++;
25 | break;
26 | case Jmp:
27 | pc += arg;
28 | break;
29 | case Acc:
30 | acc += arg;
31 | pc++;
32 | break;
33 | }
34 |
35 | if (!pcValueSet.Add(pc))
36 | {
37 | looped = true;
38 | break;
39 | }
40 | }
41 |
42 | return new Result(acc, looped);
43 | }
44 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D09/Sum.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D09;
2 |
3 | public sealed class Sum
4 | {
5 | public long Value { get; private set; }
6 | private List Elements { get; } = [];
7 |
8 | public Sum(long initial)
9 | {
10 | Value = initial;
11 | Elements.Add(initial);
12 | }
13 |
14 | public long Add(long number)
15 | {
16 | Value += number;
17 | Elements.Add(number);
18 | return Value;
19 | }
20 |
21 | public long GetExtremaSum()
22 | {
23 | return Elements.Min() + Elements.Max();
24 | }
25 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D11/Concern.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D11;
2 |
3 | public enum Concern
4 | {
5 | Adjacent,
6 | Visible
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D14/MaskSimple.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D14;
2 |
3 | public readonly struct MaskSimple
4 | {
5 | private readonly ulong _setMask;
6 | private readonly ulong _clearMask;
7 |
8 | public MaskSimple(string maskStr)
9 | {
10 | var setMask = 0UL;
11 | var clearMask = ~0UL;
12 |
13 | for (var i = 0; i < maskStr.Length; i++)
14 | {
15 | switch (maskStr[maskStr.Length - i - 1])
16 | {
17 | case '0':
18 | clearMask &= ~(1UL << i);
19 | continue;
20 | case '1':
21 | setMask |= 1UL << i;
22 | continue;
23 | }
24 | }
25 |
26 | _setMask = setMask;
27 | _clearMask = clearMask;
28 | }
29 |
30 | public ulong Apply(ulong value)
31 | {
32 | value |= _setMask;
33 | value &= _clearMask;
34 |
35 | return value;
36 | }
37 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D14/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D14;
2 |
3 | [PuzzleInfo("Docking Data", Topics.Assembly|Topics.BitwiseOperations|Topics.Simulation, Difficulty.Hard)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var program = GetInputLines();
9 | return part switch
10 | {
11 | 1 => Machine.RunV1(program),
12 | 2 => Machine.RunV2(program),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D15/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D15;
2 |
3 | [PuzzleInfo("Rambunctious Recitation", Topics.Simulation, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var initialNumbers = GetInitialNumbers(GetInputText());
9 | return part switch
10 | {
11 | 1 => GetNthSpokenNumber(initialNumbers, n: 2020),
12 | 2 => GetNthSpokenNumber(initialNumbers, n: 30000000),
13 | _ => PuzzleNotSolvedString
14 | };
15 | }
16 |
17 | private static int GetNthSpokenNumber(IEnumerable startingNumbers, int n)
18 | {
19 | var turnNumber = 1;
20 | var lastSpoken = 0;
21 | var spokenMap = new Dictionary();
22 |
23 | foreach (var number in startingNumbers)
24 | {
25 | lastSpoken = number;
26 | spokenMap[number] = (Last: turnNumber, Previous: turnNumber);
27 |
28 | turnNumber++;
29 | }
30 |
31 | while (turnNumber <= n)
32 | {
33 | lastSpoken = spokenMap[lastSpoken].Last - spokenMap[lastSpoken].Previous;
34 | spokenMap[lastSpoken] = spokenMap.ContainsKey(lastSpoken)
35 | ? spokenMap[lastSpoken] = (Last: turnNumber, Previous: spokenMap[lastSpoken].Last)
36 | : spokenMap[lastSpoken] = (Last: turnNumber, Previous: turnNumber);
37 |
38 | turnNumber++;
39 | }
40 |
41 | return lastSpoken;
42 | }
43 |
44 | private static IEnumerable GetInitialNumbers(string input)
45 | {
46 | return input.Split(separator: ',').Select(int.Parse);
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D18/Operators.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D18;
2 |
3 | public static class Operators
4 | {
5 | public const char Add = '+';
6 | public const char Mul = '*';
7 | public const char Open = '(';
8 | public const char Close = ')';
9 |
10 | public delegate long Operator(long lhs, long rhs);
11 |
12 | public static readonly Dictionary Delegates = new()
13 | {
14 | { Add, (a, b) => a + b },
15 | { Mul, (a, b) => a * b }
16 | };
17 | public static readonly Dictionary EqualPrecedence = new()
18 | {
19 | { Add, 0 },
20 | { Mul, 0 }
21 | };
22 | public static readonly Dictionary AddPrecedence = new()
23 | {
24 | { Add, 0 },
25 | { Mul, 1 }
26 | };
27 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D20/EdgeFingerprint.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D20;
2 |
3 | public readonly struct EdgeFingerprint
4 | {
5 | private readonly string _forwards;
6 | private readonly string _backwards;
7 |
8 | public EdgeFingerprint(string edgeString)
9 | {
10 | _forwards = edgeString;
11 | _backwards = string.Concat(_forwards.Reverse());
12 | }
13 |
14 | public bool IsCongruentTo(EdgeFingerprint other)
15 | {
16 | return _forwards == other._forwards || _forwards == other._backwards;
17 | }
18 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D20/SeaMonster.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2020.D20;
4 |
5 | public static class SeaMonster
6 | {
7 | public const char Chr = '#';
8 | public static int Width => 20;
9 | public static int Height => 2;
10 |
11 | public static readonly HashSet Pattern =
12 | [
13 | new(X: 0, Y: 1),
14 | new(X: 1, Y: 0),
15 | new(X: 4, Y: 0),
16 | new(X: 5, Y: 1),
17 | new(X: 6, Y: 1),
18 | new(X: 7, Y: 0),
19 | new(X: 10, Y: 0),
20 | new(X: 11, Y: 1),
21 | new(X: 12, Y: 1),
22 | new(X: 13, Y: 0),
23 | new(X: 16, Y: 0),
24 | new(X: 17, Y: 1),
25 | new(X: 18, Y: 1),
26 | new(X: 18, Y: 2),
27 | new(X: 19, Y: 1)
28 | ];
29 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D21/Food.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D21;
2 |
3 | public sealed class Food(IEnumerable ingredients, IEnumerable listedAllergens)
4 | {
5 | public HashSet Ingredients { get; } = [..ingredients];
6 | public HashSet ListedAllergens { get; } = [..listedAllergens];
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D22/Deck.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D22;
2 |
3 | public sealed class Deck(IEnumerable initial)
4 | {
5 | private readonly Queue _cards = new(collection: initial);
6 |
7 | public bool HasCards => _cards.Count != 0;
8 | public int CardsRemaining => _cards.Count;
9 | public string State => string.Join(',', _cards);
10 |
11 | public Deck Copy(int numCards)
12 | {
13 | return new Deck(_cards.Take(numCards));
14 | }
15 |
16 | public int DrawFromTop()
17 | {
18 | return _cards.Dequeue();
19 | }
20 |
21 | public void AddToBottom(int card)
22 | {
23 | _cards.Enqueue(card);
24 | }
25 |
26 | public int Score()
27 | {
28 | var score = 0;
29 | while (_cards.Count != 0)
30 | {
31 | score += _cards.Count * _cards.Dequeue();
32 | }
33 | return score;
34 | }
35 | }
--------------------------------------------------------------------------------
/Solutions/Y2020/D25/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2020.D25;
2 |
3 | [PuzzleInfo("Combo Breaker", Topics.Math, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | private const long DeviceSubject = 7L;
7 | private const long Mod = 20201227L;
8 |
9 | public override int Parts => 1;
10 |
11 | public override object Run(int part)
12 | {
13 | return part switch
14 | {
15 | 1 => CrackEncryption(ParsePublicKeys(GetInputLines())),
16 | _ => PuzzleNotSolvedString
17 | };
18 | }
19 |
20 | private static long CrackEncryption((long Key1, long Key2) publicKeys)
21 | {
22 | return Transform(publicKeys.Key2, FindLoopSize(DeviceSubject, publicKeys.Key1));
23 | }
24 |
25 | private static int FindLoopSize(long subject, long pubKey)
26 | {
27 | var loops = 0;
28 | var value = 1L;
29 |
30 | while (value != pubKey)
31 | {
32 | value = Loop(subject, value);
33 | loops++;
34 | }
35 |
36 | return loops;
37 | }
38 |
39 | private static long Transform(long subject, int numLoops)
40 | {
41 | var value = 1L;
42 | for (var i = 0; i < numLoops; i++)
43 | {
44 | value = Loop(subject, value);
45 | }
46 | return value;
47 | }
48 |
49 | private static long Loop(long subject, long value)
50 | {
51 | return value * subject % Mod;
52 | }
53 |
54 | private static (long Key1, long Key2) ParsePublicKeys(string[] input)
55 | {
56 | return (long.Parse(input[0]), long.Parse(input[1]));
57 | }
58 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D01;
2 |
3 | [PuzzleInfo("Sonar Sweep", Topics.None, Difficulty.Easy)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | return part switch
9 | {
10 | 1 => CountDepthIncreases(windowSize: 1),
11 | 2 => CountDepthIncreases(windowSize: 3),
12 | _ => PuzzleNotSolvedString
13 | };
14 | }
15 |
16 | private int CountDepthIncreases(int windowSize)
17 | {
18 | var numIncreases = 0;
19 | var window = new Queue(windowSize);
20 |
21 | var depths = GetInputLines()
22 | .Select(int.Parse)
23 | .ToList();
24 |
25 | foreach (var depth in depths)
26 | {
27 | if (window.Count < windowSize)
28 | {
29 | window.Enqueue(depth);
30 | continue;
31 | }
32 |
33 | var prevSum = window.Sum();
34 | window.Dequeue();
35 | window.Enqueue(depth);
36 | var curSum = window.Sum();
37 |
38 | if (curSum > prevSum)
39 | {
40 | numIncreases++;
41 | }
42 | }
43 |
44 | return numIncreases;
45 | }
46 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D03/BitCriteria.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D03;
2 |
3 | public enum BitCriteria
4 | {
5 | MostCommon = 0,
6 | LeastCommon
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D06/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D06;
2 |
3 | [PuzzleInfo("Lanternfish", Topics.Math | Topics.Simulation, Difficulty.Medium, favourite: true)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | private const int ResetTo = 6;
7 | private const int SpawnAt = 8;
8 |
9 | public override object Run(int part)
10 | {
11 | return part switch
12 | {
13 | 1 => ModelLanternFish(GetInitialState(), days: 80),
14 | 2 => ModelLanternFish(GetInitialState(), days: 256),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static long ModelLanternFish(IEnumerable initialState, int days)
20 | {
21 | var internalTimerCount = new long[SpawnAt + 1];
22 | foreach (var timer in initialState)
23 | {
24 | internalTimerCount[timer]++;
25 | }
26 |
27 | for (var day = 0; day < days; day++)
28 | {
29 | var readyToSpawn = internalTimerCount[0];
30 | for (var t = 0; t < SpawnAt; t++)
31 | {
32 | internalTimerCount[t] = internalTimerCount[t + 1];
33 | }
34 |
35 | internalTimerCount[ResetTo] += readyToSpawn;
36 | internalTimerCount[SpawnAt] = readyToSpawn;
37 | }
38 |
39 | return internalTimerCount.Sum();
40 | }
41 |
42 | private IEnumerable GetInitialState()
43 | {
44 | return GetInputText()
45 | .Split(separator: ',')
46 | .Select(int.Parse);
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D07/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2021.D07;
4 |
5 | [PuzzleInfo("The Treachery of Whales", Topics.Math, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => CalculateMinCumulativeCost(n => n),
13 | 2 => CalculateMinCumulativeCost(n => n * (n + 1)/2),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private int CalculateMinCumulativeCost(Func distanceCostFunc)
19 | {
20 | var positions = GetPositions();
21 | var distinctPositions = positions.Distinct();
22 | var best = int.MaxValue;
23 |
24 | foreach (var distinct in distinctPositions)
25 | {
26 | best = Math.Min(
27 | val1: best,
28 | val2: positions.Sum(pos => distanceCostFunc(Math.Abs(distinct - pos))));
29 | }
30 |
31 | return best;
32 | }
33 |
34 | private int[] GetPositions()
35 | {
36 | return GetInputText().ParseInts();
37 | }
38 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D08/DisplayObservation.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D08;
2 |
3 | public readonly struct DisplayObservation(IEnumerable uniqueSignalPatterns, IEnumerable outputDigits)
4 | {
5 | public List UniqueSegmentPatterns { get; } = [..uniqueSignalPatterns];
6 | public List OutputDigitSegments { get; } = [..outputDigits];
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D11/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 | using Utilities.Geometry.Euclidean;
3 |
4 | namespace Solutions.Y2021.D11;
5 |
6 | [PuzzleInfo("Dumbo Octopus", Topics.Simulation, Difficulty.Medium)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override object Run(int part)
10 | {
11 | var input = GetInputLines();
12 | var state = Grid2D.MapChars(strings: input, elementFunc:StringExtensions.AsDigit);
13 | var grid = new OctopusGrid(state);
14 |
15 | return part switch
16 | {
17 | 1 => grid.CountFlashes(steps: 100, type: OctopusGrid.FlashType.Single),
18 | 2 => grid.CountStepsUntilFlash(type: OctopusGrid.FlashType.All),
19 | _ => PuzzleNotSolvedString
20 | };
21 | }
22 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D12/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 |
3 | namespace Solutions.Y2021.D12;
4 |
5 | [PuzzleInfo("Passage Pathing", Topics.Graphs|Topics.Recursion, Difficulty.Medium, favourite: true)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => CountPaths(bonusSmallCaveVisit: false),
13 | 2 => CountPaths(bonusSmallCaveVisit: true),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private int CountPaths(bool bonusSmallCaveVisit)
19 | {
20 | var adjacencyMap = ParseAdjacencyMap(GetInputLines());
21 | var caveTraverser = new PathFinder(adjacencyMap, bonusSmallCaveVisit);
22 | var numPaths = 0;
23 |
24 | void OnPathFound()
25 | {
26 | numPaths++;
27 | }
28 |
29 | caveTraverser.PathFound += OnPathFound;
30 | caveTraverser.Run();
31 |
32 | return numPaths;
33 | }
34 |
35 | private static DefaultDict> ParseAdjacencyMap(IEnumerable lines)
36 | {
37 | var adjacencyMap = new DefaultDict>(defaultSelector: _ => []);
38 | foreach (var line in lines)
39 | {
40 | var vertices = line.Split(separator: '-');
41 | var v1 = vertices[0];
42 | var v2 = vertices[1];
43 |
44 | adjacencyMap[v1].Add(v2);
45 | adjacencyMap[v2].Add(v1);
46 | }
47 | return adjacencyMap;
48 | }
49 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D13/FoldType.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D13;
2 |
3 | public enum FoldType
4 | {
5 | Horizontal,
6 | Vertical
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D14/Rule.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D14;
2 |
3 | public readonly struct Rule
4 | {
5 | public char Lhs { get; init; }
6 | public char Rhs { get; init; }
7 | public char Insert { get; init; }
8 |
9 | public (char, char) MatchKey => (Lhs, Rhs);
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D16/Operator.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D16;
2 |
3 | public enum Operator
4 | {
5 | Sum,
6 | Product,
7 | Minimum,
8 | Maximum,
9 | Identity,
10 | GreaterThan,
11 | LessThan,
12 | EqualTo
13 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D16/Section.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D16;
2 |
3 | public enum Section
4 | {
5 | Version,
6 | TypeId,
7 | LengthTypeId,
8 | SubPacketCount,
9 | SubPacketBits
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D18/Element.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D18;
2 |
3 | public readonly struct Element
4 | {
5 | public static readonly Element Open = new (Type.Open);
6 | public static readonly Element Close = new (Type.Close);
7 | public static readonly Element Delim = new (Type.Delim);
8 |
9 | public Type ElementType { get; }
10 | public int Value { get; }
11 |
12 | private Element(Type elementType)
13 | {
14 | ElementType = elementType;
15 | Value = 0;
16 | }
17 |
18 | public Element(int value) : this(Type.Value)
19 | {
20 | Value = value;
21 | }
22 |
23 | public override string ToString()
24 | {
25 | return ElementType switch
26 | {
27 | Type.Open => "[",
28 | Type.Close => "]",
29 | Type.Delim => ",",
30 | Type.Value => Value.ToString(),
31 | _ => throw new ArgumentOutOfRangeException()
32 | };
33 | }
34 |
35 | public enum Type
36 | {
37 | Open,
38 | Close,
39 | Delim,
40 | Value
41 | }
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/Solutions/Y2021/D18/SnailfishParser.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D18;
2 |
3 | using SfNumber = List;
4 |
5 | public static class SnailfishParser
6 | {
7 | private const char Open = '[';
8 | private const char Close = ']';
9 | private const char Delim = ',';
10 |
11 | private static readonly HashSet SyntaxSet =
12 | [
13 | Open,
14 | Close,
15 | Delim
16 | ];
17 |
18 | public static SfNumber Parse(string number)
19 | {
20 | var elements = new SfNumber();
21 | var valueBuffer = new Queue();
22 |
23 | foreach (var c in number)
24 | {
25 | if (SyntaxSet.Contains(c) && valueBuffer.Count > 0)
26 | {
27 | var valueString = string.Concat(valueBuffer);
28 | var valueElement = new Element(int.Parse(valueString));
29 |
30 | valueBuffer.Clear();
31 | elements.Add(valueElement);
32 | }
33 |
34 | switch (c)
35 | {
36 | case Open:
37 | elements.Add(Element.Open);
38 | continue;
39 | case Close:
40 | elements.Add(Element.Close);
41 | continue;
42 | case Delim:
43 | elements.Add(Element.Delim);
44 | continue;
45 | }
46 |
47 | valueBuffer.Enqueue(c);
48 | }
49 |
50 | return elements;
51 | }
52 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D19/Map.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2021.D19;
4 |
5 | public sealed class Map
6 | {
7 | public Dictionary KnownScanners { get; } = new();
8 | public Dictionary> KnownBeacons { get; } = new();
9 |
10 | public Map(Reporting referenceReporting)
11 | {
12 | KnownScanners.Add(referenceReporting.ScannerId, Vec3D.Zero);
13 | KnownBeacons.Add(referenceReporting.ScannerId, [..referenceReporting.Beacons]);
14 | }
15 |
16 | public int GetDistinctBeaconCount()
17 | {
18 | return KnownBeacons
19 | .SelectMany(kvp => kvp.Value)
20 | .Distinct()
21 | .Count();
22 | }
23 |
24 | public int GetMaxScannerDistance()
25 | {
26 | var max = 0;
27 | var scanners = KnownScanners.Values.ToList();
28 |
29 | // ReSharper disable once LoopCanBeConvertedToQuery
30 | for (var i = 0; i < scanners.Count; i++)
31 | for (var j = 0; j < scanners.Count; j++)
32 | {
33 | if (i != j)
34 | {
35 | max = Math.Max(max, Vec3D.Distance(scanners[i], scanners[j], metric: Metric.Taxicab));
36 | }
37 | }
38 |
39 | return max;
40 | }
41 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D19/Records.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2021.D19;
4 |
5 | public readonly record struct Transform(Quaternion FacingTransform, Axis RotationAxis);
6 | public readonly record struct Reporting(int ScannerId, Vec3D[] Beacons);
--------------------------------------------------------------------------------
/Solutions/Y2021/D21/DeterministicDie.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D21;
2 |
3 | public sealed class DeterministicDie
4 | {
5 | private const int Sides = 100;
6 | private int _nextRollValue = 1;
7 |
8 | public int NumRolls { get; private set; }
9 |
10 | public int Roll()
11 | {
12 | NumRolls++;
13 | var value = _nextRollValue++;
14 |
15 | if (_nextRollValue > Sides)
16 | {
17 | _nextRollValue = 1;
18 | }
19 |
20 | return value;
21 | }
22 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D21/Records.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2021.D21;
2 |
3 | public record struct Player(int Position, int Score);
4 | public record struct GameState(Player P1, Player P2);
5 | public record struct WinCounts(long P1, long P2);
--------------------------------------------------------------------------------
/Solutions/Y2021/D23/Input.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2021.D23;
4 |
5 | public static class Input
6 | {
7 | public static readonly Dictionary Part1 = new()
8 | {
9 | { new Vec2D(X: 2, Y: 1), 'B' },
10 | { new Vec2D(X: 2, Y: 0), 'C' },
11 |
12 | { new Vec2D(X: 4, Y: 1), 'C' },
13 | { new Vec2D(X: 4, Y: 0), 'D' },
14 |
15 | { new Vec2D(X: 6, Y: 1), 'A' },
16 | { new Vec2D(X: 6, Y: 0), 'D' },
17 |
18 | { new Vec2D(X: 8, Y: 1), 'B' },
19 | { new Vec2D(X: 8, Y: 0), 'A' }
20 | };
21 |
22 | public static readonly Dictionary Part2 = new()
23 | {
24 | { new Vec2D(X: 2, Y: 3), 'B' },
25 | { new Vec2D(X: 2, Y: 2), 'D' },
26 | { new Vec2D(X: 2, Y: 1), 'D' },
27 | { new Vec2D(X: 2, Y: 0), 'C' },
28 |
29 | { new Vec2D(X: 4, Y: 3), 'C' },
30 | { new Vec2D(X: 4, Y: 2), 'C' },
31 | { new Vec2D(X: 4, Y: 1), 'B' },
32 | { new Vec2D(X: 4, Y: 0), 'D' },
33 |
34 | { new Vec2D(X: 6, Y: 3), 'A' },
35 | { new Vec2D(X: 6, Y: 2), 'B' },
36 | { new Vec2D(X: 6, Y: 1), 'A' },
37 | { new Vec2D(X: 6, Y: 0), 'D' },
38 |
39 | { new Vec2D(X: 8, Y: 3), 'B' },
40 | { new Vec2D(X: 8, Y: 2), 'A' },
41 | { new Vec2D(X: 8, Y: 1), 'C' },
42 | { new Vec2D(X: 8, Y: 0), 'A' }
43 | };
44 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D23/Move.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2021.D23;
4 |
5 | public readonly record struct Move(Vec2D From, Vec2D To, int Cost, MoveType Type);
6 |
7 | public enum MoveType
8 | {
9 | ToHallway,
10 | ToSideRoom
11 | }
--------------------------------------------------------------------------------
/Solutions/Y2021/D23/SideRoom.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2021.D23;
4 |
5 | public sealed class SideRoom
6 | {
7 | private readonly int _abscissa;
8 | private readonly int _depth;
9 |
10 | public SideRoom(int abscissa, int depth)
11 | {
12 | _abscissa = abscissa;
13 | _depth = depth;
14 | }
15 |
16 | public Vec2D Top => new(_abscissa, _depth - 1);
17 | public Vec2D Bottom => new(_abscissa, 0);
18 |
19 | public bool Contains(Vec2D pos)
20 | {
21 | return pos.X == _abscissa && pos.Y >= 0 && pos.Y < _depth;
22 | }
23 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2022.D01;
4 |
5 | [PuzzleInfo("Calorie Counting", Topics.None, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => GetMaxCalories(num: 1),
13 | 2 => GetMaxCalories(num: 3),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private int GetMaxCalories(int num)
19 | {
20 | return GetInputLines()
21 | .ChunkByNonEmpty()
22 | .Select(chunk => chunk.Sum(int.Parse))
23 | .OrderDescending()
24 | .Take(num)
25 | .Sum();
26 | }
27 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D02/RockPaperScissorsChoice.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D02;
2 |
3 | public enum RockPaperScissorsChoice
4 | {
5 | Rock = 0,
6 | Paper,
7 | Scissors
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D02/RockPaperScissorsResult.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D02;
2 |
3 | public enum RockPaperScissorsResult
4 | {
5 | Loss = 0,
6 | Draw,
7 | Win
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D04/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Numerics;
2 |
3 | namespace Solutions.Y2022.D04;
4 |
5 | [PuzzleInfo("Camp Cleanup", Topics.Math, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var assignments = ParseInputLines(parseFunc: ParseAssignment);
11 | return part switch
12 | {
13 | 1 => CountEncapsulating(assignments),
14 | 2 => CountIntersecting(assignments),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int CountEncapsulating(IEnumerable<(Range R1, Range R2)> assignments)
20 | {
21 | return assignments.Count(assignment => CheckContains(a: assignment.R1, b: assignment.R2));
22 | }
23 |
24 | private static int CountIntersecting(IEnumerable<(Range R1, Range R2)> assignments)
25 | {
26 | return assignments.Count(assignment => Range.Overlap(a: assignment.R1, b: assignment.R2, out _));
27 | }
28 |
29 | private static bool CheckContains(Range a, Range b)
30 | {
31 | return a.Contains(b) || b.Contains(a);
32 | }
33 |
34 | private static (Range R1, Range R2) ParseAssignment(string line)
35 | {
36 | var assignments = line.Split(separator: ',');
37 | var r1 = Range.Parse(assignments[0]);
38 | var r2 = Range.Parse(assignments[1]);
39 |
40 | return (r1, r2);
41 | }
42 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D05/CraneInstruction.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D05;
2 |
3 | public readonly record struct CraneInstruction(int NumMoves, int SourceStack, int DestinationStack);
--------------------------------------------------------------------------------
/Solutions/Y2022/D05/CraneOperator.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D05;
2 |
3 | public static class CraneOperator
4 | {
5 | public static Dictionary> ExecutePlan(CranePlan plan, PickupMode pickupMode)
6 | {
7 | return pickupMode switch
8 | {
9 | PickupMode.OneAtATime => ExecutePlanOneAtATime(plan),
10 | PickupMode.ManyAtATime => ExecutePlanManyAtATime(plan),
11 | _ => throw new NoSolutionException()
12 | };
13 | }
14 |
15 | private static Dictionary> ExecutePlanOneAtATime(CranePlan plan)
16 | {
17 | var stacksMap = plan.InitialStacksState;
18 | foreach (var instruction in plan.Instructions)
19 | {
20 | for (var i = 0; i < instruction.NumMoves; i++)
21 | {
22 | stacksMap[instruction.DestinationStack].Push(stacksMap[instruction.SourceStack].Pop());
23 | }
24 | }
25 | return stacksMap;
26 | }
27 |
28 | private static Dictionary> ExecutePlanManyAtATime(CranePlan plan)
29 | {
30 | var stacksMap = plan.InitialStacksState;
31 | var buffer = new Stack();
32 |
33 | foreach (var instruction in plan.Instructions)
34 | {
35 | for (var i = 0; i < instruction.NumMoves; i++)
36 | {
37 | buffer.Push(stacksMap[instruction.SourceStack].Pop());
38 | }
39 |
40 | while (buffer.Count > 0)
41 | {
42 | stacksMap[instruction.DestinationStack].Push(buffer.Pop());
43 | }
44 | }
45 |
46 | return stacksMap;
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D05/PickupMode.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D05;
2 |
3 | public enum PickupMode
4 | {
5 | OneAtATime,
6 | ManyAtATime
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D05/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace Solutions.Y2022.D05;
4 |
5 | [PuzzleInfo("Supply Stacks", Topics.Simulation, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | if (!CranePlan.TryParse(GetInputLines(), out var plan))
11 | {
12 | throw new NoSolutionException(message: "Failed to parse input");
13 | }
14 |
15 | return part switch
16 | {
17 | 1 => GetTopCratesAfterPlan(plan!, PickupMode.OneAtATime),
18 | 2 => GetTopCratesAfterPlan(plan!, PickupMode.ManyAtATime),
19 | _ => PuzzleNotSolvedString
20 | };
21 | }
22 |
23 | private static string GetTopCratesAfterPlan(CranePlan plan, PickupMode mode)
24 | {
25 | return GetTopCrates(CraneOperator.ExecutePlan(plan, mode));
26 | }
27 |
28 | private static string GetTopCrates(Dictionary> state)
29 | {
30 | var sb = new StringBuilder();
31 | foreach (var stack in state.Values)
32 | {
33 | sb.Append(stack.Peek());
34 | }
35 |
36 | return sb.ToString();
37 | }
38 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D06/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 |
3 | namespace Solutions.Y2022.D06;
4 |
5 | [PuzzleInfo("Tuning Trouble", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var datastream = GetInputText();
11 | return part switch
12 | {
13 | 1 => ListenForStartMarker(datastream, markerLength: 4),
14 | 2 => ListenForStartMarker(datastream, markerLength: 14),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int ListenForStartMarker(string datastream, int markerLength)
20 | {
21 | var buffer = new Queue(capacity: markerLength);
22 | var bufferContentMap = new DefaultDict(defaultValue: 0);
23 |
24 | for (var i = 0; i < datastream.Length; i++)
25 | {
26 | var received = datastream[i];
27 | if (buffer.Count >= markerLength)
28 | {
29 | var token = buffer.Dequeue();
30 | if (--bufferContentMap[token] <= 0)
31 | {
32 | bufferContentMap.Remove(token);
33 | }
34 | }
35 |
36 | bufferContentMap[received]++;
37 | buffer.Enqueue(received);
38 |
39 | if (bufferContentMap.Count == markerLength)
40 | {
41 | return i + 1;
42 | }
43 | }
44 |
45 | throw new NoSolutionException();
46 | }
47 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D07/Command.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D07;
2 |
3 | public enum Command
4 | {
5 | Cd,
6 | Ls
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D07/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D07;
2 |
3 | [PuzzleInfo("No Space Left On Device", Topics.StringParsing, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | var consoleOutput = GetInputLines();
9 | var consoleParser = new ConsoleParser();
10 | var sizeIndex = consoleParser.BuildDirectoryMap(consoleOutput);
11 |
12 | return part switch
13 | {
14 | 1 => SumDirectoriesUnderSize(sizeIndex, thresholdSize: 100000),
15 | 2 => FreeUpSpace(sizeIndex, systemVolume: 70000000, requiredSpace: 30000000),
16 | _ => PuzzleNotSolvedString
17 | };
18 | }
19 |
20 | private static int SumDirectoriesUnderSize(IDictionary sizeIndex, int thresholdSize)
21 | {
22 | return sizeIndex.Values
23 | .Where(v => v <= thresholdSize)
24 | .Sum();
25 | }
26 |
27 | private static int FreeUpSpace(IDictionary directorySizeIndex, int systemVolume, int requiredSpace)
28 | {
29 | var freeSpace = systemVolume - directorySizeIndex[ConsoleParser.RootDirectoryPath];
30 | var needed = requiredSpace - freeSpace;
31 |
32 | return directorySizeIndex.Values
33 | .Where(v => v >= needed)
34 | .Min();
35 | }
36 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D10/Cpu.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D10;
2 |
3 | public sealed class Cpu
4 | {
5 | public readonly struct State
6 | {
7 | public int Cycle { get; init; }
8 | public int X { get; init; }
9 | }
10 |
11 | public enum Opcode
12 | {
13 | // ReSharper disable IdentifierTypo
14 | Addx = 0,
15 | Noop = 1
16 | // ReSharper restore IdentifierTypo
17 | }
18 |
19 | public event Action? Ticked;
20 |
21 | private int Cycle { get; set; } = 1;
22 | private int X { get; set; } = 1;
23 |
24 | public void Run(IEnumerable<(Opcode opcode, int arg)> instructions)
25 | {
26 | foreach (var instruction in instructions)
27 | {
28 | switch (instruction.opcode)
29 | {
30 | case Opcode.Noop:
31 | Tick();
32 | break;
33 | case Opcode.Addx:
34 | Tick();
35 | Tick();
36 | X += instruction.arg;
37 | break;
38 | default:
39 | throw new NoSolutionException();
40 | }
41 | }
42 | }
43 |
44 | private void Tick()
45 | {
46 | Ticked?.Invoke(GetState());
47 | Cycle++;
48 | }
49 |
50 | private State GetState()
51 | {
52 | return new State { Cycle = Cycle, X = X };
53 | }
54 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D11/Operator.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D11;
2 |
3 | public enum Operator
4 | {
5 | Add,
6 | Multiply,
7 | Square
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D11/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D11;
2 |
3 | [PuzzleInfo("Monkey in the Middle", Topics.Simulation|Topics.Math, Difficulty.Medium)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | public override object Run(int part)
7 | {
8 | return part switch
9 | {
10 | 1 => QuantifyMonkeyBusiness(rounds: 20, applyBoredDivisor: true),
11 | 2 => QuantifyMonkeyBusiness(rounds: 10000, applyBoredDivisor: false),
12 | _ => PuzzleNotSolvedString
13 | };
14 | }
15 |
16 | private long QuantifyMonkeyBusiness(int rounds, bool applyBoredDivisor)
17 | {
18 | var monkeyMap = MonkeyData.Parse(GetInputLines(), applyBoredDivisor);
19 | var activityCounts = new long[monkeyMap.Count];
20 | var round = 0;
21 |
22 | while (round < rounds)
23 | {
24 | for (var i = 0; i < monkeyMap.Count; i++)
25 | {
26 | while (monkeyMap[i].IsHoldingItem)
27 | {
28 | var (throwTo, thrownItem) = monkeyMap[i].InspectNextItem();
29 | activityCounts[i]++;
30 | monkeyMap[throwTo].CatchItem(thrownItem);
31 | }
32 | }
33 |
34 | round++;
35 | }
36 |
37 | return activityCounts
38 | .OrderDescending()
39 | .Take(2)
40 | .Aggregate((first, second) => first * second);
41 |
42 | }
43 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/ComparisonResult.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D13;
2 |
3 | public enum ComparisonResult
4 | {
5 | Ordered = -1,
6 | Indeterminate,
7 | Scrambled
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/IntegerPacketElement.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D13;
2 |
3 | public sealed class IntegerPacketElement(int value) : PacketElement
4 | {
5 | public int Value { get; } = value;
6 |
7 | public ListPacketElement AsList()
8 | {
9 | return new ListPacketElement(elements: [this]);
10 | }
11 |
12 | public override string ToString()
13 | {
14 | return Value.ToString();
15 | }
16 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/ListPacketElement.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace Solutions.Y2022.D13;
4 |
5 | public sealed class ListPacketElement : PacketElement
6 | {
7 | private readonly List _elements = [];
8 |
9 | public ListPacketElement(IEnumerable? elements)
10 | {
11 | if (elements == null)
12 | {
13 | return;
14 | }
15 |
16 | foreach (var element in elements)
17 | {
18 | _elements.Add(element);
19 | }
20 | }
21 |
22 | public PacketElement this[int i] => _elements[i];
23 | public int Count => _elements.Count;
24 |
25 | public bool HasElementAtIndex(int i)
26 | {
27 | return i >= 0 && i < _elements.Count;
28 | }
29 |
30 | public override string ToString()
31 | {
32 | var sb = new StringBuilder();
33 | sb.Append(ListStart);
34 |
35 | for (var i = 0; i < _elements.Count; i++)
36 | {
37 | sb.Append(_elements[i]);
38 | if (i != _elements.Count - 1)
39 | {
40 | sb.Append(ElementDelimiter);
41 | }
42 | }
43 |
44 | sb.Append(ListEnd);
45 | return sb.ToString();
46 | }
47 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/PacketElement.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D13;
2 |
3 | public abstract class PacketElement : IComparable
4 | {
5 | public const char ElementDelimiter = ',';
6 | public const char ListStart = '[';
7 | public const char ListEnd = ']';
8 |
9 | public int CompareTo(PacketElement? other)
10 | {
11 | ArgumentNullException.ThrowIfNull(other);
12 | return (int)PacketComparator.CompareElements(this, other);
13 | }
14 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/PacketPair.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D13;
2 |
3 | public readonly record struct PacketPair(int Index, PacketElement First, PacketElement Second);
--------------------------------------------------------------------------------
/Solutions/Y2022/D13/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D13;
2 |
3 | [PuzzleInfo("Distress Signal", Topics.StringParsing, Difficulty.Hard)]
4 | public sealed class Solution : SolutionBase
5 | {
6 | private const string DivisorPacket1 = "[[2]]";
7 | private const string DivisorPacket2 = "[[6]]";
8 |
9 | public override object Run(int part)
10 | {
11 | return part switch
12 | {
13 | 1 => SumOrderedPacketIndices(PacketParser.ParsePairs(GetInputLines())),
14 | 2 => CalculateDecoderKey(PacketParser.ParsePackets(GetInputLines())),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int SumOrderedPacketIndices(IEnumerable pairs)
20 | {
21 | return pairs
22 | .Where(pair => pair.First.CompareTo(pair.Second) < 0)
23 | .Sum(pair => pair.Index);
24 | }
25 |
26 | private static int CalculateDecoderKey(IEnumerable packets)
27 | {
28 | var list = packets.ToList();
29 | var divisor1 = PacketParser.ParseElement(DivisorPacket1);
30 | var divisor2 = PacketParser.ParseElement(DivisorPacket2);
31 |
32 | list.Add(divisor1);
33 | list.Add(divisor2);
34 | list.Sort();
35 |
36 | var firstIndex = list.IndexOf(divisor1);
37 | var secondIndex = list.IndexOf(divisor2);
38 |
39 | return (firstIndex + 1) * (secondIndex + 1);
40 | }
41 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D15/Reporting.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D15;
4 |
5 | public readonly struct Reporting(Vec2D sensorPos, Vec2D beaconPos)
6 | {
7 | public Vec2D SensorPos { get; } = sensorPos;
8 | public Vec2D BeaconPos { get; } = beaconPos;
9 | public int Range { get; } = Vec2D.Distance(a: sensorPos, b: beaconPos, Metric.Taxicab);
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D16/Strategy.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D16;
2 |
3 | public readonly struct Strategy(int flow, IEnumerable opened)
4 | {
5 | public int Flow { get; } = flow;
6 | public IReadOnlySet Opened { get; } = new HashSet(opened);
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D17/JetPattern.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D17;
4 |
5 | public sealed class JetPattern
6 | {
7 | private static readonly Dictionary JetVectorMap = new()
8 | {
9 | { '>', Vec2D.Right },
10 | { '<', Vec2D.Left }
11 | };
12 |
13 |
14 | private readonly List _list;
15 |
16 | public int Index { get; private set; }
17 |
18 | private JetPattern(IEnumerable vectors)
19 | {
20 | Index = 0;
21 | _list = [..vectors];
22 | }
23 |
24 | public Vec2D Next()
25 | {
26 | var next = _list[Index % _list.Count];
27 | Index = (Index + 1) % _list.Count;
28 | return next;
29 | }
30 |
31 | public static JetPattern Parse(string sequence)
32 | {
33 | return new JetPattern(sequence.Select(c => JetVectorMap[c]));
34 | }
35 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D17/RockSource.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D17;
2 |
3 | public static class RockSource
4 | {
5 | private static readonly List List = new();
6 |
7 | static RockSource()
8 | {
9 | List.Add(new HorizontalLine());
10 | List.Add(new Plus());
11 | List.Add(new L());
12 | List.Add(new VerticalLine());
13 | List.Add(new Square());
14 | }
15 |
16 | public static Rock Get(int index)
17 | {
18 | return List[index % List.Count];
19 | }
20 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D17/Rocks.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D17;
4 |
5 | public abstract class Rock
6 | {
7 | public abstract HashSet Shape { get; }
8 | }
9 |
10 | public sealed class HorizontalLine : Rock
11 | {
12 | public override HashSet Shape { get; } =
13 | [
14 | new(X: 0, Y: 0),
15 | new(X: 1, Y: 0),
16 | new(X: 2, Y: 0),
17 | new(X: 3, Y: 0)
18 | ];
19 | }
20 |
21 | public sealed class Plus : Rock
22 | {
23 | public override HashSet Shape { get; } =
24 | [
25 | new(X: 1, Y: 0),
26 | new(X: 1, Y: 1),
27 | new(X: 1, Y: 2),
28 | new(X: 0, Y: 1),
29 | new(X: 2, Y: 1)
30 | ];
31 | }
32 |
33 | public sealed class L : Rock
34 | {
35 | public override HashSet Shape { get; } =
36 | [
37 | new(X: 0, Y: 0),
38 | new(X: 1, Y: 0),
39 | new(X: 2, Y: 0),
40 | new(X: 2, Y: 1),
41 | new(X: 2, Y: 2)
42 | ];
43 | }
44 |
45 | public sealed class VerticalLine : Rock
46 | {
47 | public override HashSet Shape { get; } =
48 | [
49 | new(X: 0, Y: 0),
50 | new(X: 0, Y: 1),
51 | new(X: 0, Y: 2),
52 | new(X: 0, Y: 3)
53 | ];
54 | }
55 |
56 | public sealed class Square : Rock
57 | {
58 | public override HashSet Shape { get; } =
59 | [
60 | new(X: 0, Y: 0),
61 | new(X: 0, Y: 1),
62 | new(X: 1, Y: 0),
63 | new(X: 1, Y: 1)
64 | ];
65 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D19/Materials.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D19;
2 |
3 | [Flags]
4 | public enum Materials
5 | {
6 | Ore = 1 << 0,
7 | Clay = 1 << 1,
8 | Obsidian = 1 << 2,
9 | Geode = 1 << 3,
10 | All = ~0
11 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D21/AlgebraicOperation.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D21;
2 |
3 | public readonly struct AlgebraicOperation
4 | {
5 | public Operator Operator { get; init; }
6 | public long KnownOperand { get; init; }
7 | public bool KnownOperandOnLhs { get; init; }
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D21/Expression.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D21;
2 |
3 | public readonly struct Expression
4 | {
5 | public string Id { get; init; }
6 | public Operator Operator { get; init; }
7 | public long Value { get; init; }
8 | public string Lhs { get; init; }
9 | public string Rhs { get; init; }
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D21/ExpressionFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D21;
2 |
3 | public static class ExpressionFactory
4 | {
5 | private const char IdDelimiter = ':';
6 | private const char ElementDelimiter = ' ';
7 | private static readonly Dictionary OperatorMap = new()
8 | {
9 | { '+', Operator.Add },
10 | { '-', Operator.Subtract },
11 | { '*', Operator.Multiply },
12 | { '/', Operator.Divide }
13 | };
14 |
15 | public static Expression Parse(string expressionStr)
16 | {
17 | var elements = expressionStr.Split(IdDelimiter, StringSplitOptions.TrimEntries);
18 | var arguments = elements[1].Split(ElementDelimiter);
19 |
20 | if (arguments.Length == 1)
21 | {
22 | return new Expression
23 | {
24 | Id = elements[0],
25 | Operator = Operator.Identity,
26 | Value = long.Parse(elements[1])
27 | };
28 | }
29 |
30 | return new Expression
31 | {
32 | Id = elements[0],
33 | Operator = OperatorMap[arguments[1][0]],
34 | Lhs = arguments[0],
35 | Rhs = arguments[2]
36 | };
37 | }
38 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D21/Operator.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D21;
2 |
3 | public enum Operator
4 | {
5 | Identity,
6 | Add,
7 | Subtract,
8 | Multiply,
9 | Divide
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D22/Instruction.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D22;
4 |
5 | public readonly struct Instruction(int steps, Quaternion rot)
6 | {
7 | public int Steps { get; } = steps;
8 | public Quaternion Rot { get; } = rot;
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D22/MoveMode.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D22;
2 |
3 | public enum MoveMode
4 | {
5 | Planar,
6 | Cubic
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D22/Square.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D22;
2 |
3 | public enum Square
4 | {
5 | Free,
6 | Blocked,
7 | OutOfBounds
8 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D23/MovePreferences.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D23;
4 |
5 | public static class MovePreferences
6 | {
7 | private static readonly List<(Vec2D target, HashSet checkSet)> Choices =
8 | [
9 | (target: Vec2D.Up, checkSet:[new Vec2D(X: -1, Y: 1), new Vec2D(X: 0, Y: 1), new Vec2D(X: 1, Y: 1)]),
10 | (target: Vec2D.Down, checkSet:[new Vec2D(X: -1, Y: -1), new Vec2D(X: 0, Y: -1), new Vec2D(X: 1, Y: -1)]),
11 | (target: Vec2D.Left, checkSet:[new Vec2D(X: -1, Y: 1), new Vec2D(X: -1, Y: 0), new Vec2D(X: -1, Y: -1)]),
12 | (target: Vec2D.Right, checkSet:[new Vec2D(X: 1, Y: 1), new Vec2D(X: 1, Y: 0), new Vec2D(X: 1, Y: -1)])
13 | ];
14 |
15 | public static int Count => Choices.Count;
16 |
17 | public static (Vec2D target, HashSet checkSet) Get(int i)
18 | {
19 | return Choices[i % Count];
20 | }
21 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D24/Blizzards.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2022.D24;
4 |
5 | public readonly struct Blizzard(Vec2D pos, Vec2D vel, Vec2D respawnAt)
6 | {
7 | private Vec2D Vel { get; } = vel;
8 |
9 | public Vec2D Pos { get; } = pos;
10 | public Vec2D RespawnAt { get; } = respawnAt;
11 | public Vec2D Ahead => Pos + Vel;
12 |
13 | public Blizzard Step()
14 | {
15 | return new Blizzard(pos: Ahead, vel: Vel, RespawnAt);
16 | }
17 |
18 | public Blizzard Respawn()
19 | {
20 | return new Blizzard(pos: RespawnAt, vel: Vel, RespawnAt);
21 | }
22 | }
--------------------------------------------------------------------------------
/Solutions/Y2022/D24/Terrain.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2022.D24;
2 |
3 | public static class Terrain
4 | {
5 | public const char Void = '.';
6 | public const char Wall = '#';
7 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D04/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using Utilities.Extensions;
3 |
4 | namespace Solutions.Y2023.D04;
5 |
6 | [PuzzleInfo("Scratchcards", Topics.None, Difficulty.Easy)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | private readonly record struct Card(int Wins);
10 |
11 | public override object Run(int part)
12 | {
13 | return part switch
14 | {
15 | 1 => CountScore(),
16 | 2 => CountCards(),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private int CountScore()
22 | {
23 | return ParseInputLines(parseFunc: ParseCard).Sum(card => (int)Math.Pow(2, card.Wins - 1));
24 | }
25 |
26 | private int CountCards()
27 | {
28 | var cards = ParseInputLines(parseFunc: ParseCard);
29 | var counts = Enumerable.Repeat(element: 1, count: cards.Length).ToArray();
30 |
31 | for (var n = 0; n < cards.Length; n++)
32 | for (var w = 1; w <= cards[n].Wins; w++)
33 | {
34 | counts[n + w] += counts[n];
35 | }
36 |
37 | return counts.Sum();
38 | }
39 |
40 | private static Card ParseCard(string line)
41 | {
42 | var match = Regex.Match(input: line, pattern: @"Card\s+(?:\d+):(?:\s+(?\d+))+\s\|(?:\s+(?\d+))+");
43 | var wins = match.Groups["Wins"].ParseInts();
44 | var have = match.Groups["Have"].ParseInts();
45 |
46 | return new Card(Wins: have.Count(wins.Contains));
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D05/Almanac.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2023.D05;
4 |
5 | public sealed class Almanac
6 | {
7 | public IReadOnlyList Seeds { get; }
8 | public IReadOnlyList Maps { get; }
9 |
10 | private Almanac(IEnumerable seeds, IEnumerable> mappings)
11 | {
12 | Seeds = seeds.ToList();
13 | Maps = mappings.Select(MapTable.Build).ToList();
14 | }
15 |
16 | public static Almanac Parse(string[] input)
17 | {
18 | var seeds = input[0].ParseLongs();
19 | var maps = input
20 | .Skip(2)
21 | .ChunkBy(l => !string.IsNullOrWhiteSpace(l) && !l.Contains(':'))
22 | .Select(c => c.Select(ParseMapEntry).ToList());
23 |
24 | return new Almanac(seeds, maps);
25 | }
26 |
27 | private static MapEntry ParseMapEntry(string line)
28 | {
29 | var longs = line.ParseLongs();
30 | return new MapEntry(
31 | DestStart: longs[0],
32 | SourceStart: longs[1],
33 | RangeLength: longs[2]);
34 | }
35 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D05/MapEntry.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Numerics;
2 |
3 | namespace Solutions.Y2023.D05;
4 |
5 | public readonly record struct MapEntry(long DestStart, long SourceStart, long RangeLength)
6 | {
7 | public long SourceMin => SourceStart;
8 | public long SourceMax => SourceStart + RangeLength - 1;
9 | public Range SourceRange => new (SourceStart, SourceMax);
10 |
11 | public Range Apply(Range range)
12 | {
13 | return new Range(
14 | min: DestStart + range.Min - SourceStart,
15 | max: DestStart + range.Max - SourceStart);
16 | }
17 |
18 | public static MapEntry Default(long min, long max)
19 | {
20 | return new MapEntry(
21 | DestStart: min,
22 | SourceStart: min,
23 | RangeLength: max - min + 1);
24 | }
25 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D05/MapTable.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D05;
2 |
3 | public sealed class MapTable
4 | {
5 | public IReadOnlyList OrderedEntries { get; }
6 |
7 | private MapTable(IEnumerable orderedEntries)
8 | {
9 | OrderedEntries = new List(collection: orderedEntries);
10 | }
11 |
12 | public static MapTable Build(IEnumerable entries)
13 | {
14 | var queue = new Queue(collection: entries.OrderBy(mapping => mapping.SourceMin));
15 | var order = new List();
16 | var upper = -1L;
17 |
18 | while (queue.Count != 0)
19 | {
20 | var next = queue.Dequeue();
21 | if (upper + 1 != next.SourceMin)
22 | {
23 | order.Add(item: MapEntry.Default(
24 | min: upper + 1,
25 | max: next.SourceMin - 1));
26 | }
27 |
28 | order.Add(next);
29 | upper = order[^1].SourceMax;
30 | }
31 |
32 | // Append with [Max(ranges.Max) + 1, long.MaxValue]
33 | //
34 | if (order[^1].SourceMax != long.MaxValue)
35 | {
36 | order.Add(item: MapEntry.Default(
37 | min: order[^1].SourceMax + 1,
38 | max: long.MaxValue));
39 | }
40 |
41 | return new MapTable(orderedEntries: order);
42 | }
43 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D07/Deck.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D07;
2 |
3 | public static class Deck
4 | {
5 | private const char Joker = 'J';
6 | private static readonly Dictionary RankMap = new()
7 | {
8 | { '2', 2 },
9 | { '3', 3 },
10 | { '4', 4 },
11 | { '5', 5 },
12 | { '6', 6 },
13 | { '7', 7 },
14 | { '8', 8 },
15 | { '9', 9 },
16 | { 'T', 10 },
17 | { 'J', 11 },
18 | { 'Q', 12 },
19 | { 'K', 13 },
20 | { 'A', 14 }
21 | };
22 |
23 | public static bool HasJokers { get; set; }
24 |
25 | public static bool IsJoker(char card) => HasJokers && card == Joker;
26 | public static int GetRank(char card) => IsJoker(card) ? 1 : RankMap[card];
27 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D07/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2023.D07;
4 |
5 | [PuzzleInfo("Camel Cards", Topics.Math, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | return part switch
11 | {
12 | 1 => ScoreHands(jokers: false),
13 | 2 => ScoreHands(jokers: true),
14 | _ => PuzzleNotSolvedString
15 | };
16 | }
17 |
18 | private int ScoreHands(bool jokers)
19 | {
20 | Deck.HasJokers = jokers;
21 | return ParseInputLines(parseFunc: ParseHand)
22 | .Order()
23 | .Select((hand, index) => hand.Bid * (index + 1))
24 | .Sum();
25 | }
26 |
27 | private static Hand ParseHand(string line)
28 | {
29 | var elements = line.Split(separator: ' ');
30 | return new Hand(cards: elements[0], bid: elements[1].ParseInt());
31 | }
32 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D09/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2023.D09;
4 |
5 | [PuzzleInfo("Mirage Maintenance", Topics.Math, Difficulty.Easy)]
6 | public class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | return part switch
12 | {
13 | 1 => input.Sum(line => Extrapolate(report: line, forwards: true)),
14 | 2 => input.Sum(line => Extrapolate(report: line, forwards: false)),
15 | _ => PuzzleNotSolvedString
16 | };
17 | }
18 |
19 | private static int Extrapolate(string report, bool forwards)
20 | {
21 | var values = report.ParseInts();
22 | var initial = forwards
23 | ? values
24 | : values.Reverse();
25 |
26 | IList sequences = [initial.ToArray()];
27 | while (sequences[^1].Any(val => val != 0))
28 | {
29 | sequences.Add(item: sequences[^1]
30 | .Skip(1)
31 | .Select((val, i) => val - sequences[^1][i])
32 | .ToArray());
33 | }
34 |
35 | return sequences
36 | .Reverse()
37 | .Skip(1)
38 | .Aggregate(seed: 0, func: (n, seq) => n + seq[^1]);
39 | }
40 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D12/State.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D12;
2 |
3 | public readonly record struct State(string Pattern, int Pos, char Current, bool RunActive, int RunIndex, int RunLength)
4 | {
5 | public static State Initial(string pattern)
6 | {
7 | return new State(Pattern: pattern, Pos: 0, Current: pattern[0], RunActive: false, RunIndex: -1, RunLength: 0);
8 | }
9 |
10 | public State Branch(char c)
11 | {
12 | return this with { Current = c };
13 | }
14 |
15 | public State Consume()
16 | {
17 | return Current == '.'
18 | ? AdvanceWorking()
19 | : AdvanceDamaged();
20 | }
21 |
22 | private State AdvanceWorking()
23 | {
24 | var c = GetTokenOrDefault(Pos + 1);
25 | return new State(Pattern: Pattern, Pos: Pos + 1, Current: c, RunActive: false, RunIndex: RunIndex, RunLength: 0);
26 | }
27 |
28 | private State AdvanceDamaged()
29 | {
30 | var c = GetTokenOrDefault(Pos + 1);
31 | return RunActive
32 | ? new State(Pattern: Pattern, Pos: Pos + 1, Current: c, RunActive: true, RunIndex: RunIndex, RunLength: RunLength + 1)
33 | : new State(Pattern: Pattern, Pos: Pos + 1, Current: c, RunActive: true, RunIndex: RunIndex + 1, RunLength: 1);
34 | }
35 |
36 | private char GetTokenOrDefault(int pos)
37 | {
38 | return pos < Pattern.Length
39 | ? Pattern[pos]
40 | : '\0';
41 | }
42 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D15/Box.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D15;
2 |
3 | public readonly struct Box(int n)
4 | {
5 | private Dictionary> Index { get; } = new();
6 | private LinkedList Lenses { get; } = [];
7 |
8 | public void Remove(string label)
9 | {
10 | if (Index.Remove(label, out var lens))
11 | {
12 | Lenses.Remove(lens);
13 | }
14 | }
15 |
16 | public void Add(string label, int focalLength)
17 | {
18 | if (Index.TryGetValue(label, out var oldLens))
19 | {
20 | oldLens.Value = focalLength;
21 | }
22 | else
23 | {
24 | Lenses.AddLast(value: focalLength);
25 | Index[label] = Lenses.Last!;
26 | }
27 | }
28 |
29 | public int GetPower()
30 | {
31 | var sum = 0;
32 | var i = 0;
33 | var lens = Lenses.First;
34 |
35 | while (lens != null)
36 | {
37 | sum += (n + 1) * (i++ + 1) * lens.Value;
38 | lens = lens.Next;
39 | }
40 |
41 | return sum;
42 | }
43 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D15/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Collections;
2 | using Utilities.Extensions;
3 |
4 | namespace Solutions.Y2023.D15;
5 |
6 | [PuzzleInfo("Lens Library", Topics.None, Difficulty.Easy)]
7 | public sealed class Solution : SolutionBase
8 | {
9 | public override object Run(int part)
10 | {
11 | var input = GetInputText();
12 | var instr = input.Split(separator: ',');
13 | var boxes = new DefaultDict(defaultSelector: n => new Box(n));
14 |
15 | return part switch
16 | {
17 | 1 => instr.Sum(Hash),
18 | 2 => Install(instr, boxes),
19 | _ => PuzzleNotSolvedString
20 | };
21 | }
22 |
23 | private static int Hash(string s)
24 | {
25 | return s.Aggregate(seed: 0, func: (val, c) => (val + c) * 17 % 256);
26 | }
27 |
28 | private static int Install(IEnumerable instructions, DefaultDict boxes)
29 | {
30 | foreach (var instr in instructions)
31 | {
32 | var label = string.Concat(instr.TakeWhile(char.IsLetter));
33 | var box = boxes[Hash(label)];
34 |
35 | switch (instr[label.Length])
36 | {
37 | case '-':
38 | box.Remove(label);
39 | break;
40 | case '=':
41 | box.Add(label, focalLength: instr.ParseInt());
42 | break;
43 | }
44 | }
45 |
46 | return boxes.Values.Sum(box => box.GetPower());
47 | }
48 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D19/Rule.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D19;
2 |
3 | public readonly record struct Rule(string Lhs, string Op, long Rhs, string Next);
--------------------------------------------------------------------------------
/Solutions/Y2023/D20/ModuleType.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D20;
2 |
3 | public enum ModuleType
4 | {
5 | Broadcaster = 0,
6 | FlipFlop,
7 | Conjunction,
8 | Untyped
9 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D20/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Numerics;
2 |
3 | namespace Solutions.Y2023.D20;
4 |
5 | [PuzzleInfo("Pulse Propagation", Topics.Graphs|Topics.Simulation, Difficulty.Medium)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var input = GetInputLines();
11 | var network = Network.Parse(input);
12 |
13 | return part switch
14 | {
15 | 1 => AggregatePulses(network, b: 1000),
16 | 2 => GetFirstRxSignal(network),
17 | _ => PuzzleNotSolvedString
18 | };
19 | }
20 |
21 | private static long AggregatePulses(Network network, int b)
22 | {
23 | return network
24 | .Simulate(b).Pulses.Values
25 | .Aggregate(seed: 1L, func: (product, count) => product * count);
26 | }
27 |
28 | private static long GetFirstRxSignal(Network network)
29 | {
30 | var trace = network.Simulate(b: 25000);
31 | var cycles = network.ReceiverInputs
32 | .Select(id => trace.Watches[id][^1] - trace.Watches[id][^2])
33 | .ToList();
34 |
35 | return Numerics.Lcm(cycles);
36 | }
37 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D20/Trace.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D20;
2 |
3 | public record struct Trace(IDictionary Pulses, IDictionary> Watches);
--------------------------------------------------------------------------------
/Solutions/Y2023/D22/Brick.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Geometry.Euclidean;
2 |
3 | namespace Solutions.Y2023.D22;
4 |
5 | public sealed class Brick(int id, Aabb3D extents)
6 | {
7 | public int Id => id;
8 | public Aabb3D Extents { get; set; } = extents;
9 |
10 | public static Brick Parse(int id, string line)
11 | {
12 | var ps = line.Split(separator: '~');
13 | var p1 = Vec3D.Parse(ps[0]);
14 | var p2 = Vec3D.Parse(ps[1]);
15 |
16 | return new Brick(id, extents: new Aabb3D(extents: [p1, p2], inclusive: true));
17 | }
18 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D24/Aabb2.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D24;
2 |
3 | public readonly record struct Aabb2(decimal Min, decimal Max)
4 | {
5 | public bool Contains(Vec3 p)
6 | {
7 | return p.X >= Min && p.X <= Max &&
8 | p.Y >= Min && p.Y <= Max;
9 | }
10 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D24/Ray3.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2023.D24;
4 |
5 | public readonly record struct Ray3(Vec3 S, Vec3 V)
6 | {
7 | private const decimal EpsilonParallel = 1e-3m;
8 |
9 | public static bool Intersect2D(Ray3 a, Ray3 b, out Vec3 p)
10 | {
11 | var dx = b.S.X - a.S.X;
12 | var dy = b.S.Y - a.S.Y;
13 | var cp = b.V.X * a.V.Y - b.V.Y * a.V.X;
14 |
15 | if (Math.Abs(cp) < EpsilonParallel)
16 | {
17 | p = Vec3.Zero;
18 | return false;
19 | }
20 |
21 | var u = (dy * b.V.X - dx * b.V.Y) / cp;
22 | var v = (dy * a.V.X - dx * a.V.Y) / cp;
23 |
24 | p = a.GetPoint(u);
25 | return u >= 0 && v >= 0;
26 | }
27 |
28 | public static Ray3 Parse(string line)
29 | {
30 | var n = line.ParseLongs();
31 | var s = new Vec3(X: n[0], Y: n[1], Z: n[2]);
32 | var v = new Vec3(X: n[3], Y: n[4], Z: n[5]);
33 |
34 | return new Ray3(s, v);
35 | }
36 |
37 | private Vec3 GetPoint(decimal t)
38 | {
39 | return new Vec3(
40 | X: S.X + t * V.X,
41 | Y: S.Y + t * V.Y,
42 | Z: S.Z + t * V.Z);
43 | }
44 | }
--------------------------------------------------------------------------------
/Solutions/Y2023/D24/Vec3.cs:
--------------------------------------------------------------------------------
1 | namespace Solutions.Y2023.D24;
2 |
3 | public readonly record struct Vec3(decimal X, decimal Y, decimal Z)
4 | {
5 | public static readonly Vec3 Zero = new(X: 0, Y: 0, Z: 0);
6 | }
--------------------------------------------------------------------------------
/Solutions/Y2024/D01/Solution.cs:
--------------------------------------------------------------------------------
1 | using Utilities.Extensions;
2 |
3 | namespace Solutions.Y2024.D01;
4 |
5 | [PuzzleInfo("Historian Hysteria", Topics.StringParsing, Difficulty.Easy)]
6 | public sealed class Solution : SolutionBase
7 | {
8 | public override object Run(int part)
9 | {
10 | var l1 = new List();
11 | var l2 = new List