|
|
@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
using AdventOfCode.Common;
|
|
|
|
|
|
|
|
using AdventOfCode.Models;
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace AdventOfCode._2023
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public class Day3 : AOCDay
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected override AOCResponse ExecutePartA()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var board = new SchematicBoard(this.GetSplitInput().ToList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var validSchematicNumbers = board.GetValidSchematicNumbers();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.Answer = validSchematicNumbers.Sum(x => x.Number);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this._response;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected override AOCResponse ExecutePartB()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var board = new SchematicBoard(this.GetSplitInput().ToList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var gearLookup = board.GetGearLookup();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!this._request.IgnoreLogMessages)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
foreach (var gl in gearLookup)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Log($"Gear at {gl.Key} has numbers: {string.Join(",", gl.Value)}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Select valid gears, those with two numbers at a location
|
|
|
|
|
|
|
|
var validGears = gearLookup.Where(x => x.Value.Count == 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Calculate the gear ration (num1 * num2)
|
|
|
|
|
|
|
|
var gearRatios = validGears.Select(x => x.Value.First() * x.Value.Last());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Sum up all the gear ratio for an answer
|
|
|
|
|
|
|
|
this.Answer = gearRatios.Sum(x => x);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this._response;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SchematicBoard
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
private List<SchematicNumber> _schematicNumbers;
|
|
|
|
|
|
|
|
private char[,] _board;
|
|
|
|
|
|
|
|
public SchematicBoard(List<string> input)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
_schematicNumbers = new List<SchematicNumber>();
|
|
|
|
|
|
|
|
var rows = input.Count;
|
|
|
|
|
|
|
|
var columns = input.First().Length;
|
|
|
|
|
|
|
|
_board = new char[rows, columns];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < rows; y++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int x = 0; x < columns; x++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
_board[x,y] = input[x][y];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_board.PrintSquareArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Pull out our schematic numbers from our board
|
|
|
|
|
|
|
|
for (int x = 0; x < _board.GetLength(0); x++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int y = 0; y < _board.GetLength(1); y++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (char.IsDigit(_board[x, y]))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var number = "";
|
|
|
|
|
|
|
|
var yPoses = new List<int>();
|
|
|
|
|
|
|
|
for (int l = y; l < _board.GetLength(1); l++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (char.IsDigit(_board[x, l]))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
yPoses.Add(l);
|
|
|
|
|
|
|
|
number += _board[x, l].ToString();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
y = l;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_schematicNumbers.Add(new SchematicNumber()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Number = int.Parse(number),
|
|
|
|
|
|
|
|
Coords = yPoses.Select(yPos => Tuple.Create(x, yPos)).ToList()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public IEnumerable<SchematicNumber> GetValidSchematicNumbers()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return _schematicNumbers.Where(x => x.IsValidNumber(_board));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Dictionary<string, HashSet<int>> GetGearLookup()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var lookup = new Dictionary<string, HashSet<int>>();
|
|
|
|
|
|
|
|
foreach (var schematicNumber in _schematicNumbers)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var gears = schematicNumber.GetGearCoordinates(_board);
|
|
|
|
|
|
|
|
foreach (var gear in gears)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (lookup.ContainsKey(gear))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lookup[gear].Add(schematicNumber.Number);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
lookup[gear] = new HashSet<int>() { schematicNumber.Number };
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return lookup;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SchematicNumber
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public int Number { get; set; }
|
|
|
|
|
|
|
|
public List<Tuple<int, int>> Coords { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsValidNumber(char[,] board)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
foreach (var coord in Coords)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int x = -1; x <= 1; x++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int y = -1; y <= 1; y++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var checkedPosX = coord.Item1 + x;
|
|
|
|
|
|
|
|
var checkedPosY = coord.Item2 + y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check if the points are within the valid size of our board
|
|
|
|
|
|
|
|
if (checkedPosX < 0 || checkedPosY < 0) continue;
|
|
|
|
|
|
|
|
if (checkedPosX >= board.GetLength(0) || checkedPosY >= board.GetLength(1)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Get our char at the given index
|
|
|
|
|
|
|
|
var charAtPoint = board[checkedPosX, checkedPosY];
|
|
|
|
|
|
|
|
//Check if the char is NOT a digit, and NOT a period, then return true its adjacent
|
|
|
|
|
|
|
|
if (!char.IsDigit(charAtPoint) && charAtPoint != '.')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<string> GetGearCoordinates(char[,] board)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var gearCoords = new List<string>();
|
|
|
|
|
|
|
|
foreach (var coord in Coords)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int x = -1; x <= 1; x++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int y = -1; y <= 1; y++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var checkedPosX = coord.Item1 + x;
|
|
|
|
|
|
|
|
var checkedPosY = coord.Item2 + y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check if the points are within the valid size of our board
|
|
|
|
|
|
|
|
if (checkedPosX < 0 || checkedPosY < 0) continue;
|
|
|
|
|
|
|
|
if (checkedPosX >= board.GetLength(0) || checkedPosY >= board.GetLength(1)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Get our char at the given index
|
|
|
|
|
|
|
|
var charAtPoint = board[checkedPosX, checkedPosY];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check if it is a gear point
|
|
|
|
|
|
|
|
if (charAtPoint == '*')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
gearCoords.Add($"{checkedPosX},{checkedPosY}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return gearCoords;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|