diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 327f6ee..c8fe206 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -57,6 +57,10 @@ + + + + diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index faab668..306c507 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -25,7 +25,7 @@ uses {$ENDIF} Classes, SysUtils, CustApp, USolver, - UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt; + UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt, UCamelCards; type @@ -56,6 +56,7 @@ begin engine.RunAndFree(TScratchcards.Create); engine.RunAndFree(TGiveSeedFertilizer.Create); engine.RunAndFree(TWaitForIt.Create); + engine.RunAndFree(TCamelCards.Create); engine.Free; end; diff --git a/solvers/UCamelCards.pas b/solvers/UCamelCards.pas new file mode 100644 index 0000000..66ecd8c --- /dev/null +++ b/solvers/UCamelCards.pas @@ -0,0 +1,213 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2023 Stefan Müller + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . +} + +unit UCamelCards; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Generics.Collections, Generics.Defaults, USolver; + +type + TCardHandType = (htHighCard, htOnePair, htTwoPair, htThreeOfAKind, htFullHouse, htFourOfAKind, htFiveOfAKind); + + TCardCounter = record + Card: Char; + Count: Integer; + end; + + TCardCounters = specialize TList; + + { TCardHand } + + TCardHand = class + private + FHand: string; + FType: TCardHandType; + FBid: Cardinal; + function GetCardValue(const ACard: Char): Cardinal; + public + constructor Create(const ALine: string); + function CompareTo(constref AOther: TCardHand): Integer; + property Bid: Cardinal read FBid; + end; + + TCardHands = specialize TObjectList; + + { TCardHandComparer } + + TCardHandComparer = class(TInterfacedObject, specialize IComparer) + public + function Compare(constref AHand1, AHand2: TCardHand): Integer; + end; + + { TCamelCards } + + TCamelCards = class(TSolver) + private + FHands: TCardHands; + public + constructor Create; + destructor Destroy; override; + procedure ProcessDataLine(const ALine: string); override; + procedure Finish; override; + function GetDataFileName: string; override; + function GetPuzzleName: string; override; + end; + +implementation + +{ TCardHand } + +function TCardHand.GetCardValue(const ACard: Char): Cardinal; +begin + if not TryStrToDWord(ACard, Result) then + case ACard of + 'A': Result := 14; + 'K': Result := 13; + 'Q': Result := 12; + 'J': Result := 11; + 'T': Result := 10; + else Result := 0; + end; +end; + +constructor TCardHand.Create(const ALine: string); +var + split: TStringArray; + counters: TCardCounters; + counter: TCardCounter; + i: Integer; + c: Char; + found: Boolean; +begin + split := ALine.Split(' '); + FHand := split[0]; + FBid := StrToDWord(split[1]); + + counters := TCardCounters.Create; + + // Creates a counter for each different card value and counts cards per card value. + for c in FHand do + begin + found := False; + i := 0; + while not found and (i < counters.Count) do + begin + if counters[i].Card = c then + begin + found := True; + counter := counters[i]; + Inc(counter.Count); + counters[i] := counter; + end; + Inc(i); + end; + + if not found then + begin + counter.Card := c; + counter.Count := 1; + counters.Add(counter); + end; + end; + + // Determines the hand type from the counters. + case counters.Count of + 1: FType := htFiveOfAKind; + 2: + if (counters[0].Count = 4) or (counters[1].Count = 4) then + FType := htFourOfAKind + else + FType := htFullHouse; + 3: + if (counters[0].Count = 3) or (counters[1].Count = 3) or (counters[2].Count = 3) then + FType := htThreeOfAKind + else + FType := htTwoPair; + 4: FType := htOnePair; + 5: FType := htHighCard; + end; + + counters.Free; +end; + +function TCardHand.CompareTo(constref AOther: TCardHand): Integer; +var + i: Integer; +begin + Result := Ord(FType) - Ord(AOther.FType); + i := 1; + while (Result = 0) and (i <= FHand.Length) do + begin + Result := GetCardValue(FHand[i]) - GetCardValue(AOther.FHand[i]); + Inc(i); + end; +end; + +{ TCardHandComparer } + +function TCardHandComparer.Compare(constref AHand1, AHand2: TCardHand): Integer; +begin + Result := AHand1.CompareTo(AHand2); +end; + +{ TCamelCards } + +constructor TCamelCards.Create; +begin + FHands := TCardHands.Create; +end; + +destructor TCamelCards.Destroy; +begin + FHands.Free; + inherited Destroy; +end; + +procedure TCamelCards.ProcessDataLine(const ALine: string); +begin + FHands.Add(TCardHand.Create(ALine)); +end; + +procedure TCamelCards.Finish; +var + comparer: TCardHandComparer; + i: Integer; +begin + comparer := TCardHandComparer.Create; + FHands.Sort(comparer); + comparer.Free; + + for i := 0 to FHands.Count - 1 do + Inc(FPart1, FHands[i].Bid * (i + 1)); +end; + +function TCamelCards.GetDataFileName: string; +begin + Result := 'camel_cards.txt'; +end; + +function TCamelCards.GetPuzzleName: string; +begin + Result := 'Day 7: Camel Cards'; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index f8c7705..c3472c8 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -68,6 +68,10 @@ + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index 3df24ee..5601cc9 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -4,7 +4,7 @@ program AdventOfCodeFPCUnit; uses Interfaces, Forms, GuiTestRunner, USolver, UBaseTestCases, UTrebuchetTestCases, UCubeConundrumTestCases, - UGearRatiosTestCases, UScratchcardsTestCases, UGiveSeedFertilizerTestCases, UWaitForItTestCases; + UGearRatiosTestCases, UScratchcardsTestCases, UGiveSeedFertilizerTestCases, UWaitForItTestCases, UCamelCardsTestCases; {$R *.res} diff --git a/tests/UCamelCardsTestCases.pas b/tests/UCamelCardsTestCases.pas new file mode 100644 index 0000000..efeccdd --- /dev/null +++ b/tests/UCamelCardsTestCases.pas @@ -0,0 +1,89 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2023 Stefan Müller + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . +} + +unit UCamelCardsTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UCamelCards; + +type + + { TCamelCardsFullDataTestCase } + + TCamelCardsFullDataTestCase = class(TEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + procedure TestPart2; + end; + + { TCamelCardsExampleTestCase } + + TCamelCardsExampleTestCase = class(TExampleEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + procedure TestPart2; + end; + +implementation + +{ TCamelCardsFullDataTestCase } + +function TCamelCardsFullDataTestCase.CreateSolver: ISolver; +begin + Result := TCamelCards.Create; +end; + +procedure TCamelCardsFullDataTestCase.TestPart1; +begin + AssertEquals(254024898, FSolver.GetResultPart1); +end; + +procedure TCamelCardsFullDataTestCase.TestPart2; +begin + AssertEquals(-1, FSolver.GetResultPart2); +end; + +{ TCamelCardsExampleTestCase } + +function TCamelCardsExampleTestCase.CreateSolver: ISolver; +begin + Result := TCamelCards.Create; +end; + +procedure TCamelCardsExampleTestCase.TestPart1; +begin + AssertEquals(6440, FSolver.GetResultPart1); +end; + +procedure TCamelCardsExampleTestCase.TestPart2; +begin + AssertEquals(-1, FSolver.GetResultPart2); +end; + +initialization + + RegisterTest(TCamelCardsFullDataTestCase); + RegisterTest(TCamelCardsExampleTestCase); +end.