michael 6 rokov pred
rodič
commit
fc8ee39898

+ 57 - 0
demo/tetris/tetris.css

@@ -0,0 +1,57 @@
+body {
+  text-align: left;
+  background: #ffffff;
+}
+
+#tetris {
+  display: flex;
+  font: "16px Roboto";
+}
+
+#my-canvas {
+  background: #ffffff;
+  margin-left: 10px;
+  margin-top: 10px;
+  width: 300px;
+  height: 480px;
+}
+
+.btn-primary {
+  margin: 10px;
+  background-color: lightskyblue;
+  border-radius: 3px;
+  border: 2px solid;
+  height: 30px;
+  padding-right: 15px;
+  padding-left: 15px;
+  font-weight: bold;
+}
+
+.envelope {
+  border-width: medium;
+  border-color: grey;
+  margin-left: 10px;
+  margin-top: 10px;
+  display: flex;
+  border-spacing: 5px;
+  border-style: solid;
+  border-radius: 4px;
+  padding: 3px;
+  font-weight: bold;
+  color: #194a8d;
+}
+
+.label {
+  margin-right:20px;
+}
+#score-envelope {
+}
+#level-envelope {
+}
+
+#status-envelope {
+}
+
+#controls-envelope {
+  display: block; !important
+}

+ 49 - 0
demo/tetris/tetris.html

@@ -0,0 +1,49 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <meta name="viewport" content="wideth=device-width, initial-scale=1">
+  <link rel="stylesheet" type="text/css" href="tetris.css">
+  <title>Tetris using pas2js</title>
+  <script src="tetris.js"></script>
+</head>
+<body>
+  <div id="tetris">
+    <div id="gameboard">
+      <canvas id="my-canvas"></canvas>
+    </div>
+    <div id="info">
+      <div id="logo">
+        <img id="logo-img" src="tetrislogo.png" width="161" height="54"/>
+      </div>
+      <div id="score-envelope" class="envelope">
+        <div id="score-label" class="label">Score:</div>
+        <div id="score">0</div>
+      </div>
+      <div id="level-envelope" class="envelope">
+        <div id="level-label" class="label">Level:</div>
+        <div id="level">1</div>
+      </div>
+      <div id="status-envelope" class="envelope">
+        <div id="status-label" class="label">Game status:</div>
+        <div id="status">Playing</div>
+      </div>
+      <div id="controls-envelope" class="envelope">
+        <div id="controls-label" class="label">Controls:</div>
+        <div id="controls">
+          <p><span id="control-left">Arrow-Left:</span> Move left</p>
+          <p><span id="control-right">Arrow-Right:</span> Move right</p>
+          <p><span id="control-down">Arrow-Down:</span> Move down</p>
+          <p><span id="control-rotate">Arrow-up:</span> Rotate block</p>
+          <p><span id="control-drop">Arrow-up:</span> Drop block</p>
+        </div>
+      </div>
+      <button id="btn-reset" class="btn-primary">Restart</button>
+    </div>
+  </div>
+  Sources: <a target="new" href="tetris.lpr">Program</a> <a target="new" href="utetris.pp">unit</a>.
+  <script>
+    window.addEventListener("load", rtl.run);
+  </script>
+</body>
+</html>

+ 96 - 0
demo/tetris/tetris.lpi

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <Runnable Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="tetris"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="5">
+      <Item0 Name="MaintainHTML" Value="1"/>
+      <Item1 Name="PasJSHTMLFile" Value="project1.html"/>
+      <Item2 Name="PasJSPort" Value="0"/>
+      <Item3 Name="PasJSWebBrowserProject" Value="1"/>
+      <Item4 Name="RunAtReady" Value="1"/>
+    </CustomData>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+      <Modes Count="0"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="tetris.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="tetris.html"/>
+        <IsPartOfProject Value="True"/>
+        <CustomData Count="1">
+          <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
+        </CustomData>
+      </Unit>
+      <Unit>
+        <Filename Value="utetris.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="tetris"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="js"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <AllowLabel Value="False"/>
+        <CPPInline Value="False"/>
+        <UseAnsiStrings Value="False"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <TargetOS Value="browser"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+        <UseLineInfoUnit Value="False"/>
+      </Debugging>
+    </Linking>
+    <Other>
+      <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
+      <CompilerPath Value="$(pas2js)"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 28 - 0
demo/tetris/tetris.lpr

@@ -0,0 +1,28 @@
+program tetris;
+
+{$mode objfpc}
+
+uses
+  browserapp, JS, Classes, SysUtils, Web, utetris;
+
+type
+  TTetrisApplication = class(TBrowserApplication)
+    FTetris : TTetris;
+    procedure doRun; override;
+  end;
+
+procedure TTetrisApplication.doRun;
+
+begin
+  FTetris:=TTetris.Create(Self);
+  FTetris.Start;
+end;
+
+var
+  Application : TTetrisApplication;
+
+begin
+  Application:=TTetrisApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

BIN
demo/tetris/tetrislogo.png


+ 637 - 0
demo/tetris/utetris.pp

@@ -0,0 +1,637 @@
+unit utetris;
+
+{$mode objfpc}
+
+interface
+
+uses
+  Classes, SysUtils, Web;
+
+Const
+  SGameOver = 'Game over!';
+  SPlaying  = 'Playing...';
+
+  BlockCount  = 7;
+  BlockHigh   = BlockCount-1;
+  BlockSize   = 4; // Number of positions in a block
+  BoardHeight = 20;
+  BoardWidth  = 12;
+  CreatePosX = 4;
+  CreatePosY = 0;
+  BlockColors : Array [0..BlockCount] of String
+    = ('white','#8F3985', '#39A275', '#D28140', '#194A8D', '#8D71B4', '#F0889D', '#DF1C44');
+
+Type
+  TDirection = (dIdle, dDown, dLeft, dRight);
+  TVerticalCollision = (vcNone,vcBlock,vcWall);
+
+{$modeswitch advancedrecords}
+
+  TCoordinate = record
+    x,y : Integer;
+    Class function Create(aX,aY : integer) : TCoordinate; static;
+  end;
+
+  TBlock = Array[0..BlockSize-1] of TCoordinate;
+  TBlocks = Array[0..BlockHigh] of TBlock;
+  TBoard = Array[0..BoardWidth-1,0..BoardHeight-1] of Integer; // Colors
+  TCoordinateBoard = Array[0..BoardWidth-1,0..BoardHeight-1] of TCoordinate; // Coordinates of squares
+
+  { TTetris }
+
+  TTetris = Class(TComponent)
+  private
+  Private
+    FCanvasID: String;
+    FGameOver : Boolean;
+    FCoordinates : TCoordinateBoard;
+    FIncLevelInterval: Integer;
+    FIncLevelScore: Integer;
+    FInterval: Integer;
+    FResetID: String;
+    FTetrisLogo : TJSHTMLImageElement;
+    FCanvas : TJSHTMLCanvasElement;
+    FCtx : TJSCanvasRenderingContext2D;
+    FScore : Integer;
+    FLevel : Integer;
+    FBoard : TBoard;
+    FBlocks : TBlocks;
+    FCurBlock : TBlock;
+    FCurBlockColor : Smallint; // Index in color array
+    FCurrPos : TCoordinate;
+    Fdirection : TDirection;
+    FElScore : TJSHTMLElement;
+    FElLevel : TJSHTMLElement;
+    FElStatus : TJSHTMLElement;
+    FBtnReset : TJSHTMLButtonElement;
+    FMyInterval : NativeInt;
+    function DoResetClick(aEvent: TJSMouseEvent): boolean;
+    procedure SetGameOver(AValue: Boolean);
+    Procedure CheckBlockDown;
+    procedure DrawBlockAt(X, Y, Color: Integer);
+    procedure DrawLevel;
+    Procedure DrawScore;
+    procedure DrawGameStatus;
+    procedure EnableTick;
+    Function HittingTheWall : Boolean;
+    Procedure MoveAllRowsDown(rowsToDelete, startOfDeletion : Integer);
+    function CheckForVerticalCollision(aDirection: TDirection; aBlock: TBlock): TVerticalCollision;
+    Function CheckForHorizontalCollision (aDirection: TDirection; aBlock: TBlock): Boolean;
+    function CheckForCompletedRows : Boolean;
+    Procedure CreateCoordArray;
+    procedure RecalcScore(aRows: integer);
+    procedure SetLevel(AValue: Integer);
+    procedure SetScore(AValue: Integer);
+    Procedure SetupTetris;
+    Procedure DrawBlock;
+    Procedure CreateBlocks;
+    Procedure CreateBlock;
+    Procedure DeleteBlock;
+    function MoveBlockDown: Boolean;
+    Procedure DropBlock;
+    Procedure RotateBlock;
+    Procedure ClearBoard;
+    function DrawTetrisLogo(e : TEventListenerEvent) : Boolean;
+    function HandleKeyPress(k : TJSKeyBoardEvent) : Boolean;
+    Property GameOver : Boolean Read FGameOver Write SetGameOver;
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Procedure Start;
+    // Reset button ID
+    Property ResetID : String Read FResetID Write FResetID;
+    // our canvas ID
+    Property CanvasID : String Read FCanvasID Write FCanvasID;
+    Property Canvas : TJSHTMLCanvasElement Read FCanvas;
+    Property Ctx : TJSCanvasRenderingContext2D Read FCTX;
+    Property Score : Integer Read FScore Write SetScore;
+    Property Level : Integer Read FLevel Write SetLevel;
+    Property Board : TBoard Read FBoard Write FBoard;
+    Property Stopped : TBoard Read FBoard Write FBoard;
+    Property Blocks : TBlocks Read FBlocks;
+    Property Coordinates : TCoordinateBoard Read FCoordinates;
+    Property Interval : Integer Read FInterval Write FInterval;
+    Property IncLevelScore : Integer Read FIncLevelScore Write FIncLevelScore;
+    Property IncLevelInterval : Integer read FIncLevelInterval write FIncLevelInterval;
+  end;
+
+implementation
+
+Class function TCoordinate.Create(aX,aY : integer) : TCoordinate;
+
+begin
+  Result.X:=aX;
+  Result.Y:=aY;
+end;
+
+procedure TTetris.CreateCoordArray;
+
+Const
+  XStart = 11;
+  XStep  = 23;
+  YStart = 9;
+  YStep  = 23;
+
+Var
+  x,y,i,j : Integer;
+
+begin
+  i:=0;
+  j:=0;
+  X:=XStart;
+  For I:=0 to BoardWidth-1 do
+    begin
+    Y:=YStart;
+    For J:=0 to BoardHeight-1 do
+      begin
+      FCoordinates[I,J]:=TCoordinate.Create(X,Y);
+      Y:=Y+YStep;
+      end;
+    X:=X+XStep;
+    end;
+end;
+
+
+procedure TTetris.SetupTetris;
+
+begin
+  if FCanvasID='' then
+    FCanvasID:='my-canvas';
+  if FResetID='' then
+    FResetID:='btn-reset';
+  FCanvas:=TJSHTMLCanvasElement(Document.getElementById(FCanvasID));
+  FElScore:=TJSHTMLCanvasElement(Document.getElementById('score'));
+  FElLevel:=TJSHTMLCanvasElement(Document.getElementById('level'));
+  FElStatus:=TJSHTMLCanvasElement(Document.getElementById('status'));
+  FBtnReset:=TJSHTMLButtonElement(Document.getElementById(FResetID));
+  if Assigned(FBtnReset) then
+    FBtnReset.OnClick:=@DoResetClick;
+  FCtx:=TJSCanvasRenderingContext2D(FCanvas.getContext('2d'));
+  FCanvas.width := Round(FCanvas.OffsetWidth);
+  FCanvas.height := Round(FCanvas.OffsetHeight);
+
+  // ctx.scale(2, 2);
+  ctx.fillStyle := 'white';
+  ctx.fillRect(0, 0, canvas.width, canvas.height);
+  ctx.strokeStyle := 'grey';
+  ctx.strokeRect(8, 8, 280, 462);
+  document.onkeydown:=@HandleKeyPress;
+end;
+
+procedure TTetris.DrawBlock;
+
+Var
+  i,X,Y : Integer;
+
+begin
+  for i:=0 to 3 do
+    begin
+    x:=FCurBlock[i].x + FCurrPos.X;
+    y:=FCurBlock[i].y + FCurrPos.Y;
+    DrawBlockAt(X,Y,FCurBlockColor);
+    end;
+end;
+
+function TTetris.DrawTetrisLogo(e : TEventListenerEvent) : Boolean;
+
+begin
+  if (e=Nil) or (FCTx=Nil)  then exit;
+
+  Fctx.drawImage(Ftetrislogo, 300, 8, 161, 54);
+  Result:=False;
+end;
+
+function TTetris.HandleKeyPress(k: TJSKeyBoardEvent) : Boolean;
+
+  Procedure DisableKey;
+
+  begin
+    k.cancelBubble:=True;
+    k.preventDefault;
+  end;
+
+begin
+  Result:=False;
+  if FGameOver then
+    exit;
+  if (k.Code = TJSKeyNames.ArrowLeft) then
+    begin
+    DisableKey;
+    Fdirection:=dLEFT;
+    if (HittingTheWall() or checkForHorizontalCollision(dLeft,FCurBlock)) then
+      exit(True);
+    DeleteBlock();
+    Dec(FCurrPos.X);
+    DrawBlock();
+    end
+  else if (k.Code = TJSKeyNames.ArrowRight) then
+    begin
+    DisableKey;
+    Fdirection:=dRIGHT;
+    if (HittingTheWall() or checkForHorizontalCollision(dRight,FCurBlock)) then
+      exit(True);
+    DeleteBlock();
+    inc(FCurrPos.X);
+    DrawBlock();
+    end
+  else if (k.Code = TJSKeyNames.ArrowDown) then
+    begin
+    DisableKey;
+    MoveBlockDown();
+    end
+  else if (k.Code = TJSKeyNames.ArrowUp) then
+    begin
+    DisableKey;
+    RotateBlock();
+    end
+  else if (k.Code = TJSKeyNames.Space) then
+    begin
+    DisableKey;
+    DropBlock();
+    end;
+end;
+
+constructor TTetris.Create(aOwner: TComponent);
+
+begin
+  inherited Create(aOwner);
+  CreateBlocks();
+  CreateCoordArray();
+  FLevel:=1;
+  FScore:=0;
+  FInterval:=1000;
+  IncLevelScore:=100;
+end;
+
+function TTetris.MoveBlockDown: Boolean;
+
+Var
+  i,x,y : Integer;
+  coll : TVerticalCollision;
+
+  Procedure ShiftBlockDown;
+
+  begin
+    DeleteBlock;
+    Inc(FCurrPos.Y);
+    DrawBlock;
+  end;
+
+begin
+  Result:=False;
+  Fdirection:=dDOWN;
+  Coll:=CheckForVerticalCollision(FDirection,FCurBlock);
+  Result:=Coll=vcNone;
+  if Result then
+    ShiftBlockDown
+  else
+    begin
+    if Coll<>vcWall then
+      ShiftBlockDown;
+    FGameOver:=(FCurrPos.Y<=2);
+    if FGameOver then
+      DrawGameStatus
+    else
+      begin
+      for I:=0 to BlockSize-1 do
+        begin
+        x:=FCurBlock[i].x + FCurrPos.X;
+        y:=FCurBlock[i].y + FCurrPos.Y;
+        FBoard[x,y]:=FCurBlockColor;
+        end;
+      CheckForCompletedRows();
+      CreateBlock();
+      FDirection:=dIdle;
+      FCurrPos.X:=4;
+      FCurrPos.Y:=0;
+      DrawBlock();
+      end;
+    end;
+end;
+
+procedure TTetris.DropBlock;
+begin
+  While MoveBlockDown do;
+end;
+
+function TTetris.HittingTheWall : Boolean;
+
+Var
+  NewX,I : Integer;
+
+begin
+  Result:=False;
+  I:=0;
+  While (I<BlockSize) and Not Result do
+    begin
+    newX:=FCurBlock[i].X + FCurrPos.X;
+    Result:=((newX <= 0) and (Fdirection = dLEFT)) or
+            ((newX >= 11) and (Fdirection = dRIGHT));
+    Inc(I);
+    end;
+end;
+
+procedure TTetris.DrawGameStatus;
+
+Var
+  S : String;
+
+begin
+  if FGameOver then
+    S:=SGameOver
+  else
+    S:=SPlaying;
+  FElStatus.InnerText:=S
+end;
+
+procedure TTetris.DrawScore;
+
+begin
+  if Assigned(FElScore) then
+    FElScore.InnerText:=IntToStr(FScore);
+end;
+
+procedure TTetris.DrawLevel;
+
+begin
+  if Assigned(FElLevel) then
+    FElLevel.InnerText:=IntToStr(Flevel);
+end;
+
+
+function TTetris.CheckForVerticalCollision(aDirection : TDirection; aBlock : TBlock): TVerticalCollision;
+
+Var
+  X,Y,I : integer;
+
+begin
+  Result:=vcNone;
+  I:=0;
+  While (I<BlockSize) and (Result=vcNone) do
+    begin
+    x:=aBlock[i].x + FCurrPos.X;
+    y:=aBlock[i].y + FCurrPos.Y;
+    if (aDirection = dDOWN) then
+      inc(Y);
+    if FBoard[x,y+1]>0 then
+      Result:=vcBlock
+    else if (Y>=20) then
+      Result:=vcWall;
+    inc(I);
+    end;
+end;
+
+function TTetris.CheckForHorizontalCollision(aDirection: TDirection; aBlock: TBlock): Boolean;
+
+Var
+  i, X,y : Integer;
+begin
+  Result:=False;
+  I:=0;
+  While (I<BlockSize) and Not Result do
+    begin
+    x:=aBlock[i].x + FCurrPos.X;
+    y:=aBlock[i].y + FCurrPos.Y;
+    if (adirection = dLEFT) then
+      Dec(x)
+    else if (adirection = dRIGHT) then
+      Inc(x);
+    Result:=FBoard[x,y]>0;
+    Inc(i);
+    end;
+end;
+
+function TTetris.CheckForCompletedRows : Boolean;
+
+Var
+  i,x,y,rowsToDelete, startOfDeletion: Integer;
+
+begin
+  Result:=False;
+  rowsToDelete:=0;
+  startOfDeletion:=0;
+  y:=0;
+  While Y<BoardHeight do
+    begin
+    Result:=true;
+    X:=0;
+    While (X<BoardWidth) and Result do
+      begin
+      Result:=FBoard[X,Y]>0;
+      Inc(X);
+      end;
+    if (Result) then
+      begin
+      if (StartOfDeletion = 0) then
+        startOfDeletion:=y;
+      Inc(rowsToDelete);
+      for I:=0 to BoardWidth-1 do
+        begin
+        FBoard[i,y]:=0;
+        DrawBlockAt(i,y,0);
+        end
+      end;
+    Inc(Y);
+    end;
+  if (RowsToDelete > 0) then
+    begin
+    MoveAllRowsDown(rowsToDelete, startOfDeletion);
+    RecalcScore(rowsToDelete);
+    end;
+end;
+
+procedure TTetris.RecalcScore(aRows : integer);
+
+Var
+  newLevel : Integer;
+
+begin
+  Inc(FScore,10*aRows);
+  DrawScore;
+  // Check if we need to increase the level.
+  // We cannot use = since score could go from 90 to 110 if 2 rows are deleted
+  newLevel:=1+(FScore div FIncLevelScore);
+  if (NewLevel>FLevel) then
+    begin
+    FLevel:=NewLevel;
+    FInterval:=FInterval-FIncLevelInterval;
+    EnableTick;
+    end;
+end;
+
+procedure TTetris.SetLevel(AValue: Integer);
+begin
+  if FLevel=AValue then Exit;
+  FLevel:=AValue;
+  DrawLevel;
+end;
+
+procedure TTetris.SetScore(AValue: Integer);
+begin
+  if FScore=AValue then Exit;
+  FScore:=AValue;
+  DrawScore;
+end;
+
+procedure TTetris.DrawBlockAt(X,Y,Color : Integer);
+
+Var
+  Coord : TCoordinate;
+
+begin
+  coord:=coordinates[x,y];
+  ctx.fillStyle:=BlockColors[Color];
+  ctx.fillRect(coord.X, coord.Y, 21, 21);
+end;
+
+procedure TTetris.MoveAllRowsDown(rowsToDelete, startOfDeletion: Integer);
+
+Var
+  I,x,y,Dest : Integer;
+
+begin
+  for i:=StartOfDeletion - 1 downto 0 do
+    for X:=0 to BoardWidth-1 do
+      begin
+      Y:=I+RowsToDelete;
+      Dest:=FBoard[x,i];
+      FBoard[x,y]:=Dest;
+      DrawBlockAt(X,Y,Dest);
+      FBoard[x,i]:=0;
+      DrawBlockAt(X,I,0);
+      end;
+end;
+
+procedure TTetris.DeleteBlock;
+
+var
+  I,X,Y : integer;
+
+begin
+  For I:=0 to BlockSize-1 do
+    begin
+    x:=FCurBlock[i].X + FCurrPos.X;
+    y:=FCurBlock[i].Y + FCurrPos.Y;
+    FBoard[x,y]:=0;
+    DrawBlockAt(X,Y,0);
+    end;
+end;
+
+procedure TTetris.CreateBlocks;
+
+  function co (x,y : Integer) : TCoordinate;
+  begin
+    Result:=TCoordinate.Create(X,Y);
+  end;
+
+begin
+  FBlocks[0]:=[co(1,0), co(0,1), co(1,1), co(2,1)]; // T
+  FBlocks[1]:=[co(0,0), co(1,0), co(2,0), co(3,0)]; // I
+  FBlocks[2]:=[co(0,0), co(0,1), co(1,1), co(2,1)]; // J
+  FBlocks[3]:=[co(0,0), co(1,0), co(0,1), co(1,1)]; // square
+  FBlocks[4]:=[co(2,0), co(0,1), co(1,1), co(2,1)]; // L
+  FBlocks[5]:=[co(1,0), co(2,0), co(0,1), co(1,1)]; // S
+  FBlocks[6]:=[co(0,0), co(1,0), co(1,1), co(2,1)]; // Z
+end;
+
+procedure TTetris.CreateBlock;
+
+Var
+  rnd : Integer;
+
+begin
+  RND:=Random(BlockCount);
+  FCurBlock:=FBlocks[RND];
+  FCurBlockColor:=RND+1; // 0 is white
+  FCurrPos.X:=CreatePosX;
+  FCurrPos.Y:=CreatePosY;
+end;
+
+
+procedure TTetris.RotateBlock;
+
+Var
+  lBlock,newBlock:TBlock;
+  x,y,i,maxX : Integer;
+
+begin
+  lBlock:=FCurBlock;
+  maxX:=0;
+  for I:=0 to BlockSize-1 do
+    if lBlock[i].x>MaxX then
+      MaxX:=lBlock[i].x;
+  for I:=0 to BlockSize-1 do
+    begin
+    x:=lBlock[i].x;
+    y:=lBlock[i].y;
+    newBlock[i].X:=maxX-y;
+    newBlock[i].Y:=x;
+    end;
+  // It can be that because of rotation, the block goes out of the board area or collisions.
+  // In that case we forbid rotating
+  // In fact we could try to reposition the block both horizontally and vertically:
+  if (CheckForVerticalCollision(dIdle,NewBlock)=vcNone)
+     and not CheckForHorizontalCollision(dIdle,NewBlock) then
+    begin
+    DeleteBlock();
+    FCurBlock:=newBlock;
+    DrawBlock();
+    end;
+end;
+
+procedure TTetris.ClearBoard;
+
+Var
+  X,Y : integer;
+
+begin
+  For X:=0 to BoardWidth-1 do
+    for Y:=0 to BoardHeight-1 do
+      begin
+      FBoard[X,Y]:=0;
+      DrawBlockAt(X,Y,0);
+      end;
+end;
+
+procedure TTetris.Start;
+
+begin
+  GameOver:=False;
+  Level:=1;
+  Score:=0;
+  SetupTetris;
+  ClearBoard;
+  CreateBlock();
+  DrawBlock();
+  EnableTick;
+end;
+
+function TTetris.DoResetClick(aEvent: TJSMouseEvent): boolean;
+begin
+  FInterval:=1000;
+  Start;
+end;
+
+procedure TTetris.SetGameOver(AValue: Boolean);
+begin
+  if FGameOver=AValue then Exit;
+  FGameOver:=AValue;
+  DrawGameStatus;
+end;
+
+procedure TTetris.CheckBlockDown;
+
+begin
+  If Not FGameOver then
+    MoveBlockDown;
+end;
+
+procedure TTetris.EnableTick;
+
+begin
+  if FMyInterval>0 then
+    window.clearInterval(FMyInterval);
+  FMyInterval:=window.setInterval(@CheckBlockDown,FInterval);
+end;
+
+end.
+