Christian Kroll
15 years ago
4 changed files with 9 additions and 1040 deletions
@ -1,19 +1,12 @@ |
|||||
mainmenu_option next_comment |
mainmenu_option next_comment |
||||
comment "Games" |
comment "Games" |
||||
|
dep_bool_menu "Tetris" GAME_TETRIS_CORE y $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
||||
mainmenu_option next_comment |
|
||||
dep_bool_menu "Tetris" GAME_TETRIS_CORE y $JOYSTICK_SUPPORT |
|
||||
if [ "$GAME_TETRIS_CORE" = "y" ]; then |
|
||||
GAME_TETRIS=y |
|
||||
dep_bool "Standard Tetris" GAME_TETRIS $GAME_TETRIS_CORE |
dep_bool "Standard Tetris" GAME_TETRIS $GAME_TETRIS_CORE |
||||
dep_bool "Bastard Tetris" GAME_BASTET $GAME_TETRIS_CORE |
dep_bool "Bastard Tetris" GAME_BASTET $GAME_TETRIS_CORE |
||||
dep_bool "First Person Tetris" GAME_TETRIS_FP $GAME_TETRIS_CORE |
dep_bool "First Person Tetris" GAME_TETRIS_FP $GAME_TETRIS_CORE |
||||
fi |
|
||||
endmenu |
endmenu |
||||
|
|
||||
dep_bool "space invaders" GAME_SPACE_INVADERS $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
dep_bool "Space Invaders" GAME_SPACE_INVADERS $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
||||
dep_bool "snake" GAME_SNAKE $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
dep_bool "Snake" GAME_SNAKE $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
||||
dep_bool "breakout" GAME_BREAKOUT $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
dep_bool "Breakout" GAME_BREAKOUT $JOYSTICK_SUPPORT $RANDOM_SUPPORT |
||||
|
|
||||
|
|
||||
endmenu |
endmenu |
||||
|
@ -1,13 +0,0 @@ |
|||||
#ifndef ORIENTATION_H_ |
|
||||
#define ORIENTATION_H_ |
|
||||
|
|
||||
typedef enum tetris_orientation_t |
|
||||
{ |
|
||||
TETRIS_ORIENTATION_0, |
|
||||
TETRIS_ORIENTATION_90, |
|
||||
TETRIS_ORIENTATION_180, |
|
||||
TETRIS_ORIENTATION_270 |
|
||||
} |
|
||||
tetris_orientation_t; |
|
||||
|
|
||||
#endif /* ORIENTATION_H_ */ |
|
@ -1,718 +0,0 @@ |
|||||
#include <stdlib.h> |
|
||||
#include <string.h> |
|
||||
#include <assert.h> |
|
||||
#include <inttypes.h> |
|
||||
#include "../../autoconf.h" |
|
||||
#include "playfield.h" |
|
||||
#include "piece.h" |
|
||||
|
|
||||
|
|
||||
/***************************
|
|
||||
* non-interface functions * |
|
||||
***************************/ |
|
||||
|
|
||||
/**
|
|
||||
* determines if piece is either hovering or gliding |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @eturn TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING |
|
||||
*/ |
|
||||
tetris_playfield_status_t tetris_playfield_hoverStatus(tetris_playfield_t* pPl) |
|
||||
{ |
|
||||
// if the piece touches the dump we ensure that the status is "gliding"
|
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1)) |
|
||||
{ |
|
||||
return TETRIS_PFS_GLIDING; |
|
||||
} |
|
||||
// otherwise the status must be "hovering"
|
|
||||
else |
|
||||
{ |
|
||||
return TETRIS_PFS_HOVERING; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/****************************
|
|
||||
* construction/destruction * |
|
||||
****************************/ |
|
||||
|
|
||||
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, |
|
||||
int8_t nHeight) |
|
||||
{ |
|
||||
assert((nWidth >= 4) && (nWidth <= 16)); |
|
||||
assert((nHeight >= 4) && (nHeight <= 124)); |
|
||||
|
|
||||
tetris_playfield_t *pPlayfield = |
|
||||
(tetris_playfield_t*) malloc(sizeof(tetris_playfield_t)); |
|
||||
|
|
||||
if (pPlayfield != NULL) |
|
||||
{ |
|
||||
// allocating mem for dump array
|
|
||||
pPlayfield->dump = (uint16_t*) calloc(nHeight, sizeof(uint16_t)); |
|
||||
|
|
||||
if (pPlayfield->dump != NULL) |
|
||||
{ |
|
||||
// setting desired attributes
|
|
||||
pPlayfield->nFirstMatterRow = nHeight - 1; |
|
||||
pPlayfield->nWidth = nWidth; |
|
||||
pPlayfield->nHeight = nHeight; |
|
||||
tetris_playfield_reset(pPlayfield); |
|
||||
|
|
||||
return pPlayfield; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
free(pPlayfield); |
|
||||
pPlayfield = NULL; |
|
||||
} |
|
||||
} |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void tetris_playfield_destruct(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// if memory for the dump array has been allocated, free it
|
|
||||
if (pPl->dump != NULL) |
|
||||
{ |
|
||||
free(pPl->dump); |
|
||||
} |
|
||||
free(pPl); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/*******************************
|
|
||||
* playfield related functions * |
|
||||
*******************************/ |
|
||||
|
|
||||
uint8_t tetris_playfield_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_playfield_reset(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
pPl->pPiece = NULL; |
|
||||
pPl->nColumn = 0; |
|
||||
pPl->nRow = 0; |
|
||||
pPl->nRowMask = 0; |
|
||||
|
|
||||
// clear dump if it has been allocated in memory
|
|
||||
if (pPl->dump != NULL) |
|
||||
{ |
|
||||
memset(pPl->dump, 0, pPl->nHeight); |
|
||||
} |
|
||||
|
|
||||
pPl->status = TETRIS_PFS_READY; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int8_t tetris_playfield_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_playfield_insertPiece(tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
tetris_piece_t** ppOldPiece) |
|
||||
{ |
|
||||
assert((pPl != NULL) && (pPiece != NULL) && (ppOldPiece != NULL)); |
|
||||
|
|
||||
// a piece can only be inserted in state TETRIS_PFS_READY
|
|
||||
assert(pPl->status == TETRIS_PFS_READY); |
|
||||
|
|
||||
// row mask is now meaningless
|
|
||||
pPl->nRowMask = 0; |
|
||||
|
|
||||
// replace old piece
|
|
||||
*ppOldPiece = pPl->pPiece; |
|
||||
pPl->pPiece = pPiece; |
|
||||
|
|
||||
// set horizontal start position (in the middle of the top line)
|
|
||||
pPl->nColumn = (pPl->nWidth - 2) / 2; |
|
||||
|
|
||||
// set vertical start position (first piece row with matter at pos. 1)
|
|
||||
pPl->nRow = tetris_playfield_getPieceStartPos(pPl->pPiece); |
|
||||
|
|
||||
// did we already collide with something?
|
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) == 1) |
|
||||
{ |
|
||||
// game over man, game over!!
|
|
||||
pPl->status = TETRIS_PFS_GAMEOVER; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// bring it on!
|
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint8_t tetris_playfield_collision(tetris_playfield_t *pPl, |
|
||||
int8_t nColumn, |
|
||||
int8_t nRow) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// only allow coordinates which are within sane ranges
|
|
||||
assert((nColumn > -4) && (nColumn < pPl->nWidth)); |
|
||||
assert((nRow > -4) && (nRow < pPl->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 playfield 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 playfield 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(pPl->pPiece); |
|
||||
uint16_t nPlayfieldPart; |
|
||||
uint16_t nPieceRowMap; |
|
||||
|
|
||||
// negative nRow values indicate that the piece hasn't fully entered the
|
|
||||
// playfield 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) >= pPl->nWidth) |
|
||||
{ |
|
||||
nBorderMask = 0x8888 >> ((nColumn + 3) - pPl->nWidth); |
|
||||
} |
|
||||
// return if piece collides with border
|
|
||||
if ((nPieceMap & nBorderMask) != 0) |
|
||||
{ |
|
||||
return 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// here we check the part which has already entered the playfield
|
|
||||
for (int8_t y = (nRow < 0) ? -nRow : 0; y < 4; ++y) |
|
||||
{ |
|
||||
// current piece row overlaps with lower border
|
|
||||
if ((y + nRow) >= pPl->nHeight) |
|
||||
{ |
|
||||
// all 4 bits represent the lower border
|
|
||||
nPlayfieldPart = 0x000F; |
|
||||
} |
|
||||
// piece overlaps with left border
|
|
||||
else if (nColumn < 0) |
|
||||
{ |
|
||||
// clear all bits we are not interested in
|
|
||||
nPlayfieldPart = (pPl->dump[y + nRow] & (0x000F >> -nColumn)); |
|
||||
// add zeros to the left (the bits "behind" the left border)
|
|
||||
nPlayfieldPart <<= -nColumn; |
|
||||
// set bits beyond left border to 1
|
|
||||
nPlayfieldPart |= 0x000F >> (4 + nColumn); |
|
||||
} |
|
||||
// piece overlaps with right border
|
|
||||
else if ((nColumn + 3) >= pPl->nWidth) |
|
||||
{ |
|
||||
// align the bits we are interested in to LSB
|
|
||||
// (thereby clearing the rest)
|
|
||||
nPlayfieldPart = pPl->dump[y + nRow] >> nColumn; |
|
||||
// set bits beyond right border to 1
|
|
||||
nPlayfieldPart |= 0xFFF8 >> (nColumn + 3 - pPl->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
|
|
||||
nPlayfieldPart = |
|
||||
(pPl->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 ((nPlayfieldPart & nPieceRowMap) != 0) |
|
||||
{ |
|
||||
return 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// if we reach here, no collision was detected
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void tetris_playfield_advancePiece(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// a piece can only be lowered if it is hovering or gliding
|
|
||||
assert ((pPl->status == TETRIS_PFS_HOVERING) || |
|
||||
(pPl->status == TETRIS_PFS_GLIDING)); |
|
||||
|
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1)) |
|
||||
{ |
|
||||
uint16_t nPiece = tetris_piece_getBitmap(pPl->pPiece); |
|
||||
|
|
||||
// Is the playfield filled up?
|
|
||||
if ((pPl->nRow < 0) && (nPiece & (0x0FFF >> ((3 + pPl->nRow) << 2))) != 0) |
|
||||
{ |
|
||||
pPl->status = TETRIS_PFS_GAMEOVER; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// determine valid start point for dump index
|
|
||||
int8_t nStartRow = ((pPl->nRow + 3) < pPl->nHeight) ? |
|
||||
(pPl->nRow + 3) : pPl->nHeight - 1; |
|
||||
for (int8_t i = nStartRow; i >= pPl->nRow; --i) |
|
||||
{ |
|
||||
int8_t y = i - pPl->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 (pPl->nColumn >= 0) |
|
||||
{ |
|
||||
nPieceMap <<= pPl->nColumn; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
nPieceMap >>= -pPl->nColumn; |
|
||||
} |
|
||||
// embed piece in playfield
|
|
||||
pPl->dump[i] |= nPieceMap; |
|
||||
} |
|
||||
|
|
||||
// update value for the highest row with matter
|
|
||||
int8_t nPieceRow = pPl->nRow; |
|
||||
uint16_t nMask = 0x000F; |
|
||||
for (int i = 0; i < 4; ++i, nMask <<= 4) |
|
||||
{ |
|
||||
if ((nMask & nPiece) != 0) |
|
||||
{ |
|
||||
nPieceRow += i; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
pPl->nFirstMatterRow = (pPl->nFirstMatterRow > nPieceRow) ? |
|
||||
nPieceRow : pPl->nFirstMatterRow; |
|
||||
|
|
||||
// the piece has finally been docked
|
|
||||
pPl->status = TETRIS_PFS_DOCKED; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// since there is no collision the piece may continue its travel
|
|
||||
// to the ground...
|
|
||||
pPl->nRow++; |
|
||||
|
|
||||
// are we gliding?
|
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl, |
|
||||
tetris_playfield_direction_t direction) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// a piece can only be moved if it is still hovering or gliding
|
|
||||
assert((pPl->status == TETRIS_PFS_HOVERING) || |
|
||||
(pPl->status == TETRIS_PFS_GLIDING)); |
|
||||
|
|
||||
int8_t nOffset = (direction == TETRIS_PFD_LEFT) ? -1 : 1; |
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn + nOffset, pPl->nRow) == 0) |
|
||||
{ |
|
||||
pPl->nColumn += nOffset; |
|
||||
|
|
||||
// are we gliding?
|
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl); |
|
||||
return 1; |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl, |
|
||||
tetris_piece_rotation_t rotation) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// a piece can only be rotation if it is still hovering or gliding
|
|
||||
assert((pPl->status == TETRIS_PFS_HOVERING) || |
|
||||
(pPl->status == TETRIS_PFS_GLIDING)); |
|
||||
|
|
||||
tetris_piece_rotate(pPl->pPiece, rotation); |
|
||||
|
|
||||
// does the rotated piece cause a collision?
|
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) != 0) |
|
||||
{ |
|
||||
// in that case we revert the rotation
|
|
||||
if (rotation == TETRIS_PC_ROT_CW) |
|
||||
{ |
|
||||
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CCW); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CW); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
// are we gliding?
|
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl); |
|
||||
|
|
||||
return 1; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
|
|
||||
// rows can only be removed if we are in state TETRIS_PFS_DOCKED
|
|
||||
assert(pPl->status == TETRIS_PFS_DOCKED); |
|
||||
|
|
||||
// bit mask of a full row
|
|
||||
uint16_t nFullRow = 0xFFFF >> (16 - pPl->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 = |
|
||||
((pPl->nRow + 3) >= pPl->nHeight) ? pPl->nHeight - 1 : pPl->nRow + 3; |
|
||||
int8_t nStopRow = (pPl->nRow < 0) ? 0 : pPl->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 = pPl->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 & pPl->dump[i]) == nFullRow) |
|
||||
{ |
|
||||
// adjust value for the highest row with matter
|
|
||||
pPl->nFirstMatterRow++; |
|
||||
|
|
||||
// set corresponding bit for the row mask
|
|
||||
// nRowMask |= 0x08 >> (nStartRow - i);
|
|
||||
nRowMask |= 0x01 << (i - pPl->nRow); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// if nLowestRow and i differ, the dump has to be shifted
|
|
||||
if (i < nLowestRow) |
|
||||
{ |
|
||||
pPl->dump[nLowestRow] = pPl->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
|
|
||||
pPl->dump[nLowestRow] = pPl->dump[i]; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// rows above the upper border are always empty
|
|
||||
pPl->dump[nLowestRow] = 0; |
|
||||
} |
|
||||
--nLowestRow; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// ready to get the next piece
|
|
||||
pPl->status = TETRIS_PFS_READY; |
|
||||
|
|
||||
pPl->nRowMask = nRowMask; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/*****************
|
|
||||
* get functions * |
|
||||
*****************/ |
|
||||
|
|
||||
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nWidth; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/* Function: tetris_playfield_getHeight
|
|
||||
* Description: returns the height of the ayfield we want information from |
|
||||
* Return value: height of the playfield |
|
||||
*/ |
|
||||
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nHeight; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->pPiece; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nColumn; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nRow; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int8_t tetris_playfield_getFirstMatterRow(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nFirstMatterRow; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->nRowMask; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
return pPl->status; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl, |
|
||||
int8_t nRow) |
|
||||
{ |
|
||||
assert(pPl != NULL); |
|
||||
assert((0 <= nRow) && (nRow < pPl->nHeight)); |
|
||||
return pPl->dump[nRow]; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#ifdef GAME_BASTET |
|
||||
|
|
||||
int8_t tetris_playfield_predictDeepestRow(tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
int8_t nColumn) |
|
||||
{ |
|
||||
int8_t nRow = tetris_playfield_getPieceStartPos(pPiece); |
|
||||
tetris_piece_t *pActualPiece = pPl->pPiece; |
|
||||
pPl->pPiece = pPiece; |
|
||||
|
|
||||
// is it actually possible to use this piece?
|
|
||||
if (tetris_playfield_collision(pPl, (pPl->nWidth - 2) / 2, nRow) || |
|
||||
(tetris_playfield_collision(pPl, nColumn, nRow))) |
|
||||
{ |
|
||||
// restore real piece
|
|
||||
pPl->pPiece = pActualPiece; |
|
||||
|
|
||||
return -4; |
|
||||
} |
|
||||
|
|
||||
// determine deepest row
|
|
||||
nRow = (nRow < pPl->nFirstMatterRow - 4) ? pPl->nFirstMatterRow - 4 : nRow; |
|
||||
while ((nRow < pPl->nHeight) && |
|
||||
(!tetris_playfield_collision(pPl, nColumn, nRow + 1))) |
|
||||
{ |
|
||||
++nRow; |
|
||||
} |
|
||||
|
|
||||
// restore real piece
|
|
||||
pPl->pPiece = pActualPiece; |
|
||||
|
|
||||
return nRow; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int8_t tetris_playfield_predictCompleteLines(tetris_playfield_t *pPl, |
|
||||
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 - pPl->nWidth); |
|
||||
|
|
||||
if (nRow > -4) |
|
||||
{ |
|
||||
// determine sane start and stop values for the dump's index
|
|
||||
int8_t nStartRow = |
|
||||
((nRow + 3) >= pPl->nHeight) ? pPl->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 = pPl->dump[i] | nPieceMap; |
|
||||
|
|
||||
// is current row a full row?
|
|
||||
if ((nFullRow & nDumpMap) == nFullRow) |
|
||||
{ |
|
||||
++nCompleteRows; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return nCompleteRows; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, |
|
||||
tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
int8_t nRow, |
|
||||
int8_t nColumn) |
|
||||
{ |
|
||||
pIt->pPlayfield = pPl; |
|
||||
pIt->pPiece = pPiece; |
|
||||
pIt->nColumn = nColumn; |
|
||||
pIt->nFullRow = 0xFFFF >> (16 - pPl->nWidth); |
|
||||
pIt->nCurrentRow = pPl->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) < pPl->nHeight) ? |
|
||||
(pIt->nPieceHighestRow + 3) : pPl->nHeight - 1; |
|
||||
|
|
||||
// don't return any trailing rows which are empty, so we look for a stop row
|
|
||||
pIt->nStopRow = pPl->nFirstMatterRow < nRow ? pPl->nFirstMatterRow : nRow; |
|
||||
pIt->nStopRow = pIt->nStopRow < 0 ? 0 : pIt->nStopRow; |
|
||||
|
|
||||
return tetris_playfield_predictNextRow(pIt); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint16_t* tetris_playfield_predictNextRow(tetris_playfield_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->pPlayfield->dump[pIt->nCurrentRow--] | nPieceMap; |
|
||||
// don't return full (and therefore removed) rows
|
|
||||
if (pIt->nRowBuffer == pIt->nFullRow) |
|
||||
{ |
|
||||
// recursively determine next (?) row instead
|
|
||||
return tetris_playfield_predictNextRow(pIt); |
|
||||
} |
|
||||
// row isn't full
|
|
||||
else |
|
||||
{ |
|
||||
return &pIt->nRowBuffer; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return NULL; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif /* GAME_BASTET */ |
|
@ -1,293 +0,0 @@ |
|||||
#ifndef TETRIS_PLAYFIELD_H_ |
|
||||
#define TETRIS_PLAYFIELD_H_ |
|
||||
|
|
||||
#include <inttypes.h> |
|
||||
#include "../../autoconf.h" |
|
||||
#include "piece.h" |
|
||||
|
|
||||
|
|
||||
/*********
|
|
||||
* types * |
|
||||
*********/ |
|
||||
|
|
||||
// directions to which a piece can be moved
|
|
||||
typedef enum tetris_playfield_direction_t |
|
||||
{ |
|
||||
TETRIS_PFD_LEFT, |
|
||||
TETRIS_PFD_RIGHT |
|
||||
} |
|
||||
tetris_playfield_direction_t; |
|
||||
|
|
||||
|
|
||||
// status of the playfield
|
|
||||
typedef enum tetris_playfield_status_t |
|
||||
{ |
|
||||
TETRIS_PFS_READY, /** ready to get next piece */ |
|
||||
TETRIS_PFS_HOVERING, /** piece is still hovering */ |
|
||||
TETRIS_PFS_GLIDING, /** piece is gliding on the dump */ |
|
||||
TETRIS_PFS_DOCKED, /** piece has been docked */ |
|
||||
TETRIS_PFS_GAMEOVER /** playfield is filled up */ |
|
||||
} |
|
||||
tetris_playfield_status_t; |
|
||||
|
|
||||
|
|
||||
// tetris_playfield_t
|
|
||||
typedef struct tetris_playfield_t |
|
||||
{ |
|
||||
int8_t nWidth; /** width of playfield */ |
|
||||
int8_t nHeight; /** height of playfield */ |
|
||||
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_playfield_status_t status; /** status */ |
|
||||
int8_t nFirstMatterRow; /** first row from top which has matter */ |
|
||||
uint16_t *dump; /** playfield itself */ |
|
||||
} |
|
||||
tetris_playfield_t; |
|
||||
|
|
||||
|
|
||||
// iterator for predicted dump rows
|
|
||||
typedef struct tetris_playfield_iterator_t |
|
||||
{ |
|
||||
tetris_playfield_t *pPlayfield; /** playfield 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 playfield */ |
|
||||
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_playfield_iterator_t; |
|
||||
|
|
||||
|
|
||||
/****************************
|
|
||||
* construction/destruction * |
|
||||
****************************/ |
|
||||
|
|
||||
/**
|
|
||||
* constructs a playfield with the given diemensions |
|
||||
* @param nWidth width of playfield (4 <= n <= 16) |
|
||||
* @param nHeight height of playfield (4 <= n <= 124) |
|
||||
* @return pointer to a newly created playfield |
|
||||
*/ |
|
||||
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, int8_t nHeight); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* destructs a playfield |
|
||||
* @param pPl pointer to the playfield to be destructed |
|
||||
*/ |
|
||||
void tetris_playfield_destruct(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/*******************************
|
|
||||
* playfield 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_playfield_calculateLines(uint8_t nRowMask); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* resets playfield to begin a new game |
|
||||
* @param pPl playfield to perform action on |
|
||||
*/ |
|
||||
void tetris_playfield_reset(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* inserts a new piece |
|
||||
* @param pPl playfield to perform action on |
|
||||
* @param pPiece piece to be inserted |
|
||||
* @param ppOldPiece [out] indirect pointer to former piece for deallocation |
|
||||
*/ |
|
||||
void tetris_playfield_insertPiece(tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
tetris_piece_t** ppOldPiece); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* detects if piece collides with s.th. at a given position |
|
||||
* @param pPl playfield 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_playfield_collision(tetris_playfield_t *pPl, |
|
||||
int8_t nColumn, |
|
||||
int8_t nRow); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* lowers piece by one row or finally docks it |
|
||||
* @param pPl playfield to perform action on |
|
||||
*/ |
|
||||
void tetris_playfield_advancePiece(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* moves piece to the given direction |
|
||||
* @param pPl playfield to perform action on |
|
||||
* @param direction direction (see tetris_playfield_direction_t) |
|
||||
* @return 1 if piece could be moved, 0 otherwise |
|
||||
*/ |
|
||||
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl, |
|
||||
tetris_playfield_direction_t direction); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* rotates piece to the given direction |
|
||||
* @param pPl playfield 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_playfield_rotatePiece(tetris_playfield_t *pPl, |
|
||||
tetris_piece_rotation_t rotation); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* removes completed lines (if any) and lowers the dump |
|
||||
* @param pPl playfield to perform action on |
|
||||
*/ |
|
||||
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/*****************
|
|
||||
* get functions * |
|
||||
*****************/ |
|
||||
|
|
||||
/**
|
|
||||
* returns the width of the playfield |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return width of the playfield |
|
||||
*/ |
|
||||
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the height of the playfield |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return height of the playfield |
|
||||
*/ |
|
||||
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the currently falling piece |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return pointer to the currently falling piece |
|
||||
*/ |
|
||||
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the column of the currently falling piece |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return column of the currently falling piece |
|
||||
*/ |
|
||||
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the row of the currently falling piece |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return row of the currently falling piece |
|
||||
*/ |
|
||||
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 |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return bit mask of removed lines (relative to current position) |
|
||||
*/ |
|
||||
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the status of the playfield |
|
||||
* @param pPl the playfield we want information from |
|
||||
* @return status of the playfield (see tetris_playfield_status_t) |
|
||||
*/ |
|
||||
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* returns the given row of the dump (as bitmap) |
|
||||
* @param pPl the playfield 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_playfield_getDumpRow(tetris_playfield_t *pPl, |
|
||||
int8_t nRow); |
|
||||
|
|
||||
|
|
||||
#ifdef GAME_BASTET |
|
||||
|
|
||||
/**
|
|
||||
* returns the deepest possible row for a given piece |
|
||||
* @param pPl the playfield 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 (playfield compliant coordinates) |
|
||||
*/ |
|
||||
int8_t tetris_playfield_predictDeepestRow(tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
int8_t nColumn); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* predicts the number of complete lines for a piece at a given column |
|
||||
* @param pPl the playfield 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_playfield_predictCompleteLines(tetris_playfield_t *pPl, |
|
||||
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 pPl the playfield 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 predicted dump row at the bottom as bit mask |
|
||||
*/ |
|
||||
uint16_t* tetris_playfield_predictBottomRow(tetris_playfield_iterator_t *pIt, |
|
||||
tetris_playfield_t *pPl, |
|
||||
tetris_piece_t *pPiece, |
|
||||
int8_t nRow, |
|
||||
int8_t nColumn); |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* predicts appearance of the next row of the playfield (for a given iterator) |
|
||||
* @param pIt a pointer to a dump iterator |
|
||||
* @return appearance of next predicted row (or NULL -> no next line) |
|
||||
*/ |
|
||||
uint16_t* tetris_playfield_predictNextRow(tetris_playfield_iterator_t *pIt); |
|
||||
|
|
||||
#endif /* GAME_BASTET */ |
|
||||
|
|
||||
#endif /*TETRIS_PLAYFIELD_H_*/ |
|
Loading…
Reference in new issue