Changeset 20 for trunk/UGame.pas


Ignore:
Timestamp:
Oct 5, 2019, 2:00:50 PM (5 years ago)
Author:
chronos
Message:
  • Added: Player can undo last move.
  • Modified: Optimized code of TGame.CanMove method.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/UGame.pas

    r19 r20  
    5555    FRunning: Boolean;
    5656    FScore: Integer;
     57    FCanUndo: Boolean;
     58    FBoardUndo: TBoard;
     59    function CanMoveDirection(Direction: TDirection): Boolean;
    5760    function GetTileColor(Value: Integer): TColor;
    5861    procedure SetScore(AValue: Integer);
    5962    procedure DoChange;
    6063    procedure RenderTile(Canvas: TCanvas; Tile: TTile; TileRect: TRect);
     64    procedure GameOver;
     65    procedure Win;
     66    function FillRandomTile: Integer;
    6167  public
    6268    Board: TBoard;
    6369    TopScore: Integer;
    6470    AnimationDuration: Integer;
    65     Won: Boolean;
    66     procedure GameOver;
    67     procedure Win;
    68     function FillRandomTile: Integer;
     71    WinScore: Integer;
     72    function CanUndo: Boolean;
     73    procedure Undo;
    6974    function CanMove: Boolean;
    7075    procedure Assign(Source: TGame);
     
    146151    CurrentContext := RegContext;
    147152
     153    WriteInteger('SizeX', Size.X);
     154    WriteInteger('SizeY', Size.Y);
    148155    Value := '';
    149156    for Y := 0 to Size.Y - 1 do begin
     
    171178    CurrentContext := RegContext;
    172179
     180    Size := Point(ReadIntegerWithDefault('SizeX', 4), ReadIntegerWithDefault('SizeY', 4));
    173181    Items := TStringList.Create;
    174182    Items.Delimiter := ',';
     
    257265procedure TGame.Win;
    258266begin
    259   if not Won then begin
    260     Won := True;
    261     if MessageDlg(SWinCaption, SWinMessage, mtConfirmation,
    262     mbYesNo, 0) = mrNo then begin
    263       Running := False;
    264     end;
     267  if MessageDlg(SWinCaption, SWinMessage, mtConfirmation,
     268  mbYesNo, 0) = mrNo then begin
     269    Running := False;
    265270  end;
    266271end;
     
    281286
    282287function TGame.CanMove: Boolean;
    283 var
    284   TempGame: TGame;
    285 begin
    286   Result := False;
    287   TempGame := TGame.Create;
    288   try
    289     TempGame.Assign(Self);
    290     TempGame.AnimationDuration := 0;
    291     Result := TempGame.MoveAll(drDown) > 0;
    292     if Result then Exit;
    293     Result := TempGame.MoveAll(drUp) > 0;
    294     if Result then Exit;
    295     Result := TempGame.MoveAll(drRight) > 0;
    296     if Result then Exit;
    297     Result := TempGame.MoveAll(drLeft) > 0;
    298   finally
    299     TempGame.Free;
    300   end;
     288begin
     289  Result := CanMoveDirection(drLeft) or CanMoveDirection(drRight) or
     290    CanMoveDirection(drUp) or CanMoveDirection(drDown);
    301291end;
    302292
    303293procedure TGame.Assign(Source: TGame);
    304 var
    305   X, Y: Integer;
    306294begin
    307295  FScore := Source.FScore;
    308296  TopScore := Source.TopScore;
    309297  AnimationDuration := Source.AnimationDuration;
    310   Won := Source.Won;
    311298  Board.Assign(Source.Board);
    312299end;
     
    316303  I: Integer;
    317304begin
     305  FCanUndo := False;
    318306  Board.Clear;
    319307  Score := 0;
    320   Won := False;
    321308  Running := True;
    322309  for I := 0 to 1 do FillRandomTile;
     
    413400end;
    414401
     402function TGame.CanUndo: Boolean;
     403begin
     404  Result := FCanUndo;
     405end;
     406
     407procedure TGame.Undo;
     408begin
     409  if CanUndo then begin
     410    Board.Assign(FBoardUndo);
     411    FCanUndo := False;
     412    FRunning := CanMove;
     413    DoChange;
     414  end;
     415end;
     416
     417function TGame.CanMoveDirection(Direction: TDirection): Boolean;
     418var
     419  StartPoint: TPoint;
     420  AreaSize: TPoint;
     421  Increment: TPoint;
     422  P: TPoint;
     423  PNew: TPoint;
     424  PI: TPoint;
     425begin
     426  Result := False;
     427  case Direction of
     428    drLeft: begin
     429      StartPoint := Point(1, 0);
     430      AreaSize := Point(Board.Size.X - 2, Board.Size.Y - 1);
     431      Increment := Point(1, 1);
     432    end;
     433    drUp: begin
     434      StartPoint := Point(0, 1);
     435      AreaSize := Point(Board.Size.X - 1, Board.Size.Y - 2);
     436      Increment := Point(1, 1);
     437    end;
     438    drRight: begin
     439      StartPoint := Point(Board.Size.X - 2, 0);
     440      AreaSize := Point(Board.Size.X - 2, Board.Size.Y - 1);
     441      Increment := Point(-1, 1);
     442    end;
     443    drDown: begin
     444      StartPoint := Point(0, Board.Size.Y - 2);
     445      AreaSize := Point(Board.Size.X - 1, Board.Size.Y - 2);
     446      Increment := Point(1, -1);
     447    end;
     448  end;
     449
     450  PI.Y := 0;
     451  while PI.Y <= AreaSize.Y do begin
     452    PI.X := 0;
     453    while PI.X <= AreaSize.X do begin
     454      P := Point(StartPoint.X + PI.X * Increment.X, StartPoint.Y + PI.Y * Increment.Y);
     455      PNew.X := P.X + DirectionDiff[Direction].X;
     456      PNew.Y := P.Y + DirectionDiff[Direction].Y;
     457      if IsValidPos(PNew) then begin
     458        if (Board.Tiles[P.Y, P.X].Value <> 0) then begin
     459          if (Board.Tiles[PNew.Y, PNew.X].Value = 0) or
     460          (Board.Tiles[PNew.Y, PNew.X].Value = Board.Tiles[P.Y, P.X].Value) then begin
     461            Result := True;
     462            Break;
     463          end;
     464        end;
     465        P.X := PNew.X;
     466        P.Y := PNew.Y;
     467        PNew.X := P.X + DirectionDiff[Direction].X;
     468        PNew.Y := P.Y + DirectionDiff[Direction].Y;
     469      end;
     470      Inc(PI.X);
     471    end;
     472    if Result then Break;
     473    Inc(PI.Y);
     474  end;
     475end;
    415476
    416477function TGame.MoveAll(Direction: TDirection): Integer;
     
    430491  Time: TDateTime;
    431492  Part: Double;
     493  HighestValue: Integer;
    432494begin
    433495  FMoving := True;
     496  HighestValue := Board.GetHighestTileValue;
     497  FBoardUndo.Assign(Board);
     498  FCanUndo := True;
    434499  //Diff := DirectionDiff[Direction];
    435500  case Direction of
     
    535600  end;
    536601  Result := MovedCount;
     602
     603  // Update state after move
     604  if MovedCount > 0 then FillRandomTile;
     605  if not CanMove and (Board.GetEmptyTilesCount = 0) then
     606    GameOver;
     607  if (HighestValue < WinScore) and
     608  (Board.GetHighestTileValue >= WinScore) then Win;
     609
    537610  FMoving := False;
    538611end;
     
    560633    WriteInteger('TopScore', TopScore);
    561634    WriteInteger('AnimationDuration', AnimationDuration);
    562     WriteInteger('SizeX', Board.Size.X);
    563     WriteInteger('SizeY', Board.Size.Y);
    564635    WriteInteger('Score', Score);
    565636    WriteBool('GameRunning', FRunning);
    566     WriteBool('Won', Won);
     637    WriteBool('CanUndo', FCanUndo);
    567638  finally
    568639    Free;
    569640  end;
    570   Board.SaveToRegistry(RegContext);
     641  FBoardUndo.SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
     642  Board.SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
    571643end;
    572644
     
    576648  try
    577649    CurrentContext := RegContext;
    578     Board.Size := Point(ReadIntegerWithDefault('SizeX', 4), ReadIntegerWithDefault('SizeY', 4));
    579650    AnimationDuration := ReadIntegerWithDefault('AnimationDuration', 30);
    580651    TopScore := ReadIntegerWithDefault('TopScore', 0);
    581652    Score := ReadIntegerWithDefault('Score', 0);
    582653    FRunning := ReadBoolWithDefault('GameRunning', False);
    583     Won := ReadBoolWithDefault('Won', False);
     654    FCanUndo := ReadBoolWithDefault('CanUndo', False);
    584655  finally
    585656    Free;
    586657  end;
    587   Board.LoadFromRegistry(RegContext);
     658  FBoardUndo.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
     659  Board.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
    588660end;
    589661
     
    591663begin
    592664  AnimationDuration := 30;
     665  WinScore := 2048;
    593666  Board := TBoard.Create;
     667  FBoardUndo := TBoard.Create;
    594668end;
    595669
    596670destructor TGame.Destroy;
    597671begin
     672  FreeAndNil(FBoardUndo);
    598673  FreeAndNil(Board);
    599674  inherited;
Note: See TracChangeset for help on using the changeset viewer.