Skip to content

Commit

Permalink
2023 Day 18 Part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
premun committed Jan 5, 2024
1 parent 0403019 commit 2bf0b4d
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 211 deletions.
144 changes: 144 additions & 0 deletions src/2023/18/LavaLagoonMeasurer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using Coor = AdventOfCode.Common.Coor<int>;
using Range = AdventOfCode.Common.Range;

internal class LavaLagoonMeasurer
{
public static long MeasureLagoon(IEnumerable<Instruction> trenchInstructions)
{
var borders = new List<Line>();
var lastEnd = Coor.Zero;
int minRow = int.MaxValue, minCol = int.MaxValue;
int maxRow = int.MinValue, maxCol = int.MinValue;

foreach (var instruction in trenchInstructions)
{
var newEnd = lastEnd + new Coor(
instruction.Distance * instruction.Direction.Y,
instruction.Distance * instruction.Direction.X);

borders.Add(Line.Create(lastEnd, newEnd));
lastEnd = newEnd;

minRow = Math.Min(minRow, lastEnd.Row);
maxRow = Math.Max(maxRow, lastEnd.Row);
minCol = Math.Min(minCol, lastEnd.Col);
maxCol = Math.Max(maxCol, lastEnd.Col);
}

var verticals = borders
.OfType<VerticalLine>()
.OrderBy(b => b.Start.Col)
.ToList();

var horizontals = borders
.OfType<HorizontalLine>()
.OrderBy(b => b.Start.Row)
.ToList();

var horizontalStripes = new List<Range>[maxRow - minRow + 1];
for (int row = minRow; row <= maxRow; row++)
{
var rowMatches = new List<Range>();

var inside = false;
var start = 0;
var collidingVerticals = verticals.Where(b => row >= b.Start.Row && row <= b.End.Row).ToList();
var matchingHorizontals = horizontals.Where(b => b.Start.Row == row).ToList();

for (int i = 0; i < collidingVerticals.Count; i++)
{
var vertical = collidingVerticals[i];

// Check if we are intersecting a horizontal border
var horizontal = matchingHorizontals.FirstOrDefault(h => h.Start.Col == vertical.Start.Col);
if (horizontal != null)
{
if (inside)
{
// This situation:
// ####~~~~~####
// ^
// (we need to add the ~ filling in between)
rowMatches.Add(new Range(start, horizontal.End.Col));
}
else
{
rowMatches.Add(new Range(horizontal.Start.Col, horizontal.End.Col));
}

// If we are not just brushing a top or bottom of the thing, we are crossing a boundary
var nextVertical = collidingVerticals[i + 1];
if ((vertical.Start.Row != row || nextVertical.Start.Row != row)
&& (vertical.End.Row != row || nextVertical.End.Row != row))
{
inside = !inside;
}

++i; // Skip the next as it will be a perpendicular border
start = horizontal.End.Col + 1;
continue;
}

if (!inside)
{
start = vertical.Start.Col;
inside = true;
}
else
{
rowMatches.Add(new Range(start, vertical.Start.Col));
inside = false;
}
}

horizontalStripes[row - minRow] = rowMatches;
}

var result = 0L;
foreach (var row in horizontalStripes)
{
foreach (var stripe in row)
{
result += stripe.End - stripe.Start + 1;
}
}
return result;
}

public static void Visualize(List<Range>[] horizontalStripes, int minCol)
{
for (int i = 0; i < horizontalStripes.Length; i++)
{
var last = 0;
for (int j = 0; j < horizontalStripes[i].Count; j++)
{
var start = (int)horizontalStripes[i][j].Start - minCol;
var end = (int)horizontalStripes[i][j].End - minCol;
Console.Write(new string(' ', start - last));

Console.Write('#');
Console.Write(new string('#', end - start - 1));
Console.Write('#');
last = end;
}

Console.WriteLine();
}
}

private abstract record Line(Coor Start, Coor End)
{
public static Line Create(Coor start, Coor end)
=> start.Row == end.Row
? start.Col < end.Col
? new HorizontalLine(start, end)
: new HorizontalLine(end, start)
: start.Row < end.Row
? new VerticalLine(start, end)
: new VerticalLine(end, start);
}

private record HorizontalLine(Coor Start, Coor End) : Line(Start, End);

private record VerticalLine(Coor Start, Coor End) : Line(Start, End);
}
236 changes: 25 additions & 211 deletions src/2023/18/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,229 +2,43 @@
using AdventOfCode.Common;

using Coor = AdventOfCode.Common.Coor<int>;
using Range = AdventOfCode.Common.Range;

var regex = new Regex(@"(?<direction>R|D|L|U) (?<distance>[0-9]+) \((?<color>#[0-9a-h]{6})\)");

var instructions = Resources.GetInputFileLines()
.Select(line => regex.Match(line))
.Where(m => m.Success)
.Select(m => new Instruction(
m.Groups["direction"].Value[0] switch
{
'R' => Coor.Right,
'D' => Coor.Down,
'U' => Coor.Up,
'L' => Coor.Left,
_ => throw new Exception()
},
int.Parse(m.Groups["distance"].Value),
m.Groups["color"].Value))
.Select(Instruction.Parse)
.ToList();

var borders = new List<Line>();
var lastEnd = Coor.Zero;
int minRow = int.MaxValue, minCol = int.MaxValue;
int maxRow = int.MinValue, maxCol = int.MinValue;

foreach (var instruction in instructions)
{
var newEnd = lastEnd + new Coor(
instruction.Distance * instruction.Direction.Y,
instruction.Distance * instruction.Direction.X);

borders.Add(Line.Create(lastEnd, newEnd));
lastEnd = newEnd;

minRow = Math.Min(minRow, lastEnd.Row);
maxRow = Math.Max(maxRow, lastEnd.Row);
minCol = Math.Min(minCol, lastEnd.Col);
maxCol = Math.Max(maxCol, lastEnd.Col);
}

var verticals = borders
.OfType<VerticalLine>()
.OrderBy(b => b.Start.Col)
.ToList();

var horizontals = borders
.OfType<HorizontalLine>()
.OrderBy(b => b.Start.Row)
.ToList();

var horizontalMatches = new List<Range>[maxRow - minRow + 1];
for (int row = minRow; row <= maxRow; row++)
{
var rowMatches = new List<Range>();

var inside = false;
var start = 0;
var matchingVerticals = verticals.Where(b => b.IntersectsRow(row)).ToList();
var matchingBorders = horizontals.Where(b => b.Start.Row == row).ToList();

for (int i = 0; i < matchingVerticals.Count; i++)
{
var match = matchingVerticals[i];

// Check if we are intersecting a horizontal border
var matchingBorder = matchingBorders.FirstOrDefault(h => h.Start.Col == match.Col);
if (matchingBorder != null)
{
if (inside)
{
rowMatches.Add(new Range(start, matchingBorder.Start.Col));
}

rowMatches.Add(new Range(matchingBorder.Start.Col, matchingBorder.End.Col));
++i; // Skip the next as it will be a perpendicular border
inside = !inside;
start = matchingBorder.End.Col;
continue;
}

if (!inside)
{
start = match.Start.Col;
inside = true;
}
else
{
rowMatches.Add(new Range(start, match.Start.Col));
}
}
Console.WriteLine($"Part 1: {LavaLagoonMeasurer.MeasureLagoon(instructions)}");
Console.WriteLine($"Part 2: {LavaLagoonMeasurer.MeasureLagoon(instructions.Select(i => i.Invert()))}");

// Unify ranges
int j = 1;
while (j < rowMatches.Count)
{
var prev = rowMatches[j - 1];
var current = rowMatches[j];
if (current.Start - prev.End <= 1)
{
rowMatches.RemoveAt(j);
rowMatches[j - 1] = new Range(prev.Start, current.End);
}
else
{
j++;
}
}

horizontalMatches[row - minRow] = rowMatches;
}

var verticalMatches = new List<Range>[maxCol - minCol + 1];
for (int col = minCol; col <= maxCol; col++)
record Instruction(Coor Direction, int Distance, string Color)
{
var colMatches = new List<Range>();

var inside = false;
var start = 0;
var matchingHorizontals = horizontals.Where(b => b.IntersectsCol(col)).ToList();
var matchingBorders = verticals.Where(b => b.Start.Col == col).ToList();
private static readonly Regex Regex = new(@"(?<direction>R|D|L|U) (?<distance>[0-9]+) \(#(?<color>[0-9a-h]{6})\)");

for (int i = 0; i < matchingHorizontals.Count; i++)
{
var match = matchingHorizontals[i];

// Check if we are intersecting a horizontal border
var matchingBorder = matchingBorders.FirstOrDefault(h => h.Start.Row == match.Row);
if (matchingBorder != null)
public Instruction Invert() => new(
Color.Last() switch
{
if (inside)
{
colMatches.Add(new Range(start, matchingBorder.Start.Row));
}

colMatches.Add(new Range(matchingBorder.Start.Row, matchingBorder.End.Row));
++i; // Skip the next as it will be a perpendicular border
inside = !inside;
start = matchingBorder.End.Row;
continue;
}

if (!inside)
{
start = match.Start.Row;
inside = true;
}
else
{
colMatches.Add(new Range(start, match.Start.Row));
inside = false;
}
}

// Unify ranges
int j = 1;
while (j < colMatches.Count)
{
var prev = colMatches[j - 1];
var current = colMatches[j];
if (current.Start - prev.End <= 1)
{
colMatches.RemoveAt(j);
colMatches[j - 1] = new Range(prev.Start, current.End);
}
else
{
j++;
}
}

verticalMatches[col - minCol] = colMatches;
}

Console.WriteLine(horizontalMatches.SelectMany(m => m.Select(x => (long)x.End - x.Start + 1)).Sum());

abstract file record Line(Coor Start, Coor End)
{
public bool IntersectsRow(int row) => row >= Start.Row && row <= End.Row;
public bool IntersectsCol(int col) => col >= Start.Col && col <= End.Col;
'0' => Coor.Right,
'1' => Coor.Down,
'3' => Coor.Up,
'2' => Coor.Left,
_ => throw new Exception()
},
Convert.ToInt32(new string(Color.Take(Color.Length - 1).ToArray()), 16),
Color);

public static Line Create(Coor start, Coor end)
public static Instruction Parse(string s)
{
if (start.Row == end.Row)
{
return start.Col < end.Col
? new HorizontalLine(start, end)
: new HorizontalLine(end, start);
}
else
{
return start.Row < end.Row
? new VerticalLine(start, end)
: new VerticalLine(end, start);
}
}
}

file record HorizontalLine(Coor Start, Coor End) : Line(Start, End)
{
public int Row => Start.Row;
}

file record VerticalLine(Coor Start, Coor End) : Line(Start, End)
{
public int Col => Start.Col;
}

file record Instruction(Coor Direction, int Distance, string Color);

file record InvertedInstruction : Instruction
{
public InvertedInstruction(string color)
: base(
color.Last() switch
var match = Regex.Match(s);
return new Instruction(
match.Groups["direction"].Value[0] switch
{
'0' => Coor.Right,
'1' => Coor.Down,
'3' => Coor.Up,
'2' => Coor.Left,
'R' => Coor.Right,
'D' => Coor.Down,
'U' => Coor.Up,
'L' => Coor.Left,
_ => throw new Exception()
},
Convert.ToInt32(new string(color.Take(color.Length - 1).ToArray()), 16),
color)
{
int.Parse(match.Groups["distance"].Value),
match.Groups["color"].Value);
}
}

0 comments on commit 2bf0b4d

Please sign in to comment.