using AdventOfCode.Core.Shared.IO; namespace AdventOfCode.Solutions._2023 { public class Day07 : IChallange { public int Year => 2023; public int Day => 7; private static readonly List CardValues = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']; private static readonly List CardValuesJoker = ['J', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'Q', 'K', 'A']; private enum SetRanks { High, OnePair, TwoPair, ThreeKind, FullHouse, FourKind, FiveKind } private readonly IInputReader _inputReader; public Day07(IInputReader inputReader) { _inputReader = inputReader; _inputReader.SetInput(this); } public async Task GetSolutionPart1() { return (await _inputReader.ReadAsArrayString()) .Select(l => new Hand(l)) .Order() .Select((h, i) => h.Bid * (i + 1)) .Sum() .ToString(); } public async Task GetSolutionPart2() { return (await _inputReader.ReadAsArrayString()) .Select(l => new Hand(l, true)) .Order() .Select((h, i) => h.Bid * (i + 1)) .Sum() .ToString(); } private record Hand : IComparable { public int Bid { get; set; } public SetRanks Rank { get; set; } public string Source { get; set; } public string JokerSource { get; set; } = string.Empty; public List CharCompairList => string.IsNullOrEmpty(JokerSource) ? CardValues : CardValuesJoker; public Hand(string line, bool jokerMode = false) { string[] splitted = line.Split(' '); Source = splitted[0]; Bid = int.Parse(splitted[1]); string finalTestLine = Source; if (jokerMode) { JokerSource = Source; if (finalTestLine.Contains('J') && finalTestLine.Any(c => c != 'J')) { // fun time char toReplace = finalTestLine.Where(c => c != 'J').GroupBy(c => c).OrderByDescending(g => g.Count()).First().Key; finalTestLine = finalTestLine.Replace('J', toReplace); JokerSource = finalTestLine; } } var grouped = finalTestLine.GroupBy(c => c).OrderByDescending(g => g.Count()); switch(grouped.Count()) { case 1: Rank = SetRanks.FiveKind; break; case 4: Rank = SetRanks.OnePair; break; case 5: Rank = SetRanks.High; break; case 3: if (grouped.Skip(1).First().Count() == 2) Rank = SetRanks.TwoPair; else Rank = SetRanks.ThreeKind; break; case 2: if (grouped.First().Count() == 4) Rank = SetRanks.FourKind; else Rank = SetRanks.FullHouse; break; } } public override string ToString() { return $"{JokerSource} [{Source}] {Rank} {Bid}"; } public int CompareTo(Hand? other) { if (other == null) return 1; if (this.Rank == other.Rank && this.Source == other.Source) return 0; if (this.Rank > other.Rank) return 1; if (this.Rank < other.Rank) return -1; for (int i = 0; i < Source.Length; i++) { if (this.Source[i] == other.Source[i]) continue; if (CharCompairList.IndexOf(this.Source[i]) > CharCompairList.IndexOf(other.Source[i])) return 1; if (CharCompairList.IndexOf(this.Source[i]) < CharCompairList.IndexOf(other.Source[i])) return -1; } return 0; } } } }