From 6b5048b7ef276d3bfe47e317400477aa93a0d1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 7 Dec 2023 17:58:54 +0100 Subject: [PATCH] Added solution for "Day 7: Camel Cards", part 2 --- solvers/UCamelCards.pas | 196 ++++++++++++++++++++++++--------- tests/UCamelCardsTestCases.pas | 4 +- 2 files changed, 144 insertions(+), 56 deletions(-) diff --git a/solvers/UCamelCards.pas b/solvers/UCamelCards.pas index 66ecd8c..56647c5 100644 --- a/solvers/UCamelCards.pas +++ b/solvers/UCamelCards.pas @@ -39,12 +39,13 @@ type TCardHand = class private FHand: string; - FType: TCardHandType; + FType, FJokerType: TCardHandType; FBid: Cardinal; - function GetCardValue(const ACard: Char): Cardinal; + procedure CalcCountersFromHand(constref counters: TCardCounters); + function CalcTypeFromCounters(constref counters: TCardCounters): TCardHandType; + function CalcJokerTypeFromCounters(constref counters: TCardCounters): TCardHandType; public constructor Create(const ALine: string); - function CompareTo(constref AOther: TCardHand): Integer; property Bid: Cardinal read FBid; end; @@ -53,8 +54,20 @@ type { TCardHandComparer } TCardHandComparer = class(TInterfacedObject, specialize IComparer) + protected + function CompareCardValue(constref AHand1, AHand2: TCardHand): Integer; + function GetCardValue(const ACard: Char): Cardinal; virtual; public - function Compare(constref AHand1, AHand2: TCardHand): Integer; + function Compare(constref AHand1, AHand2: TCardHand): Integer; virtual; + end; + + { TJokerCardHandComparer } + + TJokerCardHandComparer = class(TCardHandComparer) + protected + function GetCardValue(const ACard: Char): Cardinal; override; + public + function Compare(constref AHand1, AHand2: TCardHand): Integer; override; end; { TCamelCards } @@ -75,33 +88,14 @@ 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); +procedure TCardHand.CalcCountersFromHand(constref counters: TCardCounters); 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; + counters.Clear; // Creates a counter for each different card value and counts cards per card value. for c in FHand do @@ -127,45 +121,131 @@ begin 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; +function TCardHand.CalcTypeFromCounters(constref counters: TCardCounters): TCardHandType; 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); + // Determines the hand type from the counters. + case counters.Count of + 1: Result := htFiveOfAKind; + 2: + if (counters[0].Count = 4) or (counters[1].Count = 4) then + Result := htFourOfAKind + else + Result := htFullHouse; + 3: + if (counters[0].Count = 3) or (counters[1].Count = 3) or (counters[2].Count = 3) then + Result := htThreeOfAKind + else + Result := htTwoPair; + 4: Result := htOnePair; + 5: Result := htHighCard; end; end; +function TCardHand.CalcJokerTypeFromCounters(constref counters: TCardCounters): TCardHandType; +var + i, maxCount, maxCountIndex, jokerIndex: Integer; + jokerCounter, counter: TCardCounter; +begin + if counters.Count > 1 then + begin + // Finds Jokers and the best card value. + maxCount := 0; + jokerIndex := -1; + for i := 0 to counters.Count - 1 do + begin + if counters[i].Card = 'J' then + begin + jokerIndex := i; + jokerCounter := counters[i]; + end + else if maxCount < counters[i].Count then + begin + maxCount := counters[i].Count; + maxCountIndex := i; + end; + end; + + // Uses Jokers for the best card value. + if jokerIndex >= 0 then + begin + counter := counters[maxCountIndex]; + Inc(counter.Count, jokerCounter.Count); + counters[maxCountIndex] := counter; + counters.Delete(jokerIndex); + end; + end; + + Result := CalcTypeFromCounters(counters); +end; + +constructor TCardHand.Create(const ALine: string); +var + split: TStringArray; + counters: TCardCounters; +begin + split := ALine.Split(' '); + FHand := split[0]; + FBid := StrToDWord(split[1]); + + counters := TCardCounters.Create; + CalcCountersFromHand(counters); + FType := CalcTypeFromCounters(counters); + FJokerType := CalcJokerTypeFromCounters(counters); + counters.Free; +end; + { TCardHandComparer } +function TCardHandComparer.CompareCardValue(constref AHand1, AHand2: TCardHand): Integer; +var + i: Integer; +begin + Result := 0; + i := 1; + while (Result = 0) and (i <= AHand1.FHand.Length) do + begin + Result := GetCardValue(AHand1.FHand[i]) - GetCardValue(AHand2.FHand[i]); + Inc(i); + end; +end; + +function TCardHandComparer.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; + function TCardHandComparer.Compare(constref AHand1, AHand2: TCardHand): Integer; begin - Result := AHand1.CompareTo(AHand2); + Result := Ord(AHand1.FType) - Ord(AHand2.FType); + if Result = 0 then + Result := CompareCardValue(AHand1, AHand2); +end; + +{ TJokerCardHandComparer } + +function TJokerCardHandComparer.GetCardValue(const ACard: Char): Cardinal; +begin + if ACard = 'J' then + Result := 1 + else + Result := inherited GetCardValue(ACard); +end; + +function TJokerCardHandComparer.Compare(constref AHand1, AHand2: TCardHand): Integer; +begin + Result := Ord(AHand1.FJokerType) - Ord(AHand2.FJokerType); + if Result = 0 then + Result := CompareCardValue(AHand1, AHand2); end; { TCamelCards } @@ -189,6 +269,7 @@ end; procedure TCamelCards.Finish; var comparer: TCardHandComparer; + jokerComparer: TJokerCardHandComparer; i: Integer; begin comparer := TCardHandComparer.Create; @@ -197,6 +278,13 @@ begin for i := 0 to FHands.Count - 1 do Inc(FPart1, FHands[i].Bid * (i + 1)); + + jokerComparer := TJokerCardHandComparer.Create; + FHands.Sort(jokerComparer); + jokerComparer.Free; + + for i := 0 to FHands.Count - 1 do + Inc(FPart2, FHands[i].Bid * (i + 1)); end; function TCamelCards.GetDataFileName: string; diff --git a/tests/UCamelCardsTestCases.pas b/tests/UCamelCardsTestCases.pas index efeccdd..5ee697b 100644 --- a/tests/UCamelCardsTestCases.pas +++ b/tests/UCamelCardsTestCases.pas @@ -62,7 +62,7 @@ end; procedure TCamelCardsFullDataTestCase.TestPart2; begin - AssertEquals(-1, FSolver.GetResultPart2); + AssertEquals(254115617, FSolver.GetResultPart2); end; { TCamelCardsExampleTestCase } @@ -79,7 +79,7 @@ end; procedure TCamelCardsExampleTestCase.TestPart2; begin - AssertEquals(-1, FSolver.GetResultPart2); + AssertEquals(5905, FSolver.GetResultPart2); end; initialization