unit untCard;

{$mode Delphi}{$H+}

interface

uses
  Classes, SysUtils, fgl, ISCJSON;

type
  TFieldOwner = (foSelf, foOpponent, foShare);
  TCardType = (ctMonster, ctSpell, ctTrap, ctPendulum);
  TCardOwner = (coSelf, coOpponent);
  TCardPlayer = (cpSelf, cpOpponent);
  TCardLocation = (clDeck, clHand, clMZone, clSZone, clGrave, clRemoved, clExtra);
  TCardPosition = (cpUpAtk, cpUpDef, cpDownDef, cpUp, cpDown);
  TCardWorkAs = (waOrigin,
    waNormalMonster, waEffectMonster, waRitualMonster, waFusionMonster, waSynchroMonster, waXyzMonster, waLinkMonster, waPendulumMonster, waToken,
    waNormalSpell, waEquipSpell, waFieldSpell, waQuickPlaySpell, waContinousSpell, waRitualSpell,
    waNormalTrap, waContinousTrap, waCounterTrap);

type
  TFieldRec = record
    Owner: TFieldOwner;
    Location: TCardLocation;
    Seq: Integer;
  end;

  TFieldStrings = record
    Owner: string;
    Color: string;
    Location: string;
  end;

type

  { TCard }

  TCard = class(TObject)
  private
    FCardId: Int64;
    FCardName: string;
    FCardType: TCardType;
    FContinousEffectTo: TFPGList<TCard>;
    FEquipTo: TCard;
    FIndicatorCount: Integer;
    FIsLinkMonster: Boolean;
    FIsXyzMonster: Boolean;
    FLocation: TCardLocation;
    FOwner: TCardOwner;
    FPlayer: TCardPlayer;
    FPosition: TCardPosition;
    FProc: Boolean;
    FSeq: Integer;
    FWorkAs: TCardWorkAs;
    FXyzElements: TFPGList<Int64>;
    procedure SetEquipTo(AValue: TCard);
  public
    constructor Create;
    destructor Destroy; override;
    function ToString: string; override;
  published
    property CardId: Int64 read FCardId write FCardId;
    property CardName: string read FCardName write FCardName;
    property CardType: TCardType read FCardType write FCardType;
    // xyz and elements
    property IsXyzMonster: Boolean read FIsXyzMonster write FIsXyzMonster;
    property XyzElements: TFPGList<Int64> read FXyzElements write FXyzElements;
    property IsLinkMonster: Boolean read FIsLinkMonster write FIsLinkMonster;
    // equip, one to one
    property EquipTo: TCard read FEquipTo write SetEquipTo;
    // continous effect, one to many
    property ContinousEffectTo: TFPGList<TCard> read FContinousEffectTo write FContinousEffectTo;
    // indicators
    property IndicatorCount: Integer read FIndicatorCount write FIndicatorCount;
    // info
    property Owner: TCardOwner read FOwner write FOwner;
    property Player: TCardPlayer read FPlayer write FPlayer;
    property Location: TCardLocation read FLocation write FLocation;
    property Seq: Integer read FSeq write FSeq;
    property Position: TCardPosition read FPosition write FPosition;
    property Proc: Boolean read FProc write FProc;
    property WorkAs: TCardWorkAs read FWorkAs write FWorkAs;
  end;


 type
  { TPuzzleField }

  TPuzzleField = class(TPersistent)
  private
    FAIName: string;
    FMCards: array [0..1, 0..4] of TCard;
    FP0Deck: TFPGList<TCard>;
    FP0ExtraDeck: TFPGList<TCard>;
    FP0Grave: TFPGList<TCard>;
    FP0Hand: TFPGList<TCard>;
    FP0Removed: TFPGList<TCard>;
    FP1Deck: TFPGList<TCard>;
    FP1ExtraDeck: TFPGList<TCard>;
    FP1Grave: TFPGList<TCard>;
    FP1Hand: TFPGList<TCard>;
    FP1Removed: TFPGList<TCard>;
    FPuzzleId: Int64;
    FPuzzleName: string;
    FSCards: array [0..1, 0..5] of TCard;
    FExtraMonsterCards: array [5..6] of TCard;
    FLPOpponent: Integer;
    FLPSelf: Integer;
    FMessage: string;
    FSolution: string;
    function GetMCard(AOwner: Integer; ASeq: Integer): TCard;
    function GetSCard(AOwner: Integer; ASeq: Integer): TCard;
    function GetExtraMonster(ASeq: Integer): TCard;
    procedure SetExtraMonster(ASeq: Integer; AValue: TCard);
    procedure SetMCard(AOwner: Integer; ASeq: Integer; AValue: TCard);
    procedure SetSCard(AOwner: Integer; ASeq: Integer; AValue: TCard);
    function FindCard(ACard: TCard): Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function GetCardByFieldRec(fr: TFieldRec): TCard;
    procedure Optimize();
  published
    property PuzzleId: Int64 read FPuzzleId write FPuzzleId;
    property PuzzleName: string read FPuzzleName write FPuzzleName;
    property P0Hand: TFPGList<TCard> read FP0Hand write FP0Hand;
    property P0Deck: TFPGList<TCard> read FP0Deck write FP0Deck;
    property P0ExtraDeck: TFPGList<TCard> read FP0ExtraDeck write FP0ExtraDeck;
    property P0Grave: TFPGList<TCard> read FP0Grave write FP0Grave;
    property P0Removed: TFPGList<TCard> read FP0Removed write FP0Removed;
    property P1Hand: TFPGList<TCard> read FP1Hand write FP1Hand;
    property P1Deck: TFPGList<TCard> read FP1Deck write FP1Deck;
    property P1ExtraDeck: TFPGList<TCard> read FP1ExtraDeck write FP1ExtraDeck;
    property P1Grave: TFPGList<TCard> read FP1Grave write FP1Grave;
    property P1Removed: TFPGList<TCard> read FP1Removed write FP1Removed;

    property MCard[AOwner: Integer; ASeq: Integer]: TCard read GetMCard write SetMCard;
    property SCard[AOwner: Integer; ASeq: Integer]: TCard read GetSCard write SetSCard;
    property ExtraMonster[ASeq: Integer]: TCard read GetExtraMonster write SetExtraMonster;

    property AIName: string read FAIName write FAIName;
    property LPSelf: Integer read FLPSelf write FLPSelf;
    property LPOpponent: Integer read FLPOpponent write FLPOpponent;
    property Message: string read FMessage write FMessage;
    property Solution: string read FSolution write FSolution;
  end;

procedure FreeCards(list: TFPGList<TCard>);
procedure ClearCards(list: TFPGList<TCard>);

implementation

procedure FreeCards(list: TFPGList<TCard>);
begin
  ClearCards(list);
  list.Free;
end;

procedure ClearCards(list: TFPGList<TCard>);
var
  i: Integer;
begin
  for i := 0 to list.Count - 1 do begin
    list[i].Free;
  end;
  list.Clear;
end;


{ TCard }

procedure TCard.SetEquipTo(AValue: TCard);
begin
  if FEquipTo=AValue then Exit;
  FEquipTo:=AValue;
end;

constructor TCard.Create;
begin
  FCardId:= 0;
  FCardName:= '';
  FCardType:= ctMonster;
  FContinousEffectTo:= TFPGList<TCard>.Create;
  FEquipTo:= nil;
  FIsXyzMonster:= False;
  FLocation:= clDeck;
  FOwner:= coSelf;
  FPlayer:= cpSelf;
  FPosition:= cpUpAtk;
  FProc:= False;
  FSeq:= 0;
  FXyzElements:= TFPGList<Int64>.Create;
  FWorkAs:= waOrigin;
end;

destructor TCard.Destroy;
begin
  // if Assigned(FEquipTo) then FEquipTo.Free;
  // FreeCards(FContinousEffectTo);
  FContinousEffectTo.Free;
  FXyzElements.Free;
  inherited Destroy;
end;

function TCard.ToString: string;
begin
  Exit(Format('CardId=%d, CardName=%s', [FCardId, FCardName]));
end;


{ TPuzzleField }

function TPuzzleField.GetMCard(AOwner: Integer; ASeq: Integer): TCard;
begin
  Exit(FMCards[AOwner][ASeq]);
end;

function TPuzzleField.GetSCard(AOwner: Integer; ASeq: Integer): TCard;
begin
  Exit(FSCards[AOwner][ASeq]);
end;

function TPuzzleField.GetExtraMonster(ASeq: Integer): TCard;
begin
  Exit(FExtraMonsterCards[ASeq]);
end;

procedure TPuzzleField.SetExtraMonster(ASeq: Integer; AValue: TCard);
begin
  if (FExtraMonsterCards[ASeq] <> nil) then begin
    FExtraMonsterCards[ASeq].Free;
    FExtraMonsterCards[ASeq] := nil;
  end;
  FExtraMonsterCards[ASeq] := AValue;
end;

procedure TPuzzleField.SetMCard(AOwner: Integer; ASeq: Integer; AValue: TCard);
begin
  if (FMCards[AOwner][ASeq] <> nil) then begin
    FMCards[AOwner][ASeq].Free;
    FMCards[AOwner][ASeq] := nil;
  end;
  FMCards[AOwner][ASeq] := AValue;
end;

procedure TPuzzleField.SetSCard(AOwner: Integer; ASeq: Integer; AValue: TCard);
begin
  if (FSCards[AOwner][ASeq] <> nil) then begin
    FSCards[AOwner][ASeq].Free;
    FSCards[AOwner][ASeq] := nil;
  end;
  FSCards[AOwner][ASeq] := AValue;
end;

function TPuzzleField.FindCard(ACard: TCard): Boolean;
var
  i, j: Integer;
  c: TCard = nil;
begin
  for i := 0 to 1 do begin
    for j := 0 to 4 do begin
      c := FMCards[i][j];
      if (c = nil) then Continue;
      if (c.CardId = ACard.CardId) and (c.Owner = ACard.Owner) and (c.Player = ACard.Player) and (c.Location = ACard.Location) and (c.Seq = ACard.Seq) then begin
        Exit(True);
      end;
    end;
  end;

  for i := 0 to 1 do begin
    for j := 0 to 5 do begin
      c := FSCards[i][j];
      if (c = nil) then Continue;
      if (c.CardId = ACard.CardId) and (c.Owner = ACard.Owner) and (c.Player = ACard.Player) and (c.Location = ACard.Location) and (c.Seq = ACard.Seq) then begin
        Exit(True);
      end;
    end;
  end;

  for i := 5 to 6 do begin
    c := FExtraMonsterCards[i];
    if (c = nil) then Continue;
    if (c.CardId = ACard.CardId) and (c.Owner = ACard.Owner) and (c.Player = ACard.Player) and (c.Location = ACard.Location) and (c.Seq = ACard.Seq) then begin
      Exit(True);
    end;
  end;

  Exit(False);
end;

constructor TPuzzleField.Create;
var
  i, j: Integer;
begin
  FPuzzleId := 0;
  FPuzzleName:= '';
  FAIName:= '';
  FLPOpponent:= 0;
  FLPSelf:= 0;
  FMessage:= '';
  FSolution := '';
  FP0Hand := TFPGList<TCard>.Create;
  FP0Deck := TFPGList<TCard>.Create;
  FP0ExtraDeck := TFPGList<TCard>.Create;
  FP0Grave := TFPGList<TCard>.Create;
  FP0Removed := TFPGList<TCard>.Create;
  FP1Hand := TFPGList<TCard>.Create;
  FP1Deck := TFPGList<TCard>.Create;
  FP1ExtraDeck := TFPGList<TCard>.Create;
  FP1Grave := TFPGList<TCard>.Create;
  FP1Removed := TFPGList<TCard>.Create;
  for i := 0 to 1 do begin
    for j := 0 to 4 do begin
      FMCards[i][j] := nil;
    end;
  end;
  for i := 0 to 1 do begin
    for j := 0 to 5 do begin
      FSCards[i][j] := nil;
    end;
  end;
  for i := 5 to 6 do begin
    FExtraMonsterCards[i] := nil;
  end;
end;

destructor TPuzzleField.Destroy;
var
i, j: Integer;
begin
  FreeCards(FP0Hand);
  FreeCards(FP0Deck);
  FreeCards(FP0ExtraDeck);
  FreeCards(FP0Grave);
  FreeCards(FP0Removed);
  FreeCards(FP1Hand);
  FreeCards(FP1Deck);
  FreeCards(FP1ExtraDeck);
  FreeCards(FP1Grave);
  FreeCards(FP1Removed);
  for i := 0 to 1 do begin
    for j := 0 to 4 do begin
      if FMCards[i][j] <> nil then begin
        FreeAndNil(FMCards[i][j]);
      end;
    end;
  end;
  for i := 0 to 1 do begin
    for j := 0 to 5 do begin
      if FSCards[i][j] <> nil then begin
        FreeAndNil(FSCards[i][j]);
      end;
    end;
  end;
  for i := 5 to 6 do begin
    if Assigned(FExtraMonsterCards[i]) then begin
      FreeAndNil(FExtraMonsterCards[i]);
    end;
  end;
  inherited Destroy;
end;

function TPuzzleField.GetCardByFieldRec(fr: TFieldRec): TCard;
begin
  case fr.Owner of
  foSelf:
    begin
      case fr.Location of
      clMZone: Exit(FMCards[0][fr.Seq]);
      clSZone: Exit(FSCards[0][fr.Seq]);
      end;
    end;
  foOpponent:
    begin
      case fr.Location of
      clMZone: Exit(FMCards[1][fr.Seq]);
      clSZone: Exit(FSCards[1][fr.Seq]);
      end;
    end;
  foShare:
    begin
      Exit(FExtraMonsterCards[fr.Seq]);
    end;
  end;
  Exit(nil);
end;

procedure TPuzzleField.Optimize();
var
  i, j, k: Integer;
  c: TCard;
begin
  // remove deleted equip and continous effect
  for i := 0 to 1 do begin
    for j := 0 to 4 do begin
      c := FMCards[i][j];
      if (c = nil) then Continue;
      if (c.EquipTo <> nil) then begin
        if not FindCard(c.EquipTo) then begin
          c.EquipTo := nil;
        end;
      end;
      if (c.ContinousEffectTo.Count > 0) then begin
        for k := c.ContinousEffectTo.Count - 1 downto 0 do begin
          if not FindCard(c.ContinousEffectTo[k]) then begin
            c.ContinousEffectTo.Delete(k);
          end;
        end;
      end;
    end;
  end;

  for i := 0 to 1 do begin
    for j := 0 to 5 do begin
      c := FSCards[i][j];
      if (c = nil) then Continue;
      if (c.EquipTo <> nil) then begin
        if not FindCard(c.EquipTo) then begin
          c.EquipTo := nil;
        end;
      end;
      if (c.ContinousEffectTo.Count > 0) then begin
        for k := c.ContinousEffectTo.Count - 1 downto 0 do begin
          if not FindCard(c.ContinousEffectTo[k]) then begin
            c.ContinousEffectTo.Delete(k);
          end;
        end;
      end;
    end;
  end;

  for i := 5 to 6 do begin
    c := FExtraMonsterCards[i];
    if (c = nil) then Continue;
    if (c.EquipTo <> nil) then begin
      if not FindCard(c.EquipTo) then begin
        c.EquipTo := nil;
      end;
    end;
    if (c.ContinousEffectTo.Count > 0) then begin
      for k := c.ContinousEffectTo.Count - 1 downto 0 do begin
        if not FindCard(c.ContinousEffectTo[k]) then begin
          c.ContinousEffectTo.Delete(k);
        end;
      end;
    end;
  end;
end;

end.

