W
Optimizer schrieb:
http://home.arcor.de/firbach/hmmmmmm.jpg
Das Programm ist aber trotzdem noch weitergelaufen, bis ich irgendwann verloren hatte. Da ich keine Musik gehört habe, war's vielleicht in diesem Thread.
Danke für's Testen! Wann ist denn der Fehler aufgetaucht? Direkt nach dem Verlieren?
Optimizer schrieb:
Btw. Die Wiederholrate der Tastatur in Windows zu verstellen ist wirklich ein no-go, sehr unschön.
Wie soll ich das den sonst machen? Wenn jemand ein langsames Delay hat, dann stört das beim Spielen. Beim Schließen des Programmes wird ja wieder der Standard von vorher gesetzt.
CengizS schrieb:
@vista: Die 256Byte Variante würde ich aber nicht mit Webfritzis Variante vergleichen ... da sind Welten dazwischen
Ja. Auf jeden Fall insofern, dass es bei mir besser aussieht und ich Sound verwende. Alleine das fiese Lachen beim Verlieren hat 70 KB.
OK, "technische Daten": Als Bibliothek verwende ich die VCL vom Borland C++Builder, die die WinAPI kapselt (und noch mehr tut). Die Tetris-Klasse ist aber plattform-unabhängig. Man muss nur die GUI schreiben. Die benötigten Callback-Funktionen muss man als Members angeben. Ich hau auf Wunsch mal die cpp-Datei hier rein. Wenn es aber zu viel ist, nehm ich es wieder raus. Mal schauen...
//---------------------------------------------------------------------------
#include "Tetris.h"
//---------------------------------------------------------------------------
/* STONES
=============
Type 1
---- ---- ---- ----
-##- -##- -##- -##-
-##- -##- -##- -##-
---- ---- ---- ----
Type 2
---- --#- ---- --#-
#### --#- #### --#-
---- --#- ---- --#-
---- --#- ---- --#-
Type 3
---- --#- ---- --#-
--## --## --## --##
-##- ---# -##- ---#
---- ---- ---- ----
Type 4
---- ---# ---- ---#
-##- --## -##- --##
--## --#- --## --#-
---- ---- ---- ----
Type 5
---- --#- ---# -##-
-### --#- -### --#-
-#-- --## ---- --#-
---- ---- ---- ----
Type 6
---- --## -#-- --#-
-### --#- -### --#-
---# --#- ---- -##-
---- ---- ---- ----
Type 7
---- --#- --#- --#-
-### --## -### -##-
--#- --#- ---- --#-
---- ---- ---- ----
SCORE: (Level n)
------
four rows ("Tetris"): 1200*(n+1)
three rows : 300*(n+1)
two rows : 100*(n+1)
one row : 40*(n+1)
drop : n+1
New level after : 10 deleted rows
*/
//---------------------------------------------------------------------------
const CPoint stones[7][4][4] =
{
{ {{1,1},{2,1},{1,2},{2,2}}, {{1,1},{2,1},{1,2},{2,2}}, {{1,1},{2,1},{1,2},{2,2}}, {{1,1},{2,1},{1,2},{2,2}} },
{ {{0,1},{1,1},{2,1},{3,1}}, {{2,0},{2,1},{2,2},{2,3}}, {{0,1},{1,1},{2,1},{3,1}}, {{2,0},{2,1},{2,2},{2,3}} },
{ {{2,1},{3,1},{1,2},{2,2}}, {{2,0},{2,1},{3,1},{3,2}}, {{2,1},{3,1},{1,2},{2,2}}, {{2,0},{2,1},{3,1},{3,2}} },
{ {{1,1},{2,1},{2,2},{3,2}}, {{3,0},{2,1},{3,1},{2,2}}, {{1,1},{2,1},{2,2},{3,2}}, {{3,0},{2,1},{3,1},{2,2}} },
{ {{1,1},{2,1},{3,1},{1,2}}, {{2,0},{2,1},{2,2},{3,2}}, {{3,0},{1,1},{2,1},{3,1}}, {{1,0},{2,0},{2,1},{2,2}} },
{ {{1,1},{2,1},{3,1},{3,2}}, {{2,0},{3,0},{2,1},{2,2}}, {{1,0},{1,1},{2,1},{3,1}}, {{2,0},{2,1},{1,2},{2,2}} },
{ {{1,1},{2,1},{3,1},{2,2}}, {{2,0},{2,1},{3,1},{2,2}}, {{2,0},{1,1},{2,1},{3,1}}, {{2,0},{1,1},{2,1},{2,2}} }
};
//---------------------------------------------------------------------------
CPoint MakePoint(int x, int y)
{
CPoint p;
p.x = x;
p.y = y;
return p;
}
//---------------------------------------------------------------------------
TRotationStone::TRotationStone()
{
FStoneNumber = 0;
FRotationIndex = 0;
randomize();
}
//---------------------------------------------------------------------------
TRotationStone::TRotationStone(int stone_number, int rotation_index)
{
if(stone_number < 7 && stone_number > -1)
FStoneNumber = stone_number;
else
FStoneNumber = 0;
if(rotation_index < 4 && rotation_index > -1)
FRotationIndex = rotation_index;
else
FRotationIndex = 0;
randomize();
}
//---------------------------------------------------------------------------
void TRotationStone::SetStoneNumber(int stone_number)
{
if(stone_number < 7 && stone_number > -1)
FStoneNumber = stone_number;
}
//---------------------------------------------------------------------------
int TRotationStone::GetStoneNumber()
{
return FStoneNumber;
}
//---------------------------------------------------------------------------
int TRotationStone::GetRotationIndex()
{
return FRotationIndex;
}
//---------------------------------------------------------------------------
void TRotationStone::GetCoords(CPoint* p)
{
CPoint* stone_coords = (CPoint*)stones[FStoneNumber][FRotationIndex];
for(int n=0; n<4; ++n)
{
p[n].x = stone_coords[n].x;
p[n].y = stone_coords[n].y;
}
}
//---------------------------------------------------------------------------
void TRotationStone::RotateLeft()
{
FRotationIndex--;
if(FRotationIndex < 0)
FRotationIndex = 3;
}
//---------------------------------------------------------------------------
void TRotationStone::RotateRight()
{
FRotationIndex++;
if(FRotationIndex > 3)
FRotationIndex = 0;
}
//---------------------------------------------------------------------------
void TRotationStone::Random()
{
int r = random(19) + 1; // We have 19 different kinds of stones
int sn; // Ist die zugehörige Nummer in der Auflistung der Steine (siehe oben)
if( r < 8 )
{
if( r == 1 )
sn = 1;
else
{
if(r % 2)
sn = 2*r;
else
sn = 2*r + 1;
}
}
else if( r >= 8 )
sn = r + 9;
FStoneNumber = (sn - 1) / 4;
FRotationIndex = sn - (FStoneNumber * 4) - 1;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
/*===========================================================================
CLASS TTetris
===========================================================================*/
TTetris::TTetris()
{
for(int i=0; i<10; ++i)
for(int j=0; j<20; ++j)
FPile[i][j] = 0; // No stone anywhere
FCurStone.Random();
FNextStone.Random();
FStonePos = GetNewStonePos(FCurStone);
FScore = 0;
FLevel = 0;
FLines = 0;
FStopped = true;
FOnVisualUpdate = NULL;
FOnNewNext = NULL;
FOnGameOver = NULL;
FOnDeleteLines = NULL;
FOnTimerGo = NULL;
FOnTimerStop = NULL;
}
//---------------------------------------------------------------------------
void TTetris::Reset()
{
for(int i=0; i<10; ++i)
for(int j=0; j<20; ++j)
FPile[i][j] = 0; // No stone anywhere
FCurStone.Random();
FNextStone.Random();
FStonePos = GetNewStonePos(FCurStone);
FScore = 0;
FLevel = 0;
FLines = 0;
}
//---------------------------------------------------------------------------
TTetris::~TTetris()
{
}
//---------------------------------------------------------------------------
void TTetris::Start()
{
FStopped = false;
if(FOnNewNext)
FOnNewNext(0);
if(FOnTimerGo)
FOnTimerGo(0);
if(FOnVisualUpdate)
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::Pause()
{
if(FOnTimerStop)
{
FOnTimerStop(0);
FStopped = true;
}
}
//---------------------------------------------------------------------------
void TTetris::DoNextStep()
{
static int del_lines[5] = {0,20,20,20,20};
static int stat_level = 0;
if( IsDownCollision() )
{
if(FStonePos.y <= 0) // Game Over
{
if(FOnGameOver)
{
if(FOnTimerStop)
{
FOnTimerStop(0);
FStopped = true;
}
FOnGameOver(0);
}
return;
}
AddStoneToPile(FCurStone, FStonePos);
GetFullLines(del_lines);
FCurStone = FNextStone;
FStonePos = GetNewStonePos(FCurStone);
FNextStone.Random();
if( FOnVisualUpdate )
FOnVisualUpdate(0); // Draw only pile
if( del_lines[0] )
{
FLines += del_lines[0];
ComputeNewScore( del_lines[0] );
stat_level = FLevel;
FLevel = FLines / 10;
if(stat_level < FLevel) // Level Jump
{
if(FOnNewLevel)
FOnNewLevel(FLevel);
}
if(FOnDeleteLines) // Blinking lines
{
if(FOnTimerStop)
{
FOnTimerStop(0);
FStopped = true;
}
FOnDeleteLines(del_lines);
}
DeleteLines(del_lines);
if(FStopped && FOnTimerGo)
{
FOnTimerGo(0);
FStopped = false;
}
}
if(FOnNewNext)
FOnNewNext(0);
}
else // no down-collision
{
FStonePos.y++;
}
if( FOnVisualUpdate )
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::GetFullLines(int* out_lines)
{
int k = FStonePos.y;
int min = (k+3 > 19) ? 19 : k+3;
int m = 0;
for(int j=k; j<=min; ++j)
{
if( IsLineFull(j) )
{
out_lines[m+1] = j;
m++;
}
}
out_lines[0] = m;
}
//---------------------------------------------------------------------------
void TTetris::DeleteLines(int* in_lines)
{
/* in_lines is a 5-element array n|m1 m2 m3 m4 where n is the number of
full lines and m1-m4 are the indices of the full lines */
int n = in_lines[0];
for(int m=0; m<n; ++m)
DeleteLine(in_lines[m+1]);
}
//---------------------------------------------------------------------------
void TTetris::DeleteLine(int j)
{
if( j==0 ) // Oberste Line
{
for(int i=0; i<10; ++i)
FPile[i][j] = 0;
return;
}
for(int k=j; k>0; --k)
{
for(int i=0; i<10; ++i)
{
FPile[i][k] = FPile[i][k-1];
FPile[i][k-1] = 0;
}
}
}
//---------------------------------------------------------------------------
void TTetris::InputRight()
{
if( !IsRightCollision() )
FStonePos.x++;
if( FOnVisualUpdate )
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::InputLeft()
{
if( !IsLeftCollision() )
FStonePos.x--;
if( FOnVisualUpdate )
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::InputDown()
{
if( !IsDownCollision() )
FStonePos.y++;
if( FOnVisualUpdate )
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::InputRotate()
{
FCurStone.RotateRight();
if( IsDirectCollision() )
FCurStone.RotateLeft();
if( FOnVisualUpdate )
FOnVisualUpdate(1);
}
//---------------------------------------------------------------------------
void TTetris::InputDrop()
{
while( !IsDownCollision() )
FStonePos.y++;
FScore += FLevel + 1;
DoNextStep();
}
//---------------------------------------------------------------------------
int TTetris::GetPileCoord(int i, int j)
{
return FPile[i][j];
}
//---------------------------------------------------------------------------
void TTetris::GetCurStoneCoords(CPoint* pp)
{
CPoint* stone_coords = (CPoint*)stones[FCurStone.GetStoneNumber()][FCurStone.GetRotationIndex()];
for(int n=0; n<4; ++n)
{
int i = stone_coords[n].x + FStonePos.x;
int j = stone_coords[n].y + FStonePos.y;
pp[n] = MakePoint(i,j);
}
}
//---------------------------------------------------------------------------
void TTetris::GetNextStoneCoords(CPoint* pp)
{
CPoint* stone_coords = (CPoint*)stones[FNextStone.GetStoneNumber()][FNextStone.GetRotationIndex()];
for(int n=0; n<4; ++n)
{
pp[n] = stone_coords[n];
}
}
//---------------------------------------------------------------------------
int TTetris::GetCurStoneType()
{
return FCurStone.GetStoneNumber();
}
//---------------------------------------------------------------------------
int TTetris::GetNextStoneType()
{
return FNextStone.GetStoneNumber();
}
//---------------------------------------------------------------------------
int TTetris::GetDeletedLines()
{
return FLines;
}
//---------------------------------------------------------------------------
int TTetris::GetLevel()
{
return FLevel;
}
//---------------------------------------------------------------------------
int TTetris::GetScore()
{
return FScore;
}
//---------------------------------------------------------------------------
void TTetris::SetOnVisualUpdateFunc(TCallbackFunc onVisualUpdate)
{
FOnVisualUpdate = onVisualUpdate;
}
//---------------------------------------------------------------------------
void TTetris::SetOnNewNextFunc(TCallbackFunc onNewNext)
{
FOnNewNext = onNewNext;
}
//---------------------------------------------------------------------------
void TTetris::SetOnGameOverFunc(TCallbackFunc onGameOver)
{
FOnGameOver = onGameOver;
}
//---------------------------------------------------------------------------
void TTetris::SetOnTimerGoFunc(TCallbackFunc onTimerGo)
{
FOnTimerGo = onTimerGo;
}
//---------------------------------------------------------------------------
void TTetris::SetOnTimerStopFunc(TCallbackFunc onTimerStop)
{
FOnTimerStop = onTimerStop;
}
//---------------------------------------------------------------------------
void TTetris::SetOnNewLevelFunc(TCallbackFunc onNewLevel)
{
FOnNewLevel = onNewLevel;
}
//---------------------------------------------------------------------------
void TTetris::SetOnDeleteLinesFunc(TDeleteLinesFunc onDeleteLines)
{
FOnDeleteLines = onDeleteLines;
}
//---------------------------------------------------------------------------
bool TTetris::IsStopped()
{
return FStopped;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
/*===========================================================================
PRIVATE FUNCTIONS
===========================================================================*/
bool TTetris::IsDownCollision() // Returns true if one step down causes collision
{
bool collision;
FStonePos.y++;
collision = IsDirectCollision();
FStonePos.y--;
return collision;
}
//---------------------------------------------------------------------------
bool TTetris::IsRightCollision() // Returns true if one step right causes collision
{
bool collision;
FStonePos.x++;
collision = IsDirectCollision();
FStonePos.x--;
return collision;
}
//---------------------------------------------------------------------------
bool TTetris::IsLeftCollision() // Returns true if one step left causes collision
{
bool collision;
FStonePos.x--;
collision = IsDirectCollision();
FStonePos.x++;
return collision;
}
//---------------------------------------------------------------------------
void TTetris::AddStoneToPile(TRotationStone stone, CPoint stone_pos)
{
CPoint* stone_coords = (CPoint*)stones[stone.GetStoneNumber()][stone.GetRotationIndex()];
for(int n=0; n<4; ++n)
{
int i = stone_coords[n].x + stone_pos.x;
int j = stone_coords[n].y + stone_pos.y;
FPile[i][j] = stone.GetStoneNumber() + 1;
}
}
//---------------------------------------------------------------------------
bool TTetris::IsDirectCollision() // Returns true if FCurStone collides with pile
{
CPoint* stone_coords = (CPoint*)stones[FCurStone.GetStoneNumber()][FCurStone.GetRotationIndex()];
for(int n=0; n<4; ++n)
{
// See if n-th coord of stone collides with pile
int i = stone_coords[n].x + FStonePos.x;
int j = stone_coords[n].y + FStonePos.y;
if( i > 9 || i < 0 || j > 19 )
return true;
if( j < 0 )
return false;
if( FPile[i][j] )
return true;
}
return false;
}
//---------------------------------------------------------------------------
CPoint TTetris::GetNewStonePos(TRotationStone stone)
{
CPoint p;
CPoint* stone_coords = (CPoint*)stones[stone.GetStoneNumber()][stone.GetRotationIndex()];
if( stone_coords[0].y == 0 )
p = MakePoint(3,0);
else
p = MakePoint(3,-1);
return p;
}
//---------------------------------------------------------------------------
bool TTetris::IsLineFull(int j)
{
for(int i=0; i<10; ++i)
if( !FPile[i][j] )
return false;
return true;
}
//---------------------------------------------------------------------------
void TTetris::ComputeNewScore(int deleted_lines)
{
switch(deleted_lines)
{
case 1:
FScore += 40 * (FLevel + 1);
break;
case 2:
FScore += 100 * (FLevel + 1);
break;
case 3:
FScore += 300 * (FLevel + 1);
break;
case 4:
FScore += 1200 * (FLevel + 1);
break;
}
}
//---------------------------------------------------------------------------