mirror of
https://github.com/jcreek/advent-of-code.git
synced 2026-07-13 03:03:46 +00:00
chore(*): Move old solutions into a separate folder
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>_15</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="input.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Day15
|
||||
{
|
||||
public class Dijkstra
|
||||
{
|
||||
public object PartOne(string input) => Solve(GetRiskLevelMap(input));
|
||||
public object PartTwo(string input) => Solve(ScaleUp(GetRiskLevelMap(input)));
|
||||
|
||||
int Solve(Dictionary<Point, int> riskMap)
|
||||
{
|
||||
// Disjktra algorithm
|
||||
|
||||
var topLeft = new Point(0, 0);
|
||||
var bottomRight = new Point(riskMap.Keys.MaxBy(p => p.x).x, riskMap.Keys.MaxBy(p => p.y).y);
|
||||
|
||||
// Visit points in order of cumulted risk
|
||||
// ⭐ .Net 6 finally has a PriorityQueue collection :)
|
||||
var q = new PriorityQueue<Point, int>();
|
||||
var totalRiskMap = new Dictionary<Point, int>();
|
||||
|
||||
totalRiskMap[topLeft] = 0;
|
||||
q.Enqueue(topLeft, 0);
|
||||
|
||||
// It would be enough to go until we find the bottom right corner, but computing all
|
||||
// risk levels is not much more work
|
||||
while (q.Count > 0)
|
||||
{
|
||||
var p = q.Dequeue();
|
||||
|
||||
foreach (var n in Neighbours(p))
|
||||
{
|
||||
if (riskMap.ContainsKey(n) && !totalRiskMap.ContainsKey(n))
|
||||
{
|
||||
var totalRisk = totalRiskMap[p] + riskMap[n];
|
||||
totalRiskMap[n] = totalRisk;
|
||||
if (n == bottomRight)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var h = 0; //bottomRight.y - n.y + bottomRight.x - n.x;
|
||||
q.Enqueue(n, totalRisk + h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return bottom right corner's total risk:
|
||||
return totalRiskMap[bottomRight];
|
||||
}
|
||||
|
||||
// Create an 5x scaled up map, as described in part 2
|
||||
Dictionary<Point, int> ScaleUp(Dictionary<Point, int> map)
|
||||
{
|
||||
var (ccol, crow) = (map.Keys.MaxBy(p => p.x).x + 1, map.Keys.MaxBy(p => p.y).y + 1);
|
||||
|
||||
var res = new Dictionary<Point, int>(
|
||||
from y in Enumerable.Range(0, crow * 5)
|
||||
from x in Enumerable.Range(0, ccol * 5)
|
||||
|
||||
// x, y and risk level in the original map:
|
||||
let tileY = y % crow
|
||||
let tileX = x % ccol
|
||||
let tileRiskLevel = map[new Point(tileX, tileY)]
|
||||
|
||||
// risk level is increased by tile distance from origin:
|
||||
let tileDistance = (y / crow) + (x / ccol)
|
||||
|
||||
// risk level wraps around from 9 to 1:
|
||||
let riskLevel = (tileRiskLevel + tileDistance - 1) % 9 + 1
|
||||
select new KeyValuePair<Point, int>(new Point(x, y), riskLevel)
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// store the points in a dictionary so that we can iterate over them and
|
||||
// to easily deal with points outside the area
|
||||
Dictionary<Point, int> GetRiskLevelMap(string input)
|
||||
{
|
||||
var map = input.Split("\n");
|
||||
return new Dictionary<Point, int>(
|
||||
from y in Enumerable.Range(0, map[0].Length)
|
||||
from x in Enumerable.Range(0, map.Length)
|
||||
select new KeyValuePair<Point, int>(new Point(x, y), map[y][x] - '0')
|
||||
);
|
||||
}
|
||||
|
||||
IEnumerable<Point> Neighbours(Point point) =>
|
||||
new[] {
|
||||
point with {y = point.y + 1},
|
||||
point with {y = point.y - 1},
|
||||
point with {x = point.x + 1},
|
||||
point with {x = point.x - 1},
|
||||
};
|
||||
}
|
||||
}
|
||||
record Point(int x, int y);
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// A C# program for Dijkstra1's single
|
||||
// source shortest path algorithm.
|
||||
// The program is for adjacency matrix
|
||||
// representation of the graph
|
||||
|
||||
namespace Day15
|
||||
{
|
||||
class Dijkstra1
|
||||
{
|
||||
// A utility function to find the
|
||||
// vertex with minimum distance
|
||||
// value, from the set of vertices
|
||||
// not yet included in shortest
|
||||
// path tree
|
||||
static int V = 9;
|
||||
int minDistance(int[] dist,
|
||||
bool[] sptSet)
|
||||
{
|
||||
// Initialize min value
|
||||
int min = int.MaxValue, min_index = -1;
|
||||
|
||||
for (int v = 0; v < V; v++)
|
||||
if (sptSet[v] == false && dist[v] <= min)
|
||||
{
|
||||
min = dist[v];
|
||||
min_index = v;
|
||||
}
|
||||
|
||||
return min_index;
|
||||
}
|
||||
|
||||
// A utility function to print
|
||||
// the constructed distance array
|
||||
void printSolution(int[] dist, int n)
|
||||
{
|
||||
Console.Write("Vertex Distance "
|
||||
+ "from Source\n");
|
||||
for (int i = 0; i < V; i++)
|
||||
Console.Write(i + " \t\t " + dist[i] + "\n");
|
||||
}
|
||||
|
||||
// Function that implements Dijkstra1's
|
||||
// single source shortest path algorithm
|
||||
// for a graph represented using adjacency
|
||||
// matrix representation
|
||||
public void dijkstra(int[,] graph, int src)
|
||||
{
|
||||
int[] dist = new int[V]; // The output array. dist[i]
|
||||
// will hold the shortest
|
||||
// distance from src to i
|
||||
|
||||
// sptSet[i] will true if vertex
|
||||
// i is included in shortest path
|
||||
// tree or shortest distance from
|
||||
// src to i is finalized
|
||||
bool[] sptSet = new bool[V];
|
||||
|
||||
// Initialize all distances as
|
||||
// INFINITE and stpSet[] as false
|
||||
for (int i = 0; i < V; i++)
|
||||
{
|
||||
dist[i] = int.MaxValue;
|
||||
sptSet[i] = false;
|
||||
}
|
||||
|
||||
// Distance of source vertex
|
||||
// from itself is always 0
|
||||
dist[src] = 0;
|
||||
|
||||
// Find shortest path for all vertices
|
||||
for (int count = 0; count < V - 1; count++)
|
||||
{
|
||||
// Pick the minimum distance vertex
|
||||
// from the set of vertices not yet
|
||||
// processed. u is always equal to
|
||||
// src in first iteration.
|
||||
int u = minDistance(dist, sptSet);
|
||||
|
||||
// Mark the picked vertex as processed
|
||||
sptSet[u] = true;
|
||||
|
||||
// Update dist value of the adjacent
|
||||
// vertices of the picked vertex.
|
||||
for (int v = 0; v < V; v++)
|
||||
|
||||
// Update dist[v] only if is not in
|
||||
// sptSet, there is an edge from u
|
||||
// to v, and total weight of path
|
||||
// from src to v through u is smaller
|
||||
// than current value of dist[v]
|
||||
if (!sptSet[v] && graph[u, v] != 0 &&
|
||||
dist[u] != int.MaxValue && dist[u] + graph[u, v] < dist[v])
|
||||
dist[v] = dist[u] + graph[u, v];
|
||||
}
|
||||
|
||||
// print the constructed distance array
|
||||
printSolution(dist, V);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
// public static void Main()
|
||||
// {
|
||||
// /* Let us create the example
|
||||
//graph discussed above */
|
||||
// int[,] graph = new int[,] { { 0, 4, 0, 0, 0, 0, 0, 8, 0 },
|
||||
// { 4, 0, 8, 0, 0, 0, 0, 11, 0 },
|
||||
// { 0, 8, 0, 7, 0, 4, 0, 0, 2 },
|
||||
// { 0, 0, 7, 0, 9, 14, 0, 0, 0 },
|
||||
// { 0, 0, 0, 9, 0, 10, 0, 0, 0 },
|
||||
// { 0, 0, 4, 14, 10, 0, 2, 0, 0 },
|
||||
// { 0, 0, 0, 0, 0, 2, 0, 1, 6 },
|
||||
// { 8, 11, 0, 0, 0, 0, 1, 0, 7 },
|
||||
// { 0, 0, 2, 0, 0, 0, 6, 7, 0 } };
|
||||
// Dijkstra1 t = new Dijkstra1();
|
||||
// t.dijkstra(graph, 0);
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
namespace Day15
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string[] lines = File.ReadAllLines("input.txt");
|
||||
|
||||
|
||||
Part1(lines);
|
||||
//Part2(lines);
|
||||
}
|
||||
|
||||
static void Part1(string[] lines)
|
||||
{
|
||||
Dictionary<Position, int> cavernMap = GenerateCavernMap(lines);
|
||||
|
||||
// Disjktra's algorithm
|
||||
|
||||
Position startPosition = new Position(0, 0);
|
||||
Position endPosition = new Position(cavernMap.Keys.MaxBy(p => p.x).x, cavernMap.Keys.MaxBy(p => p.y).y);
|
||||
|
||||
PriorityQueue<Position, int> priorityQueue = new PriorityQueue<Position, int>();
|
||||
Dictionary<Position, int> totalRiskMap = new Dictionary<Position, int>();
|
||||
|
||||
totalRiskMap[startPosition] = 0;
|
||||
priorityQueue.Enqueue(startPosition, 0);
|
||||
|
||||
while (priorityQueue.Count > 0)
|
||||
{
|
||||
Position position = priorityQueue.Dequeue();
|
||||
|
||||
IEnumerable<Position> nearbyPositions = GetNearbyNonDiagonalPositions(position);
|
||||
|
||||
foreach (Position nearbyPosition in nearbyPositions)
|
||||
{
|
||||
// If it's in the cavern but we haven't calculated its risk yet
|
||||
if (cavernMap.ContainsKey(nearbyPosition) && !totalRiskMap.ContainsKey(nearbyPosition))
|
||||
{
|
||||
// The total risk for this nearby position is the total risk for the current position,
|
||||
// plus the value at this nearby position
|
||||
int totalRisk = totalRiskMap[position] + cavernMap[nearbyPosition];
|
||||
totalRiskMap[nearbyPosition] = totalRisk;
|
||||
|
||||
// If we've reached the end position of the cavern, then stop
|
||||
if (nearbyPosition == endPosition)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the position to the queue
|
||||
priorityQueue.Enqueue(nearbyPosition, totalRisk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// What is the lowest total risk of any path from the top left to the bottom right?
|
||||
Console.WriteLine(totalRiskMap[endPosition]);
|
||||
}
|
||||
|
||||
static IEnumerable<Position> GetNearbyNonDiagonalPositions(Position position)
|
||||
{
|
||||
return new[] {
|
||||
position with {y = position.y + 1},
|
||||
position with {y = position.y - 1},
|
||||
position with {x = position.x + 1},
|
||||
position with {x = position.x - 1},
|
||||
};
|
||||
}
|
||||
|
||||
static Dictionary<Position, int> GenerateCavernMap(string[] lines)
|
||||
{
|
||||
Dictionary<Position, int> keyValuePairs = new Dictionary<Position, int>();
|
||||
for (int x = 0; x < lines.Length; x++)
|
||||
{
|
||||
for (int y = 0; y < lines[x].Length; y++)
|
||||
{
|
||||
// TODO - why is this "lines[y][x] - '0'" rather than "lines[x][y]"
|
||||
// as I think it should be?
|
||||
keyValuePairs.Add(new Position(x, y), lines[y][x] - '0');
|
||||
}
|
||||
}
|
||||
|
||||
return keyValuePairs;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
record Position(int x, int y);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
--- Day 15: Chiton ---
|
||||
You've almost reached the exit of the cave, but the walls are getting closer together. Your submarine can barely still fit, though; the main problem is that the walls of the cave are covered in chitons, and it would be best not to bump any of them.
|
||||
|
||||
The cavern is large, but has a very low ceiling, restricting your motion to two dimensions. The shape of the cavern resembles a square; a quick scan of chiton density produces a map of risk level throughout the cave (your puzzle input). For example:
|
||||
|
||||
1163751742
|
||||
1381373672
|
||||
2136511328
|
||||
3694931569
|
||||
7463417111
|
||||
1319128137
|
||||
1359912421
|
||||
3125421639
|
||||
1293138521
|
||||
2311944581
|
||||
You start in the top left position, your destination is the bottom right position, and you cannot move diagonally. The number at each position is its risk level; to determine the total risk of an entire path, add up the risk levels of each position you enter (that is, don't count the risk level of your starting position unless you enter it; leaving it adds no risk to your total).
|
||||
|
||||
Your goal is to find a path with the lowest total risk. In this example, a path with the lowest total risk is highlighted here:
|
||||
|
||||
1163751742
|
||||
1381373672
|
||||
2136511328
|
||||
3694931569
|
||||
7463417111
|
||||
1319128137
|
||||
1359912421
|
||||
3125421639
|
||||
1293138521
|
||||
2311944581
|
||||
The total risk of this path is 40 (the starting position is never entered, so its risk is not counted).
|
||||
|
||||
What is the lowest total risk of any path from the top left to the bottom right?
|
||||
@@ -0,0 +1,10 @@
|
||||
1163751742
|
||||
1381373672
|
||||
2136511328
|
||||
3694931569
|
||||
7463417111
|
||||
1319128137
|
||||
1359912421
|
||||
3125421639
|
||||
1293138521
|
||||
2311944581
|
||||
Reference in New Issue
Block a user