 | Code: /***************************************************************** ** Types and Structures *****************************************************************/ typedef bool BlockPattern[4][4];
typedef struct { int maxWidth, maxHeight; int curRow, curCol; BlockPattern pattern; } PieceInfo;
typedef enum { eNothing, eDoneWithPiece, eGameOver, } DropResult;
/***************************************************************** ** Constants and Configuation Info *****************************************************************/ const TTimers KEYPRESS_TIMER = T1;
const int BOX_DIM = 4; const int NUM_ROWS = 16; const int NUM_COLS = 10;
const int SCREEN_WIDTH = 100; const int SCREEN_HEIGHT = 64; const int TEXT_HEIGHT = 8; const int TEXT_WIDTH = 6;
const int BOARD_WIDTH = NUM_COLS * BOX_DIM; const int BOARD_LEFT = (SCREEN_WIDTH - BOARD_WIDTH) / 4; const int BOARD_RIGHT = BOARD_LEFT + BOARD_WIDTH - 1;
const int NEXT_AREA_DIM = BOX_DIM * 5; const int NEXT_AREA_LEFT = BOARD_RIGHT + (SCREEN_WIDTH - BOARD_RIGHT - NEXT_AREA_DIM) / 2; const int NEXT_AREA_TOP = SCREEN_HEIGHT - 1 - (TEXT_HEIGHT * 2);
const int STD_SCORES[4] = {1, 3, 6, 10};
const int NUM_PATTERNS = 7;
const BlockPattern ptnSquare = { {true , true , false, false}, {true , true , false, false}, {false, false, false, false}, {false, false, false, false} };
const BlockPattern ptnRightL = { {true , false, false, false}, {true , false, false, false}, {true , true , false, false}, {false, false, false, false} };
const BlockPattern ptnLeftL = { {false, true , false, false}, {false, true , false, false}, {true , true , false, false}, {false, false, false, false} };
const BlockPattern ptnStraight = { {true , false, false, false}, {true , false, false, false}, {true , false, false, false}, {true , false, false, false} };
const BlockPattern ptnSnakeR = { {false, true , true , false}, {true , true , false, false}, {false, false, false, false}, {false, false, false, false} };
const BlockPattern ptnSnakeL = { {true , true , false, false}, {false, true , true , false}, {false, false, false, false}, {false, false, false, false} };
const BlockPattern ptnTee = { {false, true , false, false}, {true , true , true , false}, {false, false, false, false}, {false, false, false, false} };
/***************************************************************** ** Global Variables *****************************************************************/ bool gameBoard[NUM_ROWS][NUM_COLS];
PieceInfo curPiece, nextPiece; int score = 0; int lines = 0; int level = 1; int latency;
/***************************************************************** ** Functions *****************************************************************/ void updateScore() { nxtDisplayStringAt(BOARD_RIGHT + 8, SCREEN_HEIGHT - 1 - (TEXT_HEIGHT * 6.5), "%5d", score); }
void initScreen() { eraseDisplay();
// Separate the game board from the rest of the screen nxtDrawLine(BOARD_LEFT - 2, 0, BOARD_LEFT - 2, SCREEN_HEIGHT - 1); nxtDrawLine(BOARD_LEFT - 1, 0, BOARD_LEFT - 1, SCREEN_HEIGHT - 1); nxtDrawLine(BOARD_RIGHT + 1, 0, BOARD_RIGHT + 1, SCREEN_HEIGHT - 1); nxtDrawLine(BOARD_RIGHT + 2, 0, BOARD_RIGHT + 2, SCREEN_HEIGHT - 1);
nxtDisplayStringAt(BOARD_RIGHT + 8, SCREEN_HEIGHT - 1 - (TEXT_HEIGHT / 2), "Next:"); nxtDisplayStringAt(BOARD_RIGHT + 8, SCREEN_HEIGHT - 1 - (TEXT_HEIGHT * 5), "Score:");
// Draw the 'next block' area nxtDrawRect(NEXT_AREA_LEFT, NEXT_AREA_TOP, NEXT_AREA_LEFT + NEXT_AREA_DIM, NEXT_AREA_TOP - NEXT_AREA_DIM); updateScore(); }
void drawBox(int row, int col) { int x1 = BOARD_LEFT + col * BOX_DIM; int y1 = (SCREEN_HEIGHT - 1) - row * BOX_DIM; int x2 = x1 + BOX_DIM - 1; int y2 = y1 - BOX_DIM + 1; nxtDrawRect(x1, y1, x2, y2); }
void eraseBox(int row, int col) { int x1 = BOARD_LEFT + col * BOX_DIM; int y1 = (SCREEN_HEIGHT - 1) - row * BOX_DIM; int x2 = x1 + BOX_DIM - 1; int y2 = y1 - BOX_DIM + 1; nxtEraseRect(x1, y1, x2, y2); }
void drawNextPiece(PieceInfo &piece) { int xPos = NEXT_AREA_LEFT; int yPos = NEXT_AREA_TOP;
xPos += (NEXT_AREA_DIM - (piece.maxWidth * BOX_DIM)) / 2; yPos -= (NEXT_AREA_DIM - (piece.maxHeight * BOX_DIM)) / 2;
for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j]) nxtDrawRect(xPos + j * BOX_DIM, yPos - i * BOX_DIM, xPos + (j + 1) * BOX_DIM - 1, yPos - (i + 1) * BOX_DIM + 1); }
void eraseNextPiece(PieceInfo &piece) { int xPos = NEXT_AREA_LEFT; int yPos = NEXT_AREA_TOP;
xPos += (NEXT_AREA_DIM - (piece.maxWidth * BOX_DIM)) / 2; yPos -= (NEXT_AREA_DIM - (piece.maxHeight * BOX_DIM)) / 2;
for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j]) nxtEraseRect(xPos + j * BOX_DIM, yPos - i * BOX_DIM, xPos + (j + 1) * BOX_DIM - 1, yPos - (i + 1) * BOX_DIM + 1); }
void nextRandomPiece(PieceInfo &nextPiece) { int nextIndex = random(NUM_PATTERNS);
switch(nextIndex) { case 0: memcpy(nextPiece.pattern, ptnSquare, sizeof(BlockPattern)); break; case 1: memcpy(nextPiece.pattern, ptnRightL, sizeof(BlockPattern)); break; case 2: memcpy(nextPiece.pattern, ptnLeftL, sizeof(BlockPattern)); break; case 3: memcpy(nextPiece.pattern, ptnStraight, sizeof(BlockPattern)); break; case 4: memcpy(nextPiece.pattern, ptnSnakeL, sizeof(BlockPattern)); break; case 5: memcpy(nextPiece.pattern, ptnSnakeR, sizeof(BlockPattern)); break; case 6: memcpy(nextPiece.pattern, ptnTee, sizeof(BlockPattern)); break; }
nextPiece.maxWidth = 0; nextPiece.maxHeight = 0; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { if (nextPiece.pattern[i][j]) { if (i >= nextPiece.maxHeight) nextPiece.maxHeight = i + 1; if (j >= nextPiece.maxWidth) nextPiece.maxWidth = j + 1; } } nextPiece.curRow = 0; nextPiece.curCol = (NUM_COLS - nextPiece.maxWidth) / 2; }
void drawPiece(PieceInfo &piece) { for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j]) drawBox(piece.curRow + i, piece.curCol + j); }
void erasePiece(PieceInfo &piece) { for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j]) eraseBox(piece.curRow + i, piece.curCol + j); }
void init() { srand(nSysTime);
// "Hi-jack" buttons for user program control. nNxtButtonTask = -2; nNxtExitClicks = 3;
initScreen();
score = 0; lines = 0; level = 1; latency = 1000;
memset(gameBoard, 0, sizeof(gameBoard)); nextRandomPiece(curPiece); nextRandomPiece(nextPiece);
drawNextPiece(nextPiece); drawPiece(curPiece); }
int waitForKey(int &howLong) { int buttonPressed = kNoButton; bool cleanKey = (nNxtButtonPressed == kNoButton);
if (howLong == 0) return kNoButton;
ClearTimer(KEYPRESS_TIMER);
do { if (nNxtButtonPressed == kNoButton) cleanKey = true; if (cleanKey) buttonPressed = nNxtButtonPressed; wait1Msec(1); } while((buttonPressed == kNoButton) && ((howLong < 0) || ((howLong >= 0) && (time1[KEYPRESS_TIMER] < howLong))));
if (howLong >= 0) { howLong -= time1[KEYPRESS_TIMER]; if (howLong < 0) howLong = 0; }
return buttonPressed; }
bool movePiece(PieceInfo &piece, int direction) { // Don't let them move off the board if ((piece.curCol + direction) < 0) return false; if ((piece.curCol + piece.maxWidth + direction) > NUM_COLS) return false;
// Check to see if moving the piece causes a collision for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j] && gameBoard[curPiece.curRow + i][curPiece.curCol + j + direction]) return false;
// Allow the piece to move erasePiece(piece); piece.curCol += direction; drawPiece(piece); return true; }
bool rotatePiece(PieceInfo &piece) { BlockPattern possiblePattern; int temp;
memset(possiblePattern, 0, sizeof(BlockPattern));
// Rotate the pattern and determine if we can use it when rotated for (int i = 0; i < piece.maxWidth; i++) for (int j = 0; j < piece.maxHeight; j++) { if ((piece.curRow + i >= NUM_ROWS) || (piece.curCol + j >= NUM_COLS)) return false; possiblePattern[i][j] = piece.pattern[piece.maxHeight - 1 - j][i]; if (possiblePattern[i][j] && gameBoard[piece.curRow + i][piece.curCol + j]) return false; }
// If we get here the new pattern is fine. Go ahead and modify the piece info erasePiece(piece); memcpy(piece.pattern, possiblePattern, sizeof(BlockPattern)); temp = piece.maxHeight; piece.maxHeight = piece.maxWidth; piece.maxWidth = temp; drawPiece(piece);
return true; }
DropResult letPieceFall(PieceInfo &piece) { // See if we should actually let it move, or if it's going to get stuck here if (piece.curRow + piece.maxHeight >= NUM_ROWS) return eDoneWithPiece;
for (int i = 0; i < piece.maxHeight; i++) for (int j = 0; j < piece.maxWidth; j++) if (piece.pattern[i][j] && gameBoard[curPiece.curRow + i + 1][curPiece.curCol + j]) return eDoneWithPiece;
// Actually let the piece drop erasePiece(piece); piece.curRow++; drawPiece(piece);
return eNothing; }
void letPieceFallCompletely(PieceInfo &piece) { DropResult result; do { result = letPieceFall(piece); } while(result == eNothing); }
void animateEliminatedRows(int &eliminatedCount, int e1, int e2, int e3, int e4) { int blinkSpeed = latency / 5; int eliminatedRows[4] = {e1, e2, e3, e4};
// Make the eliminated rows flash, then replace them for (int iteration = 0; iteration < 5; iteration++) { for (int rowIndex = 0; rowIndex < eliminatedCount; rowIndex++) for (int col = 0; col < NUM_COLS; col++) if (iteration & 0x01) drawBox(eliminatedRows[rowIndex], col); else eraseBox(eliminatedRows[rowIndex], col); wait1Msec(blinkSpeed); }
// Now actually remove the rows and update the screen for (int rowIndex = 0; rowIndex < eliminatedCount; rowIndex++) for (int fallingRow = eliminatedRows[rowIndex]; fallingRow > 0; fallingRow--) for (int col = 0; col < NUM_COLS; col++) { gameBoard[fallingRow][col] = gameBoard[fallingRow - 1][col]; if (gameBoard[fallingRow][col]) drawBox(fallingRow, col); else eraseBox(fallingRow, col);
// make sure to clear out the top row as well if (fallingRow == 1) { gameBoard[0][col] = false; eraseBox(0, col); } } }
void clearCompletedRows() { int oldLevel = level; int eliminatedRows[4]; int eliminatedCount = 0;
for (int row = 0; row < NUM_ROWS; row++) { int rowCount = 0;
for (int col = 0; col < NUM_COLS; col++) if (gameBoard[row][col]) rowCount++;
if (rowCount == NUM_COLS) { eliminatedRows[eliminatedCount] = row; eliminatedCount++; } }
if (eliminatedCount > 0) { score += STD_SCORES[eliminatedCount - 1] * level; updateScore(); lines += eliminatedCount; level = lines / 10 + 1; if (level != oldLevel) { latency = (latency * 9) / 10; } animateEliminatedRows(eliminatedCount, eliminatedRows[0], eliminatedRows[1], eliminatedRows[2], eliminatedRows[3]); } }
void animateEndOfGame() { for (int row = NUM_ROWS - 1; row >= 0; row--) { for (int col = 0; col < NUM_COLS; col++) drawBox(row, col); wait1Msec(100); } }
DropResult finishPiece() { // Place the piece on the game board for (int i = 0; i < curPiece.maxHeight; i++) for (int j = 0; j < curPiece.maxWidth; j++) if (curPiece.pattern[i][j]) gameBoard[curPiece.curRow + i][curPiece.curCol + j] = true;
// Did we complete any rows? clearCompletedRows();
// Make the 'next' piece the current piece eraseNextPiece(nextPiece); memcpy(curPiece.pattern, nextPiece.pattern, sizeof(BlockPattern)); curPiece.curCol = nextPiece.curCol; curPiece.curRow = nextPiece.curRow; curPiece.maxHeight = nextPiece.maxHeight; curPiece.maxWidth = nextPiece.maxWidth; nextRandomPiece(nextPiece); drawNextPiece(nextPiece); drawPiece(curPiece);
// See if the game is over now for (int i = 0; i < curPiece.maxHeight; i++) for (int j = 0; j < curPiece.maxWidth; j++) if (curPiece.pattern[i][j] && gameBoard[curPiece.curRow + i][curPiece.curCol + j]) return eGameOver;
return eNothing; }
void playGame() { bool done = false; int timeUntilDrop; int nextKey; bool doneWithPiece = false;
timeUntilDrop = latency;
while (!done) { switch(waitForKey(timeUntilDrop)) { case kLeftButton : movePiece(curPiece, -1); break; case kRightButton: movePiece(curPiece, 1); break; case kEnterButton: rotatePiece(curPiece); break; case kExitButton : letPieceFallCompletely(curPiece); doneWithPiece = true; break;
default: // timeout timeUntilDrop = latency; if (letPieceFall(curPiece) == eDoneWithPiece) doneWithPiece = true; break; } if (doneWithPiece) { timeUntilDrop = latency; if (finishPiece() == eGameOver) done = true; doneWithPiece = false; updateScore(); } } animateEndOfGame();
// Wait for a keypress (infinite timeout) timeUntilDrop = -1; waitForKey(timeUntilDrop); }
task main() { init(); playGame(); }
|  |