diff --git a/AdventOfCode/AdventOfCode.csproj b/AdventOfCode/AdventOfCode.csproj
index ab3abdc..0ff1ded 100644
--- a/AdventOfCode/AdventOfCode.csproj
+++ b/AdventOfCode/AdventOfCode.csproj
@@ -13,7 +13,6 @@
-
diff --git a/AdventOfCode/Common/AOCExtensions.cs b/AdventOfCode/Common/AOCExtensions.cs
index ca3760d..9fab128 100644
--- a/AdventOfCode/Common/AOCExtensions.cs
+++ b/AdventOfCode/Common/AOCExtensions.cs
@@ -39,5 +39,17 @@ namespace AdventOfCode.Common
Console.WriteLine();
}
}
+
+ public static void PrintSquareArray(this char[,] arr)
+ {
+ for (int x = 0; x < arr.GetLength(0); x++)
+ {
+ for (int y = 0; y < arr.GetLength(1); y++)
+ {
+ Console.Write(arr[x, y] + " ");
+ }
+ Console.WriteLine();
+ }
+ }
}
}
diff --git a/AdventOfCode/Models/AOCDay.cs b/AdventOfCode/Models/AOCDay.cs
index 43d70f8..71265db 100644
--- a/AdventOfCode/Models/AOCDay.cs
+++ b/AdventOfCode/Models/AOCDay.cs
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
namespace AdventOfCode.Models
{
@@ -47,8 +48,8 @@ namespace AdventOfCode.Models
this._response.Status = false;
this._response.StackTrace = e.StackTrace;
}
- if (!request.IgnoreLogMessages)
- this._response.Debug = this._debugMessages;
+
+ this._response.Debug = this._debugMessages;
return this._response;
}
@@ -66,6 +67,26 @@ namespace AdventOfCode.Models
{
return this._request.Input.Trim().Replace("\r", "").Split("\n");
}
+
+ protected List SplitInputOnEmptyLine()
+ {
+ var rows = GetSplitInput();
+ var input = new List();
+ int set = 0;
+ for (int i = 0; i < rows.Length; i++)
+ {
+ if (string.IsNullOrWhiteSpace(rows[i]))
+ {
+ input.Add(rows.Skip(set).Take(i - set).ToArray());
+ set = i + 1;
+ }
+ if (i == rows.Length - 1)
+ {
+ input.Add(rows.Skip(set).ToArray());
+ }
+ }
+ return input;
+ }
public void SetLogger(ILogger logger)
{
this._logger = logger;
@@ -73,8 +94,16 @@ namespace AdventOfCode.Models
protected void Log(object message, params object[] args)
{
- if (_logger != null && !this._request.IgnoreLogMessages)
- _logger.LogInformation(message.ToString(), args);
+ if (!this._request.IgnoreLogMessages)
+ {
+ if (_logger != null)
+ _logger.LogInformation(message.ToString(), args);
+ _debugMessages.Add(string.Format(message.ToString(), args));
+ }
+ }
+
+ protected void Info(object message, params object[] args)
+ {
_debugMessages.Add(string.Format(message.ToString(), args));
}
}
diff --git a/AdventOfCode/_2022/Day11/Day11.cs b/AdventOfCode/_2022/Day11/Day11.cs
new file mode 100644
index 0000000..fec9b59
--- /dev/null
+++ b/AdventOfCode/_2022/Day11/Day11.cs
@@ -0,0 +1,68 @@
+using AdventOfCode._2022.Models;
+using AdventOfCode.Common;
+using AdventOfCode.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AdventOfCode._2022.Day11
+{
+ [AOC(year: 2022, day: 11)]
+ public class Day11 : AOCDay
+ {
+ protected override AOCResponse ExecutePartA()
+ {
+ ExecuteMonkeyBusiness(20);
+ return _response;
+ }
+
+ protected override AOCResponse ExecutePartB()
+ {
+ ExecuteMonkeyBusiness(10000);
+ return _response;
+ }
+
+ private void ExecuteMonkeyBusiness(int rounds)
+ {
+ var input = SplitInputOnEmptyLine();
+ var monkeys = new List();
+ foreach (var monkey in input)
+ {
+ monkeys.Add(new Monkey(_request.Version, monkey.ToArray()));
+ }
+
+ long lcm = 1; //Only needed for part b
+ //Target Monkey Mapping
+ foreach (var monkey in monkeys)
+ {
+ lcm *= monkey.DivisibleNumber;
+ monkey.MapTargetMonkeys(monkeys.First(x => x.MonkeyId == monkey.TargetMonkeyIds.Item1), monkeys.First(x => x.MonkeyId == monkey.TargetMonkeyIds.Item2));
+ }
+
+ //Start round
+ for (int i = 0; i < rounds; i++)
+ {
+ foreach (var monkey in monkeys)
+ {
+ monkey.Business(lcm);
+ }
+
+ if (_request.Version == AOCVersion.A)
+ monkeys.ForEach(x => Log("Round" + (i + 1) + "_M" + x.MonkeyId + ": " + string.Join(", ", x.Items)));
+ else
+ {
+ if ((i + 1) == 1 || (i + 1) == 20 || (i + 1) == 1000 || (i + 1) % 1000 == 0)
+ {
+ monkeys.ForEach(x => Log($"R{i + 1}Monkey{x.MonkeyId} inspected {x.ItemsInspected} items"));
+ monkeys.ForEach(x => Log("Round" + (i + 1) + "_M" + x.MonkeyId + ": " + string.Join(", ", x.Items)));
+ }
+ }
+
+ }
+ monkeys.ForEach(x => Info($"Monkey{x.MonkeyId} inspected {x.ItemsInspected} items"));
+
+ var crazyMonkeys = monkeys.OrderByDescending(x => x.ItemsInspected).ToArray();
+ _response.Answer = crazyMonkeys[0].ItemsInspected * crazyMonkeys[1].ItemsInspected;
+ }
+ }
+}
diff --git a/AdventOfCode/_2022/Day11/Monkey.cs b/AdventOfCode/_2022/Day11/Monkey.cs
new file mode 100644
index 0000000..b4fd514
--- /dev/null
+++ b/AdventOfCode/_2022/Day11/Monkey.cs
@@ -0,0 +1,105 @@
+using AdventOfCode.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace AdventOfCode._2022.Day11
+{
+ public class Monkey
+ {
+ private AOCVersion _version;
+ public int MonkeyId { get; set; }
+ public Queue Items { get; set; }
+ public Tuple Operation { get; set; }
+ public long DivisibleNumber { get; set; }
+ public Tuple TargetMonkeyIds { get; set; }
+ public Tuple TargetMonkeys { get; set; }
+ public long ItemsInspected { get; set; }
+ public Monkey(AOCVersion version, string[] monkey)
+ {
+ _version = version;
+ //Pull monkey id
+ MonkeyId = Convert.ToInt32(Regex.Replace(monkey.First(), "[^0-9.]", ""));
+
+ //Item creation
+ Items = new Queue();
+ Regex.Replace(monkey[1], "[^0-9. ]", "").Trim().Split(' ').ToList().ForEach(x => Items.Enqueue(Convert.ToInt32(x)));
+
+ //Generate operation
+ var splitOperation = monkey[2].Split(' ');
+ Operation = Tuple.Create(splitOperation[splitOperation.Length - 2], splitOperation[splitOperation.Length - 1]);
+
+ //Divisible number
+ DivisibleNumber = Convert.ToInt32(Regex.Replace(monkey[3], "[^0-9.]", ""));
+
+ //TargetMonkey
+ TargetMonkeyIds = Tuple.Create(Convert.ToInt32(Regex.Replace(monkey[4], "[^0-9.]", "")), Convert.ToInt32(Regex.Replace(monkey[5], "[^0-9.]", "")));
+
+ //Set items insepcts to 0
+ ItemsInspected = 0;
+ }
+
+ public void MapTargetMonkeys(Monkey trueMonkey, Monkey falseMonkey)
+ {
+ TargetMonkeys = Tuple.Create(trueMonkey, falseMonkey);
+ }
+
+ public void Business(long lcm = 0)
+ {
+ while (Items.Any())
+ {
+ var item = Items.Dequeue();
+ ItemsInspected++;
+ if (_version == AOCVersion.A)
+ {
+ item = ExecuteOperation(item); //Worry level
+ item = item / 3; //Monkey boredom
+
+ //Throw item to next monkey
+ if (item % DivisibleNumber == 0)
+ {
+ TargetMonkeys.Item1.Items.Enqueue(item);
+ }
+ else
+ {
+ TargetMonkeys.Item2.Items.Enqueue(item);
+ }
+ }
+ else
+ {
+ long number;
+ if (!long.TryParse(Operation.Item2, out number))
+ number = item;
+ item %= lcm;
+ number %= lcm;
+ var result = Operation.Item1 == "+" ? item + number : item * number;
+ if (result% DivisibleNumber == 0)
+ {
+ TargetMonkeys.Item1.Items.Enqueue(result);
+ }
+ else
+ {
+ TargetMonkeys.Item2.Items.Enqueue(result);
+ }
+ }
+
+
+ }
+ }
+
+ private long ExecuteOperation(long item)
+ {
+ long number;
+ if (!long.TryParse(Operation.Item2, out number))
+ number = item;
+ if (Operation.Item1 == "+")
+ return number + item;
+ else if (Operation.Item1 == "*")
+ return number * item;
+ throw new ArgumentException("Unknown operation " + Operation.Item1);
+ }
+
+ }
+}
diff --git a/AdventOfCode/_2022/Day12/Day12.cs b/AdventOfCode/_2022/Day12/Day12.cs
new file mode 100644
index 0000000..5361d1f
--- /dev/null
+++ b/AdventOfCode/_2022/Day12/Day12.cs
@@ -0,0 +1,29 @@
+using AdventOfCode.Common;
+using AdventOfCode.Models;
+using System.Linq;
+
+namespace AdventOfCode._2022.Day12
+{
+ [AOC(year: 2022, day: 12)]
+ [IgnoreTestAnswer(AOCVersion.B)] //Run time is 1min 38sec 648ms for PartB
+ public class Day12 : AOCDay
+ {
+ protected override AOCResponse ExecutePartA()
+ {
+ var map = new Heightmap(GetSplitInput());
+ map.Traverse(_request.Version);
+ map.Steps.ToList().ForEach(x => Info(x));
+ _response.Answer = map.TotalSteps;
+
+ return _response;
+ }
+
+ protected override AOCResponse ExecutePartB()
+ {
+ //Only difference is done VIA AOCVersion variable
+ //Run time is 1min 38sec 648ms for PartB, might want to optimize
+ return ExecutePartA();
+ }
+
+ }
+}
diff --git a/AdventOfCode/_2022/Day12/Heightmap.cs b/AdventOfCode/_2022/Day12/Heightmap.cs
new file mode 100644
index 0000000..4f488e5
--- /dev/null
+++ b/AdventOfCode/_2022/Day12/Heightmap.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AdventOfCode.Common;
+using AdventOfCode.Models;
+
+namespace AdventOfCode._2022.Day12
+{
+ public class Heightmap
+ {
+ public int TotalSteps { get; set; }
+ public string[] Steps { get; set; }
+ private Coord _start, _end;
+ private char[,] _map;
+ private readonly int MaxX, MaxY;
+ public Heightmap(string[] input)
+ {
+ //Create our 2d board
+ _map = new char[input.Count(), input.First().Length];
+ for (int i = 0; i < input.Count(); i++)
+ {
+ var line = input[i];
+ for (int j = 0; j < line.Length; j++)
+ {
+ _map[i, j] = line[j];
+ }
+ }
+
+ //Find the start and end
+ MaxX = _map.GetLength(0);
+ MaxY = _map.GetLength(1);
+ for (int x = 0; x < _map.GetLength(0); x++)
+ {
+ for (int y = 0; y < _map.GetLength(1); y++)
+ {
+ if (_map[x, y] == 'S') _start = new Coord(x, y);
+ if (_map[x, y] == 'E') _end = new Coord(x, y);
+ }
+ }
+
+ //Ensure a start and end was found
+ if (_start == null) throw new ArgumentException("No start coord found!");
+ if (_end == null) throw new ArgumentException("No end coord found!");
+ _start.SetDistance(_end);//Set the distance between start and end
+ }
+
+ public void Traverse(AOCVersion version)
+ {
+ if (version == AOCVersion.A)
+ {
+ //Traverse our 2d matrix
+ AStarTraversal();
+ }
+ else
+ {
+ _map[_start.X, _start.Y] = 'a'; //Change our S to an 'a' elevation
+ var possibleStarts = new List();
+ for (int x = 0; x < _map.GetLength(0); x++)
+ {
+ for (int y = 0; y < _map.GetLength(1); y++)
+ {
+ if (_map[x, y] == 'a') possibleStarts.Add(new Coord(x, y));
+ }
+ }
+
+ var routes = new List();
+ foreach (var possibleStart in possibleStarts)
+ {
+ _start = possibleStart;
+ AStarTraversal();
+ routes.Add(Steps.ToArray());
+ }
+ Steps = routes.OrderByDescending(x => x.Length).Last();
+ TotalSteps = Steps.Length - 1;
+ }
+ }
+
+ private List GetAdjacentCoords(Coord currentCoord)
+ {
+ var adjCoords = new List()
+ {
+ new Coord { X = currentCoord.X, Y = currentCoord.Y - 1, Parent = currentCoord, Cost = currentCoord.Cost + 1 }, //Left
+ new Coord { X = currentCoord.X, Y = currentCoord.Y + 1, Parent = currentCoord, Cost = currentCoord.Cost + 1},// Right
+ new Coord { X = currentCoord.X - 1, Y = currentCoord.Y, Parent = currentCoord, Cost = currentCoord.Cost + 1 }, //Up
+ new Coord { X = currentCoord.X + 1, Y = currentCoord.Y, Parent = currentCoord, Cost = currentCoord.Cost + 1 }, //Down
+ };
+
+ adjCoords.ForEach(coord => coord.SetDistance(_end));
+ var currentChar = _map[currentCoord.X, currentCoord.Y];
+ if (currentChar == 'S') currentChar = 'a'; //Set the starting char to 'a' so it can do the diff in char instead of from 'S'
+
+ return adjCoords
+ .Where(coord => coord.X >= 0 && coord.X < MaxX)
+ .Where(coord => coord.Y >= 0 && coord.Y < MaxY)
+ .Where(coord => (_map[coord.X, coord.Y] != 'E' && (_map[coord.X, coord.Y] - currentChar) <= 1) //At most upper height is 1, can drop any amount
+ || (_map[coord.X, coord.Y] == 'E' && (currentChar == 'z' || currentChar == 'y'))) //E's height IS 'z', so it can go z->z or y->z for the final move.
+ .ToList();
+ }
+
+ private void AStarTraversal()
+ {
+ var activeCoords = new List();
+ activeCoords.Add(_start);
+ var visitedCoords = new List();
+
+ while (activeCoords.Any())
+ {
+ var checkCoord = activeCoords.OrderByDescending(x => x.CostDistance).Last();
+
+ if (checkCoord.X == _end.X && checkCoord.Y == _end.Y)
+ {
+ //We found the destination and we can be sure (Because the the OrderBy above)
+ //That it's the most low cost option.
+ var coord = checkCoord;
+ var steps = new List();
+ while (true)
+ {
+ steps.Add($"({coord.X},{coord.Y}) -> {_map[coord.X, coord.Y]}");
+ coord = coord.Parent;
+ if (coord == null)
+ {
+ Steps = steps.Reverse().ToArray();
+ TotalSteps = steps.Count-1; //N coords touched, so N-1 steps.
+ return;
+ }
+ }
+ }
+
+ visitedCoords.Add(checkCoord);
+ activeCoords.Remove(checkCoord);
+
+ var adjacentCoords = GetAdjacentCoords(checkCoord);
+
+ foreach (var adjCoor in adjacentCoords)
+ {
+ //Skip to next coord if we've already visited
+ if (visitedCoords.Any(x => x.X == adjCoor.X && x.Y == adjCoor.Y))
+ continue;
+
+ //Its already pending as an active coord, but we need to re-check and update it if the cost is smaller
+ if (activeCoords.Any(x => x.X == adjCoor.X && x.Y == adjCoor.Y))
+ {
+ var existingCoord = activeCoords.First(x => x.X == adjCoor.X && x.Y == adjCoor.Y);
+ if (existingCoord.CostDistance > checkCoord.CostDistance)
+ {
+ activeCoords.Remove(existingCoord);
+ activeCoords.Add(adjCoor);
+ }
+ }
+ else
+ {
+ //New coord so we are adding it to the list
+ activeCoords.Add(adjCoor);
+ }
+ }
+ }
+ }
+
+ ///
+ /// This is a brute force method, it works for the sample but not my input, it takes way to long as it tries to explore every single path and square.
+ ///
+ ///
+ ///
+ ///
+ [Obsolete]
+ public List Traverse(List visited, Coord pos)
+ {
+ visited.Add(pos); //Signify that we visited this coord in our search
+ char val = _map[pos.X, pos.Y];
+ if (val == 'E')
+ {
+ //We only want to return if the last visited char is 'z' since it needs to complete alphabet before continuing
+ var lastVisitedLocation = visited[visited.Count - 2];
+ return _map[lastVisitedLocation.X, lastVisitedLocation.Y] == 'z' ? visited : null;
+ }
+
+ if (val == 'S') val = 'a'; //So its on an even playing field.
+
+ List shortestPath = null;
+ //left
+ var newCoord = new Coord(pos.X, pos.Y - 1);
+ if ((pos.Y - 1) >= 0
+ && !visited.Any(c => c.X == newCoord.X && c.Y == newCoord.Y)
+ && _map[newCoord.X, newCoord.Y] - val <= 1)
+ {
+ var newVisited = visited.Select(x => x).ToList();
+ var path = Traverse(newVisited, newCoord);
+ if (path != null && (shortestPath == null || path.Count() < shortestPath.Count()))
+ shortestPath = path;
+ }
+ //right
+ newCoord = new Coord(pos.X, pos.Y + 1);
+ if ((pos.Y + 1) < MaxY
+ && !visited.Any(c => c.X == newCoord.X && c.Y == newCoord.Y)
+ && _map[newCoord.X, newCoord.Y] - val <= 1)
+ {
+ var newVisited = visited.Select(x => x).ToList();
+ var path = Traverse(newVisited, newCoord);
+ if (path != null && (shortestPath == null || path.Count() < shortestPath.Count()))
+ shortestPath = path;
+ }
+ //up
+ newCoord = new Coord(pos.X - 1, pos.Y);
+ if ((pos.X - 1) >= 0
+ && !visited.Any(c => c.X == newCoord.X && c.Y == newCoord.Y)
+ && _map[newCoord.X, newCoord.Y] - val <= 1)
+ {
+ var newVisited = visited.Select(x => x).ToList();
+ var path = Traverse(newVisited, newCoord);
+ if (path != null && (shortestPath == null || path.Count() < shortestPath.Count()))
+ shortestPath = path;
+ }
+ //down
+ newCoord = new Coord(pos.X + 1, pos.Y);
+ if ((pos.X + 1) < MaxX
+ && !visited.Any(c => c.X == newCoord.X && c.Y == newCoord.Y)
+ && _map[newCoord.X, newCoord.Y] - val <= 1)
+ {
+ var newVisited = visited.Select(x => x).ToList();
+ var path = Traverse(newVisited, newCoord);
+ if (path != null && (shortestPath == null || path.Count() < shortestPath.Count()))
+ shortestPath = path;
+ }
+ return shortestPath;
+ }
+ }
+
+ public class Coord
+ {
+ public Coord() { }
+ public Coord(int x, int y)
+ {
+ this.X = x;
+ this.Y = y;
+ }
+ public int X { get; set; }
+ public int Y { get; set; }
+ public int Cost { get; set; }
+ public int Distance { get; set; }
+ public int CostDistance { get { return Cost + Distance; } }
+ public Coord Parent { get; set; }
+
+ //An estimated distance to a specific target
+ public void SetDistance(Coord target)
+ {
+ this.Distance = Math.Abs(target.X - X) + Math.Abs(target.Y - Y);
+ }
+ }
+}