Changeset 31 for trunk/UGame.pas


Ignore:
Timestamp:
Oct 12, 2019, 10:47:02 PM (5 years ago)
Author:
chronos
Message:
  • Added: New form accessible from menu Tools - Moves history with game moves history.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/UGame.pas

    r30 r31  
    1010
    1111type
     12  TGame = class;
     13  TMoveDirection = (drNone, drLeft, drUp, drRight, drDown);
    1214
    1315  { TTile }
    1416
    1517  TTile = class
     18    Index: TPoint;
    1619    Value: Integer;
    1720    NewValue: Integer;
     
    3235    function Create(P1, P2: TPoint): TArea; overload;
    3336    function Create(X1, Y1, X2, Y2: Integer): TArea; overload;
     37  end;
     38
     39  { THistoryMove }
     40
     41  THistoryMove = class
     42    Direction: TMoveDirection;
     43    NewItemPos: TPoint;
     44    NewItemValue: Integer;
     45    procedure SaveToRegistry(RegContext: TRegistryContext);
     46    procedure LoadFromRegistry(RegContext: TRegistryContext);
     47  end;
     48
     49  { THistoryMoves }
     50
     51  THistoryMoves = class(TFPGObjectList<THistoryMove>)
     52    procedure SaveToRegistry(RegContext: TRegistryContext);
     53    procedure LoadFromRegistry(RegContext: TRegistryContext);
     54  end;
     55
     56  { THistory }
     57
     58  THistory = class
     59    Game: TGame;
     60    Moves: THistoryMoves;
     61    InitialTilesPos: array of TPoint;
     62    procedure GetStep(GameStep: TGame; Step: Integer);
     63    constructor Create;
     64    destructor Destroy; override;
     65    procedure SaveToRegistry(RegContext: TRegistryContext);
     66    procedure LoadFromRegistry(RegContext: TRegistryContext);
    3467  end;
    3568
     
    5588  end;
    5689
    57   TDirection = (drLeft, drUp, drRight, drDown);
    58 
    5990  { TGame }
    6091
     
    75106    procedure GameOver;
    76107    procedure Win;
    77     function FillRandomTile(Value4Change: Double = 0.1): Integer;
    78     function GetMoveArea(Direction: TDirection): TArea;
     108    function FillRandomTile(Value4Change: Double = 0.1): TTile;
     109    function GetMoveArea(Direction: TMoveDirection): TArea;
    79110  public
    80111    Board: TBoard;
     
    83114    WinScore: Integer;
    84115    UndoEnabled: Boolean;
     116    History: THistory;
    85117    function CanUndo: Boolean;
    86118    procedure Undo;
    87     function CanMergeDirection(Direction: TDirection): Boolean;
    88     function CanMoveDirection(Direction: TDirection): Boolean;
     119    function CanMergeDirection(Direction: TMoveDirection): Boolean;
     120    function CanMoveDirection(Direction: TMoveDirection): Boolean;
    89121    function CanMove: Boolean;
    90122    procedure Assign(Source: TGame);
    91123    procedure New;
    92124    procedure Render(Canvas: TCanvas; CanvasSize: TPoint);
    93     procedure MoveAll(Direction: TDirection);
    94     procedure MoveAllAndUpdate(Direction: TDirection);
     125    procedure MoveAll(Direction: TMoveDirection);
     126    procedure MoveAllAndUpdate(Direction: TMoveDirection);
    95127    procedure MoveTile(SourceTile, TargetTile: TTile);
    96128    function IsValidPos(Pos: TPoint): Boolean;
     
    111143
    112144const
    113   DirectionDiff: array[TDirection] of TPoint = (
    114     (X: -1; Y: 0), (X: 0; Y: -1), (X: 1; Y: 0), (X: 0; Y: 1)
     145  DirectionDiff: array[TMoveDirection] of TPoint = (
     146    (X: 0; Y: 0), (X: -1; Y: 0), (X: 0; Y: -1), (X: 1; Y: 0), (X: 0; Y: 1)
    115147  );
    116   DirectionText: array[TDirection] of string = ('Left', 'Up', 'Right', 'Down');
     148  DirectionText: array[TMoveDirection] of string = ('None', 'Left', 'Up', 'Right', 'Down');
    117149
    118150resourcestring
     
    121153
    122154implementation
     155
     156{ THistoryMoves }
     157
     158procedure THistoryMoves.SaveToRegistry(RegContext: TRegistryContext);
     159var
     160  I: Integer;
     161begin
     162  with TRegistryEx.Create do
     163  try
     164    CurrentContext := RegContext;
     165    WriteInteger('Count', Count);
     166    for I := 0 to Count - 1 do begin
     167      Items[I].SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '/' + IntToStr(I)));
     168    end;
     169  finally
     170    Free;
     171  end;
     172end;
     173
     174procedure THistoryMoves.LoadFromRegistry(RegContext: TRegistryContext);
     175var
     176  I: Integer;
     177  C: Integer;
     178  HistoryMove: THistoryMove;
     179begin
     180  with TRegistryEx.Create do
     181  try
     182    CurrentContext := RegContext;
     183    C := ReadIntegerWithDefault('Count', 0);
     184    for I := 0 to C - 1 do begin
     185      HistoryMove := THistoryMove.Create;
     186      Add(HistoryMove);
     187      HistoryMove.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '/' + IntToStr(I)));
     188    end;
     189  finally
     190    Free;
     191  end;
     192end;
     193
     194{ THistoryMove }
     195
     196procedure THistoryMove.SaveToRegistry(RegContext: TRegistryContext);
     197begin
     198  with TRegistryEx.Create do
     199  try
     200    CurrentContext := RegContext;
     201    WriteInteger('Direction', Integer(Direction));
     202    WriteInteger('NewItemPosX', NewItemPos.X);
     203    WriteInteger('NewItemPosY', NewItemPos.Y);
     204    WriteInteger('NewItemValue', NewItemValue);
     205  finally
     206    Free;
     207  end;
     208end;
     209
     210procedure THistoryMove.LoadFromRegistry(RegContext: TRegistryContext);
     211begin
     212  with TRegistryEx.Create do
     213  try
     214    CurrentContext := RegContext;
     215    Direction := TMoveDirection(ReadIntegerWithDefault('Direction', Integer(drNone)));
     216    NewItemPos := Point(ReadIntegerWithDefault('NewItemPosX', 0),
     217      ReadIntegerWithDefault('NewItemPosY', 0));
     218    NewItemValue := ReadIntegerWithDefault('NewItemValue', 0);
     219  finally
     220    Free;
     221  end;
     222end;
     223
     224{ THistory }
     225
     226procedure THistory.GetStep(GameStep: TGame; Step: Integer);
     227var
     228  I: Integer;
     229begin
     230  GameStep.Board.Size := Game.Board.Size;
     231  GameStep.Board.Clear;
     232  GameStep.Score := 0;
     233  for I := 0 to Length(InitialTilesPos) - 1 do
     234    GameStep.Board.Tiles[InitialTilesPos[I].Y, InitialTilesPos[I].X].Value := 2;
     235  for I := 0 to Step - 1 do
     236  with Moves[I] do begin
     237    GameStep.MoveAll(Direction);
     238    if GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value = 0 then
     239      GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value := NewItemValue
     240      else raise Exception.Create('Tile should be empty');
     241  end;
     242end;
     243
     244constructor THistory.Create;
     245begin
     246  Moves := THistoryMoves.Create;
     247end;
     248
     249destructor THistory.Destroy;
     250begin
     251  FreeAndNil(Moves);
     252  inherited Destroy;
     253end;
     254
     255procedure THistory.SaveToRegistry(RegContext: TRegistryContext);
     256var
     257  I: Integer;
     258begin
     259  with TRegistryEx.Create do
     260  try
     261    CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
     262    WriteInteger('Count', Length(InitialTilesPos));
     263    for I := 0 to Length(InitialTilesPos) - 1 do begin
     264      WriteInteger('X' + IntToStr(I), InitialTilesPos[I].X);
     265      WriteInteger('Y' + IntToStr(I), InitialTilesPos[I].Y);
     266    end;
     267  finally
     268    Free;
     269  end;
     270  Moves.SaveToRegistry(RegContext);
     271end;
     272
     273procedure THistory.LoadFromRegistry(RegContext: TRegistryContext);
     274var
     275  I: Integer;
     276begin
     277  with TRegistryEx.Create do
     278  try
     279    CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
     280    SetLength(InitialTilesPos, ReadIntegerWithDefault('Count', 0));
     281    for I := 0 to Length(InitialTilesPos) - 1 do begin
     282      InitialTilesPos[I] := Point(ReadIntegerWithDefault('X' + IntToStr(I), 0),
     283        ReadIntegerWithDefault('Y' + IntToStr(I), 0));
     284    end;
     285  finally
     286    Free;
     287  end;
     288  Moves.LoadFromRegistry(RegContext);
     289end;
    123290
    124291{ TArea }
     
    154321  SetLength(Tiles, FSize.Y, FSize.X);
    155322  for Y := 0 to FSize.Y - 1 do
    156     for X := 0 to FSize.X - 1 do
     323    for X := 0 to FSize.X - 1 do begin
    157324      Tiles[Y, X] := TTile.Create;
     325      Tiles[Y, X].Index := Point(X, Y);
     326    end;
    158327end;
    159328
     
    228397      end;
    229398    end;
     399    Lines.Free;
     400    Items.Free;
    230401  finally
    231402    Free;
     
    315486end;
    316487
    317 function TGame.FillRandomTile(Value4Change: Double = 0.1): Integer;
     488function TGame.FillRandomTile(Value4Change: Double = 0.1): TTile;
    318489var
    319490  EmptyTiles: TTiles;
    320491  NewValue: Integer;
    321492begin
    322   Result := 0;
     493  Result := nil;
    323494  EmptyTiles := TTiles.Create(False);
    324495  Board.GetEmptyTiles(EmptyTiles);
    325496  if EmptyTiles.Count > 0 then begin
    326497    if Random < Value4Change then NewValue := 4 else NewValue := 2;
    327     EmptyTiles[Random(EmptyTiles.Count)].Value := NewValue;
    328     Result := 1;
     498    Result := EmptyTiles[Random(EmptyTiles.Count)];
     499    Result.Value := NewValue;
    329500  end;
    330501  EmptyTiles.Free;
    331502end;
    332503
    333 function TGame.GetMoveArea(Direction: TDirection): TArea;
     504function TGame.GetMoveArea(Direction: TMoveDirection): TArea;
    334505begin
    335506  case Direction of
     
    371542var
    372543  I: Integer;
     544  NewTile: TTile;
    373545begin
    374546  FCanUndo := False;
     
    376548  Score := 0;
    377549  Running := True;
    378   for I := 0 to 1 do FillRandomTile(0);
     550  with History do begin
     551    Moves.Clear;
     552
     553    SetLength(InitialTilesPos, 0);
     554    for I := 0 to 1 do begin
     555      NewTile := FillRandomTile(0);
     556      SetLength(InitialTilesPos, Length(InitialTilesPos) + 1);
     557      InitialTilesPos[Length(InitialTilesPos) - 1] := NewTile.Index;
     558    end;
     559  end;
    379560  DoChange;
    380561end;
     
    484665end;
    485666
    486 function TGame.CanMergeDirection(Direction: TDirection): Boolean;
     667function TGame.CanMergeDirection(Direction: TMoveDirection): Boolean;
    487668var
    488669  P: TPoint;
     
    495676  for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
    496677    P := Area.P1;
    497     while P.Y <> Area.P2.Y do begin
     678    while P.Y <> Area.P2.Y + Area.Increment.Y do begin
    498679      P.X := Area.P1.X;
    499       while P.X <> Area.P2.X do begin
     680      while P.X <> Area.P2.X + Area.Increment.X do begin
    500681        PNew := P + DirectionDiff[Direction];
    501682        if IsValidPos(PNew) then begin
     
    519700end;
    520701
    521 function TGame.CanMoveDirection(Direction: TDirection): Boolean;
     702function TGame.CanMoveDirection(Direction: TMoveDirection): Boolean;
    522703var
    523704  P: TPoint;
     
    548729end;
    549730
    550 procedure TGame.MoveAll(Direction: TDirection);
     731procedure TGame.MoveAll(Direction: TMoveDirection);
    551732var
    552733  P: TPoint;
     
    560741  Area: TArea;
    561742begin
     743  if Direction = drNone then Exit;
    562744  if not CanMoveDirection(Direction) then Exit;
    563745  FMoving := True;
     
    634816end;
    635817
    636 procedure TGame.MoveAllAndUpdate(Direction: TDirection);
     818procedure TGame.MoveAllAndUpdate(Direction: TMoveDirection);
    637819var
    638820  HighestValue: Integer;
     821  HistoryMove: THistoryMove;
     822  NewTile: TTile;
    639823begin
    640824  HighestValue := Board.GetHighestTileValue;
    641825  MoveAll(Direction);
    642   FillRandomTile;
     826
     827  NewTile := FillRandomTile;
     828  HistoryMove := THistoryMove.Create;
     829  HistoryMove.Direction := Direction;
     830  HistoryMove.NewItemPos := NewTile.Index;
     831  HistoryMove.NewItemValue := NewTile.Value;
     832  History.Moves.Add(HistoryMove);
     833
    643834  if not CanMove and (Board.GetEmptyTilesCount = 0) then
    644835    GameOver;
     
    678869  FBoardUndo.SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
    679870  Board.SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
     871  History.SaveToRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
    680872end;
    681873
     
    696888  FBoardUndo.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
    697889  Board.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
     890  History.LoadFromRegistry(TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
    698891  DoChange;
    699892end;
     
    705898  Board := TBoard.Create;
    706899  FBoardUndo := TBoard.Create;
     900  History := THistory.Create;
     901  History.Game := Self;
    707902end;
    708903
    709904destructor TGame.Destroy;
    710905begin
     906  FreeAndNil(History);
    711907  FreeAndNil(FBoardUndo);
    712908  FreeAndNil(Board);
Note: See TracChangeset for help on using the changeset viewer.