Initial Commit
This commit is contained in:
334
2024/Day06CSharp/Program.cs
Normal file
334
2024/Day06CSharp/Program.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
var exParsed = new Day06Parsed("example-input.txt");
|
||||
|
||||
Console.WriteLine("Example Part01: {0}", exParsed.GetPart01());
|
||||
Console.WriteLine("Example Part02: {0}", exParsed.GetPart02());
|
||||
|
||||
var pzParsed = new Day06Parsed("puzzle-input.txt");
|
||||
|
||||
Console.WriteLine("Puzzle Part01: {0}", pzParsed.GetPart01());
|
||||
Console.WriteLine("Puzzle Part02: {0}", pzParsed.GetPart02());
|
||||
|
||||
class Day06Parsed : AdventCommon.ParsedInput
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
UP,
|
||||
DOWN,
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
|
||||
public enum BLOCK_STATE
|
||||
{
|
||||
UNTOUCHED,
|
||||
OBSTACLE,
|
||||
VISITED
|
||||
}
|
||||
|
||||
public class GridSquare
|
||||
{
|
||||
public GridSquare(BLOCK_STATE state)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
|
||||
public BLOCK_STATE State { get; set; } = BLOCK_STATE.UNTOUCHED;
|
||||
|
||||
public bool hasVisited() { return State == BLOCK_STATE.VISITED; }
|
||||
|
||||
public bool VisitedFacingUp { get; set; } = false;
|
||||
public bool VisitedFacingDown { get; set; } = false;
|
||||
public bool VisitedFacingLeft { get; set; } = false;
|
||||
public bool VisitedFacingRight { get; set; } = false;
|
||||
|
||||
public GridSquare Clone()
|
||||
{
|
||||
return (GridSquare)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
public class Guard
|
||||
{
|
||||
public Guard() { }
|
||||
public Guard(Guard other)
|
||||
{
|
||||
CurrentDirection = other.CurrentDirection;
|
||||
X = other.X;
|
||||
Y = other.Y;
|
||||
IsGone = other.IsGone;
|
||||
}
|
||||
|
||||
public Direction CurrentDirection { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public bool IsGone { get; set; } = false;
|
||||
|
||||
public Guard Clone()
|
||||
{
|
||||
return (Guard)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
public class Level
|
||||
{
|
||||
public List<List<GridSquare>> Grid { get; set; } = new List<List<GridSquare>>();
|
||||
public Guard SecurityGuard { get; set; } = new Guard();
|
||||
|
||||
public int CountVisited() => Grid.SelectMany(x => x).Count(x => x.State == BLOCK_STATE.VISITED);
|
||||
|
||||
public bool GuardIsAround()
|
||||
{
|
||||
return SecurityGuard.X >= 0 && SecurityGuard.Y >= 0;
|
||||
}
|
||||
|
||||
public enum SIMULATION_RESULT
|
||||
{
|
||||
CONTINUE,
|
||||
DONE,
|
||||
LOOP
|
||||
}
|
||||
|
||||
public SIMULATION_RESULT SimStep()
|
||||
{
|
||||
if (SecurityGuard.IsGone) return SIMULATION_RESULT.DONE;
|
||||
|
||||
try
|
||||
{
|
||||
int newY = SecurityGuard.Y, newX = SecurityGuard.X;
|
||||
|
||||
switch (SecurityGuard.CurrentDirection)
|
||||
{
|
||||
case Direction.UP:
|
||||
newY = SecurityGuard.Y-1;
|
||||
break;
|
||||
case Direction.DOWN:
|
||||
newY = SecurityGuard.Y+1;
|
||||
break;
|
||||
case Direction.LEFT:
|
||||
newX = SecurityGuard.X-1;
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
newX = SecurityGuard.X+1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Grid[newY][newX].State == BLOCK_STATE.OBSTACLE)
|
||||
{
|
||||
switch (SecurityGuard.CurrentDirection)
|
||||
{
|
||||
case Direction.UP:
|
||||
SecurityGuard.CurrentDirection = Direction.RIGHT;
|
||||
break;
|
||||
case Direction.DOWN:
|
||||
SecurityGuard.CurrentDirection = Direction.LEFT;
|
||||
break;
|
||||
case Direction.LEFT:
|
||||
SecurityGuard.CurrentDirection = Direction.UP;
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
SecurityGuard.CurrentDirection = Direction.DOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Grid[newY][newX].State == BLOCK_STATE.VISITED)
|
||||
{
|
||||
// We've already visited this block, check if we're looping
|
||||
bool hasLooped = false;
|
||||
|
||||
switch (SecurityGuard.CurrentDirection)
|
||||
{
|
||||
case Direction.UP:
|
||||
if (Grid[newY][newX].VisitedFacingUp) hasLooped = true;
|
||||
break;
|
||||
case Direction.DOWN:
|
||||
if (Grid[newY][newX].VisitedFacingDown) hasLooped = true;
|
||||
break;
|
||||
case Direction.LEFT:
|
||||
if (Grid[newY][newX].VisitedFacingLeft) hasLooped = true;
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
if (Grid[newY][newX].VisitedFacingRight) hasLooped = true;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown direction");
|
||||
}
|
||||
|
||||
if (hasLooped) return SIMULATION_RESULT.LOOP;
|
||||
}
|
||||
|
||||
SecurityGuard.X = newX;
|
||||
SecurityGuard.Y = newY;
|
||||
|
||||
Grid[newY][newX].State = BLOCK_STATE.VISITED;
|
||||
switch (SecurityGuard.CurrentDirection)
|
||||
{
|
||||
case Direction.UP:
|
||||
Grid[newY][newX].VisitedFacingUp = true;
|
||||
break;
|
||||
case Direction.DOWN:
|
||||
Grid[newY][newX].VisitedFacingDown = true;
|
||||
break;
|
||||
case Direction.LEFT:
|
||||
Grid[newY][newX].VisitedFacingLeft = true;
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
Grid[newY][newX].VisitedFacingRight = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Guard left the area
|
||||
SecurityGuard.IsGone = true;
|
||||
return SIMULATION_RESULT.DONE;
|
||||
}
|
||||
|
||||
return SIMULATION_RESULT.CONTINUE;
|
||||
}
|
||||
|
||||
public SIMULATION_RESULT SimulateAll()
|
||||
{
|
||||
SIMULATION_RESULT result;
|
||||
|
||||
for (result = SIMULATION_RESULT.CONTINUE; result == SIMULATION_RESULT.CONTINUE; result = SimStep());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Level Clone()
|
||||
{
|
||||
bool useJson = false;
|
||||
|
||||
if (!useJson)
|
||||
{
|
||||
Level other = new Level();
|
||||
|
||||
// Copy the grid
|
||||
foreach (var row in this.Grid)
|
||||
{
|
||||
List<GridSquare> newRow = new List<GridSquare>();
|
||||
foreach (var s in row)
|
||||
{
|
||||
newRow.Add(s.Clone());
|
||||
}
|
||||
other.Grid.Add(newRow);
|
||||
}
|
||||
|
||||
// Copy the guard
|
||||
other.SecurityGuard = SecurityGuard.Clone();
|
||||
|
||||
return other;
|
||||
}
|
||||
else
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize(this);
|
||||
var copy = JsonSerializer.Deserialize<Level>(jsonString);
|
||||
if (copy == null) throw new Exception("Failed to clone");
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
public GridSquare GetGridSquare(int x, int y) => Grid[y][x];
|
||||
}
|
||||
|
||||
Level CurrentLevel { get; set; } = new Level();
|
||||
|
||||
public Day06Parsed(string fileName) : base(fileName)
|
||||
{
|
||||
}
|
||||
|
||||
public override int GetPart01()
|
||||
{
|
||||
var level = CurrentLevel.Clone();
|
||||
level.SimulateAll();
|
||||
return level.CountVisited();
|
||||
}
|
||||
|
||||
public override int GetPart02()
|
||||
{
|
||||
// First simulate everything as a base
|
||||
var simulatedOriginalLevel = CurrentLevel.Clone();
|
||||
simulatedOriginalLevel.SimulateAll();
|
||||
|
||||
int loops = 0;
|
||||
|
||||
// Iterate across the level and find loops
|
||||
List<(int,int)> visited = new List<(int,int)>();
|
||||
|
||||
for (int y = 0; y < simulatedOriginalLevel.Grid.Count; y++)
|
||||
{
|
||||
for (int x = 0; x < simulatedOriginalLevel.Grid[y].Count; x++)
|
||||
{
|
||||
if (simulatedOriginalLevel.Grid[y][x].State == BLOCK_STATE.VISITED)
|
||||
{
|
||||
visited.Add((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run simulations in parallel because we can
|
||||
Parallel.ForEach(visited, coords =>
|
||||
{
|
||||
int x = coords.Item1;
|
||||
int y = coords.Item2;
|
||||
//var loopTest = coords.Item3;
|
||||
|
||||
var loopTest = CurrentLevel.Clone();
|
||||
loopTest.GetGridSquare(x, y).State = BLOCK_STATE.OBSTACLE;
|
||||
var result = loopTest.SimulateAll();
|
||||
if (result == Level.SIMULATION_RESULT.LOOP) loops++;
|
||||
});
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
public override bool ParseLine(string line, object? context = null)
|
||||
{
|
||||
List<GridSquare> list = new List<GridSquare>();
|
||||
|
||||
int index = 0;
|
||||
foreach (char c in line)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '.':
|
||||
list.Add(new GridSquare(BLOCK_STATE.UNTOUCHED));
|
||||
break;
|
||||
case '#':
|
||||
list.Add(new GridSquare(BLOCK_STATE.OBSTACLE));
|
||||
break;
|
||||
case '^':
|
||||
var gridSquare = new GridSquare(BLOCK_STATE.VISITED);
|
||||
|
||||
int guardY = CurrentLevel.Grid.Count;
|
||||
int guardX = index;
|
||||
|
||||
CurrentLevel.SecurityGuard.CurrentDirection = Direction.UP;
|
||||
CurrentLevel.SecurityGuard.X = guardX;
|
||||
CurrentLevel.SecurityGuard.Y = guardY;
|
||||
|
||||
gridSquare.VisitedFacingUp = true;
|
||||
|
||||
list.Add(gridSquare);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Unknown character");
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
CurrentLevel.Grid.Add(list);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user