|
|
@ -1,6 +1,7 @@ |
|
|
|
#include <inttypes.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include "../../random/prng.h" |
|
|
|
#include "piece.h" |
|
|
|
#include "playfield.h" |
|
|
|
#include "bast.h" |
|
|
@ -13,18 +14,46 @@ |
|
|
|
/* Function: tetris_bastet_clearColHeights;
|
|
|
|
* Description: resets the array for the column heights |
|
|
|
* Argument pBastet: bastet instance whose array should be reset |
|
|
|
* Argument nStart: start index |
|
|
|
* Argument nStop: stop index |
|
|
|
* Return value: void |
|
|
|
*/ |
|
|
|
void tetris_bastet_clearColHeights(tetris_bastet_t *pBastet) |
|
|
|
void tetris_bastet_clearColHeights(tetris_bastet_t *pBastet, |
|
|
|
int8_t nStart, |
|
|
|
int8_t nStop) |
|
|
|
{ |
|
|
|
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); |
|
|
|
for (int i = 0; i < nWidth; ++i) |
|
|
|
for (int i = nStart; i <= nStop; ++i) |
|
|
|
{ |
|
|
|
pBastet->pColHeights[i] = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Function: tetris_bastet_qsortCompare
|
|
|
|
* Description: compare function for quick sorting the pieces by score |
|
|
|
* Argument pa: the first value to compare |
|
|
|
* Argument pb: the second value to compare |
|
|
|
* Return value: void |
|
|
|
*/ |
|
|
|
int tetris_bastet_qsortCompare(const void *pa, const void *pb) |
|
|
|
{ |
|
|
|
tetris_bastet_scorepair_t *pScoreA = (tetris_bastet_scorepair_t *)pa; |
|
|
|
tetris_bastet_scorepair_t *pScoreB = (tetris_bastet_scorepair_t *)pb; |
|
|
|
if (pScoreA->nScore == pScoreB->nScore) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
else if (pScoreA->nScore < pScoreB->nScore) |
|
|
|
{ |
|
|
|
return -1; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/****************************
|
|
|
|
* construction/destruction * |
|
|
|
****************************/ |
|
|
@ -43,7 +72,7 @@ tetris_bastet_t* tetris_bastet_construct(tetris_playfield_t *pPl) |
|
|
|
|
|
|
|
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); |
|
|
|
pBastet->pColHeights = (int8_t*) calloc(nWidth, sizeof(int8_t)); |
|
|
|
tetris_bastet_clearColHeights(pBastet); |
|
|
|
tetris_bastet_clearColHeights(pBastet, 0, nWidth - 1); |
|
|
|
|
|
|
|
return pBastet; |
|
|
|
} |
|
|
@ -87,20 +116,46 @@ int16_t tetris_bastet_evalPos(tetris_bastet_t *pBastet, |
|
|
|
pPiece, nColumn); |
|
|
|
nScore += 5000 * nLines; |
|
|
|
|
|
|
|
// determine sane start and stop columns whose heights we want to calculate
|
|
|
|
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); |
|
|
|
int8_t nStartCol = ((nColumn - 1) < 0) ? 0 : nColumn - 1; |
|
|
|
int8_t nStopCol; |
|
|
|
// do we start at the left most position?
|
|
|
|
if (nColumn <= -3) |
|
|
|
{ |
|
|
|
// in case we start at the left most position we calculate all heights
|
|
|
|
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 piece
|
|
|
|
else |
|
|
|
{ |
|
|
|
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
|
|
|
|
tetris_playfield_iterator_t iterator; |
|
|
|
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); |
|
|
|
int8_t nHeight = 1; |
|
|
|
uint16_t *pDump = tetris_playfield_predictBottomRow(&iterator, |
|
|
|
pBastet->pPlayfield, pPiece, nColumn); |
|
|
|
if (pDump == NULL) |
|
|
|
{ |
|
|
|
// an immediately returned NULL is caused by a full dump -> low score
|
|
|
|
return -32766; |
|
|
|
} |
|
|
|
while (pDump != NULL) |
|
|
|
{ |
|
|
|
for (int x = 0; x < nWidth; ++x) |
|
|
|
uint16_t nColMask = 0x0001 << nStartCol; |
|
|
|
for (int x = nStartCol; x <= nStopCol; ++x) |
|
|
|
{ |
|
|
|
if ((*pDump & (0x0001 << x)) != 0) |
|
|
|
if ((*pDump & nColMask) != 0) |
|
|
|
{ |
|
|
|
pBastet->pColHeights[x] = nHeight; |
|
|
|
} |
|
|
|
nColMask <<= 1; |
|
|
|
} |
|
|
|
pDump = tetris_playfield_predictNextRow(&iterator); |
|
|
|
++nHeight; |
|
|
@ -111,8 +166,84 @@ int16_t tetris_bastet_evalPos(tetris_bastet_t *pBastet, |
|
|
|
nScore -= 5 * pBastet->pColHeights[x]; |
|
|
|
} |
|
|
|
|
|
|
|
// reset column height array for later use
|
|
|
|
tetris_bastet_clearColHeights(pBastet); |
|
|
|
|
|
|
|
return nScore; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Function: tetris_bastet_minimax
|
|
|
|
* Description: calculates the best possible score for every piece |
|
|
|
* Argument pBastet: the bastet instance of interest |
|
|
|
* Return value: void |
|
|
|
*/ |
|
|
|
void tetris_bastet_minimax(tetris_bastet_t *pBastet) |
|
|
|
{ |
|
|
|
int8_t nWidth = tetris_playfield_getWidth(pBastet->pPlayfield); |
|
|
|
tetris_piece_t *pPiece = tetris_piece_construct(TETRIS_PC_LINE, |
|
|
|
TETRIS_PC_ANGLE_0); |
|
|
|
for (int8_t nBlock = TETRIS_PC_LINE; nBlock <= TETRIS_PC_Z; ++nBlock) |
|
|
|
{ |
|
|
|
int16_t nMaxScore = -32768; |
|
|
|
tetris_piece_changeShape(pPiece, nBlock); |
|
|
|
int8_t nAngleCount = tetris_piece_angleCount(pPiece); |
|
|
|
for (int8_t nAngle = TETRIS_PC_ANGLE_0; nAngle < nAngleCount; ++nAngle) |
|
|
|
{ |
|
|
|
tetris_piece_changeAngle(pPiece, nAngle); |
|
|
|
for (int8_t nCol = -3; nCol < nWidth; ++nCol) |
|
|
|
{ |
|
|
|
int16_t nScore = tetris_bastet_evalPos(pBastet, pPiece, nCol); |
|
|
|
nMaxScore = nMaxScore > nScore ? nMaxScore : nScore; |
|
|
|
} |
|
|
|
} |
|
|
|
pBastet->nPieceScores[nBlock].shape = nBlock; |
|
|
|
pBastet->nPieceScores[nBlock].nScore = nMaxScore; |
|
|
|
} |
|
|
|
tetris_piece_destruct(pPiece); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Function: tetris_bastet_choosePiece
|
|
|
|
* Description: calculates the worst possible piece |
|
|
|
* Argument pBastet: the bastet instance of interest |
|
|
|
* Return value: the worst possible piece |
|
|
|
*/ |
|
|
|
tetris_piece_t* tetris_bastet_choosePiece(tetris_bastet_t *pBastet) |
|
|
|
{ |
|
|
|
const uint8_t nPercent[7] = {75, 92, 98, 100, 100, 100, 100}; |
|
|
|
tetris_bastet_minimax(pBastet); |
|
|
|
|
|
|
|
// perturb score (-2 to +2) to avoid stupid tie handling
|
|
|
|
for (uint8_t i = 0; i < 7; ++i) |
|
|
|
{ |
|
|
|
pBastet->nPieceScores[i].nScore += random8() % 5 - 2; |
|
|
|
} |
|
|
|
|
|
|
|
qsort(pBastet->nPieceScores, 7, sizeof(tetris_bastet_scorepair_t), |
|
|
|
&tetris_bastet_qsortCompare); |
|
|
|
|
|
|
|
uint8_t rnd = rand() % 100; |
|
|
|
for (uint8_t i = 0; i < 7; i++) |
|
|
|
{ |
|
|
|
if (rnd < nPercent[i]) |
|
|
|
{ |
|
|
|
return tetris_piece_construct(pBastet->nPieceScores[i].shape, |
|
|
|
TETRIS_PC_ANGLE_0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//should not arrive here
|
|
|
|
return tetris_piece_construct(pBastet->nPieceScores[0].shape, |
|
|
|
TETRIS_PC_ANGLE_0); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Function: tetris_bastet_choosePreviewPiece
|
|
|
|
* Description: returns the best possible piece |
|
|
|
* (run tetris_bastet_choosePiece first!) |
|
|
|
* Argument pBastet: the bastet instance of interest |
|
|
|
* Return value: the worst possible piece |
|
|
|
*/ |
|
|
|
tetris_piece_t* tetris_bastet_choosePreviewPiece(tetris_bastet_t *pBastet) |
|
|
|
{ |
|
|
|
return tetris_piece_construct(pBastet->nPieceScores[6].shape, |
|
|
|
TETRIS_PC_ANGLE_0); |
|
|
|
} |
|
|
|