Added Day 11 and Day 12 for AdventOfCode
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
436cad8605
commit
f655768af4
@ -13,7 +13,6 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="_2021\" />
|
||||
<Folder Include="_2022\Day11\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace AdventOfCode.Models
|
||||
{
|
||||
@ -47,7 +48,7 @@ namespace AdventOfCode.Models
|
||||
this._response.Status = false;
|
||||
this._response.StackTrace = e.StackTrace;
|
||||
}
|
||||
if (!request.IgnoreLogMessages)
|
||||
|
||||
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<string[]> SplitInputOnEmptyLine()
|
||||
{
|
||||
var rows = GetSplitInput();
|
||||
var input = new List<string[]>();
|
||||
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,9 +94,17 @@ namespace AdventOfCode.Models
|
||||
|
||||
protected void Log(object message, params object[] args)
|
||||
{
|
||||
if (_logger != null && !this._request.IgnoreLogMessages)
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
68
AdventOfCode/_2022/Day11/Day11.cs
Normal file
68
AdventOfCode/_2022/Day11/Day11.cs
Normal file
@ -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<Monkey>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
105
AdventOfCode/_2022/Day11/Monkey.cs
Normal file
105
AdventOfCode/_2022/Day11/Monkey.cs
Normal file
@ -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<long> Items { get; set; }
|
||||
public Tuple<string, string> Operation { get; set; }
|
||||
public long DivisibleNumber { get; set; }
|
||||
public Tuple<int, int> TargetMonkeyIds { get; set; }
|
||||
public Tuple<Monkey, Monkey> 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<long>();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
29
AdventOfCode/_2022/Day12/Day12.cs
Normal file
29
AdventOfCode/_2022/Day12/Day12.cs
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
249
AdventOfCode/_2022/Day12/Heightmap.cs
Normal file
249
AdventOfCode/_2022/Day12/Heightmap.cs
Normal file
@ -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<Coord>();
|
||||
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<string[]>();
|
||||
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<Coord> GetAdjacentCoords(Coord currentCoord)
|
||||
{
|
||||
var adjCoords = new List<Coord>()
|
||||
{
|
||||
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<Coord>();
|
||||
activeCoords.Add(_start);
|
||||
var visitedCoords = new List<Coord>();
|
||||
|
||||
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<string>();
|
||||
while (true)
|
||||
{
|
||||
steps.Add($"({coord.X},{coord.Y}) -> {_map[coord.X, coord.Y]}");
|
||||
coord = coord.Parent;
|
||||
if (coord == null)
|
||||
{
|
||||
Steps = steps.Reverse<string>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="visited"></param>
|
||||
/// <param name="pos"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
public List<Coord> Traverse(List<Coord> 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<Coord> 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user