source: tags/1.3.0/UGame.pas

Last change on this file was 73, checked in by chronos, 4 years ago
  • Modified: Count total score using standard power of two numbers for all tile skins.
File size: 37.3 KB
Line 
1unit UGame;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, Dialogs, fgl, Graphics, Types, Forms, Math, DateUtils,
9 Controls, URegistry;
10
11type
12 TGame = class;
13 TMoveDirection = (drNone, drLeft, drUp, drRight, drDown);
14 TTileAction = (taNone, taMove, taMerge, taAppear);
15
16 { TTile }
17
18 TTile = class
19 Index: TPoint;
20 Value: Integer;
21 NewValue: Integer;
22 Merged: Boolean;
23 Action: TTileAction;
24 Shift: TPoint;
25 procedure Assign(Source: TTile);
26 end;
27
28 TTiles = class(TFPGObjectList<TTile>)
29 end;
30
31 { TArea }
32
33 TArea = record
34 P1, P2: TPoint;
35 function Increment: TPoint;
36 class function Create(P1, P2: TPoint): TArea; static; overload;
37 class function Create(X1, Y1, X2, Y2: Integer): TArea; static; overload;
38 end;
39
40 { THistoryMove }
41
42 THistoryMove = class
43 Direction: TMoveDirection;
44 NewItemPos: TPoint;
45 NewItemValue: Integer;
46 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
47 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
48 end;
49
50 { THistoryMoves }
51
52 THistoryMoves = class(TFPGObjectList<THistoryMove>)
53 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
54 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
55 end;
56
57 TTilePosValue = record
58 Pos: TPoint;
59 Value: Integer;
60 end;
61
62 { THistory }
63
64 THistory = class
65 Game: TGame;
66 Moves: THistoryMoves;
67 InitialTiles: array of TTilePosValue;
68 procedure GetStep(GameStep: TGame; Step: Integer);
69 procedure Clear;
70 constructor Create;
71 destructor Destroy; override;
72 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
73 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
74 end;
75
76 { TBoard }
77
78 TBoard = class
79 private
80 FSize: TPoint;
81 procedure SetSize(AValue: TPoint);
82 public
83 Tiles: array of array of TTile;
84 procedure Assign(Source: TBoard);
85 procedure Clear;
86 procedure ClearMerged;
87 function GetValueSum: Integer;
88 function GetHighestTileValue: Integer;
89 function GetEmptyTilesCount: Integer;
90 procedure GetEmptyTiles(EmptyTiles: TTiles);
91 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
92 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
93 destructor Destroy; override;
94 property Size: TPoint read FSize write SetSize;
95 end;
96
97 TTileSkin = (tsLinear, tsPowerOfTwo, tsAlpha, tsRoman, tsBinary);
98
99 { TGame }
100
101 TGame = class
102 private
103 FMoving: Boolean;
104 FOnChange: TNotifyEvent;
105 FOnGameOver: TNotifyEvent;
106 FOnPaint: TNotifyEvent;
107 FOnWin: TNotifyEvent;
108 FRecordHistory: Boolean;
109 FRunning: Boolean;
110 FScore: Integer;
111 FCanUndo: Boolean;
112 FBoardUndo: TBoard;
113 FSkin: TTileSkin;
114 function GetTileColor(Value: Integer): TColor;
115 procedure SetRecordHistory(AValue: Boolean);
116 procedure SetScore(AValue: Integer);
117 procedure DoChange;
118 procedure DoPaint;
119 procedure RenderTile(Canvas: TCanvas; Tile: TTile; TileRect: TRect; WithText: Boolean);
120 procedure GameOver;
121 procedure SetSkin(AValue: TTileSkin);
122 procedure Win;
123 function FillRandomTile: TTile;
124 function GetMoveArea(Direction: TMoveDirection): TArea;
125 procedure MoveAllAnimate(Direction: TMoveDirection);
126 function CanMergeTile(Value1, Value2: Integer): Boolean;
127 function MergeTile(Value1, Value2: Integer): Integer;
128 procedure AnimateTiles;
129 public
130 Board: TBoard;
131 TopScore: Integer;
132 AnimationDuration: Integer;
133 AnimationTick: Integer;
134 WinTileValue: Integer;
135 UndoEnabled: Boolean;
136 History: THistory;
137 BackgroundColor: TColor;
138 Value2Chance: Double;
139 function CanUndo: Boolean;
140 procedure Undo;
141 function CanMergeDirection(Direction: TMoveDirection): Boolean;
142 function CanMoveDirection(Direction: TMoveDirection): Boolean;
143 function CanMove: Boolean;
144 procedure Assign(Source: TGame);
145 procedure New;
146 procedure Render(Canvas: TCanvas; CanvasSize: TPoint);
147 procedure MoveAll(Direction: TMoveDirection; Animation: Boolean);
148 procedure MoveAllAndUpdate(Direction: TMoveDirection; Animation: Boolean);
149 procedure MoveTile(SourceTile, TargetTile: TTile);
150 function IsValidPos(Pos: TPoint): Boolean;
151 procedure SaveToRegistry(RegContext: TRegistryContext);
152 procedure LoadFromRegistry(RegContext: TRegistryContext);
153 function GetTileSkinValue(Value: Integer): string;
154 function GetTileSkinScore(Value: Integer): Integer;
155 constructor Create;
156 destructor Destroy; override;
157 property Score: Integer read FScore write SetScore;
158 property Running: Boolean read FRunning write FRunning;
159 property OnChange: TNotifyEvent read FOnChange write FOnChange;
160 property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
161 property OnWin: TNotifyEvent read FOnWin write FOnWin;
162 property OnGameOver: TNotifyEvent read FOnGameOver write FOnGameOver;
163 property Moving: Boolean read FMoving;
164 property RecordHistory: Boolean read FRecordHistory write SetRecordHistory;
165 property Skin: TTileSkin read FSkin write SetSkin;
166 end;
167
168 TGames = class(TFPGObjectList<TGame>)
169 end;
170
171var
172 SkinText: array[TTileSkin] of string;
173
174const
175 DirectionDiff: array[TMoveDirection] of TPoint = (
176 (X: 0; Y: 0), (X: -1; Y: 0), (X: 0; Y: -1), (X: 1; Y: 0), (X: 0; Y: 1)
177 );
178 DirectionText: array[TMoveDirection] of string = ('None', 'Left', 'Up', 'Right', 'Down');
179 InitialTileCount = 2;
180
181resourcestring
182 SScore = 'Score';
183 STopScore = 'Top score';
184 SSkinLinear = 'Linear';
185 SSkinPowerOfTwo = 'Power of two';
186 SSkinAlpha = 'Alpha';
187 SSkinRoman = 'Roman';
188 SSkinBinary = 'Binary';
189 STileShouldBeEmpty = 'Tile should be empty';
190
191
192procedure Translate;
193
194
195implementation
196
197uses
198 UCore, UMetaCanvas;
199
200procedure Translate;
201begin
202 SkinText[tsLinear] := SSkinLinear;
203 SkinText[tsPowerOfTwo] := SSkinPowerOfTwo;
204 SkinText[tsAlpha] := SSkinAlpha;
205 SkinText[tsRoman] := SSkinRoman;
206 SkinText[tsBinary] := SSkinBinary;
207end;
208
209{ THistoryMoves }
210
211procedure THistoryMoves.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
212var
213 I: Integer;
214begin
215 with Reg do begin
216 CurrentContext := RegContext;
217 WriteInteger('Count', Count);
218 for I := 0 to Count - 1 do begin
219 Items[I].SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\' + IntToStr(I)));
220 end;
221 end;
222end;
223
224procedure THistoryMoves.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
225var
226 I: Integer;
227begin
228 with Reg do begin
229 CurrentContext := RegContext;
230 Count := ReadIntegerWithDefault('Count', 0);
231 for I := 0 to Count - 1 do begin
232 Items[I] := THistoryMove.Create;
233 THistoryMove(Items[I]).LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\' + IntToStr(I)));
234 end;
235 end;
236end;
237
238{ THistoryMove }
239
240procedure THistoryMove.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
241begin
242 with Reg do begin
243 CurrentContext := RegContext;
244 WriteInteger('Direction', Integer(Direction));
245 WriteInteger('NewItemPosX', NewItemPos.X);
246 WriteInteger('NewItemPosY', NewItemPos.Y);
247 WriteInteger('NewItemValue', NewItemValue);
248 end;
249end;
250
251procedure THistoryMove.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
252begin
253 with Reg do begin
254 CurrentContext := RegContext;
255 Direction := TMoveDirection(ReadIntegerWithDefault('Direction', Integer(drNone)));
256 NewItemPos := Point(ReadIntegerWithDefault('NewItemPosX', 0),
257 ReadIntegerWithDefault('NewItemPosY', 0));
258 NewItemValue := ReadIntegerWithDefault('NewItemValue', 0);
259 end;
260end;
261
262{ THistory }
263
264procedure THistory.GetStep(GameStep: TGame; Step: Integer);
265var
266 I: Integer;
267begin
268 GameStep.Board.Size := Game.Board.Size;
269 GameStep.Board.Clear;
270 GameStep.Skin := Game.Skin;
271 GameStep.Score := 0;
272 for I := 0 to Length(InitialTiles) - 1 do
273 GameStep.Board.Tiles[InitialTiles[I].Pos.Y, InitialTiles[I].Pos.X].Value := InitialTiles[I].Value;
274 for I := 0 to Step - 1 do
275 with Moves[I] do begin
276 GameStep.MoveAll(Direction, False);
277 if GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value = 0 then
278 GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value := NewItemValue
279 else raise Exception.Create(STileShouldBeEmpty);
280 end;
281end;
282
283procedure THistory.Clear;
284begin
285 Moves.Clear;
286 SetLength(InitialTiles, 0);
287end;
288
289constructor THistory.Create;
290begin
291 Moves := THistoryMoves.Create;
292end;
293
294destructor THistory.Destroy;
295begin
296 FreeAndNil(Moves);
297 inherited Destroy;
298end;
299
300procedure THistory.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
301var
302 I: Integer;
303begin
304 with Reg do begin
305 CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
306 WriteInteger('Count', Length(InitialTiles));
307 for I := 0 to Length(InitialTiles) - 1 do begin
308 WriteInteger('X' + IntToStr(I), InitialTiles[I].Pos.X);
309 WriteInteger('Y' + IntToStr(I), InitialTiles[I].Pos.Y);
310 WriteInteger('Value' + IntToStr(I), InitialTiles[I].Value);
311 end;
312 Moves.SaveToRegistry(Reg, RegContext);
313 end;
314end;
315
316procedure THistory.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
317var
318 I: Integer;
319begin
320 with Reg do begin
321 CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
322 SetLength(InitialTiles, ReadIntegerWithDefault('Count', 0));
323 for I := 0 to Length(InitialTiles) - 1 do begin
324 InitialTiles[I].Pos := Point(ReadIntegerWithDefault('X' + IntToStr(I), 0),
325 ReadIntegerWithDefault('Y' + IntToStr(I), 0));
326 InitialTiles[I].Value := ReadIntegerWithDefault('Value' + IntToStr(I), 0);
327 end;
328 end;
329 Moves.LoadFromRegistry(Reg, RegContext);
330end;
331
332{ TArea }
333
334function TArea.Increment: TPoint;
335begin
336 Result := Point(Sign(P2.X - P1.X), Sign(P2.Y - P1.Y));
337 if Result.X = 0 then Result.X := 1;
338 if Result.Y = 0 then Result.Y := 1;
339end;
340
341class function TArea.Create(P1, P2: TPoint): TArea;
342begin
343 Result.P1 := P1;
344 Result.P2 := P2;
345end;
346
347class function TArea.Create(X1, Y1, X2, Y2: Integer): TArea;
348begin
349 Result.P1 := Point(X1, Y1);
350 Result.P2 := Point(X2, Y2);
351end;
352
353{ TBoard }
354
355procedure TBoard.SetSize(AValue: TPoint);
356var
357 X, Y: Integer;
358begin
359 if FSize = AValue then Exit;
360 for Y := 0 to FSize.Y - 1 do
361 for X := 0 to FSize.X - 1 do
362 Tiles[Y, X].Free;
363 FSize := AValue;
364 SetLength(Tiles, FSize.Y, FSize.X);
365 for Y := 0 to FSize.Y - 1 do
366 for X := 0 to FSize.X - 1 do begin
367 Tiles[Y, X] := TTile.Create;
368 Tiles[Y, X].Index := Point(X, Y);
369 end;
370end;
371
372procedure TBoard.Assign(Source: TBoard);
373var
374 X, Y: Integer;
375begin
376 Size := Source.Size;
377 for Y := 0 to Size.Y - 1 do
378 for X := 0 to Size.X - 1 do
379 Tiles[Y, X].Assign(Source.Tiles[Y, X]);
380end;
381
382procedure TBoard.GetEmptyTiles(EmptyTiles: TTiles);
383var
384 X, Y: Integer;
385begin
386 EmptyTiles.Clear;
387 for Y := 0 to Size.Y - 1 do
388 for X := 0 to Size.X - 1 do
389 if Tiles[Y, X].Value = 0 then
390 EmptyTiles.Add(Tiles[Y, X]);
391end;
392
393procedure TBoard.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
394var
395 X, Y: Integer;
396 Value: string;
397begin
398 with Reg do begin
399 CurrentContext := RegContext;
400
401 WriteInteger('SizeX', Size.X);
402 WriteInteger('SizeY', Size.Y);
403 Value := '';
404 for Y := 0 to Size.Y - 1 do begin
405 for X := 0 to Size.X - 1 do begin
406 Value := Value + IntToStr(Tiles[Y, X].Value);
407 if X < Size.X - 1 then Value := Value + ',';
408 end;
409 if Y < Size.Y - 1 then Value := Value + ';'
410 end;
411 WriteString('TileValues', Value);
412 end;
413end;
414
415procedure TBoard.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
416var
417 X, Y: Integer;
418 Items: TStringList;
419 Lines: TStringList;
420 Number: Integer;
421begin
422 with Reg do begin
423 CurrentContext := RegContext;
424
425 Size := Point(ReadIntegerWithDefault('SizeX', 4), ReadIntegerWithDefault('SizeY', 4));
426 Items := TStringList.Create;
427 Items.Delimiter := ',';
428 Lines := TStringList.Create;
429 Lines.Delimiter := ';';
430 Lines.DelimitedText := ReadStringWithDefault('TileValues', '');
431 for Y := 0 to Lines.Count - 1 do begin
432 Items.DelimitedText := Lines[Y];
433 for X := 0 to Items.Count - 1 do begin
434 if TryStrToInt(Items[X], Number) and (X < Size.X) and (Y < Size.Y) then
435 Tiles[Y, X].Value := Number;
436 end;
437 end;
438 Lines.Free;
439 Items.Free;
440 end;
441end;
442
443destructor TBoard.Destroy;
444begin
445 Size := Point(0, 0);
446 inherited Destroy;
447end;
448
449procedure TBoard.ClearMerged;
450var
451 X, Y: Integer;
452begin
453 for Y := 0 to Size.Y - 1 do
454 for X := 0 to Size.X - 1 do
455 Tiles[Y, X].Merged := False;
456end;
457
458function TBoard.GetValueSum: Integer;
459var
460 X, Y: Integer;
461begin
462 Result := 0;
463 for Y := 0 to Size.Y - 1 do
464 for X := 0 to Size.X - 1 do
465 Inc(Result, Tiles[Y, X].Value);
466end;
467
468function TBoard.GetEmptyTilesCount: Integer;
469var
470 X, Y: Integer;
471begin
472 Result := 0;
473 for Y := 0 to Size.Y - 1 do
474 for X := 0 to Size.X - 1 do
475 if Tiles[Y, X].Value = 0 then
476 Inc(Result);
477end;
478
479function TBoard.GetHighestTileValue: Integer;
480var
481 X, Y: Integer;
482begin
483 Result := 0;
484 for Y := 0 to Size.Y - 1 do
485 for X := 0 to Size.X - 1 do
486 if Result < Tiles[Y, X].Value then Result := Tiles[Y, X].Value;
487end;
488
489procedure TBoard.Clear;
490var
491 X, Y: Integer;
492begin
493 for Y := 0 to Size.Y - 1 do
494 for X := 0 to Size.X - 1 do
495 Tiles[Y, X].Value := 0;
496end;
497
498
499{ TTile }
500
501procedure TTile.Assign(Source: TTile);
502begin
503 Value := Source.Value;
504 Merged := Source.Merged;
505end;
506
507{ TGame }
508
509procedure TGame.DoChange;
510begin
511 if Assigned(FOnChange) then FOnChange(Self);
512end;
513
514procedure TGame.DoPaint;
515begin
516 if Assigned(FOnPaint) then FOnPaint(Self);
517end;
518
519procedure TGame.GameOver;
520begin
521 if Running and Assigned(FOnGameOver) then FOnGameOver(Self);
522 Running := False;
523end;
524
525procedure TGame.SetSkin(AValue: TTileSkin);
526begin
527 if FSkin = AValue then Exit;
528 FSkin := AValue;
529 DoPaint;
530end;
531
532procedure TGame.Win;
533begin
534 if Assigned(FOnWin) then FOnWin(Self);
535end;
536
537function TGame.FillRandomTile: TTile;
538var
539 EmptyTiles: TTiles;
540 NewValue: Integer;
541begin
542 Result := nil;
543 EmptyTiles := TTiles.Create(False);
544 Board.GetEmptyTiles(EmptyTiles);
545 if EmptyTiles.Count > 0 then begin
546 if Random < Value2Chance then NewValue := 2 else NewValue := 1;
547 Result := EmptyTiles[Random(EmptyTiles.Count)];
548 Result.Value := NewValue;
549 Result.Action := taAppear;
550 end;
551 EmptyTiles.Free;
552end;
553
554function TGame.GetMoveArea(Direction: TMoveDirection): TArea;
555begin
556 case Direction of
557 drNone: Result := TArea.Create(0, 0, 0, 0);
558 drLeft: Result := TArea.Create(1, 0, Board.Size.X - 1, Board.Size.Y - 1);
559 drUp: Result := TArea.Create(0, 1, Board.Size.X - 1, Board.Size.Y - 1);
560 drRight: Result := TArea.Create(Board.Size.X - 2, 0, 0, Board.Size.Y - 1);
561 drDown: Result := TArea.Create(0, Board.Size.Y - 2, Board.Size.X - 1, 0);
562 end;
563end;
564
565function TGame.CanMove: Boolean;
566begin
567 Result := CanMoveDirection(drLeft) or CanMoveDirection(drRight) or
568 CanMoveDirection(drUp) or CanMoveDirection(drDown);
569end;
570
571procedure TGame.Assign(Source: TGame);
572begin
573 Board.Assign(Source.Board);
574 FBoardUndo.Assign(Source.FBoardUndo);
575 FCanUndo := Source.FCanUndo;
576 TopScore := Source.TopScore;
577 AnimationDuration := Source.AnimationDuration;
578 WinTileValue := Source.WinTileValue;
579 UndoEnabled := Source.UndoEnabled;
580 FScore := Source.FScore;
581 FRunning := Source.FRunning;
582 Skin := Source.Skin;
583end;
584
585procedure TGame.New;
586var
587 I: Integer;
588 Tile: TTile;
589begin
590 FCanUndo := False;
591 Board.Clear;
592 Score := 0;
593 Running := True;
594 History.Clear;
595 if RecordHistory then begin
596 for I := 0 to InitialTileCount - 1 do begin
597 SetLength(History.InitialTiles, Length(History.InitialTiles) + 1);
598 Tile := FillRandomTile;
599 History.InitialTiles[Length(History.InitialTiles) - 1].Pos := Tile.Index;
600 History.InitialTiles[Length(History.InitialTiles) - 1].Value := Tile.Value;
601 end;
602 end else begin
603 for I := 0 to InitialTileCount - 1 do
604 FillRandomTile;
605 end;
606 AnimateTiles;
607 DoChange;
608 DoPaint;
609end;
610
611procedure TGame.Render(Canvas: TCanvas; CanvasSize: TPoint);
612var
613 X, Y: Integer;
614 TileSize: TPoint;
615 ValueStr: string;
616 Frame: TRect;
617 TileRect: TRect;
618 TopBarHeight: Integer;
619 TileMargin: Integer;
620 TileCenter: TPoint;
621 S: TPoint;
622 MetaCanvas: TMetaCanvas;
623begin
624 MetaCanvas := TMetaCanvas.Create;
625 MetaCanvas.SetSize(Point(Canvas.Width, Canvas.Height));
626
627 TopBarHeight := ScaleY(24, 96);
628 MetaCanvas.Brush.Style := bsSolid;
629 MetaCanvas.Brush.Color := Core.ThemeManager1.Theme.ColorControl;
630 MetaCanvas.FillRect(0, 0, MetaCanvas.Width, MetaCanvas.Height);
631
632 ValueStr := SScore + ': ' + IntToStr(Score);
633 MetaCanvas.Font.Color := Core.ThemeManager1.Theme.ColorControlText;
634 MetaCanvas.Font.Height := Trunc(TopBarHeight * 0.7);
635 MetaCanvas.TextOut(ScaleY(16, 96), (TopBarHeight - MetaCanvas.TextHeight(ValueStr)) div 2, ValueStr);
636
637 ValueStr := STopScore + ': ' + IntToStr(TopScore);
638 MetaCanvas.Font.Color := Core.ThemeManager1.Theme.ColorControlText;
639 MetaCanvas.Font.Height := Trunc(TopBarHeight * 0.7);
640 MetaCanvas.TextOut(ScaleY(136, 96), (TopBarHeight - MetaCanvas.TextHeight(ValueStr)) div 2, ValueStr);
641
642 // Form.Canvas.Width and Form.Canvas.Height is not working correctly under Windows.
643 // So dimensions are provided by CanvasSize parameter.
644 Frame := Rect(2, TopBarHeight, CanvasSize.X - 2, CanvasSize.Y - 2);
645 TileSize := Point(Frame.Width div Board.Size.X, Frame.Height div Board.Size.Y);
646 if TileSize.X < TileSize.Y then TileSize.Y := TileSize.X;
647 if TileSize.Y < TileSize.X then TileSize.X := TileSize.Y;
648 Frame := Rect(Frame.Width div 2 - (Board.Size.X * TileSize.X) div 2,
649 Frame.Top + Frame.Height div 2 - (Board.Size.Y * TileSize.Y) div 2,
650 Frame.Width div 2 + (Board.Size.X * TileSize.X) div 2,
651 Frame.Top + Frame.Height div 2 + (Board.Size.Y * TileSize.Y) div 2);
652
653 TileMargin := Round(Frame.Width / Board.Size.X * 0.03);
654
655 MetaCanvas.Brush.Style := bsSolid;
656 MetaCanvas.Brush.Color := clGray;
657 MetaCanvas.Pen.Style := psClear;
658 MetaCanvas.RoundRect(Frame, ScaleX(Frame.Width div (20 * Board.Size.X), 96),
659 ScaleY(Frame.Height div (20 * Board.Size.Y), 96));
660
661 Frame := Rect(Frame.Left + TileMargin, Frame.Top + TileMargin,
662 Frame.Right - TileMargin, Frame.Bottom - TileMargin);
663 TileSize := Point(Frame.Width div Board.Size.X, Frame.Height div Board.Size.Y);
664
665 MetaCanvas.Font.Color := clBlack;
666
667 // Draw static tiles
668 for Y := 0 to Board.Size.Y - 1 do
669 for X := 0 to Board.Size.X - 1 do begin
670 if (Board.Tiles[Y, X].Action <> taNone) then MetaCanvas.Brush.Color := GetTileColor(0)
671 else MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
672 MetaCanvas.Brush.Style := bsSolid;
673 TileRect := Bounds(
674 Frame.Left + X * TileSize.X + TileMargin,
675 Frame.Top + Y * TileSize.Y + TileMargin,
676 TileSize.X - 2 * TileMargin, TileSize.Y - 2 * TileMargin);
677 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, Board.Tiles[Y, X].Action = taNone);
678 end;
679
680 // Draw moving tiles
681 for Y := 0 to Board.Size.Y - 1 do
682 for X := 0 to Board.Size.X - 1 do
683 if Board.Tiles[Y, X].Action = taMove then begin
684 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
685 MetaCanvas.Brush.Style := bsSolid;
686 TileRect := Bounds(
687 Frame.Left + X * TileSize.X + Trunc(Board.Tiles[Y, X].Shift.X / 100 * TileSize.X + TileMargin),
688 Frame.Top + Y * TileSize.Y + Trunc(Board.Tiles[Y, X].Shift.Y / 100 * TileSize.Y + TileMargin),
689 TileSize.X - 2 * TileMargin, TileSize.Y - 2 * TileMargin);
690 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
691 end;
692
693 // Draw appearing tiles
694 for Y := 0 to Board.Size.Y - 1 do
695 for X := 0 to Board.Size.X - 1 do
696 if Board.Tiles[Y, X].Action = taAppear then begin
697 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
698 MetaCanvas.Brush.Style := bsSolid;
699 TileRect := Bounds(
700 Frame.Left + X * TileSize.X + TileMargin,
701 Frame.Top + Y * TileSize.Y + TileMargin,
702 TileSize.X - 2 * TileMargin,
703 TileSize.Y - 2 * TileMargin);
704 TileCenter := TileRect.CenterPoint;
705 S := Point(
706 Trunc(Board.Tiles[Y, X].Shift.X / 100 * (TileSize.X - TileMargin)),
707 Trunc(Board.Tiles[Y, X].Shift.Y / 100 * (TileSize.Y - TileMargin))
708 );
709 TileRect := Rect(TileCenter.X - S.X div 2, TileCenter.Y - S.Y div 2,
710 TileCenter.X + S.X div 2, TileCenter.Y + S.Y div 2);
711 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
712 end;
713
714 // Draw merging tiles
715 for Y := 0 to Board.Size.Y - 1 do
716 for X := 0 to Board.Size.X - 1 do
717 if Board.Tiles[Y, X].Action = taMerge then begin
718 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
719 MetaCanvas.Brush.Style := bsSolid;
720 TileRect := Bounds(
721 Frame.Left + X * TileSize.X + TileMargin,
722 Frame.Top + Y * TileSize.Y + TileMargin,
723 TileSize.X - 2 * TileMargin,
724 TileSize.Y - 2 * TileMargin);
725 S := Point(
726 Trunc((50 - Abs(Board.Tiles[Y, X].Shift.X - 50)) / 50 * TileMargin),
727 Trunc((50 - Abs(Board.Tiles[Y, X].Shift.Y - 50)) / 50 * TileMargin)
728 );
729 TileRect := Rect(TileRect.Left - S.X, TileRect.Top - S.Y,
730 TileRect.Right + S.X, TileRect.Bottom + S.Y);
731 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
732 end;
733
734 MetaCanvas.DrawTo(Canvas);
735 MetaCanvas.Free;
736end;
737
738procedure TGame.MoveAll(Direction: TMoveDirection; Animation: Boolean);
739var
740 P: TPoint;
741 PNew: TPoint;
742 I: Integer;
743 Area: TArea;
744 DstTile: TTile;
745 SrcTile: TTile;
746 TileMoved: Boolean;
747begin
748 if Animation then begin
749 MoveAllAnimate(Direction);
750 Exit;
751 end;
752 if Direction = drNone then Exit;
753 if not CanMoveDirection(Direction) then Exit;
754 FMoving := True;
755 FBoardUndo.Assign(Board);
756 FCanUndo := True;
757 Area := GetMoveArea(Direction);
758 Board.ClearMerged;
759 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
760 TileMoved := False;
761 P := Area.P1;
762 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
763 P.X := Area.P1.X;
764 while P.X <> Area.P2.X + Area.Increment.X do begin
765 PNew := P + DirectionDiff[Direction];
766 if IsValidPos(PNew) then begin
767 SrcTile := Board.Tiles[P.Y, P.X];
768 DstTile := Board.Tiles[PNew.Y, PNew.X];
769 if SrcTile.Value <> 0 then begin
770 if DstTile.Value = 0 then begin
771 DstTile.Value := SrcTile.Value;
772 DstTile.Merged := SrcTile.Merged;
773 SrcTile.Value := 0;
774 SrcTile.Merged := False;
775 TileMoved := True;
776 end else
777 if (not SrcTile.Merged) and (not DstTile.Merged) and
778 CanMergeTile(DstTile.Value, SrcTile.Value) then begin
779 DstTile.Value := MergeTile(DstTile.Value, SrcTile.Value);
780 DstTile.Merged := True;
781 SrcTile.Value := 0;
782 SrcTile.Merged := False;
783 Score := Score + GetTileSkinScore(SrcTile.Value);
784 TileMoved := True;
785 end;
786 end;
787 end;
788 Inc(P.X, Area.Increment.X);
789 end;
790 Inc(P.Y, Area.Increment.Y);
791 end;
792 if not TileMoved then Break;
793 end;
794 DoPaint;
795 FMoving := False;
796end;
797
798procedure TGame.RenderTile(Canvas: TCanvas; Tile: TTile; TileRect: TRect; WithText: Boolean);
799var
800 ValueStr: string;
801 TextSize: TSize;
802begin
803 Canvas.Pen.Style := psClear;
804 Canvas.RoundRect(TileRect, ScaleX(TileRect.Width div 20, 96), ScaleY(TileRect.Height div 20, 96));
805 if WithText and (Tile.Value <> 0) then begin
806 ValueStr := GetTileSkinValue(Tile.Value);
807 Canvas.Brush.Style := bsClear;
808 Canvas.Font.Height := Trunc(TileRect.Height * 0.7);
809 TextSize := Canvas.TextExtent(ValueStr);
810 if TextSize.Width > TileRect.Width then
811 Canvas.Font.Height := Trunc(Canvas.Font.Height / TextSize.Width * TileRect.Width);
812 TextSize := Canvas.TextExtent(ValueStr);
813 Canvas.TextOut(TileRect.Left + TileRect.Width div 2 - TextSize.Width div 2,
814 TileRect.Top + TileRect.Height div 2 - TextSize.Height div 2, ValueStr);
815 end;
816end;
817
818function TGame.CanUndo: Boolean;
819begin
820 Result := UndoEnabled and FCanUndo;
821end;
822
823procedure TGame.Undo;
824begin
825 if UndoEnabled and CanUndo then begin
826 Board.Assign(FBoardUndo);
827 FCanUndo := False;
828 FRunning := CanMove;
829 if RecordHistory then History.Moves.Delete(History.Moves.Count - 1);
830 DoChange;
831 DoPaint;
832 end;
833end;
834
835function TGame.CanMergeDirection(Direction: TMoveDirection): Boolean;
836var
837 P: TPoint;
838 PNew: TPoint;
839 I: Integer;
840 Area: TArea;
841begin
842 Result := False;
843 if Direction = drNone then Exit;
844 Area := GetMoveArea(Direction);
845 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
846 P := Area.P1;
847 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
848 P.X := Area.P1.X;
849 while P.X <> Area.P2.X + Area.Increment.X do begin
850 PNew := P + DirectionDiff[Direction];
851 if IsValidPos(PNew) then begin
852 if (Board.Tiles[PNew.Y, PNew.X].Value = 0) then begin
853 Board.Tiles[PNew.Y, PNew.X].Value := Board.Tiles[P.Y, P.X].Value;
854 Board.Tiles[P.Y, P.X].Value := 0;
855 end else
856 if (Board.Tiles[P.Y, P.X].Value <> 0) then begin
857 if CanMergeTile(Board.Tiles[PNew.Y, PNew.X].Value, Board.Tiles[P.Y, P.X].Value) then begin
858 Result := True;
859 Break;
860 end;
861 end;
862 end;
863 Inc(P.X, Area.Increment.Y);
864 end;
865 if Result then Break;
866 Inc(P.Y, Area.Increment.Y);
867 end;
868 end;
869end;
870
871function TGame.CanMoveDirection(Direction: TMoveDirection): Boolean;
872var
873 P: TPoint;
874 PNew: TPoint;
875 Area: TArea;
876begin
877 Result := False;
878 if Direction = drNone then Exit;
879 Area := GetMoveArea(Direction);
880 P := Area.P1;
881 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
882 P.X := Area.P1.X;
883 while P.X <> Area.P2.X + Area.Increment.X do begin
884 PNew := P + DirectionDiff[Direction];
885 if IsValidPos(PNew) then begin
886 if (Board.Tiles[P.Y, P.X].Value <> 0) then begin
887 if (Board.Tiles[PNew.Y, PNew.X].Value = 0) or
888 CanMergeTile(Board.Tiles[PNew.Y, PNew.X].Value, Board.Tiles[P.Y, P.X].Value) then begin
889 Result := True;
890 Break;
891 end;
892 end;
893 end;
894 Inc(P.X, Area.Increment.X);
895 end;
896 if Result then Break;
897 Inc(P.Y, Area.Increment.Y);
898 end;
899end;
900
901procedure TGame.MoveAllAnimate(Direction: TMoveDirection);
902var
903 P: TPoint;
904 PNew: TPoint;
905 X, Y: Integer;
906 I: Integer;
907 StartTime: TDateTime;
908 EndTime: TDateTime;
909 Time: TDateTime;
910 Part: Double;
911 Area: TArea;
912 TileMoved: Boolean;
913begin
914 if Direction = drNone then Exit;
915 if not CanMoveDirection(Direction) then Exit;
916 FMoving := True;
917 FBoardUndo.Assign(Board);
918 FCanUndo := True;
919 Area := GetMoveArea(Direction);
920 Board.ClearMerged;
921 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
922 for Y := 0 to Board.Size.Y - 1 do
923 for X := 0 to Board.Size.X - 1 do begin
924 Board.Tiles[Y, X].NewValue := Board.Tiles[Y, X].Value;
925 Board.Tiles[Y, X].Action := taNone;
926 end;
927
928 TileMoved := False;
929 P := Area.P1;
930 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
931 P.X := Area.P1.X;
932 while P.X <> Area.P2.X + Area.Increment.X do begin
933 PNew := P + DirectionDiff[Direction];
934 if IsValidPos(PNew) then begin
935 if (Board.Tiles[P.Y, P.X].NewValue <> 0) then begin
936 if (Board.Tiles[PNew.Y, PNew.X].NewValue = 0) then begin
937 Board.Tiles[P.Y, P.X].Action := taMove;
938 Board.Tiles[PNew.Y, PNew.X].NewValue := Board.Tiles[P.Y, P.X].NewValue;
939 Board.Tiles[PNew.Y, PNew.X].Merged := Board.Tiles[P.Y, P.X].Merged;
940 Board.Tiles[P.Y, P.X].NewValue := 0;
941 Board.Tiles[P.Y, P.X].Merged := False;
942 TileMoved := True;
943 end else
944 if (not Board.Tiles[P.Y, P.X].Merged) and (not Board.Tiles[PNew.Y, PNew.X].Merged) and
945 CanMergeTile(Board.Tiles[PNew.Y, PNew.X].NewValue, Board.Tiles[P.Y, P.X].NewValue) then begin
946 Board.Tiles[P.Y, P.X].Action := taMove;
947 Board.Tiles[PNew.Y, PNew.X].NewValue := MergeTile(Board.Tiles[PNew.Y, PNew.X].NewValue, Board.Tiles[P.Y, P.X].NewValue);
948 Board.Tiles[PNew.Y, PNew.X].Merged := True;
949 Board.Tiles[P.Y, P.X].NewValue := 0;
950 Board.Tiles[P.Y, P.X].Merged := False;
951 Score := Score + GetTileSkinScore(Board.Tiles[PNew.Y, PNew.X].NewValue);
952 TileMoved := True;
953 end;
954 end;
955 end;
956 Inc(P.X, Area.Increment.X);
957 end;
958 Inc(P.Y, Area.Increment.Y);
959 end;
960 if not TileMoved then Break;
961
962 // Animate tiles move
963 StartTime := Now;
964 EndTime := StartTime + AnimationDuration / 300 * OneSecond / Max(Board.Size.X, Board.Size.Y);
965 if AnimationDuration > 0 then
966 repeat
967 Time := Now;
968 Part := (Time - StartTime) / (EndTime - StartTime);
969 if Part > 1 then Part := 1;
970 for Y := 0 to Board.Size.Y - 1 do
971 for X := 0 to Board.Size.X - 1 do begin
972 if Board.Tiles[Y, X].Action = taMove then
973 Board.Tiles[Y, X].Shift := Point(Trunc(Part * DirectionDiff[Direction].X * 100),
974 Trunc(Part * DirectionDiff[Direction].Y * 100));
975 end;
976 DoPaint;
977 //Application.ProcessMessages;
978 Sleep(AnimationTick);
979 until Time > EndTime;
980
981 // Set final tiles values
982 for Y := 0 to Board.Size.Y - 1 do
983 for X := 0 to Board.Size.X - 1 do begin
984 Board.Tiles[Y, X].Value := Board.Tiles[Y, X].NewValue;
985 end;
986 end;
987
988 // Set final tiles values
989 for Y := 0 to Board.Size.Y - 1 do
990 for X := 0 to Board.Size.X - 1 do begin
991 if Board.Tiles[Y, X].Merged then
992 Board.Tiles[Y, X].Action := taMerge;
993 Board.Tiles[Y, X].Shift := Point(0, 0);
994 if Board.Tiles[Y, X].Action = taMove then begin
995 Board.Tiles[Y, X].Action := taNone;
996 end;
997 Board.Tiles[Y, X].Value := Board.Tiles[Y, X].NewValue;
998 end;
999 DoPaint;
1000 FMoving := False;
1001end;
1002
1003function TGame.CanMergeTile(Value1, Value2: Integer): Boolean;
1004begin
1005 Result := MergeTile(Value1, Value2) <> -1;
1006end;
1007
1008function TGame.MergeTile(Value1, Value2: Integer): Integer;
1009begin
1010 if Value1 = Value2 then Result := Value1 + 1
1011 else Result := -1;
1012end;
1013
1014procedure TGame.AnimateTiles;
1015var
1016 StartTime: TDateTime;
1017 EndTime: TDateTime;
1018 Time: TDateTime;
1019 Part: Double;
1020 X, Y: Integer;
1021begin
1022 FMoving := True;
1023
1024 // Animate tiles move
1025 StartTime := Now;
1026 EndTime := StartTime + AnimationDuration / 300 * OneSecond / Max(Board.Size.X, Board.Size.Y);
1027 if AnimationDuration > 0 then
1028 repeat
1029 Time := Now;
1030 Part := (Time - StartTime) / (EndTime - StartTime);
1031 if Part > 1 then Part := 1;
1032 for Y := 0 to Board.Size.Y - 1 do
1033 for X := 0 to Board.Size.X - 1 do begin
1034 if Board.Tiles[Y, X].Action = taAppear then
1035 Board.Tiles[Y, X].Shift := Point(Trunc(Part * 100), Trunc(Part * 100));
1036 if Board.Tiles[Y, X].Action = taMerge then
1037 Board.Tiles[Y, X].Shift := Point(Trunc(Part * 100), Trunc(Part * 100));
1038 end;
1039 DoPaint;
1040 //Application.ProcessMessages;
1041 Sleep(AnimationTick);
1042 until Time > EndTime;
1043
1044 for Y := 0 to Board.Size.Y - 1 do
1045 for X := 0 to Board.Size.X - 1 do
1046 if Board.Tiles[Y, X].Action <> taNone then begin
1047 Board.Tiles[Y, X].Action := taNone;
1048 Board.Tiles[Y, X].Shift := Point(0, 0);
1049 end;
1050 DoPaint;
1051 FMoving := False;
1052end;
1053
1054function IntToStrRoman(Num: Integer): string;
1055const
1056 Nvals = 13;
1057 Vals: array [1..Nvals] of Word =
1058 (1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000);
1059 Roms: array [1..Nvals] of string[2] =
1060 ('I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M');
1061var
1062 B: 1..Nvals;
1063begin
1064 Result := '';
1065 B := Nvals;
1066 while Num > 0 do
1067 begin
1068 while Vals[b] > Num do
1069 Dec(B);
1070 Dec(Num, Vals[B]);
1071 Result := Result + Roms[B];
1072 end;
1073end;
1074
1075function IntToBin(Num: Integer): string;
1076begin
1077 Result := '';
1078 while Num > 0 do begin
1079 Result := IntToStr(Num mod 2) + Result;
1080 Num := Num shr 1;
1081 end;
1082end;
1083
1084function TGame.GetTileSkinValue(Value: Integer): string;
1085begin
1086 case FSkin of
1087 tsLinear: Result := IntToStr(Value);
1088 tsPowerOfTwo: Result := IntToStr(1 shl Value);
1089 tsAlpha: Result := Chr(Ord('A') + Value - 1);
1090 tsRoman: Result := IntToStrRoman(Value);
1091 tsBinary: Result := IntToBin(Value);
1092 else Result := IntToStr(Value);
1093 end;
1094end;
1095
1096function TGame.GetTileSkinScore(Value: Integer): Integer;
1097begin
1098 case FSkin of
1099 tsLinear: Result := 1 shl Value;
1100 tsPowerOfTwo: Result := 1 shl Value;
1101 tsAlpha: Result := 1 shl Value;
1102 tsRoman: Result := 1 shl Value;
1103 tsBinary: Result := 1 shl Value;
1104 else Result := 1 shl Value;
1105 end;
1106end;
1107
1108procedure TGame.MoveAllAndUpdate(Direction: TMoveDirection; Animation: Boolean);
1109var
1110 HighestValue: Integer;
1111 HistoryMove: THistoryMove;
1112 NewTile: TTile;
1113 X, Y: Integer;
1114begin
1115 if CanMoveDirection(Direction) then begin
1116 HighestValue := Board.GetHighestTileValue;
1117 MoveAll(Direction, Animation);
1118
1119 NewTile := FillRandomTile;
1120 if Animation then AnimateTiles else begin
1121 for Y := 0 to Board.Size.Y - 1 do
1122 for X := 0 to Board.Size.X - 1 do
1123 if Board.Tiles[Y, X].Action <> taNone then begin
1124 Board.Tiles[Y, X].Action := taNone;
1125 Board.Tiles[Y, X].Shift := Point(0, 0);
1126 end;
1127 DoPaint;
1128 end;
1129
1130 if RecordHistory then begin
1131 HistoryMove := THistoryMove.Create;
1132 HistoryMove.Direction := Direction;
1133 HistoryMove.NewItemPos := NewTile.Index;
1134 HistoryMove.NewItemValue := NewTile.Value;
1135 History.Moves.Add(HistoryMove);
1136 end;
1137
1138 if not CanMove and (Board.GetEmptyTilesCount = 0) then
1139 GameOver;
1140 if (HighestValue < WinTileValue) and
1141 (Board.GetHighestTileValue >= WinTileValue) then Win;
1142 DoChange;
1143 end;
1144end;
1145
1146procedure TGame.MoveTile(SourceTile, TargetTile: TTile);
1147begin
1148 TargetTile.Value := SourceTile.Value;
1149 SourceTile.Value := 0;
1150 TargetTile.Merged := SourceTile.Merged;
1151 SourceTile.Merged := False;
1152end;
1153
1154function TGame.IsValidPos(Pos: TPoint): Boolean;
1155begin
1156 Result := (Pos.X >= 0) and (Pos.X < Board.Size.X) and
1157 (Pos.Y >= 0) and (Pos.Y < Board.Size.Y);
1158end;
1159
1160procedure TGame.SaveToRegistry(RegContext: TRegistryContext);
1161var
1162 Reg: TRegistryEx;
1163begin
1164 Reg := TRegistryEx.Create;
1165 with Reg do
1166 try
1167 CurrentContext := RegContext;
1168
1169 WriteInteger('TopScore', TopScore);
1170 WriteInteger('AnimationDuration', AnimationDuration);
1171 WriteInteger('Score', Score);
1172 WriteBool('GameRunning', FRunning);
1173 WriteBool('CanUndo', FCanUndo);
1174 WriteBool('UndoEnabled', UndoEnabled);
1175 WriteBool('RecordHistory', RecordHistory);
1176 WriteInteger('Skin', Integer(Skin));
1177 FBoardUndo.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
1178 Board.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
1179 History.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
1180 finally
1181 Free;
1182 end;
1183end;
1184
1185procedure TGame.LoadFromRegistry(RegContext: TRegistryContext);
1186var
1187 Reg: TRegistryEx;
1188begin
1189 Reg := TRegistryEx.Create;
1190 with Reg do
1191 try
1192 CurrentContext := RegContext;
1193 AnimationDuration := ReadIntegerWithDefault('AnimationDuration', 30);
1194 TopScore := ReadIntegerWithDefault('TopScore', 0);
1195 Score := ReadIntegerWithDefault('Score', 0);
1196 FRunning := ReadBoolWithDefault('GameRunning', False);
1197 FCanUndo := ReadBoolWithDefault('CanUndo', False);
1198 UndoEnabled := ReadBoolWithDefault('UndoEnabled', True);
1199 RecordHistory := ReadBoolWithDefault('RecordHistory', False);
1200 Skin := TTileSkin(ReadIntegerWithDefault('Skin', Integer(tsPowerOfTwo)));
1201 FBoardUndo.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
1202 Board.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
1203 History.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
1204 finally
1205 Free;
1206 end;
1207 DoChange;
1208 DoPaint;
1209end;
1210
1211constructor TGame.Create;
1212begin
1213 AnimationDuration := 30;
1214 AnimationTick := 10; // ms
1215 WinTileValue := 11; // 2^11 = 2048
1216 Board := TBoard.Create;
1217 FBoardUndo := TBoard.Create;
1218 History := THistory.Create;
1219 History.Game := Self;
1220 Value2Chance := 0.1;
1221end;
1222
1223destructor TGame.Destroy;
1224begin
1225 FreeAndNil(History);
1226 FreeAndNil(FBoardUndo);
1227 FreeAndNil(Board);
1228 inherited;
1229end;
1230
1231function TGame.GetTileColor(Value: Integer): TColor;
1232begin
1233 if Core.ThemeManager1.Theme.Name = 'Dark' then begin
1234 case Value of
1235 0: Result := $222629;
1236 1: Result := $dae4ee;
1237 2: Result := $c8e0ed;
1238 3: Result := $79b1f2;
1239 4: Result := $6395f5;
1240 5: Result := $5f7cf6;
1241 6: Result := $3b5ef6;
1242 7: Result := $72cfed;
1243 8: Result := $61cced;
1244 9: Result := $50c8ed;
1245 10: Result := $3fc5ed;
1246 11: Result := $2ec2ed;
1247 else Result := $323a3c;
1248 end;
1249 end else begin
1250 case Value of
1251 0: Result := $f2f6f9;
1252 1: Result := $dae4ee;
1253 2: Result := $c8e0ed;
1254 3: Result := $79b1f2;
1255 4: Result := $6395f5;
1256 5: Result := $5f7cf6;
1257 6: Result := $3b5ef6;
1258 7: Result := $72cfed;
1259 8: Result := $61cced;
1260 9: Result := $50c8ed;
1261 10: Result := $3fc5ed;
1262 11: Result := $2ec2ed;
1263 else Result := $323a3c;
1264 end;
1265 end;
1266end;
1267
1268procedure TGame.SetRecordHistory(AValue: Boolean);
1269begin
1270 if FRecordHistory = AValue then Exit;
1271 FRecordHistory := AValue;
1272 if not FRecordHistory then History.Clear;
1273end;
1274
1275procedure TGame.SetScore(AValue: Integer);
1276begin
1277 if FScore = AValue then Exit;
1278 FScore := AValue;
1279 if FScore > TopScore then TopScore := FScore;
1280end;
1281
1282initialization
1283 Translate;
1284
1285end.
1286
Note: See TracBrowser for help on using the repository browser.