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.