modified bastet algorithm to behave more like the original

This commit is contained in:
Christian Kroll 2010-04-11 06:40:28 +00:00
parent e4186bd02b
commit 1d28b75a0a
4 changed files with 117 additions and 38 deletions

View File

@ -528,6 +528,13 @@ int8_t tetris_playfield_getRow(tetris_playfield_t *pPl)
} }
int8_t tetris_playfield_getFirstMatterRow(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nFirstMatterRow;
}
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl) uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl)
{ {
assert(pPl != NULL); assert(pPl != NULL);

View File

@ -203,6 +203,14 @@ int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl);
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl); int8_t tetris_playfield_getRow(tetris_playfield_t *pPl);
/**
* returns the row of the currently falling piece
* @param pPl the playfield we want information from
* @return highest row with matter
*/
int8_t tetris_playfield_getFirstMatterRow(tetris_playfield_t *pPl);
/** /**
* returns the row mask relative to nRow * returns the row mask relative to nRow
* @param pPl the playfield we want information from * @param pPl the playfield we want information from

View File

@ -37,6 +37,75 @@ void tetris_bastet_clearColHeights(tetris_bastet_variant_t *pBastet,
} }
/**
* calculate the actual column heights (without any prediction)
* @param pBastet bastet instance whose column heights should be calculated
*/
void tetris_bastet_calcActualColHeights(tetris_bastet_variant_t *pBastet)
{
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield);
int8_t nStartRow = tetris_playfield_getHeight(pBastet->pPlayfield) - 1;
int8_t nStopRow = tetris_playfield_getFirstMatterRow(pBastet->pPlayfield);
for (int8_t y = nStartRow; y >= nStopRow; --y)
{
uint16_t nDumpRow = tetris_playfield_getDumpRow(pBastet->pPlayfield, y);
uint16_t nColMask = 0x0001;
for (int8_t x = 0; x < nWidth; ++x)
{
if ((nDumpRow & nColMask) != 0)
{
pBastet->pActualColHeights[x] = nStartRow - y + 1;
}
nColMask <<= 1;
}
}
}
/**
* calculate the predicted column heights for a given column range
* @param pBastet bastet instance whose column heights should be predicted
* @param pPiece the piece to be tested
* @param nColum the column where the piece should be dropped
* @param nStartCol the first column of the range to be predicted
* @param nStopCol the last column of the range to be predicted
* @return 0 if dropped piece would cause an overflow, 1 if prediction succeeds
*/
uint8_t tetris_bastet_calcPredictedColHeights(tetris_bastet_variant_t *pBastet,
tetris_piece_t *pPiece,
int8_t nDeepestRow,
int8_t nColumn,
int8_t nStartCol,
int8_t nStopCol)
{
// go through every row and calculate column heights
tetris_playfield_iterator_t iterator;
int8_t nHeight = 1;
uint16_t *pDump = tetris_playfield_predictBottomRow(&iterator,
pBastet->pPlayfield, pPiece, nDeepestRow, nColumn);
if (pDump == NULL)
{
// an immediately returned NULL is caused by a full dump -> low score
return 0;
}
while (pDump != NULL)
{
uint16_t nColMask = 0x0001 << nStartCol;
for (int x = nStartCol; x <= nStopCol; ++x)
{
if ((*pDump & nColMask) != 0)
{
pBastet->pColHeights[x] = nHeight;
}
nColMask <<= 1;
}
pDump = tetris_playfield_predictNextRow(&iterator);
++nHeight;
}
return 1;
}
/** /**
* compare function for quick sorting the pieces by score * compare function for quick sorting the pieces by score
* @param pa the first value to compare * @param pa the first value to compare
@ -119,6 +188,7 @@ void *tetris_bastet_construct(tetris_playfield_t *pPl)
pBastet->pPlayfield = pPl; pBastet->pPlayfield = pPl;
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield);
pBastet->pActualColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t));
pBastet->pColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t)); pBastet->pColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t));
tetris_bastet_clearColHeights(pBastet, 0, nWidth - 1); tetris_bastet_clearColHeights(pBastet, 0, nWidth - 1);
@ -131,6 +201,10 @@ void tetris_bastet_destruct(void *pVariantData)
assert(pVariantData != 0); assert(pVariantData != 0);
tetris_bastet_variant_t *pBastetVariant = tetris_bastet_variant_t *pBastetVariant =
(tetris_bastet_variant_t *)pVariantData; (tetris_bastet_variant_t *)pVariantData;
if (pBastetVariant->pActualColHeights != NULL)
{
free(pBastetVariant->pActualColHeights);
}
if (pBastetVariant->pColHeights != NULL) if (pBastetVariant->pColHeights != NULL)
{ {
free(pBastetVariant->pColHeights); free(pBastetVariant->pColHeights);
@ -152,66 +226,53 @@ int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet,
tetris_piece_t *pPiece, tetris_piece_t *pPiece,
int8_t nColumn) int8_t nColumn)
{ {
// initial score of the given piece
int16_t nScore = -32000;
// the row where the given piece collides // the row where the given piece collides
int8_t nDeepestRow = tetris_playfield_predictDeepestRow(pBastet->pPlayfield, int8_t nDeepestRow = tetris_playfield_predictDeepestRow(pBastet->pPlayfield,
pPiece, nColumn); pPiece, nColumn);
// initial score of the given piece
int16_t nScore = -32000;
// modify score based on complete lines // modify score based on complete lines
int8_t nLines = tetris_playfield_predictCompleteLines(pBastet->pPlayfield, int8_t nLines = tetris_playfield_predictCompleteLines(pBastet->pPlayfield,
pPiece, nDeepestRow, nColumn); pPiece, nDeepestRow, nColumn);
nScore += 5000 * nLines; nScore += 5000 * nLines;
// determine sane start and stop columns whose heights we want to calculate // determine a sane range of columns whose heights we want to predict
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield);
int8_t nStartCol = ((nColumn - 1) < 0) ? 0 : nColumn - 1; int8_t nStartCol, nStopCol;
int8_t nStopCol; // if lines have been removed, we need to recalculate all column heights
// Do we start at the left most position? if (nLines != 0)
// If we do we MUST calculate the heights of ALL columns (initial step)
if (nColumn <= -3)
{ {
nStartCol = 0;
nStopCol = nWidth - 1; nStopCol = nWidth - 1;
// reset all column heights to zero
tetris_bastet_clearColHeights(pBastet, 0 , nWidth);
} }
// If not, only calculate columns which are affected by the moved piece. // if no lines were removed, we only need to recalculate a few columns
else else
{ {
nStartCol = (nColumn < 0) ? 0 : nColumn;
nStopCol = (nColumn + 3) < nWidth ? nColumn + 3 : nWidth - 1; nStopCol = (nColumn + 3) < nWidth ? nColumn + 3 : nWidth - 1;
// clear affected column heights to prevent miscalculations
tetris_bastet_clearColHeights(pBastet, nStartCol, nStopCol);
} }
// go through every row and calculate column heights // predicted column heights
tetris_playfield_iterator_t iterator; if (!tetris_bastet_calcPredictedColHeights(pBastet, pPiece, nDeepestRow,
int8_t nHeight = 1; nColumn, nStartCol, nStopCol))
uint16_t *pDump = tetris_playfield_predictBottomRow(&iterator,
pBastet->pPlayfield, pPiece, nDeepestRow, nColumn);
if (pDump == NULL)
{ {
// an immediately returned NULL is caused by a full dump -> low score // in case the prediction fails we return the lowest possible score
return -32766; return -32766;
} }
while (pDump != NULL)
{
uint16_t nColMask = 0x0001 << nStartCol;
for (int x = nStartCol; x <= nStopCol; ++x)
{
if ((*pDump & nColMask) != 0)
{
pBastet->pColHeights[x] = nHeight;
}
nColMask <<= 1;
}
pDump = tetris_playfield_predictNextRow(&iterator);
++nHeight;
}
// modify score based on predicted column heights // modify score based on predicted column heights
for (int x = 0; x < nWidth; ++x) for (int x = 0; x < nWidth; ++x)
{ {
nScore -= 5 * pBastet->pColHeights[x]; if ((x >= nStartCol) && (x <= nStopCol))
{
nScore -= 5 * pBastet->pColHeights[x];
}
else
{
nScore -= 5 * pBastet->pActualColHeights[x];
}
} }
return nScore; return nScore;
@ -220,6 +281,8 @@ int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet,
void tetris_bastet_evaluatePieces(tetris_bastet_variant_t *pBastet) void tetris_bastet_evaluatePieces(tetris_bastet_variant_t *pBastet)
{ {
// precache actual column heights
tetris_bastet_calcActualColHeights(pBastet);
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield);
tetris_piece_t *pPiece = tetris_piece_construct(TETRIS_PC_LINE, tetris_piece_t *pPiece = tetris_piece_construct(TETRIS_PC_LINE,
TETRIS_PC_ANGLE_0); TETRIS_PC_ANGLE_0);

View File

@ -39,7 +39,8 @@ typedef struct tetris_bastet_variant_t
uint16_t nLines; /** number of completed lines */ uint16_t nLines; /** number of completed lines */
tetris_piece_t *pPreviewPiece; /** the piece for the preview */ tetris_piece_t *pPreviewPiece; /** the piece for the preview */
tetris_playfield_t *pPlayfield; /** playfield to be examined */ tetris_playfield_t *pPlayfield; /** playfield to be examined */
int8_t *pColHeights; /** calculated heights */ int8_t *pActualColHeights; /** actual columns heights */
int8_t *pColHeights; /** predicted column heights */
tetris_bastet_scorepair_t nPieceScores[7]; /** score for every piece */ tetris_bastet_scorepair_t nPieceScores[7]; /** score for every piece */
} }
tetris_bastet_variant_t; tetris_bastet_variant_t;
@ -74,7 +75,7 @@ void tetris_bastet_destruct(void *pVariantData);
* calculates a score for a piece at a given column * calculates a score for a piece at a given column
* @param pBastet the bastet instance of interest * @param pBastet the bastet instance of interest
* @param pPiece the piece to be tested * @param pPiece the piece to be tested
* @param pnColum the column where the piece should be dropped * @param nColum the column where the piece should be dropped
* @return score for the given move * @return score for the given move
*/ */
int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet, int16_t tetris_bastet_evaluateMove(tetris_bastet_variant_t *pBastet,