Started 2023 Year and completed days 1-4
continuous-integration/drone/push Build is passing Details

pull/1/head
Alexander Sigler 12 months ago
parent 96d3a9cb9e
commit ee9ecad64a

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
namespace AdventOfCode.Controllers 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) 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 }; 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) private AOCDay GetAOCDay(int year, int day)
@ -44,6 +46,29 @@ namespace AdventOfCode.Controllers
aocDay.SetLogger(this._logger); 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; return aocDay;
} }

@ -11,7 +11,7 @@ namespace AdventOfCode.Models
{ {
protected AOCRequest _request; protected AOCRequest _request;
protected AOCResponse _response; protected AOCResponse _response;
protected ILogger _logger; private ILogger _logger;
private List<string> _debugMessages; private List<string> _debugMessages;
protected object Answer { set { this._response.Answer = value; } } protected object Answer { set { this._response.Answer = value; } }
public AOCDay() public AOCDay()

@ -23,7 +23,7 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"dotnetRunMessages": "true", "dotnetRunMessages": "true",
"applicationUrl": "https://localhost:5001;http://localhost:5000" "applicationUrl": "https://localhost:5023;http://localhost:5123"
} }
} }
} }

@ -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;
}
}
}

@ -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];
}
}
}

@ -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;
}
}
}

@ -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…
Cancel
Save