{
  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 <http://www.gnu.org/licenses/>.
}

unit UScratchcards;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, Generics.Collections, USolver;

type

  { TScratchcards }

  TScratchcards = class(TSolver)
  private
    FCardCopies: specialize TList<Integer>;
    function GetNumber(const AString: string; const AIndex: Integer): Integer;
  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

{ TScratchcards }

function TScratchcards.GetNumber(const AString: string; const AIndex: Integer): Integer;
begin
  Result := StrToInt(Trim(Copy(AString, AIndex * 3 + 1, 3)))
end;

constructor TScratchcards.Create;
begin
  FCardCopies := specialize TList<Integer>.Create;
end;

destructor TScratchcards.Destroy;
begin
  FCardCopies.Free;
  inherited Destroy;
end;

procedure TScratchcards.ProcessDataLine(const ALine: string);
var
  cardSplit: TStringArray;
  wins: array of Integer;
  count, i, have, win, cardPoints, cardMatches, cardCopies: Integer;
begin
  // Counts copies of this card.
  if FCardCopies.Count > 0 then
  begin
    cardCopies := FCardCopies[0];
    FCardCopies.Delete(0);
  end
  else
    cardCopies := 1;
  Inc(FPart2, cardCopies);

  cardSplit := ALine.Split([':', '|']);

  // Determines winning numbers.
  count := cardSplit[1].Length div 3;
  SetLength(wins, count);
  for i := 0 to count - 1 do
  begin
    wins[i] := GetNumber(cardSplit[1], i);
  end;

  // Checks have numbers against winning numbers.
  cardPoints := 0;
  cardMatches := 0;
  count := cardSplit[2].Length div 3;
  for i := 0 to count - 1 do
  begin
    have := GetNumber(cardSplit[2], i);
    for win in wins do
    begin
      if win = have then
      begin
        if cardPoints = 0 then
          cardPoints := 1
        else
          Inc(cardPoints, cardPoints);
        Inc(cardMatches);
        Break;
      end;
    end;
  end;

  Inc(FPart1, cardPoints);

  // Adds copies of following cards and deletes the current card count.
  if FCardCopies.Capacity < cardMatches then
    FCardCopies.Capacity := cardMatches;
  for i := 0 to cardMatches - 1 do
  begin
    if FCardCopies.Count <= i then
      FCardCopies.Add(cardCopies + 1)
    else
      FCardCopies[i] :=  FCardCopies[i] + cardCopies;
  end;
end;

procedure TScratchcards.Finish;
begin

end;

function TScratchcards.GetDataFileName: string;
begin
  Result := 'scratchcards.txt';
end;

function TScratchcards.GetPuzzleName: string;
begin
  Result := 'Day 4: Scratchcards';
end;

end.