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()
{
//IsDebug = true
IsDebug = true
};
//inputReader.SetInputByChallange(3);

View File

@ -1,4 +1,5 @@
using AdventOfCode.Core.Shared;
using AdventOfCode.Core.Shared.Grid;
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();
int row = 0;
await foreach(string line in ReadAsStringLine())
{
// 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++;
}
@ -113,7 +114,7 @@ namespace AdventOfCode.Core
continue;
}
// 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++;
}
yield return result;

View File

@ -27,7 +27,7 @@
}
var neighbors = GetNeighbors(current, false);
neighbors = neighbors.Where(n => current.CanMoveTo(n));
neighbors = neighbors.Where(n => current.CanMoveTo(current));
// calc costs
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>
/// Distance from start node
/// </summary>
public int GCost { get; set; } = 0;
public long GCost { get; set; } = 0;
/// <summary>
/// Distance from end node
/// </summary>
public int HCost { get; set; }
public long HCost { get; set; }
/// <summary>
/// Total cost (G + F)
/// </summary>
public int FCost => GCost + HCost;
public long FCost => GCost + HCost;
public bool? IsClosed { get; private set; } = null;
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)
@ -33,8 +35,10 @@
public bool CanMoveTo(AStarNode target)
{
int diff = target.Integer - Integer; ;
return diff == 0 || diff == 1;
return false;
// 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 int Columns => DataGrid.Max(n => n.X) + 1;
public int Rows => DataGrid.Max(n => n.Y) + 1;
public long Columns => DataGrid.Max(n => n.X) + 1;
public long Rows => DataGrid.Max(n => n.Y) + 1;
public Grid() { }
@ -13,12 +16,12 @@
public Grid(IEnumerable<T> data) : this(data.ToArray()) { }
public IEnumerable<T> GetRow(int 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 IEnumerable<T> GetColumn(int 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 T GetNode(int x, int 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> GetRow(long rowNumber) => DataGrid.Where(n => n.X == rowNumber);
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(long columnNumber) => DataGrid.Where(n => n.Y == columnNumber);
public string GetColumnAsString(long rowNumber) => string.Concat(DataGrid.Where(n => n.X == rowNumber).OrderBy(n => n.Y).Select(n => n.Value));
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.Value == toFind);
public void UpdateNode(T node)
{
@ -40,7 +43,7 @@
|| (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);
}

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 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)
{
@ -22,5 +29,23 @@ namespace AdventOfCode.Core.Shared.Grid
{
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 int Y { get; set; }
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 Node() : this(-1, -1, '0') { }
public Node(long X, long Y) : this(X, Y, ' ') { }
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();
return Symbols
.Where(s => s.Value == "*")
.Where(s => s.Value == '*')
.Select(g => Numbers.Where(n => n.Intersect(g)))
.Where(n => n.Count() == 2)
.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<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);
}

View File

@ -155,7 +155,7 @@ namespace AdventOfCode.Solutions._2023
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);
}
}

View File

@ -1,4 +1,5 @@
using AdventOfCode.Core;
using AdventOfCode.Core.Shared.Grid;
using System.Linq.Expressions;
namespace AdventOfCode.Solutions._2023
@ -31,17 +32,17 @@ namespace AdventOfCode.Solutions._2023
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;
if ((prevSegment == 'L' && value.Char == '7') // this is L7 meaning that there is no crossing
|| prevSegment == 'F' && value.Char == 'J') // this is FJ meaning that there is no crossing
if ((prevSegment == 'L' && value.Value == '7') // this is L7 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;
}
prevSegment = value.Char;
prevSegment = value.Value;
pipesCrossed++;
continue;
}
@ -60,7 +61,7 @@ namespace AdventOfCode.Solutions._2023
startPipe.Steps = 0;
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))
.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;
@ -115,7 +116,7 @@ namespace AdventOfCode.Solutions._2023
public IEnumerable<Pipe> GetNeighbors()
{
switch (Char)
switch (Value)
{
case '|':
yield return GetNorth();

View File

@ -1,4 +1,9 @@
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
{
@ -8,38 +13,154 @@ namespace AdventOfCode.Solutions._2023
public async Task<string> GetSolutionPart1()
{
string[] lines = await _inputReader.ReadAsVerticalArrayString();
int totalWeight = 0;
for(int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
{
int weight = lines[lineIndex].Length, lineWeight = 0;
for (int charIndex = 0; charIndex < lines[lineIndex].Length; charIndex++)
{
if (lines[lineIndex][charIndex] is '.')
continue;
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();
if (lines[lineIndex][charIndex] is '#')
{
weight = lines[lineIndex].Length - charIndex - 1;
continue;
}
bolders = MoveUp(bolders, cubes);
if (lines[lineIndex][charIndex] is 'O')
{
lineWeight += weight;
weight--;
}
}
totalWeight += lineWeight;
}
return totalWeight.ToString();
return bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
}
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;
}
}
}