this slowpoke moves

Tetris

Der folgende Code ist eine originalgetreue Umsetzung des Klassikerspiels TETRIS. Es werden zwei PaintBoxen benötigt, wobei in der ersten gespielt wird und in der zweiten der nächste Tetrisstein angezeigt wird. Gespielt wird mit den Cursortasten und das Drehen der Steine mit der Leertaste.

uses Math

type
 TBoard=array[-4..19,0..9] of integer;
 TOrientation=(toleft,toright,todown,torotate);
 TPiece=
  record
   matrix:array[0..3,0..3] of smallint;
   width:integer;
   height:integer;
  end;
 PPiece=^TPiece;
 TPieceKey=record
  key1,key2,key3:integer;
 end;
 
private
	{ Private declarations }
   fboard: TBoard;
   fposcol,fposrow,foffx,foffy:integer;
   fscore:integer;
   flines:integer;
   flevel:integer;
   ftime:longword;
   fpieceplaced,fterminate:boolean;
   fcurpiece,fnextpiece:TPieceKey;
  protected
   function GetCellRect(arow,acol:integer):TRect;
   function GetPreviewCellRect(arow,acol:integer):TRect;
   function CanPutPiece(apiece:PPiece;arow,acol:integer):boolean;
   function ClearLines:integer;
   procedure InvalidatePiece(arow,acol:integer);
   procedure InvalidatePreviewGrid;
   procedure PutPiece(apiece:PPiece;arow,acol:integer;ainv:boolean=true);
   procedure ClearPiece(apiece:PPiece;arow,acol:integer;ainv:boolean=true);
   procedure MovePiece(aOrientation:TOrientation);
   procedure Initialize;
   procedure GameLoop;
   procedure WndProc(var msg:TMessage);override;
  public
  { Public declarations }	
  end;
  
const
 CELL_SIZE         :integer=21;
 PREVIEW_CELL_SIZE :integer=17;
 ROW_CNT           :integer=20;
 COL_CNT           :integer=10;
 CLR_CNT           :integer=10;
 PIECE_SIZE        :integer= 4;
 LINE_SCORE        :integer=10;
 BONUS_SCORE       :integer=20;
 WM_STARTGAME :longword =WM_USER+1;
 COLORS   :array[1..10] of TColor =
 (clred,clyellow,clTeal,clblue,clPurple,cllime,clFuchsia,clAqua,clMaroon,clSilver);
 PIECES   :array[0..6,0..3] of TPiece=
 (
  (
   (matrix :((1,0,0,0),(1,0,0,0),(1,0,0,0),(1,0,0,0));width:1;height:4),
   (matrix :((1,1,1,1),(0,0,0,0),(0,0,0,0),(0,0,0,0));width:4;height:1),
   (matrix :((1,0,0,0),(1,0,0,0),(1,0,0,0),(1,0,0,0));width:1;height:4),
   (matrix :((0,0,0,0),(1,1,1,1),(0,0,0,0),(0,0,0,0));width:4;height:1)
  ),
  (
   (matrix :((1,0,0,0),(1,0,0,0),(1,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((0,0,1,0),(1,1,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((1,1,0,0),(0,1,0,0),(0,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,1,1,0),(1,0,0,0),(0,0,0,0),(0,0,0,0));width:3;height:2)
  ),
  (
   (matrix :((0,1,0,0),(0,1,0,0),(1,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,1,1,0),(0,0,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((1,1,0,0),(1,0,0,0),(1,0,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,0,0,0),(1,1,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2)
  ),
  (
   (matrix :((1,0,0,0),(1,1,0,0),(0,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((0,1,1,0),(1,1,0,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((1,0,0,0),(1,1,0,0),(0,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((0,1,1,0),(1,1,0,0),(0,0,0,0),(0,0,0,0));width:3;height:2)
  ),
  (
   (matrix :((0,1,0,0),(1,1,0,0),(1,0,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,1,0,0),(0,1,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((0,1,0,0),(1,1,0,0),(1,0,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,1,0,0),(0,1,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2)
  ),
  (
   (matrix :((0,1,0,0),(1,1,1,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((0,1,0,0),(1,1,0,0),(0,1,0,0),(0,0,0,0));width:2;height:3),
   (matrix :((1,1,1,0),(0,1,0,0),(0,0,0,0),(0,0,0,0));width:3;height:2),
   (matrix :((1,0,0,0),(1,1,0,0),(1,0,0,0),(0,0,0,0));width:2;height:3)
  ),
  (
   (matrix :((1,1,0,0),(1,1,0,0),(0,0,0,0),(0,0,0,0)); width:2;height:3),
   (matrix :((1,1,0,0),(1,1,0,0),(0,0,0,0),(0,0,0,0)); width:2;height:3),
   (matrix :((1,1,0,0),(1,1,0,0),(0,0,0,0),(0,0,0,0)); width:2;height:3),
   (matrix :((1,1,0,0),(1,1,0,0),(0,0,0,0),(0,0,0,0)); width:2;height:3)
  ) );

var
 Form1: TForm1;
 sofortabbruch : boolean;
 
////////////////

procedure TForm1.Initialize;
begin
  foffx:=PaintBox1.left;
  foffy:=PaintBox1.top;
  fposrow:=-4;
  fposcol:=5;
  fscore:=0;
  flines:=0;
  flevel:=0;
  ftime:=400;
  fterminate:=false;
  fpieceplaced:=false;
  Label4.visible:=false; // Spielende
end;

procedure TForm1.WndProc(var msg:TMessage);
begin
  case Msg.Msg of
     WM_CLOSE: begin
                 if not sofortabbruch then sofortabbruch:=true;
                 close;
               end
   Else
     inherited;
   if msg.msg=WM_STARTGAME then GameLoop;
  end;
end;

procedure TForm1.MovePiece(aOrientation:TOrientation);
var
 acurpiece:PPiece;
begin
  acurpiece:= @PIECES[fcurpiece.key1,fcurpiece.key2];
  ClearPiece(acurpiece,fposrow,fposcol);
  case aOrientation of
      toleft:
        if CanPutPiece(acurpiece,fposrow,fposcol-1) then fposcol:=fposcol-1;
      toright:
        if CanPutPiece(acurpiece,fposrow,fposcol+1) then fposcol:=fposcol+1;
      todown:
        if CanPutPiece(acurpiece,fposrow+1,fposcol) then fposrow:=fposrow+1
          else fpieceplaced:=true;
      torotate:
        if CanPutPiece(@PIECES[fcurpiece.key1,(fcurpiece.key2+1) mod 4],fposrow,fposcol) then begin
          fcurpiece.key2:=(fcurpiece.key2+1) mod 4;
          acurpiece:= @PIECES[fcurpiece.key1,fcurpiece.key2];
        end;
  end;
  putpiece(acurpiece,fposrow,fposcol);
end;

function TForm1.GetCellRect(arow,acol:integer):TRect;
begin
  SetRect(result,-1,-1,-1,-1);
  if (arow<0) or (arow>ROW_CNT) or (acol<0) or (acol>COL_CNT) then exit;
  result.left:=(PaintBox1.width div COL_CNT)*acol+1;
  result.top:=(PaintBox1.Height div ROW_CNT)*arow+1;
  result.right:=result.left+CELL_SIZE;
  result.bottom:=result.top+CELL_SIZE;
end;

function TForm1.GetPreviewCellRect(arow,acol:integer):TRect;
begin
  SetRect(result,-1,-1,-1,-1);
  if (arow<0) or (arow>PIECE_SIZE) or (acol<0) or (acol>PIECE_SIZE) then exit;
  result.left:=(PaintBox2.width div PIECE_SIZE)*acol+1;
  result.top:=(PaintBox2.Height div PIECE_SIZE)*arow+1;
  result.right:=result.left+PREVIEW_CELL_SIZE;
  result.bottom:=result.top+PREVIEW_CELL_SIZE;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
 tmprect:TRect;
 i,j,hdc:integer;
begin
  PaintBox1.Canvas.Pen.Color:=clred;
  PaintBox1.Canvas.Rectangle(0,0,PaintBox1.width,PaintBox1.Height);
  hdc:=PaintBox1.canvas.handle;
  for i:=0 to 19 do
    for j:=0 to 9 do begin
      if fboard[i,j]>0 then begin
        PaintBox1.canvas.Brush.Color:=COLORS[fboard[i,j]];
        tmprect:=GetCellRect(i,j);
        PaintBox1.Canvas.Rectangle(tmprect);
        DrawEdge(hdc,tmprect,EDGE_BUMP,BF_RECT or BF_SOFT);
      end
    end;
end;

procedure TForm1.InvalidatePreviewGrid;
var
 tmprect:TRect;
begin
  tmprect.left:=PaintBox2.left;
  tmprect.top:=PaintBox2.top;
  tmprect.right:=tmprect.left+PaintBox2.width;
  tmprect.bottom:=tmprect.top+PaintBox2.height;
  InvalidateRect(handle,@tmprect,false);
end;

procedure TForm1.InvalidatePiece(arow,acol:integer);
var
 i,j:integer;
 tmprect:TRect;
begin
  SetRectEmpty(tmprect);
  for i:=-1 to 3 do
    for j:=-1 to 4 do
      UnionRect(tmprect,tmprect,getcellrect(arow+i,acol+j));
  offsetrect(tmprect,foffx,foffy);
  invalidaterect(handle,@tmprect,false);
end;

function TForm1.ClearLines:integer;
var
 i,j,k,l:integer;
 tmprect:TRect;
begin
  result:=0;
  l:=ROW_CNT-1;
  for i:=ROW_CNT-1 downto 0 do begin
    k:=0;
    for j:=COL_CNT-1 downto 0 do
      if(fboard[i,j]>0) then k:=k+1;
    if k=COL_CNT then
      result:=result+1
    else begin
      for k:=0 to COL_CNT-1 do
        fboard[l,k]:=fboard[i,k];
      l:=l-1;
    end;
  end;
  tmprect.left:=foffx;
  tmprect.top:=foffy;
  tmprect.right:=tmprect.left+PaintBox1.Width;
  tmprect.bottom:=tmprect.top+PaintBox1.height;
  if result>0 then
    InvalidateRect(handle,@tmprect,false);
end;

function TForm1.CanPutPiece(apiece:PPiece;arow,acol:integer):boolean;
var
 i,j,k:integer;
 broke:boolean;
begin
  result:=false;k:=0;
  if (arow<-4) or (acol<0) then exit;
  for i:=0 to 3 do begin
    broke:=false;
    for j:=0 to 3 do begin
      if ((arow+i<ROW_CNT) and
         (acol+j<COL_CNT) and
         (fboard[arow+i,acol+j]=0)) or (apiece.matrix[i,j]=0) then k:=k+1
      else begin
        broke:=false;
        break;
      end;
    end;
    if broke then break;
  end;
  if k=16 then result:=true;
end;

procedure TForm1.PutPiece(apiece:PPiece;arow,acol:integer;ainv:boolean=true);
var
 i,j:integer;
begin
  for i:=0 to 3 do
    for j:=0 to 3 do
      if (arow+i<ROW_CNT) and (acol+j<COL_CNT) and (apiece.matrix[i,j]=1) then
        fboard[arow+i,acol+j]:=apiece.matrix[i,j]*fcurpiece.key3;
  if ainv then InvalidatePiece(arow,acol);
end;

procedure TForm1.ClearPiece(apiece:PPiece;arow,acol:integer;ainv:boolean=true);
var
 i,j:integer;
begin
  for i:=0 to 3 do
    for j:=0 to 3 do
      if (arow+i<ROW_CNT) and (acol+j<COL_CNT) and (apiece.matrix[i,j]=1) then
  fboard[arow+i,acol+j]:=0;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Initialize;
  doublebuffered:=true;
  postMessage(handle,WM_STARTGAME,0,0);
end;

procedure TForm1.GameLoop;
var
 atime:longword;
 alinecnt:integer;
begin
  sofortabbruch:=false;
  Randomize;
  fcurpiece.key1:=Random(7);
  fcurpiece.key2:=Random(4);
  fcurpiece.key3:=Random(CLR_CNT)+1;
  fnextpiece.key1:=Random(7);
  fnextpiece.key2:=Random(4);
  fnextpiece.key3:=Random(CLR_CNT)+1;
  atime:=GettickCount;
  while not fterminate do begin
    Application.ProcessMessages;
    if sofortabbruch then exit;
    if (Gettickcount-atime)>ftime then begin
      MovePiece(todown);
      atime:=GetTickCount;
    end;
    if fpieceplaced then begin
      if fposrow<0 then begin
        Label4.visible:=true; // Spielende
        fterminate:=true;
        break;
      end;
      alinecnt:=ClearLines;
      if alinecnt>0 then begin
        fscore:=fscore+alinecnt*LINE_SCORE;
        fscore:=fscore+(alinecnt-1)*BONUS_SCORE;
        flines:=flines+alinecnt;
        if fscore>=(flevel+1)*500 then begin
          flevel:=flevel+1;
          ftime:=max(ftime-25,25);
        end;
        Label1.caption:=inttostr(fscore); // Ereichte Punkte
        Label2.caption:=inttostr(flines); // Linien
        Label3.Caption:=inttostr(flevel); // Spielstufe
      end;
      fposrow:=-4;fposcol:=5;
      fpieceplaced:=false;
      fcurpiece:=fnextpiece;
      fnextpiece.key1:=Random(7);
      fnextpiece.key2:=Random(4);
      fnextpiece.key3:=Random(CLR_CNT)+1;
      InvalidatePreviewGrid;
    end;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  fterminate:=true;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if fterminate then exit;
  if key=VK_DOWN then
    MovePiece(todown)
  else
    if key=VK_LEFT then
      MovePiece(toLeft)
    else
      if key=VK_RIGHT then
        MovePiece(toright)
      else
        if key=VK_SPACE then
          MovePiece(torotate);
end;

procedure TForm1.PaintBox2Paint(Sender: TObject);
var
 tmprect:TRect;
 i,j,hdc:integer;
 apiece:PPiece;
begin
  PaintBox2.Canvas.Pen.Color:=clred;
  PaintBox2.Canvas.Rectangle(0,0,PaintBox2.width,PaintBox2.Height);
  hdc:=PaintBox2.canvas.handle;
  apiece:=@PIECES[fnextpiece.key1,fnextpiece.key2];
  PaintBox2.canvas.Brush.Color:=COLORS[fnextpiece.key3];
  for i:=0 to PIECE_SIZE-1 do
    for j:=0 to PIECE_SIZE-1 do begin
      if apiece.matrix[i,j]>0 then begin
        tmprect:=GetPreviewCellRect(i,j);
        PaintBox2.Canvas.Rectangle(tmprect);
        DrawEdge(hdc,tmprect,EDGE_BUMP,BF_RECT or BF_SOFT);
      end
    end;
end;

procedure TForm1.M2C(Sender: TObject);
var i,j:integer;
begin
  if not sofortabbruch then sofortabbruch:=true;
  foffx:=PaintBox1.left;
  foffy:=PaintBox1.top;
  fposrow:=-4;
  fposcol:=5;
  fscore:=0;
  flines:=0;
  flevel:=0;
  ftime:=400;
  fterminate:=false;
  fpieceplaced:=false;
  Label4.visible:=false;  // Spielende
  Label1.caption:=inttostr(fscore); // Erreichte Punkte
  Label2.caption:=inttostr(flines); // Linien
  Label3.Caption:=inttostr(flevel); // Spielstufe
  for i:=-4 to 19 do
    for j:=0 to 9 do fboard[i,j]:=0;
  PaintBox1.canvas.brush.color:=clblack;
  PaintBox1Paint(sender);
  postMessage(handle,WM_STARTGAME,0,0);
end;

procedure TForm1.M1C(Sender: TObject);
begin
  if not sofortabbruch then sofortabbruch:=true;
  close;
end;

Keine Kommentare:

Kommentar veröffentlichen

Beliebte Posts

Translate