partial day 14 p2, added empty day 15

This commit is contained in:
Rob 2023-12-14 23:50:04 +01:00
parent eb599df171
commit a91dd1c661
13 changed files with 249 additions and 92 deletions

View File

@ -4,7 +4,7 @@ using AdventOfCode.Core;
InputReader inputReader = new() InputReader inputReader = new()
{ {
//IsDebug = true IsDebug = true
}; };
//inputReader.SetInputByChallange(3); //inputReader.SetInputByChallange(3);

View File

@ -1,4 +1,5 @@
using AdventOfCode.Core.Shared; using AdventOfCode.Core.Shared;
using AdventOfCode.Core.Shared.Grid;
namespace AdventOfCode.Core namespace AdventOfCode.Core
{ {
@ -84,14 +85,14 @@ namespace AdventOfCode.Core
} }
} }
public async Task<Grid<T>> ReadToGrid<T>() where T : Node, new() public async Task<Grid<T>> ReadToGrid<T>() where T : Point, new()
{ {
Grid<T> result = new(); Grid<T> result = new();
int row = 0; int row = 0;
await foreach(string line in ReadAsStringLine()) await foreach(string line in ReadAsStringLine())
{ {
// create the nodes from the lines // create the nodes from the lines
result.DataGrid.AddRange(line.Select((c, i) => new T { X = i, Y = row, Char = c })); result.DataGrid.AddRange(line.Select((c, i) => new T { X = i, Y = row, Value = c }));
row++; row++;
} }
@ -113,7 +114,7 @@ namespace AdventOfCode.Core
continue; continue;
} }
// create the nodes from the lines // create the nodes from the lines
result.DataGrid.AddRange(line.Select((c, i) => new T { X = i, Y = row, Char = c })); result.DataGrid.AddRange(line.Select((c, i) => new T { X = i, Y = row, Value = c }));
row++; row++;
} }
yield return result; yield return result;

View File

@ -27,7 +27,7 @@
} }
var neighbors = GetNeighbors(current, false); var neighbors = GetNeighbors(current, false);
neighbors = neighbors.Where(n => current.CanMoveTo(n)); neighbors = neighbors.Where(n => current.CanMoveTo(current));
// calc costs // calc costs
foreach(AStarNode neighbor in neighbors) foreach(AStarNode neighbor in neighbors)

View File

@ -1,27 +1,29 @@
namespace AdventOfCode.Core.Shared.A_Star using AdventOfCode.Core.Shared.Grid;
namespace AdventOfCode.Core.Shared.A_Star
{ {
public class AStarNode : Node public class AStarNode : Point
{ {
/// <summary> /// <summary>
/// Distance from start node /// Distance from start node
/// </summary> /// </summary>
public int GCost { get; set; } = 0; public long GCost { get; set; } = 0;
/// <summary> /// <summary>
/// Distance from end node /// Distance from end node
/// </summary> /// </summary>
public int HCost { get; set; } public long HCost { get; set; }
/// <summary> /// <summary>
/// Total cost (G + F) /// Total cost (G + F)
/// </summary> /// </summary>
public int FCost => GCost + HCost; public long FCost => GCost + HCost;
public bool? IsClosed { get; private set; } = null; public bool? IsClosed { get; private set; } = null;
public AStarNode? ParentNode { get; set; } public AStarNode? ParentNode { get; set; }
public AStarNode(Node position) : base(position) public AStarNode(Point position) : base(position)
{ } { }
public AStarNode(int x, int y, char value) : base(x, y, value) public AStarNode(int x, int y, char value) : base(x, y, value)
@ -33,8 +35,10 @@
public bool CanMoveTo(AStarNode target) public bool CanMoveTo(AStarNode target)
{ {
int diff = target.Integer - Integer; ; return false;
return diff == 0 || diff == 1; // TODO FIX
//int diff = target.Integer - Integer;
//return diff == 0 || diff == 1;
} }
} }
} }

View File

@ -1,11 +1,14 @@
namespace AdventOfCode.Core.Shared using AdventOfCode.Core.Shared.Grid;
using System.Runtime.CompilerServices;
namespace AdventOfCode.Core.Shared
{ {
public class Grid<T> where T : Node public class Grid<T> where T : Point
{ {
public List<T> DataGrid { get; set; } = []; public List<T> DataGrid { get; set; } = [];
public int Columns => DataGrid.Max(n => n.X) + 1; public long Columns => DataGrid.Max(n => n.X) + 1;
public int Rows => DataGrid.Max(n => n.Y) + 1; public long Rows => DataGrid.Max(n => n.Y) + 1;
public Grid() { } public Grid() { }
@ -13,12 +16,12 @@
public Grid(IEnumerable<T> data) : this(data.ToArray()) { } public Grid(IEnumerable<T> data) : this(data.ToArray()) { }
public IEnumerable<T> GetRow(int rowNumber) => DataGrid.Where(n => n.X == rowNumber); public IEnumerable<T> GetRow(long rowNumber) => DataGrid.Where(n => n.X == rowNumber);
public string GetRowAsString(int columnNumber) => string.Concat(DataGrid.Where(n => n.Y == columnNumber).OrderBy(n => n.X).Select(n => n.Char)); public string GetRowAsString(long columnNumber) => string.Concat(DataGrid.Where(n => n.Y == columnNumber).OrderBy(n => n.X).Select(n => n.Value));
public IEnumerable<T> GetColumn(int columnNumber) => DataGrid.Where(n => n.Y == columnNumber); public IEnumerable<T> GetColumn(long columnNumber) => DataGrid.Where(n => n.Y == columnNumber);
public string GetColumnAsString(int rowNumber) => string.Concat(DataGrid.Where(n => n.X == rowNumber).OrderBy(n => n.Y).Select(n => n.Char)); public string GetColumnAsString(long rowNumber) => string.Concat(DataGrid.Where(n => n.X == rowNumber).OrderBy(n => n.Y).Select(n => n.Value));
public T GetNode(int x, int y) => DataGrid.First(n => n.X == x && n.Y == y); public T GetNode(long x, long y) => DataGrid.First(n => n.X == x && n.Y == y);
public IEnumerable<T> FindWithValue(char toFind) => DataGrid.Where(n => n.Char == toFind); public IEnumerable<T> FindWithValue(char toFind) => DataGrid.Where(n => n.Value == toFind);
public void UpdateNode(T node) public void UpdateNode(T node)
{ {
@ -40,7 +43,7 @@
|| (source.X == target.X && Math.Abs(source.Y - target.Y) <= 1)); || (source.X == target.X && Math.Abs(source.Y - target.Y) <= 1));
} }
public IEnumerable<T> GetSection(int fromX, int fromY, int toX, int toY) public IEnumerable<T> GetSection(long fromX, long fromY, long toX, long toY)
{ {
return DataGrid.Where(node => node.X >= fromX && node.X <= toX && node.Y >= fromY && node.Y <= toY); return DataGrid.Where(node => node.X >= fromX && node.X <= toX && node.Y >= fromY && node.Y <= toY);
} }

View File

@ -1,15 +1,22 @@
using static System.Collections.Specialized.BitVector32; namespace AdventOfCode.Core.Shared.Grid
namespace AdventOfCode.Core.Shared.Grid
{ {
public class Point(long X, long Y) public class Point(long X, long Y) : IEquatable<Point>
{ {
public long X { get; set; } = X; public long X { get; set; } = X;
public long Y { get; set; } = Y; public long Y { get; set; } = Y;
public string Value { get; set; } = string.Empty; public char Value { get; set; } = ' ';
public Point(long X, long Y, string value) : this(X, Y) => Value = value; public Point() : this(-1, -1) { }
public Point(Point point) : this(point.X, point.Y) { }
public Point(long X, long Y, char value) : this(X, Y) => Value = value;
public long DistanceTo(Point other)
{
return Math.Abs(X - other.X) + Math.Abs(Y - other.Y);
}
public bool Intersect(Point other) public bool Intersect(Point other)
{ {
@ -22,5 +29,23 @@ namespace AdventOfCode.Core.Shared.Grid
{ {
return $"[{Y},{X}] {Value}"; return $"[{Y},{X}] {Value}";
} }
public bool Equals(Point? other)
{
if (other is null)
return false;
if (this.X != other.X
|| this.Y != other.Y
|| this.Value != other.Value)
return false;
return true;
}
public override int GetHashCode()
{
return this.X.GetHashCode() ^ this.Y.GetHashCode() ^ this.Value.GetHashCode();
}
} }
} }

View File

@ -1,32 +1,15 @@
namespace AdventOfCode.Core.Shared using AdventOfCode.Core.Shared.Grid;
namespace AdventOfCode.Core.Shared
{ {
public class Node public class Node(long X, long Y, char value) : Point(X, Y, value)
{ {
public int X { get; set; } public Node() : this(-1, -1, '0') { }
public int Y { get; set; } public Node(long X, long Y) : this(X, Y, ' ') { }
public char Char { get; set; }
public short Integer => (short)Char;
public Node() { }
public Node(int x, int y, char character)
{
X = x;
Y = y;
Char = character;
}
public Node(Node position) : this(position.X, position.Y, position.Char)
{ }
public int DistanceTo(Node other)
{
return Math.Abs(X - other.X) + Math.Abs(Y - other.Y);
}
public override string ToString() public override string ToString()
{ {
return $"[{Y},{X}] {Char}"; return $"[{Y},{X}] {Value}";
} }
} }
} }

View File

@ -37,7 +37,7 @@ namespace AdventOfCode.Solutions._2023
{ {
var (Numbers, Symbols) = await GetNumbersAndSymbols(); var (Numbers, Symbols) = await GetNumbersAndSymbols();
return Symbols return Symbols
.Where(s => s.Value == "*") .Where(s => s.Value == '*')
.Select(g => Numbers.Where(n => n.Intersect(g))) .Select(g => Numbers.Where(n => n.Intersect(g)))
.Where(n => n.Count() == 2) .Where(n => n.Count() == 2)
.Select(num => int.Parse(num.First().Value) * int.Parse(num.Last().Value)) .Select(num => int.Parse(num.First().Value) * int.Parse(num.Last().Value))
@ -57,7 +57,7 @@ namespace AdventOfCode.Solutions._2023
} }
List<Rectangle> numbers = parts.Where(p => p.IsDigit()).Select(number => new Rectangle(new Point(number.X - 1, number.Y - 1), new Point(number.X + number.Length, number.Y + 1), number.Section)).ToList(); List<Rectangle> numbers = parts.Where(p => p.IsDigit()).Select(number => new Rectangle(new Point(number.X - 1, number.Y - 1), new Point(number.X + number.Length, number.Y + 1), number.Section)).ToList();
List<Point> symbols = parts.Where(p => p.IsPartSymbol()).Select(symbol => new Point(symbol.X, symbol.Y, symbol.Section)).ToList(); List<Point> symbols = parts.Where(p => p.IsPartSymbol()).Select(symbol => new Point(symbol.X, symbol.Y, symbol.Section[0])).ToList();
return (numbers, symbols); return (numbers, symbols);
} }

View File

@ -155,7 +155,7 @@ namespace AdventOfCode.Solutions._2023
if (lastId < currentSeedRange.End.X) if (lastId < currentSeedRange.End.X)
{ {
Line map = new(new Point(lastId, 0, ""), currentSeedRange.End); Line map = new(new Point(lastId, 0, ' '), currentSeedRange.End);
resultRanges.Add(map); resultRanges.Add(map);
} }
} }

View File

@ -1,4 +1,5 @@
using AdventOfCode.Core; using AdventOfCode.Core;
using AdventOfCode.Core.Shared.Grid;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace AdventOfCode.Solutions._2023 namespace AdventOfCode.Solutions._2023
@ -31,17 +32,17 @@ namespace AdventOfCode.Solutions._2023
if (value.Steps >= 0) // this is in the main loop if (value.Steps >= 0) // this is in the main loop
{ {
if (value.Char == '-') // never crosses, also to not update prev if (value.Value == '-') // never crosses, also to not update prev
continue; continue;
if ((prevSegment == 'L' && value.Char == '7') // this is L7 meaning that there is no crossing if ((prevSegment == 'L' && value.Value == '7') // this is L7 meaning that there is no crossing
|| prevSegment == 'F' && value.Char == 'J') // this is FJ meaning that there is no crossing || prevSegment == 'F' && value.Value == 'J') // this is FJ meaning that there is no crossing
{ {
prevSegment = value.Char; prevSegment = value.Value;
continue; continue;
} }
prevSegment = value.Char; prevSegment = value.Value;
pipesCrossed++; pipesCrossed++;
continue; continue;
} }
@ -60,7 +61,7 @@ namespace AdventOfCode.Solutions._2023
startPipe.Steps = 0; startPipe.Steps = 0;
Pipe[] lineToWalk = grid.GetNeighbors(startPipe, false) Pipe[] lineToWalk = grid.GetNeighbors(startPipe, false)
.Where(n => n.Char != '.') .Where(n => n.Value != '.')
.Where(n => n.GetNeighbors().Any(np => np.X == startPipe.X && np.Y == startPipe.Y)) .Where(n => n.GetNeighbors().Any(np => np.X == startPipe.X && np.Y == startPipe.Y))
.ToArray(); .ToArray();
@ -99,9 +100,9 @@ namespace AdventOfCode.Solutions._2023
} }
} }
public class Pipe : Node public class Pipe : Point
{ {
public Pipe From { get; set; } public Pipe From { get; set; } = null;
public int Steps { get; set; } = -1; public int Steps { get; set; } = -1;
@ -115,7 +116,7 @@ namespace AdventOfCode.Solutions._2023
public IEnumerable<Pipe> GetNeighbors() public IEnumerable<Pipe> GetNeighbors()
{ {
switch (Char) switch (Value)
{ {
case '|': case '|':
yield return GetNorth(); yield return GetNorth();

View File

@ -1,4 +1,9 @@
using AdventOfCode.Core; using AdventOfCode.Core;
using AdventOfCode.Core.Shared.Grid;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading.Tasks.Dataflow;
namespace AdventOfCode.Solutions._2023 namespace AdventOfCode.Solutions._2023
{ {
@ -8,38 +13,154 @@ namespace AdventOfCode.Solutions._2023
public async Task<string> GetSolutionPart1() public async Task<string> GetSolutionPart1()
{ {
string[] lines = await _inputReader.ReadAsVerticalArrayString(); Grid<Point> grid = await _inputReader.ReadToGrid<Point>();
int totalWeight = 0; HashSet<Point> bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet();
for(int lineIndex = 0; lineIndex < lines.Length; lineIndex++) HashSet<Point> cubes = grid.FindWithValue('#').Select(p => new Point(p.X, p.Y)).ToHashSet();
{
int weight = lines[lineIndex].Length, lineWeight = 0;
for (int charIndex = 0; charIndex < lines[lineIndex].Length; charIndex++)
{
if (lines[lineIndex][charIndex] is '.')
continue;
if (lines[lineIndex][charIndex] is '#') bolders = MoveUp(bolders, cubes);
{
weight = lines[lineIndex].Length - charIndex - 1;
continue;
}
if (lines[lineIndex][charIndex] is 'O') return bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
{
lineWeight += weight;
weight--;
}
}
totalWeight += lineWeight;
}
return totalWeight.ToString();
} }
public async Task<string> GetSolutionPart2() public async Task<string> GetSolutionPart2()
{ {
return string.Empty; long maxCycles = 1_000_000_000;
Dictionary<string, int> mapStates = [];
bool foundLoop = false;
Grid<Point> grid = await _inputReader.ReadToGrid<Point>();
HashSet<Point> bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet();
HashSet<Point> cubes = grid.FindWithValue('#').Select(p => new Point(p.X, p.Y)).ToHashSet();
for (int currentCycle = 0; currentCycle < maxCycles; currentCycle++)
{
mapStates.TryAdd(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), currentCycle);
bolders = MoveUp(bolders, cubes);
bolders = MoveLeft(bolders, cubes);
bolders = MoveDown(bolders, cubes, grid.Rows);
bolders = MoveRight(bolders, cubes, grid.Columns);
if (foundLoop) continue;
int hash = bolders.GetHashCode();
if (mapStates.TryGetValue(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), out int prevCycle))
{
// calculate remaining cycles to the first exit point
long remainingCycles = (maxCycles - prevCycle) % (currentCycle + 1 - prevCycle) + 1;
// set the total the first exit point
maxCycles = remainingCycles + currentCycle;
foundLoop = true;
}
}
return bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
}
private HashSet<Point> MoveUp(HashSet<Point> bolders, HashSet<Point> cubes)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y) )
{
// current location
long ySearch = bolder.Y;
while (ySearch - 1 >= 0 // ensure in map
&& !boldersMoved.Contains(new(bolder.X, ySearch - 1)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(bolder.X, ySearch - 1))) // check that the next tile has not cube
{
ySearch--; // move to next
}
// something found on next location so place bolder here
boldersMoved.Add(new(bolder.X, ySearch));
}
return boldersMoved;
}
private HashSet<Point> MoveDown(HashSet<Point> bolders, HashSet<Point> cubes, long ySize)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y))
{
// current location
long ySearch = bolder.Y;
while (ySearch + 1 < ySize // ensure in map
&& !boldersMoved.Contains(new(bolder.X, ySearch + 1)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(bolder.X, ySearch + 1))) // check that the next tile has not cube
{
ySearch++; // move to next
}
// something found on next location so place bolder here
boldersMoved.Add(new(bolder.X, ySearch));
}
return boldersMoved;
}
private HashSet<Point> MoveLeft(HashSet<Point> bolders, HashSet<Point> cubes)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.X))
{
// current location
long xSearch = bolder.X;
while (xSearch - 1 >= 0 // ensure in map
&& !boldersMoved.Contains(new(xSearch, bolder.Y)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(xSearch, bolder.Y))) // check that the next tile has not cube
{
xSearch--; // move to next
}
// something found on next location so place bolder here
boldersMoved.Add(new(xSearch, bolder.Y));
}
return boldersMoved;
}
private HashSet<Point> MoveRight(HashSet<Point> bolders, HashSet<Point> cubes, long xSize)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y))
{
// current location
long xSearch = bolder.Y;
while (xSearch + 1 < xSize // ensure in map
&& !boldersMoved.Contains(new(xSearch, bolder.Y)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(xSearch, bolder.Y))) // check that the next tile has not cube
{
xSearch++; // move to next
}
// something found on next location so place bolder here
boldersMoved.Add(new(xSearch, bolder.Y));
}
return boldersMoved;
}
private static string CreateStringMap(HashSet<Point> bolders, HashSet<Point> cubess, long maxX, long maxY)
{
var stringBuilder = new StringBuilder();
for (var y = 0; y < maxY; y++)
{
for (var x = 0; x < maxX; x++)
{
if (bolders.Contains(new Point(x, y)))
stringBuilder.Append('O');
else if (cubess.Contains(new Point(x, y)))
stringBuilder.Append('#');
else
stringBuilder.Append('.');
}
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
} }
} }
} }

View File

@ -0,0 +1,19 @@
using AdventOfCode.Core;
namespace AdventOfCode.Solutions._2023
{
public class Day15(InputReader reader) : IChallange
{
private InputReader _inputReader = reader;
public async Task<string> GetSolutionPart1()
{
return string.Empty;
}
public async Task<string> GetSolutionPart2()
{
return string.Empty;
}
}
}