Started 2023 Year and completed days 1-4
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
96d3a9cb9e
commit
ee9ecad64a
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AdventOfCode.Controllers
|
||||
{
|
||||
@ -23,7 +24,8 @@ namespace AdventOfCode.Controllers
|
||||
public AOCResponse Day(int year, int day, AOCVersion version, [FromBody] string input, bool IgnoreLogMessages = false)
|
||||
{
|
||||
AOCRequest request = new AOCRequest() { Input = input, Version = version, IgnoreLogMessages = IgnoreLogMessages };
|
||||
return GetAOCDay(year, day).ExecuteDay(request);
|
||||
var resp = GetAOCDay(year, day).ExecuteDay(request);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private AOCDay GetAOCDay(int year, int day)
|
||||
@ -44,6 +46,29 @@ namespace AdventOfCode.Controllers
|
||||
aocDay.SetLogger(this._logger);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var ns = x.Namespace;
|
||||
var className = x.Name;
|
||||
var dayParsed = 0;
|
||||
_ = int.TryParse(Regex.Replace(className, "[^0-9.]", ""), out dayParsed);
|
||||
|
||||
var match = Regex.Match(ns, @"\d{4}");
|
||||
if (match.Success)
|
||||
{
|
||||
var yearParsed = 0;
|
||||
_ = int.TryParse(match.Value, out yearParsed);
|
||||
if (yearParsed == year && dayParsed == day)
|
||||
{
|
||||
aocDay = (AOCDay)(IAOCService)Activator.CreateInstance(x);
|
||||
aocDay.SetLogger(this._logger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (aocDay != null) break; //Means we found a match and it was created!
|
||||
}
|
||||
return aocDay;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace AdventOfCode.Models
|
||||
{
|
||||
protected AOCRequest _request;
|
||||
protected AOCResponse _response;
|
||||
protected ILogger _logger;
|
||||
private ILogger _logger;
|
||||
private List<string> _debugMessages;
|
||||
protected object Answer { set { this._response.Answer = value; } }
|
||||
public AOCDay()
|
||||
|
@ -23,7 +23,7 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": "true",
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
"applicationUrl": "https://localhost:5023;http://localhost:5123"
|
||||
}
|
||||
}
|
||||
}
|
83
AdventOfCode/_2023/Day1.cs
Normal file
83
AdventOfCode/_2023/Day1.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using AdventOfCode.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AdventOfCode._2023
|
||||
{
|
||||
public class Day1 : AOCDay
|
||||
{
|
||||
private Dictionary<string, int> lookup = new Dictionary<string, int>()
|
||||
{
|
||||
{"zero", 0 },
|
||||
{"one", 1 },
|
||||
{"two", 2 },
|
||||
{"three", 3 },
|
||||
{"four", 4 },
|
||||
{"five", 5 },
|
||||
{"six", 6 },
|
||||
{"seven", 7 },
|
||||
{"eight", 8 },
|
||||
{"nine", 9 },
|
||||
};
|
||||
protected override AOCResponse ExecutePartA()
|
||||
{
|
||||
var totalCount = 0;
|
||||
foreach (var line in this.GetSplitInput())
|
||||
{
|
||||
totalCount += this._request.Version == AOCVersion.A ? ParseLineIntoNumber(line) : ParseTextLineIntoNumber(line);
|
||||
}
|
||||
this.Answer = totalCount;
|
||||
return this._response;
|
||||
}
|
||||
|
||||
protected override AOCResponse ExecutePartB()
|
||||
{
|
||||
return ExecutePartA();
|
||||
}
|
||||
|
||||
private int ParseLineIntoNumber(string line)
|
||||
{
|
||||
var firstDigit = line.First(x => char.IsDigit(x));
|
||||
var lastDigit = line.Reverse().First(x => char.IsDigit(x));
|
||||
Log($"{line} => first: {firstDigit} last: {lastDigit}");
|
||||
return int.Parse($"{firstDigit}{lastDigit}");
|
||||
}
|
||||
|
||||
private int ParseTextLineIntoNumber(string line)
|
||||
{
|
||||
var foundDigits = new List<int>();
|
||||
for (int c = 0; c < line.Length; c++)
|
||||
{
|
||||
if (char.IsDigit(line[c]))
|
||||
{
|
||||
foundDigits.Add(int.Parse(line[c].ToString()));
|
||||
continue;
|
||||
}
|
||||
foreach (var key in lookup.Keys)
|
||||
{
|
||||
var length = c + key.Length;
|
||||
if (length <= line.Length) //Check if we exceeded our line length
|
||||
{
|
||||
var section = new String(line.Skip(c).Take(key.Length).ToArray());
|
||||
if (section.Equals(key))
|
||||
{
|
||||
foundDigits.Add(lookup[key]);
|
||||
|
||||
//We can't skip the length because eighthree => 83 and sevenine => 79. We only advance one char to fix
|
||||
//c += key.Length-1; //Outer forloop auto increments one already
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var firstDigit = foundDigits.First();
|
||||
var lastDigit = foundDigits.Last();
|
||||
if (firstDigit > 9 || lastDigit > 9) throw new Exception("No digit should be higher than 9");
|
||||
var total = int.Parse($"{firstDigit}{lastDigit}");
|
||||
Log($"{line} => first: {firstDigit} last: {lastDigit} for total of {total}" );
|
||||
return total;
|
||||
}
|
||||
}
|
||||
}
|
142
AdventOfCode/_2023/Day2.cs
Normal file
142
AdventOfCode/_2023/Day2.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using AdventOfCode.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AdventOfCode._2023
|
||||
{
|
||||
public class Day2 : AOCDay
|
||||
{
|
||||
private readonly List<Cube> ANSWER_CUBES = new List<Cube>()
|
||||
{
|
||||
new Cube("12 red"),
|
||||
new Cube("13 green"),
|
||||
new Cube("14 blue")
|
||||
};
|
||||
|
||||
protected override AOCResponse ExecutePartA()
|
||||
{
|
||||
//Create game objects for each of our lines
|
||||
var games = this.GetSplitInput().Select(x => new Game(x)).ToList();
|
||||
|
||||
//Select only our valid games
|
||||
var validGames = games.Where(x => x.CompareAgainstAnswer(ANSWER_CUBES));
|
||||
|
||||
this.Answer = validGames.Sum(x => x.Id);
|
||||
return this._response;
|
||||
}
|
||||
|
||||
protected override AOCResponse ExecutePartB()
|
||||
{
|
||||
//Create game objects for each of our lines
|
||||
var games = this.GetSplitInput().Select(x => new Game(x)).ToList();
|
||||
|
||||
if (!this._request.IgnoreLogMessages)
|
||||
{
|
||||
games.ForEach(x =>
|
||||
{
|
||||
var minAnswerCubes = x.GetMinAnswerCubes();
|
||||
Log($"Game ID {x.Id} generated the min answer: {string.Join(", ", minAnswerCubes.Select(x => $"{x.Count} {x.Color}"))} with power {x.GetGamePower()}");
|
||||
});
|
||||
}
|
||||
this.Answer = games.Sum(x => x.GetGamePower());
|
||||
return this._response;
|
||||
}
|
||||
}
|
||||
|
||||
class Game
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public List<Drawing> drawings { get; set; } = new List<Drawing>();
|
||||
public Game(string input)
|
||||
{
|
||||
var splitInput = input.Split(':');
|
||||
Id = int.Parse(splitInput[0].Split(' ')[1]); //Extract our game id out of input string
|
||||
|
||||
//Parse our drawings into objects
|
||||
foreach (var drawing in splitInput[1].Split(';'))
|
||||
{
|
||||
drawings.Add(new Drawing(drawing.Trim()));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompareAgainstAnswer(List<Cube> baggedCubes)
|
||||
{
|
||||
foreach (var drawing in drawings)
|
||||
{
|
||||
foreach (var cube in drawing.Cubes)
|
||||
{
|
||||
var answerCube = baggedCubes.First(x => x.Color == cube.Color);
|
||||
if (cube.Count > answerCube.Count)
|
||||
{
|
||||
//Means the drawed cube count exceeds the number of cubes in our bag
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Cube> GetMinAnswerCubes()
|
||||
{
|
||||
var minAnswerCubes = new List<Cube>();
|
||||
foreach (var drawing in drawings)
|
||||
{
|
||||
foreach (var cube in drawing.Cubes)
|
||||
{
|
||||
var matchedCube = minAnswerCubes.FirstOrDefault(x => x.Color == cube.Color);
|
||||
if (matchedCube == null)
|
||||
{
|
||||
minAnswerCubes.Add(new Cube()
|
||||
{
|
||||
Color = cube.Color,
|
||||
Count = cube.Count
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedCube.Count = Math.Max(matchedCube.Count, cube.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
return minAnswerCubes;
|
||||
}
|
||||
|
||||
public int GetGamePower()
|
||||
{
|
||||
var minAnswerCubes = GetMinAnswerCubes();
|
||||
var power = 1;
|
||||
minAnswerCubes.ForEach(x => power *= x.Count);
|
||||
return power;
|
||||
}
|
||||
}
|
||||
|
||||
class Drawing
|
||||
{
|
||||
public List<Cube> Cubes { get; set; } = new List<Cube>();
|
||||
|
||||
public Drawing(string drawing)
|
||||
{
|
||||
foreach (var input in drawing.Split(','))
|
||||
{
|
||||
Cubes.Add(new Cube(input.Trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Cube
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public string Color { get; set; }
|
||||
|
||||
public Cube() { }
|
||||
public Cube(string input)
|
||||
{
|
||||
var split = input.Split(' ');
|
||||
Count = int.Parse(split[0]);
|
||||
Color = split[1];
|
||||
}
|
||||
}
|
||||
}
|
196
AdventOfCode/_2023/Day3.cs
Normal file
196
AdventOfCode/_2023/Day3.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
116
AdventOfCode/_2023/Day4.cs
Normal file
116
AdventOfCode/_2023/Day4.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using AdventOfCode.Common;
|
||||
using AdventOfCode.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AdventOfCode._2023
|
||||
{
|
||||
public class Day4 : AOCDay
|
||||
{
|
||||
|
||||
protected override AOCResponse ExecutePartA()
|
||||
{
|
||||
var cards = new List<Card>();
|
||||
foreach (var entry in this.GetSplitInput())
|
||||
{
|
||||
cards.Add(new Card(entry));
|
||||
}
|
||||
|
||||
foreach (var card in cards)
|
||||
{
|
||||
Log($"Card {card.Id} has a score of {card.GenerateScore()}");
|
||||
}
|
||||
|
||||
this.Answer = cards.Sum(x => x.GenerateScore());
|
||||
return this._response;
|
||||
}
|
||||
|
||||
protected override AOCResponse ExecutePartB()
|
||||
{
|
||||
|
||||
var cards = new List<Card>();
|
||||
foreach (var entry in this.GetSplitInput())
|
||||
{
|
||||
cards.Add(new Card(entry));
|
||||
}
|
||||
|
||||
foreach (var card in cards)
|
||||
{
|
||||
for (int cardCount = 0; cardCount < card.CardCount; cardCount++)
|
||||
{
|
||||
var count = card.GetCountOfWinningNumbers();
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
cards[(card.Id + i)-1].CardCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Answer = cards.Sum(x => x.CardCount);
|
||||
|
||||
return this._response;
|
||||
}
|
||||
}
|
||||
|
||||
class Card
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int CardCount { get; set; }
|
||||
public List<int> WinningNumbers { get; set; } = new List<int>();
|
||||
public List<int> Numbers { get; set; } = new List<int>();
|
||||
public Card(string entry)
|
||||
{
|
||||
CardCount = 1;
|
||||
entry = entry.Replace(" ", " ").Replace(" ", " "); //Replace two spaces with 1
|
||||
var split = entry.Split(':');
|
||||
Id = int.Parse(split[0].Split(' ')[1]);
|
||||
|
||||
//Winning numbers
|
||||
foreach (var number in split[1].Split('|')[0].Trim().Split(' '))
|
||||
{
|
||||
WinningNumbers.Add(int.Parse(number));
|
||||
}
|
||||
|
||||
//Winning numbers
|
||||
foreach (var number in split[1].Split('|')[1].Trim().Split(' '))
|
||||
{
|
||||
Numbers.Add(int.Parse(number));
|
||||
}
|
||||
}
|
||||
|
||||
public int GenerateScore()
|
||||
{
|
||||
var score = 0;
|
||||
foreach (var number in Numbers)
|
||||
{
|
||||
if (WinningNumbers.Contains(number))
|
||||
{
|
||||
if (score == 0)
|
||||
{
|
||||
score = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
score *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
public int GetCountOfWinningNumbers()
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var number in Numbers)
|
||||
{
|
||||
if (WinningNumbers.Contains(number))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user