Christian Kroll
15 years ago
18 changed files with 1454 additions and 407 deletions
@ -0,0 +1,13 @@ |
|||
#ifndef BEARING_H_ |
|||
#define BEARING_H_ |
|||
|
|||
typedef enum tetris_bearing_t |
|||
{ |
|||
TETRIS_BEARING_0, |
|||
TETRIS_BEARING_90, |
|||
TETRIS_BEARING_180, |
|||
TETRIS_BEARING_270 |
|||
} |
|||
tetris_bearing_t; |
|||
|
|||
#endif /* BEARING_H_ */ |
@ -0,0 +1,717 @@ |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <assert.h> |
|||
#include <inttypes.h> |
|||
#include "../../autoconf.h" |
|||
#include "bucket.h" |
|||
#include "piece.h" |
|||
|
|||
|
|||
/***************************
|
|||
* non-interface functions * |
|||
***************************/ |
|||
|
|||
/**
|
|||
* determines if piece is either hovering or gliding |
|||
* @param pBucket the bucket we want information from |
|||
* @return TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING |
|||
*/ |
|||
tetris_bucket_status_t tetris_bucket_hoverStatus(tetris_bucket_t* pBucket) |
|||
{ |
|||
// if the piece touches the dump we ensure that the status is "gliding"
|
|||
if (tetris_bucket_collision(pBucket, pBucket->nColumn, pBucket->nRow + 1)) |
|||
{ |
|||
return TETRIS_BUS_GLIDING; |
|||
} |
|||
// otherwise the status must be "hovering"
|
|||
else |
|||
{ |
|||
return TETRIS_BUS_HOVERING; |
|||
} |
|||
} |
|||
|
|||
|
|||
/****************************
|
|||
* construction/destruction * |
|||
****************************/ |
|||
|
|||
tetris_bucket_t *tetris_bucket_construct(int8_t nWidth, |
|||
int8_t nHeight) |
|||
{ |
|||
assert((nWidth >= 4) && (nWidth <= 16)); |
|||
assert((nHeight >= 4) && (nHeight <= 124)); |
|||
|
|||
tetris_bucket_t *pBucket = |
|||
(tetris_bucket_t *)malloc(sizeof(tetris_bucket_t)); |
|||
|
|||
if (pBucket != NULL) |
|||
{ |
|||
// allocating memory for dump array
|
|||
pBucket->dump = (uint16_t*) calloc(nHeight, sizeof(uint16_t)); |
|||
|
|||
if (pBucket->dump != NULL) |
|||
{ |
|||
// setting requested attributes
|
|||
pBucket->nFirstMatterRow = nHeight - 1; |
|||
pBucket->nWidth = nWidth; |
|||
pBucket->nHeight = nHeight; |
|||
tetris_bucket_reset(pBucket); |
|||
|
|||
return pBucket; |
|||
} |
|||
else |
|||
{ |
|||
free(pBucket); |
|||
pBucket = NULL; |
|||
} |
|||
} |
|||
return NULL; |
|||
} |
|||
|
|||
|
|||
void tetris_bucket_destruct(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// if memory for the dump array has been allocated, free it
|
|||
if (pBucket->dump != NULL) |
|||
{ |
|||
free(pBucket->dump); |
|||
} |
|||
free(pBucket); |
|||
} |
|||
|
|||
|
|||
/*******************************
|
|||
* bucket related functions * |
|||
*******************************/ |
|||
|
|||
uint8_t tetris_bucket_calculateLines(uint8_t nRowMask) |
|||
{ |
|||
uint8_t nMask = 0x0001; |
|||
uint8_t nLines = 0; |
|||
for (uint8_t i = 0; i < 4; ++i) |
|||
{ |
|||
if ((nMask & nRowMask) != 0) |
|||
{ |
|||
++nLines; |
|||
} |
|||
nMask <<= 1; |
|||
} |
|||
|
|||
return nLines; |
|||
} |
|||
|
|||
|
|||
void tetris_bucket_reset(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
pBucket->pPiece = NULL; |
|||
pBucket->nColumn = 0; |
|||
pBucket->nRow = 0; |
|||
pBucket->nRowMask = 0; |
|||
|
|||
// clear dump if it has been allocated in memory
|
|||
if (pBucket->dump != NULL) |
|||
{ |
|||
memset(pBucket->dump, 0, pBucket->nHeight); |
|||
} |
|||
|
|||
pBucket->status = TETRIS_BUS_READY; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_getPieceStartPos(tetris_piece_t *pPiece) |
|||
{ |
|||
// set vertical start position (first piece row with matter at pos. 1)
|
|||
uint16_t nPieceMap = tetris_piece_getBitmap(pPiece); |
|||
uint16_t nElementMask = 0xF000; |
|||
int8_t nRow = -3; |
|||
while ((nPieceMap & nElementMask) == 0) |
|||
{ |
|||
++nRow; |
|||
nElementMask >>= 4; |
|||
} |
|||
if (nRow < 0) |
|||
{ |
|||
++nRow; |
|||
} |
|||
|
|||
return nRow; |
|||
} |
|||
|
|||
|
|||
void tetris_bucket_insertPiece(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
tetris_piece_t** ppOldPiece) |
|||
{ |
|||
assert((pBucket != NULL) && (pPiece != NULL) && (ppOldPiece != NULL)); |
|||
|
|||
// a piece can only be inserted in state TETRIS_PFS_READY
|
|||
assert(pBucket->status == TETRIS_BUS_READY); |
|||
|
|||
// row mask is now meaningless
|
|||
pBucket->nRowMask = 0; |
|||
|
|||
// replace old piece
|
|||
*ppOldPiece = pBucket->pPiece; |
|||
pBucket->pPiece = pPiece; |
|||
|
|||
// set horizontal start position (in the middle of the top line)
|
|||
pBucket->nColumn = (pBucket->nWidth - 2) / 2; |
|||
|
|||
// set vertical start position (first piece row with matter at pos. 1)
|
|||
pBucket->nRow = tetris_bucket_getPieceStartPos(pBucket->pPiece); |
|||
|
|||
// did we already collide with something?
|
|||
if (tetris_bucket_collision(pBucket, pBucket->nColumn, pBucket->nRow) == 1) |
|||
{ |
|||
// game over man, game over!!
|
|||
pBucket->status = TETRIS_BUS_GAMEOVER; |
|||
} |
|||
else |
|||
{ |
|||
// bring it on!
|
|||
pBucket->status = tetris_bucket_hoverStatus(pBucket); |
|||
} |
|||
} |
|||
|
|||
|
|||
uint8_t tetris_bucket_collision(tetris_bucket_t *pBucket, |
|||
int8_t nColumn, |
|||
int8_t nRow) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// only allow coordinates which are within sane ranges
|
|||
assert((nColumn > -4) && (nColumn < pBucket->nWidth)); |
|||
assert((nRow > -4) && (nRow < pBucket->nHeight)); |
|||
|
|||
// The rows of a piece get compared with the background one by one
|
|||
// until either a collision occures or all rows are compared. Both the
|
|||
// piece row and the part of the bucket it covers are represented in
|
|||
// 4 bits which were singled out from their corresponding uint16_t
|
|||
// values and are aligned to LSB. In case where a piece overlaps with
|
|||
// either the left or the right border we "enhance" the bucket part
|
|||
// via bit shifting and set all bits representing the border to 1.
|
|||
//
|
|||
// NOTE: LSB represents the left most position.
|
|||
uint16_t nPieceMap = tetris_piece_getBitmap(pBucket->pPiece); |
|||
uint16_t nBucketPart; |
|||
uint16_t nPieceRowMap; |
|||
|
|||
// negative nRow values indicate that the piece hasn't fully entered the
|
|||
// bucket yet which requires special treatment if the piece overlaps
|
|||
// with either the left or the right border
|
|||
if (nRow < 0) |
|||
{ |
|||
uint16_t nBorderMask = 0x0000; |
|||
// piece overlaps with left border
|
|||
if (nColumn < 0) |
|||
{ |
|||
nBorderMask = 0x1111 << (-nColumn - 1); |
|||
} |
|||
// piece overlaps with right border
|
|||
else if ((nColumn + 3) >= pBucket->nWidth) |
|||
{ |
|||
nBorderMask = 0x8888 >> ((nColumn + 3) - pBucket->nWidth); |
|||
} |
|||
// return if piece collides with border
|
|||
if ((nPieceMap & nBorderMask) != 0) |
|||
{ |
|||
return 1; |
|||
} |
|||
} |
|||
|
|||
// here we check the part which has already entered the bucket
|
|||
for (int8_t y = (nRow < 0) ? -nRow : 0; y < 4; ++y) |
|||
{ |
|||
// current piece row overlaps with lower border
|
|||
if ((y + nRow) >= pBucket->nHeight) |
|||
{ |
|||
// all 4 bits represent the lower border
|
|||
nBucketPart = 0x000F; |
|||
} |
|||
// piece overlaps with left border
|
|||
else if (nColumn < 0) |
|||
{ |
|||
// clear all bits we are not interested in
|
|||
nBucketPart = (pBucket->dump[y + nRow] & (0x000F >> -nColumn)); |
|||
// add zeros to the left (the bits "behind" the left border)
|
|||
nBucketPart <<= -nColumn; |
|||
// set bits beyond left border to 1
|
|||
nBucketPart |= 0x000F >> (4 + nColumn); |
|||
} |
|||
// piece overlaps with right border
|
|||
else if ((nColumn + 3) >= pBucket->nWidth) |
|||
{ |
|||
// align the bits we are interested in to LSB
|
|||
// (thereby clearing the rest)
|
|||
nBucketPart = pBucket->dump[y + nRow] >> nColumn; |
|||
// set bits beyond right border to 1
|
|||
nBucketPart |= 0xFFF8 >> (nColumn + 3 - pBucket->nWidth); |
|||
} |
|||
// current row neither overlaps with left, right nor lower border
|
|||
else |
|||
{ |
|||
// clear all bits we are not interested in and align the
|
|||
// remaing row to LSB
|
|||
nBucketPart = |
|||
(pBucket->dump[y + nRow] & (0x000F << nColumn)) >> nColumn; |
|||
} |
|||
|
|||
// clear all bits of the piece we are not interested in and
|
|||
// align the remaing row to LSB
|
|||
nPieceRowMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2); |
|||
|
|||
// finally check for a collision
|
|||
if ((nBucketPart & nPieceRowMap) != 0) |
|||
{ |
|||
return 1; |
|||
} |
|||
} |
|||
|
|||
// if we reach here, no collision was detected
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
void tetris_bucket_advancePiece(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// a piece can only be lowered if it is hovering or gliding
|
|||
assert ((pBucket->status == TETRIS_BUS_HOVERING) || |
|||
(pBucket->status == TETRIS_BUS_GLIDING)); |
|||
|
|||
if (tetris_bucket_collision(pBucket, pBucket->nColumn, pBucket->nRow + 1)) |
|||
{ |
|||
uint16_t nPiece = tetris_piece_getBitmap(pBucket->pPiece); |
|||
|
|||
// Is the bucket filled up?
|
|||
if ((pBucket->nRow < 0) && (nPiece & (0x0FFF >> ((3 + pBucket->nRow) << 2))) != 0) |
|||
{ |
|||
pBucket->status = TETRIS_BUS_GAMEOVER; |
|||
} |
|||
else |
|||
{ |
|||
// determine valid start point for dump index
|
|||
int8_t nStartRow = ((pBucket->nRow + 3) < pBucket->nHeight) ? |
|||
(pBucket->nRow + 3) : pBucket->nHeight - 1; |
|||
for (int8_t i = nStartRow; i >= pBucket->nRow; --i) |
|||
{ |
|||
int8_t y = i - pBucket->nRow; |
|||
|
|||
// clear all bits of the piece we are not interested in and
|
|||
// align the rest to LSB
|
|||
uint16_t nPieceMap = (nPiece & (0x000F << (y << 2))) >> (y << 2); |
|||
// shift the remaining content to the current column
|
|||
if (pBucket->nColumn >= 0) |
|||
{ |
|||
nPieceMap <<= pBucket->nColumn; |
|||
} |
|||
else |
|||
{ |
|||
nPieceMap >>= -pBucket->nColumn; |
|||
} |
|||
// embed piece in bucket
|
|||
pBucket->dump[i] |= nPieceMap; |
|||
} |
|||
|
|||
// update value for the highest row with matter
|
|||
int8_t nPieceRow = pBucket->nRow; |
|||
uint16_t nMask = 0x000F; |
|||
for (int i = 0; i < 4; ++i, nMask <<= 4) |
|||
{ |
|||
if ((nMask & nPiece) != 0) |
|||
{ |
|||
nPieceRow += i; |
|||
break; |
|||
} |
|||
} |
|||
pBucket->nFirstMatterRow = (pBucket->nFirstMatterRow > nPieceRow) ? |
|||
nPieceRow : pBucket->nFirstMatterRow; |
|||
|
|||
// the piece has finally been docked
|
|||
pBucket->status = TETRIS_BUS_DOCKED; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// since there is no collision the piece may continue its travel
|
|||
// to the ground...
|
|||
pBucket->nRow++; |
|||
|
|||
// are we gliding?
|
|||
pBucket->status = tetris_bucket_hoverStatus(pBucket); |
|||
} |
|||
} |
|||
|
|||
|
|||
uint8_t tetris_bucket_movePiece(tetris_bucket_t *pBucket, |
|||
tetris_bucket_direction_t direction) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// a piece can only be moved if it is still hovering or gliding
|
|||
assert((pBucket->status == TETRIS_BUS_HOVERING) || |
|||
(pBucket->status == TETRIS_BUS_GLIDING)); |
|||
|
|||
int8_t nOffset = (direction == TETRIS_BUD_LEFT) ? -1 : 1; |
|||
if (tetris_bucket_collision(pBucket, pBucket->nColumn + nOffset, |
|||
pBucket->nRow) == 0) |
|||
{ |
|||
pBucket->nColumn += nOffset; |
|||
|
|||
// are we gliding?
|
|||
pBucket->status = tetris_bucket_hoverStatus(pBucket); |
|||
return 1; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
uint8_t tetris_bucket_rotatePiece(tetris_bucket_t *pBucket, |
|||
tetris_piece_rotation_t rotation) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// a piece can only be rotation if it is still hovering or gliding
|
|||
assert((pBucket->status == TETRIS_BUS_HOVERING) || |
|||
(pBucket->status == TETRIS_BUS_GLIDING)); |
|||
|
|||
tetris_piece_rotate(pBucket->pPiece, rotation); |
|||
|
|||
// does the rotated piece cause a collision?
|
|||
if (tetris_bucket_collision(pBucket, pBucket->nColumn, pBucket->nRow) != 0) |
|||
{ |
|||
// in that case we revert the rotation
|
|||
if (rotation == TETRIS_PC_ROT_CW) |
|||
{ |
|||
tetris_piece_rotate(pBucket->pPiece, TETRIS_PC_ROT_CCW); |
|||
} |
|||
else |
|||
{ |
|||
tetris_piece_rotate(pBucket->pPiece, TETRIS_PC_ROT_CW); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// are we gliding?
|
|||
pBucket->status = tetris_bucket_hoverStatus(pBucket); |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
|
|||
void tetris_bucket_removeCompleteLines(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
|
|||
// rows can only be removed if we are in state TETRIS_PFS_DOCKED
|
|||
assert(pBucket->status == TETRIS_BUS_DOCKED); |
|||
|
|||
// bit mask of a full row
|
|||
uint16_t nFullRow = 0xFFFF >> (16 - pBucket->nWidth); |
|||
|
|||
// bit mask (only 4 bits) that tells us if the n-th row after the
|
|||
// current nRow is complete (n-th bit set to 1, LSB represents nRow itself)
|
|||
uint8_t nRowMask = 0; |
|||
|
|||
// determine sane start and stop values for the dump' index
|
|||
int8_t nStartRow = ((pBucket->nRow + 3) >= pBucket->nHeight) ? |
|||
pBucket->nHeight - 1 : pBucket->nRow + 3; |
|||
int8_t nStopRow = (pBucket->nRow < 0) ? 0 : pBucket->nRow; |
|||
|
|||
// dump index variables
|
|||
// for incomplete rows, both variables will be decremented
|
|||
// for complete rows, only i gets decremented
|
|||
int8_t nLowestRow = nStartRow; |
|||
|
|||
// save old value for the first dump index with matter
|
|||
int8_t nFormerFirstMatterRow = pBucket->nFirstMatterRow; |
|||
|
|||
// this loop only considers rows which are affected by the piece
|
|||
for (int8_t i = nStartRow; i >= nStopRow; --i) |
|||
{ |
|||
// is current row a full row?
|
|||
if ((nFullRow & pBucket->dump[i]) == nFullRow) |
|||
{ |
|||
// adjust value for the highest row with matter
|
|||
pBucket->nFirstMatterRow++; |
|||
|
|||
// set corresponding bit for the row mask
|
|||
// nRowMask |= 0x08 >> (nStartRow - i);
|
|||
nRowMask |= 0x01 << (i - pBucket->nRow); |
|||
} |
|||
else |
|||
{ |
|||
// if nLowestRow and i differ, the dump has to be shifted
|
|||
if (i < nLowestRow) |
|||
{ |
|||
pBucket->dump[nLowestRow] = pBucket->dump[i]; |
|||
} |
|||
--nLowestRow; |
|||
} |
|||
} |
|||
|
|||
// if rows have been removed, this loop shifts the rest of the dump
|
|||
uint8_t nComplete = nLowestRow - nStopRow + 1; |
|||
if (nComplete > 0) |
|||
{ |
|||
for (int8_t i = nStopRow - 1; nLowestRow >= nFormerFirstMatterRow; --i) |
|||
{ |
|||
// is the row we are copying from below the upper border?
|
|||
if (i >= nFormerFirstMatterRow) |
|||
{ |
|||
// just copy from that row
|
|||
pBucket->dump[nLowestRow] = pBucket->dump[i]; |
|||
} |
|||
else |
|||
{ |
|||
// rows above the upper border are always empty
|
|||
pBucket->dump[nLowestRow] = 0; |
|||
} |
|||
--nLowestRow; |
|||
} |
|||
} |
|||
|
|||
// ready to get the next piece
|
|||
pBucket->status = TETRIS_BUS_READY; |
|||
|
|||
pBucket->nRowMask = nRowMask; |
|||
} |
|||
|
|||
|
|||
/*****************
|
|||
* get functions * |
|||
*****************/ |
|||
|
|||
int8_t tetris_bucket_getWidth(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nWidth; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_getHeight(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nHeight; |
|||
} |
|||
|
|||
|
|||
tetris_piece_t *tetris_bucket_getPiece(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->pPiece; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_getColumn(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nColumn; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_getRow(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nRow; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_getFirstMatterRow(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nFirstMatterRow; |
|||
} |
|||
|
|||
|
|||
uint8_t tetris_bucket_getRowMask(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->nRowMask; |
|||
} |
|||
|
|||
|
|||
tetris_bucket_status_t tetris_bucket_getStatus(tetris_bucket_t *pBucket) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
return pBucket->status; |
|||
} |
|||
|
|||
|
|||
uint16_t tetris_bucket_getDumpRow(tetris_bucket_t *pBucket, |
|||
int8_t nRow) |
|||
{ |
|||
assert(pBucket != NULL); |
|||
assert((0 <= nRow) && (nRow < pBucket->nHeight)); |
|||
return pBucket->dump[nRow]; |
|||
} |
|||
|
|||
|
|||
#ifdef GAME_BASTET |
|||
|
|||
int8_t tetris_bucket_predictDeepestRow(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nColumn) |
|||
{ |
|||
int8_t nRow = tetris_bucket_getPieceStartPos(pPiece); |
|||
tetris_piece_t *pActualPiece = pBucket->pPiece; |
|||
pBucket->pPiece = pPiece; |
|||
|
|||
// is it actually possible to use this piece?
|
|||
if (tetris_bucket_collision(pBucket, (pBucket->nWidth - 2) / 2, nRow) || |
|||
(tetris_bucket_collision(pBucket, nColumn, nRow))) |
|||
{ |
|||
// restore real piece
|
|||
pBucket->pPiece = pActualPiece; |
|||
|
|||
return -4; |
|||
} |
|||
|
|||
// determine deepest row
|
|||
nRow = (nRow < pBucket->nFirstMatterRow - 4) ? |
|||
pBucket->nFirstMatterRow - 4 : nRow; |
|||
while ((nRow < pBucket->nHeight) && |
|||
(!tetris_bucket_collision(pBucket, nColumn, nRow + 1))) |
|||
{ |
|||
++nRow; |
|||
} |
|||
|
|||
// restore real piece
|
|||
pBucket->pPiece = pActualPiece; |
|||
|
|||
return nRow; |
|||
} |
|||
|
|||
|
|||
int8_t tetris_bucket_predictCompleteLines(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nRow, |
|||
int8_t nColumn) |
|||
{ |
|||
int8_t nCompleteRows = 0; |
|||
|
|||
// bit mask of a full row
|
|||
uint16_t nFullRow = 0xFFFF >> (16 - pBucket->nWidth); |
|||
|
|||
if (nRow > -4) |
|||
{ |
|||
// determine sane start and stop values for the dump's index
|
|||
int8_t nStartRow = |
|||
((nRow + 3) >= pBucket->nHeight) ? pBucket->nHeight - 1 : nRow + 3; |
|||
int8_t nStopRow = (nRow < 0) ? 0 : nRow; |
|||
|
|||
uint16_t nPiece = tetris_piece_getBitmap(pPiece); |
|||
|
|||
for (int8_t i = nStartRow; i >= nStopRow; --i) |
|||
{ |
|||
int8_t y = i - nRow; |
|||
|
|||
// clear all bits of the piece we are not interested in and
|
|||
// align the rest to LSB
|
|||
uint16_t nPieceMap = (nPiece & (0x000F << (y << 2))) >> (y << 2); |
|||
// shift the remaining content to the current column
|
|||
if (nColumn >= 0) |
|||
{ |
|||
nPieceMap <<= nColumn; |
|||
} |
|||
else |
|||
{ |
|||
nPieceMap >>= -nColumn; |
|||
} |
|||
// embed piece in dump map
|
|||
uint16_t nDumpMap = pBucket->dump[i] | nPieceMap; |
|||
|
|||
// is current row a full row?
|
|||
if ((nFullRow & nDumpMap) == nFullRow) |
|||
{ |
|||
++nCompleteRows; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return nCompleteRows; |
|||
} |
|||
|
|||
|
|||
uint16_t* tetris_bucket_predictBottomRow(tetris_bucket_iterator_t *pIt, |
|||
tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nRow, |
|||
int8_t nColumn) |
|||
{ |
|||
pIt->pBucket = pBucket; |
|||
pIt->pPiece = pPiece; |
|||
pIt->nColumn = nColumn; |
|||
pIt->nFullRow = 0xFFFF >> (16 - pBucket->nWidth); |
|||
pIt->nCurrentRow = pBucket->nHeight - 1; |
|||
pIt->nRowBuffer = 0; |
|||
|
|||
// determine sane start and stop values for the piece's row indices
|
|||
pIt->nPieceHighestRow = nRow; |
|||
pIt->nPieceLowestRow = ((pIt->nPieceHighestRow + 3) < pBucket->nHeight) ? |
|||
(pIt->nPieceHighestRow + 3) : pBucket->nHeight - 1; |
|||
|
|||
// don't return any trailing rows which are empty, so we look for a stop row
|
|||
pIt->nStopRow = pBucket->nFirstMatterRow < nRow ? |
|||
pBucket->nFirstMatterRow : nRow; |
|||
pIt->nStopRow = pIt->nStopRow < 0 ? 0 : pIt->nStopRow; |
|||
|
|||
return tetris_bucket_predictNextRow(pIt); |
|||
} |
|||
|
|||
|
|||
uint16_t* tetris_bucket_predictNextRow(tetris_bucket_iterator_t *pIt) |
|||
{ |
|||
uint16_t nPieceMap = 0; |
|||
|
|||
if ((pIt->nPieceHighestRow > -4) && (pIt->nCurrentRow >= pIt->nStopRow)) |
|||
{ |
|||
uint16_t nPiece = tetris_piece_getBitmap(pIt->pPiece); |
|||
|
|||
if ((pIt->nCurrentRow <= pIt->nPieceLowestRow) && |
|||
(pIt->nCurrentRow >= pIt->nPieceHighestRow)) |
|||
{ |
|||
int8_t y = pIt->nCurrentRow - pIt->nPieceHighestRow; |
|||
|
|||
// clear all bits of the piece we are not interested in and
|
|||
// align the rest to LSB
|
|||
nPieceMap = (nPiece & (0x000F << (y << 2))) >> (y << 2); |
|||
// shift the remaining content to the current column
|
|||
if (pIt->nColumn >= 0) |
|||
{ |
|||
nPieceMap <<= pIt->nColumn; |
|||
} |
|||
else |
|||
{ |
|||
nPieceMap >>= -pIt->nColumn; |
|||
} |
|||
} |
|||
|
|||
pIt->nRowBuffer = pIt->pBucket->dump[pIt->nCurrentRow--] | nPieceMap; |
|||
// don't return full (and therefore removed) rows
|
|||
if (pIt->nRowBuffer == pIt->nFullRow) |
|||
{ |
|||
// recursively determine next (?) row instead
|
|||
return tetris_bucket_predictNextRow(pIt); |
|||
} |
|||
// row isn't full
|
|||
else |
|||
{ |
|||
return &pIt->nRowBuffer; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
#endif /* GAME_BASTET */ |
@ -0,0 +1,295 @@ |
|||
#ifndef BUCKET_H_ |
|||
#define BUCKET_H_ |
|||
|
|||
#include <inttypes.h> |
|||
#include "../../autoconf.h" |
|||
#include "piece.h" |
|||
|
|||
|
|||
/*********
|
|||
* types * |
|||
*********/ |
|||
|
|||
// directions to which a piece can be moved
|
|||
typedef enum tetris_bucket_direction_t |
|||
{ |
|||
TETRIS_BUD_LEFT, |
|||
TETRIS_BUD_RIGHT |
|||
} |
|||
tetris_bucket_direction_t; |
|||
|
|||
|
|||
// status of the bucket
|
|||
typedef enum tetris_bucket_status_t |
|||
{ |
|||
TETRIS_BUS_READY, /** ready to get next piece */ |
|||
TETRIS_BUS_HOVERING, /** piece is still hovering */ |
|||
TETRIS_BUS_GLIDING, /** piece is gliding on the dump */ |
|||
TETRIS_BUS_DOCKED, /** piece has been docked */ |
|||
TETRIS_BUS_GAMEOVER /** bucket is filled up */ |
|||
} |
|||
tetris_bucket_status_t; |
|||
|
|||
|
|||
// tetris_bucket_t
|
|||
typedef struct tetris_bucket_t |
|||
{ |
|||
int8_t nWidth; /** width of bucket */ |
|||
int8_t nHeight; /** height of bucket */ |
|||
tetris_piece_t *pPiece; /** currently falling piece */ |
|||
int8_t nColumn; /** horz. piece pos. (0 is left) */ |
|||
int8_t nRow; /** vert. piece pos. (0 is top) */ |
|||
uint8_t nRowMask; /** removed lines relative to nRow */ |
|||
tetris_bucket_status_t status; /** status of the bucket */ |
|||
int8_t nFirstMatterRow; /** top most row which has matter */ |
|||
uint16_t *dump; /** bucket itself */ |
|||
} |
|||
tetris_bucket_t; |
|||
|
|||
|
|||
// iterator for predicted dump rows
|
|||
typedef struct tetris_bucket_iterator_t |
|||
{ |
|||
tetris_bucket_t *pBucket; /** bucket to be examined */ |
|||
tetris_piece_t *pPiece; /** piece which should be tested */ |
|||
int8_t nColumn; /** column where piece should be dropped */ |
|||
uint16_t nFullRow; /** value of a full row */ |
|||
int8_t nCurrentRow; /** the actual row in the bucket */ |
|||
int8_t nPieceHighestRow; /** the highest row index of the piece */ |
|||
int8_t nPieceLowestRow; /** the lowest row index of the piece */ |
|||
int8_t nStopRow; /** the last row to be examined */ |
|||
uint16_t nRowBuffer; /** internal buffer for returned rows */ |
|||
} |
|||
tetris_bucket_iterator_t; |
|||
|
|||
|
|||
/****************************
|
|||
* construction/destruction * |
|||
****************************/ |
|||
|
|||
/**
|
|||
* constructs a bucket with the given dimensions |
|||
* @param nWidth width of bucket (4 <= n <= 16) |
|||
* @param nHeight height of bucket (4 <= n <= 124) |
|||
* @return pointer to a newly created bucket |
|||
*/ |
|||
tetris_bucket_t *tetris_bucket_construct(int8_t nWidth, |
|||
int8_t nHeight); |
|||
|
|||
|
|||
/**
|
|||
* destructs a bucket |
|||
* @param pBucket pointer to the bucket to be destructed |
|||
*/ |
|||
void tetris_bucket_destruct(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/*******************************
|
|||
* bucket related functions * |
|||
*******************************/ |
|||
|
|||
/**
|
|||
* calculates number of lines for the given row mask |
|||
* @param nRowMask row mask from which the no. of lines will be calculated |
|||
* @return number of lines of the row mask |
|||
*/ |
|||
uint8_t tetris_bucket_calculateLines(uint8_t nRowMask); |
|||
|
|||
|
|||
/**
|
|||
* resets bucket to begin a new game |
|||
* @param pBucket bucket to perform action on |
|||
*/ |
|||
void tetris_bucket_reset(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* inserts a new piece |
|||
* @param pBucket bucket to perform action on |
|||
* @param pPiece piece to be inserted |
|||
* @param ppOldPiece [out] indirect pointer to former piece for deallocation |
|||
*/ |
|||
void tetris_bucket_insertPiece(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
tetris_piece_t** ppOldPiece); |
|||
|
|||
|
|||
/**
|
|||
* detects if piece collides with s.th. at a given position |
|||
* @param pBucket bucket to perform action on |
|||
* @param nColumn column where the piece should be moved |
|||
* @param nRow row where the piece should be moved |
|||
* @return 1 for collision, 0 otherwise |
|||
*/ |
|||
uint8_t tetris_bucket_collision(tetris_bucket_t *pBucket, |
|||
int8_t nColumn, |
|||
int8_t nRow); |
|||
|
|||
|
|||
/**
|
|||
* lowers piece by one row or finally docks it |
|||
* @param pBucket bucket to perform action on |
|||
*/ |
|||
void tetris_bucket_advancePiece(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* moves piece to the given direction |
|||
* @param pBucket bucket to perform action on |
|||
* @param direction direction (see tetris_bucket_direction_t) |
|||
* @return 1 if piece could be moved, 0 otherwise |
|||
*/ |
|||
uint8_t |
|||
tetris_bucket_movePiece(tetris_bucket_t *pBucket, |
|||
tetris_bucket_direction_t direction); |
|||
|
|||
|
|||
/**
|
|||
* rotates piece to the given direction |
|||
* @param pBucket bucket to perform action on |
|||
* @param r type of rotation (see tetris_piece_rotation_t) |
|||
* @return 1 if piece could be rotated, 0 otherwise |
|||
*/ |
|||
uint8_t tetris_bucket_rotatePiece(tetris_bucket_t *pBucket, |
|||
tetris_piece_rotation_t rotation); |
|||
|
|||
|
|||
/**
|
|||
* removes completed lines (if any) and lowers the dump |
|||
* @param pBucket bucket to perform action on |
|||
*/ |
|||
void tetris_bucket_removeCompleteLines(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/*****************
|
|||
* get functions * |
|||
*****************/ |
|||
|
|||
/**
|
|||
* returns the width of the bucket |
|||
* @param pBucket the bucket we want information from |
|||
* @return width of the bucket |
|||
*/ |
|||
int8_t tetris_bucket_getWidth(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the height of the bucket |
|||
* @param pBucket the bucket we want information from |
|||
* @return height of the bucket |
|||
*/ |
|||
int8_t tetris_bucket_getHeight(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the currently falling piece |
|||
* @param pBucket the bucket we want information from |
|||
* @return pointer to the currently falling piece |
|||
*/ |
|||
tetris_piece_t *tetris_bucket_getPiece(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the column of the currently falling piece |
|||
* @param pBucket the bucket we want information from |
|||
* @return column of the currently falling piece |
|||
*/ |
|||
int8_t tetris_bucket_getColumn(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the row of the currently falling piece |
|||
* @param pBucket the bucket we want information from |
|||
* @return row of the currently falling piece |
|||
*/ |
|||
int8_t tetris_bucket_getRow(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the row of the currently falling piece |
|||
* @param pBucket the bucket we want information from |
|||
* @return highest row with matter |
|||
*/ |
|||
int8_t tetris_bucket_getFirstMatterRow(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the row mask relative to nRow |
|||
* @param pBucket the bucket we want information from |
|||
* @return bit mask of removed lines (relative to current position) |
|||
*/ |
|||
uint8_t tetris_bucket_getRowMask(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the status of the bucket |
|||
* @param pBucket the bucket we want information from |
|||
* @return status of the bucket (see tetris_bucket_status_t) |
|||
*/ |
|||
tetris_bucket_status_t tetris_bucket_getStatus(tetris_bucket_t *pBucket); |
|||
|
|||
|
|||
/**
|
|||
* returns the given row of the dump (as bitmap) |
|||
* @param pBucket the bucket we want information from |
|||
* @param nRow the number of the row (0 <= nRow <= 124) |
|||
* @return bitmap of the requested row (LSB is leftmost column) |
|||
*/ |
|||
uint16_t tetris_bucket_getDumpRow(tetris_bucket_t *pBucket, |
|||
int8_t nRow); |
|||
|
|||
|
|||
#ifdef GAME_BASTET |
|||
|
|||
/**
|
|||
* returns the deepest possible row for a given piece |
|||
* @param pBucket the bucket on which we want to test a piece |
|||
* @param pPiece the piece which should be tested |
|||
* @param nColumn the column where the piece should be dropped |
|||
* @return the row of the piece (bucket compliant coordinates) |
|||
*/ |
|||
int8_t tetris_bucket_predictDeepestRow(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nColumn); |
|||
|
|||
|
|||
/**
|
|||
* predicts the number of complete lines for a piece at a given column |
|||
* @param pBucket the bucket on which we want to test a piece |
|||
* @param pPiece the piece which should be tested |
|||
* @param nRow the row where the given piece collides |
|||
* @param nColumn the column where the piece should be dropped |
|||
* @return amount of complete lines |
|||
*/ |
|||
int8_t tetris_bucket_predictCompleteLines(tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nRow, |
|||
int8_t nColumn); |
|||
|
|||
|
|||
/**
|
|||
* predicts appearance of the bottom row and initializes an iterator structure |
|||
* @param pIt a pointer to an iterator which should be initialized |
|||
* @param pBucket the bucket on which we want to test a piece |
|||
* @param pPiece the piece which should be tested |
|||
* @param nRow the row where the given piece collides |
|||
* @param nColumn the column where the piece should be dropped |
|||
* @return appearance of the bottom row of the predicted dump (bit mask) |
|||
*/ |
|||
uint16_t *tetris_bucket_predictBottomRow(tetris_bucket_iterator_t *pIt, |
|||
tetris_bucket_t *pBucket, |
|||
tetris_piece_t *pPiece, |
|||
int8_t nRow, |
|||
int8_t nColumn); |
|||
|
|||
|
|||
/**
|
|||
* predicts appearance of the next row (via iterator) of the bucket |
|||
* @param pIt a pointer to a dump iterator |
|||
* @return appearance of next predicted row (or NULL -> no next line) |
|||
*/ |
|||
uint16_t *tetris_bucket_predictNextRow(tetris_bucket_iterator_t *pIt); |
|||
|
|||
#endif /* GAME_BASTET */ |
|||
|
|||
#endif /*BUCKET_H_*/ |
@ -1,163 +1,162 @@ |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
#include <inttypes.h> |
|||
#include "highscore.h" |
|||
#include "../../config.h" |
|||
#include "../../scrolltext/scrolltext.h" |
|||
#include "../../joystick/joystick.h" |
|||
#include "../../compat/eeprom.h" |
|||
|
|||
// global array for the highscores
|
|||
uint16_t tetris_highscore[TETRIS_HISCORE_END] EEMEM; |
|||
|
|||
// global array for the champions' initials
|
|||
uint16_t tetris_highscore_name[TETRIS_HISCORE_END] EEMEM; |
|||
|
|||
|
|||
uint16_t tetris_highscore_inputName(void) |
|||
{ |
|||
#ifdef SCROLLTEXT_SUPPORT |
|||
char pszNick[4], pszTmp[40]; |
|||
uint8_t nOffset; |
|||
uint8_t nPos = 0, nBlink = 0, nDone = 0, nHadfire = 0; |
|||
|
|||
sprintf(pszNick, "AAA"); |
|||
while (!nDone) |
|||
{ |
|||
// we need our own blink interval
|
|||
nBlink = (nBlink + 1) % 4; |
|||
|
|||
// determine start position on screen depending on active character
|
|||
switch (nPos) |
|||
{ |
|||
case 0: |
|||
nOffset = 15; |
|||
break; |
|||
case 1: |
|||
nOffset = 19; |
|||
break; |
|||
default: |
|||
nOffset = 23; |
|||
break; |
|||
} |
|||
|
|||
// construct command for scrolltext and execute
|
|||
sprintf(pszTmp, "x%d+p1#%c#x%d+p1#%c#x%dp1#%c", nOffset, |
|||
(!nBlink && nPos == 0) ? ' ' : pszNick[0], nOffset - 8, |
|||
(!nBlink && nPos == 1) ? ' ' : pszNick[1], nOffset - 15, |
|||
(!nBlink && nPos == 2) ? ' ' : pszNick[2]); |
|||
scrolltext(pszTmp); |
|||
|
|||
// up and down control current char
|
|||
if (JOYISUP) |
|||
{ |
|||
pszNick[nPos]++; |
|||
if (pszNick[nPos] == '`') |
|||
{ |
|||
pszNick[nPos] = 'A'; |
|||
} |
|||
if (pszNick[nPos] == '[') |
|||
{ |
|||
pszNick[nPos] = '_'; |
|||
} |
|||
} |
|||
else if (JOYISDOWN) |
|||
{ |
|||
pszNick[nPos]--; |
|||
if (pszNick[nPos] == '@') |
|||
{ |
|||
pszNick[nPos] = '_'; |
|||
} |
|||
if (pszNick[nPos] == '^') |
|||
{ |
|||
pszNick[nPos] = 'Z'; |
|||
} |
|||
} |
|||
// left and right control char selections
|
|||
else if (JOYISLEFT && nPos > 0) |
|||
{ |
|||
nPos--; |
|||
} |
|||
else if (JOYISRIGHT && nPos < 2) |
|||
{ |
|||
nPos++; |
|||
} |
|||
|
|||
// fire switches to next char or exits
|
|||
if (JOYISFIRE && !nHadfire) |
|||
{ |
|||
nHadfire = 1; |
|||
switch (nPos) |
|||
{ |
|||
case 0: |
|||
nPos = 1; |
|||
break; |
|||
case 1: |
|||
nPos = 2; |
|||
break; |
|||
case 2: |
|||
nDone = 1; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (nHadfire && !JOYISFIRE) |
|||
{ |
|||
nHadfire = 0; |
|||
} |
|||
} |
|||
|
|||
// return result
|
|||
return (pszNick[0] - 65) << 10 | (pszNick[1] - 65) << 5 | (pszNick[2] - 65); |
|||
#else |
|||
return (0); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex) |
|||
{ |
|||
uint16_t nHighscore = 0; |
|||
nHighscore = eeprom_read_word(&tetris_highscore[nIndex]); |
|||
|
|||
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
|
|||
if (nHighscore == 65535) |
|||
{ |
|||
nHighscore = 0; |
|||
} |
|||
|
|||
return nHighscore; |
|||
} |
|||
|
|||
|
|||
void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscore) |
|||
{ |
|||
if (nHighscore > tetris_highscore_retrieveHighscore(nIndex)) |
|||
{ |
|||
eeprom_write_word(&tetris_highscore[nIndex], nHighscore); |
|||
} |
|||
} |
|||
|
|||
|
|||
uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx) |
|||
{ |
|||
uint16_t nHighscoreName = 0; |
|||
nHighscoreName = eeprom_read_word(&tetris_highscore_name[nIdx]); |
|||
|
|||
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
|
|||
if (nHighscoreName == 65535) |
|||
{ |
|||
nHighscoreName = 0; |
|||
} |
|||
|
|||
return nHighscoreName; |
|||
} |
|||
|
|||
|
|||
void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscoreName) |
|||
{ |
|||
eeprom_write_word(&tetris_highscore_name[nIndex], nHighscoreName); |
|||
} |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
#include <inttypes.h> |
|||
#include "highscore.h" |
|||
#include "../../config.h" |
|||
#include "../../scrolltext/scrolltext.h" |
|||
#include "../../joystick/joystick.h" |
|||
#include "../../compat/eeprom.h" |
|||
|
|||
// global array for the high score
|
|||
uint16_t tetris_highscore[TETRIS_HISCORE_END] EEMEM; |
|||
|
|||
// global array for the champion's initials
|
|||
uint16_t tetris_highscore_name[TETRIS_HISCORE_END] EEMEM; |
|||
|
|||
|
|||
uint16_t tetris_highscore_inputName(void) |
|||
{ |
|||
#ifdef SCROLLTEXT_SUPPORT |
|||
char pszNick[4], pszTmp[26]; |
|||
unsigned int nOffset; |
|||
uint8_t nPos = 0, nBlink = 0, nDone = 0, nHadfire = 0; |
|||
|
|||
strncpy(pszNick, "AAA", sizeof(pszNick)); |
|||
while (!nDone) |
|||
{ |
|||
// we need our own blink interval
|
|||
nBlink = (nBlink + 1) % 4; |
|||
|
|||
// determine start position on screen depending on active character
|
|||
switch (nPos) |
|||
{ |
|||
case 0: |
|||
nOffset = 15; |
|||
break; |
|||
case 1: |
|||
nOffset = 19; |
|||
break; |
|||
default: |
|||
nOffset = 23; |
|||
break; |
|||
} |
|||
|
|||
// construct command for scrolltext and execute
|
|||
snprintf(pszTmp, sizeof(pszTmp), "x%u+p1#%c#x%u+p1#%c#x%up1#%c", |
|||
nOffset , (!nBlink && nPos == 0) ? ' ' : pszNick[0], |
|||
nOffset - 8, (!nBlink && nPos == 1) ? ' ' : pszNick[1], |
|||
nOffset - 15, (!nBlink && nPos == 2) ? ' ' : pszNick[2]); |
|||
scrolltext(pszTmp); |
|||
|
|||
// up and down control current char
|
|||
if (JOYISUP) |
|||
{ |
|||
pszNick[nPos]++; |
|||
if (pszNick[nPos] == '`') |
|||
{ |
|||
pszNick[nPos] = 'A'; |
|||
} |
|||
else if (pszNick[nPos] == '[') |
|||
{ |
|||
pszNick[nPos] = '_'; |
|||
} |
|||
} |
|||
else if (JOYISDOWN) |
|||
{ |
|||
pszNick[nPos]--; |
|||
if (pszNick[nPos] == '@') |
|||
{ |
|||
pszNick[nPos] = '_'; |
|||
} |
|||
else if (pszNick[nPos] == '^') |
|||
{ |
|||
pszNick[nPos] = 'Z'; |
|||
} |
|||
} |
|||
// left and right control char selections
|
|||
else if (JOYISLEFT && nPos > 0) |
|||
{ |
|||
nPos--; |
|||
} |
|||
else if (JOYISRIGHT && nPos < 2) |
|||
{ |
|||
nPos++; |
|||
} |
|||
|
|||
// fire switches to next char or exits
|
|||
if (JOYISFIRE && !nHadfire) |
|||
{ |
|||
nHadfire = 1; |
|||
switch (nPos) |
|||
{ |
|||
case 0: |
|||
nPos = 1; |
|||
break; |
|||
case 1: |
|||
nPos = 2; |
|||
break; |
|||
case 2: |
|||
nDone = 1; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (nHadfire && !JOYISFIRE) |
|||
{ |
|||
nHadfire = 0; |
|||
} |
|||
} |
|||
|
|||
// return result
|
|||
return (pszNick[0] - 65) << 10 | (pszNick[1] - 65) << 5 | (pszNick[2] - 65); |
|||
#else |
|||
return (0); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex) |
|||
{ |
|||
uint16_t nHighscore = 0; |
|||
nHighscore = eeprom_read_word(&tetris_highscore[nIndex]); |
|||
|
|||
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
|
|||
if (nHighscore == 65535) |
|||
{ |
|||
nHighscore = 0; |
|||
} |
|||
|
|||
return nHighscore; |
|||
} |
|||
|
|||
|
|||
void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscore) |
|||
{ |
|||
if (nHighscore > tetris_highscore_retrieveHighscore(nIndex)) |
|||
{ |
|||
eeprom_write_word(&tetris_highscore[nIndex], nHighscore); |
|||
} |
|||
} |
|||
|
|||
|
|||
uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx) |
|||
{ |
|||
uint16_t nHighscoreName = 0; |
|||
nHighscoreName = eeprom_read_word(&tetris_highscore_name[nIdx]); |
|||
|
|||
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
|
|||
if (nHighscoreName == 65535) |
|||
{ |
|||
nHighscoreName = 0; |
|||
} |
|||
|
|||
return nHighscoreName; |
|||
} |
|||
|
|||
|
|||
void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscoreName) |
|||
{ |
|||
eeprom_write_word(&tetris_highscore_name[nIndex], nHighscoreName); |
|||
} |
|||
|
@ -1,57 +1,57 @@ |
|||
#ifndef TETRIS_HIGHSCORE_H_ |
|||
#define TETRIS_HIGHSCORE_H_ |
|||
|
|||
/**
|
|||
* indexes for different tetris variants |
|||
*/ |
|||
typedef enum tetris_highscore_index_t |
|||
{ |
|||
TETRIS_HISCORE_TETRIS, /**< highscore index for the standard variant */ |
|||
TETRIS_HISCORE_BASTET, /**< highscore index for the bastet variant */ |
|||
TETRIS_HISCORE_FP, /**< highscore index for the first person variant */ |
|||
TETRIS_HISCORE_PAD, /**< don't use (padding for an even array boundary)*/ |
|||
TETRIS_HISCORE_END /**< boundary for the highscore array */ |
|||
} tetris_highscore_index_t; |
|||
|
|||
|
|||
/**
|
|||
* let user input a three character name |
|||
* @return name packed into a uint16_t |
|||
*/ |
|||
uint16_t tetris_highscore_inputName(void); |
|||
|
|||
|
|||
/**
|
|||
* retrieves the highscore from storage |
|||
* @param nIndex the variant dependent index of the highscore |
|||
* @return the highscore |
|||
*/ |
|||
uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex); |
|||
|
|||
|
|||
/**
|
|||
* saves the highscore into the storage |
|||
* @param nIndex the variant dependent index of the highscore |
|||
* @param nHighscoreName the highscore |
|||
*/ |
|||
void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscore); |
|||
|
|||
|
|||
/**
|
|||
* retrieves the initials of the champion from storage |
|||
* @param nIdx the variant dependent index of the highscore |
|||
* @return the initials of the champion packed into a uint16_t |
|||
*/ |
|||
uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx); |
|||
|
|||
|
|||
/**
|
|||
* saves the initials of the champion |
|||
* @param nIndex the variant dependent index of the highscore |
|||
* @param nHighscoreName the initials of the champion packed into a uint16_t |
|||
*/ |
|||
void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscoreName); |
|||
|
|||
#endif /*TETRIS_HIGHSCORE_H_*/ |
|||
#ifndef TETRIS_HIGHSCORE_H_ |
|||
#define TETRIS_HIGHSCORE_H_ |
|||
|
|||
/**
|
|||
* indexes for different tetris variants |
|||
*/ |
|||
typedef enum tetris_highscore_index_t |
|||
{ |
|||
TETRIS_HISCORE_TETRIS, /**< high score index for the standard variant */ |
|||
TETRIS_HISCORE_BASTET, /**< high score index for the bastet variant */ |
|||
TETRIS_HISCORE_FP, /**< high score index for the first person variant */ |
|||
TETRIS_HISCORE_PAD, /**< don't use (padding for an even array boundary)*/ |
|||
TETRIS_HISCORE_END /**< boundary for the high score array */ |
|||
} tetris_highscore_index_t; |
|||
|
|||
|
|||
/**
|
|||
* lets the user enter his initials (three characters) |
|||
* @return name packed into a uint16_t |
|||
*/ |
|||
uint16_t tetris_highscore_inputName(void); |
|||
|
|||
|
|||
/**
|
|||
* retrieves the high score from storage (EEPROM) |
|||
* @param nIndex the variant dependent index of the high score |
|||
* @return the high score |
|||
*/ |
|||
uint16_t tetris_highscore_retrieveHighscore(tetris_highscore_index_t nIndex); |
|||
|
|||
|
|||
/**
|
|||
* saves the high score into the storage (EEPROM) |
|||
* @param nIndex the variant dependent index of the high score |
|||
* @param nHighscoreName the high score |
|||
*/ |
|||
void tetris_highscore_saveHighscore(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscore); |
|||
|
|||
|
|||
/**
|
|||
* retrieves the champion's initials from storage (EEPROM) |
|||
* @param nIdx the variant dependent index of the high score |
|||
* @return the initials of the champion packed into a uint16_t |
|||
*/ |
|||
uint16_t tetris_highscore_retrieveHighscoreName(tetris_highscore_index_t nIdx); |
|||
|
|||
|
|||
/**
|
|||
* saves the champion's initials into the storage (EEPROM) |
|||
* @param nIndex the variant dependent index of the high score |
|||
* @param nHighscoreName the initials of the champion packed into a uint16_t |
|||
*/ |
|||
void tetris_highscore_saveHighscoreName(tetris_highscore_index_t nIndex, |
|||
uint16_t nHighscoreName); |
|||
|
|||
#endif /*TETRIS_HIGHSCORE_H_*/ |
|||
|
Loading…
Reference in new issue