#include #include #include #include #include #include #define MAX_KEY_COUNT 10 #define FIELD_WIDTH 12 #define FIELD_HEIGHT 22 #define COLOR_PAIR_I 1 #define COLOR_PAIR_O 2 #define COLOR_PAIR_T 3 #define COLOR_PAIR_J 4 #define COLOR_PAIR_L 5 #define COLOR_PAIR_S 6 #define COLOR_PAIR_Z 7 #define COLOR_PAIR_SHADOW 8 #define COLOR_PAIR_SPEED 9 #define FIGURE_CELL_COUNT 4 #define SPEEDS_COUNT 6 #define CBUTTON_DROP KEY_UP #define CBUTTON_RIGHT KEY_RIGHT #define CBUTTON_DOWN KEY_DOWN #define CBUTTON_LEFT KEY_LEFT #define CBUTTON_ROTCW 'x' #define CBUTTON_ROTCCW 'z' #define CBUTTON_NEWGAME 'g' #define CBUTTON_STORAGE ' ' #define CBUTTON_EXIT KEY_F(10) #define CBUTTON_PAUSE 'p' typedef struct { int x; int y; } Point; typedef struct { int height; int width; } Size; typedef enum { TetrominoInit = -1, TetrominoNone = 0, TetrominoI = 1, TetrominoO = 2, TetrominoT = 3, TetrominoJ = 4, TetrominoL = 5, TetrominoS = 6, TetrominoZ = 7, } Tetromino; typedef enum { Clockwise, Counterclockwise, } Rotation; void init(void); void work(void); void draw(void); void kbin(void); void drawField(void); void drawScore(void); void drawSpeed(void); void drawShadow(void); void drawFigure(void); void drawNextFigure(void); void drawStoredFigure(void); int keyWasPressed(int key); void setNewRotationClockwise(void); void setNewRotationCounterclockwise(void); Point rotatePoint(Point point, Point origin, Rotation direction); void rotateClockwise(void); void rotateCounterclockwise(void); int canBeRotatedClockwise(void); int canBeRotatedCounterclockwise(void); int moveUp(void); int moveRight(void); int moveDown(void); int moveLeft(void); void dropDown(void); int canBeMovedUp(void); int canBeMovedRight(void); int canBeMovedDown(void); int canBeMovedLeft(void); void deployFigure(void); void newFigure(void); Tetromino randomTetromino(void); void checkForFilledLines(void); void updateSpeed(void); int isCellFilled(int x, int y); void setCellFilling(int x, int y, int filling); void newGame(void); void exitGame(void); void storageFigure(void); int moveFigureToDefaultPosition(void); void updateShadowPosition(void); void pauseGame(void); int *keys; WINDOW *wField; WINDOW *wScore; WINDOW *wSpeed; WINDOW *wNextFigure; WINDOW *wStoredFigure; Size mainWindowSize = {0, 0}; Size keysWindowSize = {0, 0}; Size fieldWindowSize = {0, 0}; Size scoreWindowSize = {0, 0}; Size speedWindowSize = {0, 0}; Size nextFigureWindowSize = {0, 0}; Size storedFigureWindowSize = {0, 0}; int filledCells[FIELD_WIDTH][FIELD_HEIGHT]; Tetromino figure; Tetromino nextFigure; Tetromino storedFigure; Point figureCellsPos[FIGURE_CELL_COUNT]; Point shadowCellsPos[FIGURE_CELL_COUNT]; int isGameOver; int isPaused; int speed; int score; int fieldRedrawNeeded; int chanceI; int chanceO; int chanceT; int chanceJ; int chanceL; int chanceS; int chanceZ; int isMoving; int storageUsed; int hasColors; unsigned long workCount = 0; int *chances[7] = {&chanceI, &chanceO, &chanceT, &chanceJ, &chanceL, &chanceS, &chanceZ}; int speedList[SPEEDS_COUNT] = {25, 20, 15, 10, 5, 1}; int scoreList[SPEEDS_COUNT] = { 0, 10, 100, 250, 500, 1000}; int main(void) { init(); newGame(); while (1) { kbin(); work(); draw(); usleep(50000); } return 0; } void init(void) { srand((unsigned int)time(NULL)); initscr(); nodelay(stdscr, TRUE); cbreak(); curs_set(FALSE); keypad(stdscr, TRUE); noecho(); keys = malloc(sizeof(*keys)*MAX_KEY_COUNT); getmaxyx(stdscr, mainWindowSize.height, mainWindowSize.width); Size realFieldSize; realFieldSize.height = 22; realFieldSize.width = 12; wField = newwin(realFieldSize.height, realFieldSize.width, 1, mainWindowSize.width/2 - realFieldSize.width/2); getmaxyx(wField, fieldWindowSize.height, fieldWindowSize.width); Size realScoreSize; realScoreSize.height = 3; realScoreSize.width = 8; wScore = newwin(realScoreSize.height, realScoreSize.width, 1, mainWindowSize.width/2 - realScoreSize.width/2 +fieldWindowSize.width); getmaxyx(wScore, scoreWindowSize.height, scoreWindowSize.width); Size realSpeedSize; realSpeedSize.height = 3; realSpeedSize.width = 8; wSpeed = newwin(realSpeedSize.height, realSpeedSize.width, 1, mainWindowSize.width/2 - realSpeedSize.width/2 - fieldWindowSize.width); getmaxyx(wSpeed, speedWindowSize.height, speedWindowSize.width); Size realNextFigureSize; realNextFigureSize.height = 6; realNextFigureSize.width = 8; wNextFigure = newwin(realNextFigureSize.height, realNextFigureSize.width, scoreWindowSize.height+1, mainWindowSize.width/2 - realNextFigureSize.width/2 + fieldWindowSize.width); getmaxyx(wNextFigure, nextFigureWindowSize.height, nextFigureWindowSize.width); Size realStoredFigureSize; realStoredFigureSize.height = 6; realStoredFigureSize.width = 8; wStoredFigure = newwin(realStoredFigureSize.height, realStoredFigureSize.width, speedWindowSize.height+1, mainWindowSize.width/2 - realStoredFigureSize.width/2 - fieldWindowSize.width); getmaxyx(wStoredFigure, storedFigureWindowSize.height, storedFigureWindowSize.width); hasColors = has_colors() == TRUE; if (hasColors) { start_color(); init_pair(COLOR_PAIR_I, COLOR_CYAN, COLOR_CYAN); init_pair(COLOR_PAIR_O, COLOR_YELLOW, COLOR_YELLOW); init_pair(COLOR_PAIR_T, COLOR_MAGENTA, COLOR_MAGENTA); init_pair(COLOR_PAIR_J, COLOR_BLUE, COLOR_BLUE); init_pair(COLOR_PAIR_L, COLOR_WHITE, COLOR_WHITE); init_pair(COLOR_PAIR_S, COLOR_GREEN, COLOR_GREEN); init_pair(COLOR_PAIR_Z, COLOR_RED, COLOR_RED); init_pair(COLOR_PAIR_SHADOW, COLOR_BLACK, COLOR_BLACK); init_pair(COLOR_PAIR_SPEED, COLOR_RED, COLOR_RED); } } void kbin(void) { memset(keys, 0, sizeof(*keys)*MAX_KEY_COUNT); int key; int keyPointer = 0; while ((key = getch()) != ERR) { if (keyPointer < MAX_KEY_COUNT && !keyWasPressed(key)) { switch (key) { case CBUTTON_DROP: case CBUTTON_RIGHT: case CBUTTON_DOWN: case CBUTTON_LEFT: case CBUTTON_ROTCW: case CBUTTON_ROTCCW: case CBUTTON_NEWGAME: case CBUTTON_STORAGE: case CBUTTON_EXIT: case CBUTTON_PAUSE: keys[keyPointer++] = key; break; } } } } void draw(void) { drawField(); drawScore(); drawSpeed(); drawNextFigure(); drawStoredFigure(); } void drawField(void) { if (fieldRedrawNeeded) { wclear(wField); box(wField, ACS_VLINE, ACS_HLINE); int x; int y; for (x = 1; x < FIELD_WIDTH-1; x++) { for (y = 0; y < FIELD_HEIGHT-1; y++) { if (isCellFilled(x, y) > 0) { if (hasColors) { wattron(wField, COLOR_PAIR(isCellFilled(x, y))); } mvwaddch(wField, y, x, ACS_BLOCK); if (hasColors) { wattroff(wField, COLOR_PAIR(isCellFilled(x, y))); } } else if (isCellFilled(x, y) < 0) { mvwaddch(wField, y, x, ACS_BLOCK); } else { mvwaddch(wField, y, x, '.'); } } } drawShadow(); drawFigure(); if (isGameOver) { mvwprintw(wField, 0, 2, "GAME OVER"); } else if (isPaused) { mvwprintw(wField, 0, 3, "PAUSED"); } touchwin(wField); wrefresh(wField); fieldRedrawNeeded = 0; } } void drawScore(void) { static int oldScore = -1; if (oldScore != score) { oldScore = score; wclear(wScore); box(wScore, ACS_VLINE, ACS_HLINE); mvwprintw(wScore, 1, 1, "%6d", score); mvwprintw(wScore, 0, 2, "SCORE"); touchwin(wScore); wrefresh(wScore); } } void drawSpeed(void) { static int oldSpeed = -1; if (oldSpeed != speed) { oldSpeed = speed; wclear(wSpeed); box(wSpeed, ACS_VLINE, ACS_HLINE); if (hasColors) { wattron(wSpeed, COLOR_PAIR(COLOR_PAIR_SPEED)); } int i; for (i = 0; i < SPEEDS_COUNT; i++) { if (speed <= speedList[i]) { mvwaddch(wSpeed, 1, 1+i, ACS_BLOCK); } else { break; } } if (hasColors) { wattroff(wSpeed, COLOR_PAIR(COLOR_PAIR_SPEED)); } mvwprintw(wSpeed, 0, 2, "SPEED"); touchwin(wSpeed); wrefresh(wSpeed); } } void drawShadow(void) { if (hasColors) { wattron(wField, COLOR_PAIR(COLOR_PAIR_SHADOW)); } int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (shadowCellsPos[i].x >= 0 && shadowCellsPos[i].x >= 0) { mvwaddch(wField, shadowCellsPos[i].y, shadowCellsPos[i].x, ACS_BLOCK); } } if (hasColors) { wattroff(wField, COLOR_PAIR(COLOR_PAIR_SHADOW)); } } void drawFigure() { int colorPair; switch (figure) { case TetrominoI: colorPair = COLOR_PAIR_I; break; case TetrominoO: colorPair = COLOR_PAIR_O; break; case TetrominoT: colorPair = COLOR_PAIR_T; break; case TetrominoJ: colorPair = COLOR_PAIR_J; break; case TetrominoL: colorPair = COLOR_PAIR_L; break; case TetrominoS: colorPair = COLOR_PAIR_S; break; case TetrominoZ: colorPair = COLOR_PAIR_Z; break; case TetrominoNone: case TetrominoInit: return; } if (hasColors) { wattron(wField, COLOR_PAIR(colorPair)); } int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (figureCellsPos[i].x >= 0 && figureCellsPos[i].x >= 0) { mvwaddch(wField, figureCellsPos[i].y, figureCellsPos[i].x, ACS_BLOCK); } } if (hasColors) { wattroff(wField, COLOR_PAIR(colorPair)); } } void drawNextFigure(void) { static Tetromino oldNextFigure = TetrominoNone; if (nextFigure != oldNextFigure) { oldNextFigure = nextFigure; wclear(wNextFigure); box(wNextFigure, ACS_VLINE, ACS_HLINE); int colorPair = -1; Point pos[FIGURE_CELL_COUNT]; switch (nextFigure) { case TetrominoI: colorPair = COLOR_PAIR_I; pos[0].x = 2; pos[0].y = 3; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoO: colorPair = COLOR_PAIR_O; pos[0].x = 3; pos[0].y = 2; pos[1].x = 4; pos[1].y = 2; pos[2].x = 3; pos[2].y = 3; pos[3].x = 4; pos[3].y = 3; break; case TetrominoT: colorPair = COLOR_PAIR_T; pos[0].x = 4; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoJ: colorPair = COLOR_PAIR_J; pos[0].x = 3; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoL: colorPair = COLOR_PAIR_L; pos[0].x = 5; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoS: colorPair = COLOR_PAIR_S; pos[0].x = 4; pos[0].y = 2; pos[1].x = 5; pos[1].y = 2; pos[2].x = 3; pos[2].y = 3; pos[3].x = 4; pos[3].y = 3; break; case TetrominoZ: colorPair = COLOR_PAIR_Z; pos[0].x = 3; pos[0].y = 2; pos[1].x = 4; pos[1].y = 2; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoNone: case TetrominoInit: break; } if (colorPair >= 0) { if (hasColors) { wattron(wNextFigure, COLOR_PAIR(colorPair)); } int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { mvwaddch(wNextFigure, pos[i].y, pos[i].x, ACS_BLOCK); } if (hasColors) { wattroff(wNextFigure, COLOR_PAIR(colorPair)); } } mvwprintw(wNextFigure, 0, 2, "NEXT"); touchwin(wNextFigure); wrefresh(wNextFigure); } } void drawStoredFigure(void) { static Tetromino oldStoredFigure = TetrominoNone; if (storedFigure != oldStoredFigure) { oldStoredFigure = storedFigure; wclear(wStoredFigure); box(wStoredFigure, ACS_VLINE, ACS_HLINE); int colorPair = -1; Point pos[FIGURE_CELL_COUNT]; switch (storedFigure) { case TetrominoI: colorPair = COLOR_PAIR_I; pos[0].x = 2; pos[0].y = 3; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoO: colorPair = COLOR_PAIR_O; pos[0].x = 3; pos[0].y = 2; pos[1].x = 4; pos[1].y = 2; pos[2].x = 3; pos[2].y = 3; pos[3].x = 4; pos[3].y = 3; break; case TetrominoT: colorPair = COLOR_PAIR_T; pos[0].x = 4; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoJ: colorPair = COLOR_PAIR_J; pos[0].x = 3; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoL: colorPair = COLOR_PAIR_L; pos[0].x = 5; pos[0].y = 2; pos[1].x = 3; pos[1].y = 3; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoS: colorPair = COLOR_PAIR_S; pos[0].x = 4; pos[0].y = 2; pos[1].x = 5; pos[1].y = 2; pos[2].x = 3; pos[2].y = 3; pos[3].x = 4; pos[3].y = 3; break; case TetrominoZ: colorPair = COLOR_PAIR_Z; pos[0].x = 3; pos[0].y = 2; pos[1].x = 4; pos[1].y = 2; pos[2].x = 4; pos[2].y = 3; pos[3].x = 5; pos[3].y = 3; break; case TetrominoNone: case TetrominoInit: break; } if (colorPair >= 0) { if (hasColors) { wattron(wStoredFigure, COLOR_PAIR(colorPair)); } int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { mvwaddch(wStoredFigure, pos[i].y, pos[i].x, ACS_BLOCK); } if (hasColors) { wattroff(wStoredFigure, COLOR_PAIR(colorPair)); } } mvwprintw(wStoredFigure, 0, 1, "STORED"); touchwin(wStoredFigure); wrefresh(wStoredFigure); } } void work(void) { int i = 0; while (i < MAX_KEY_COUNT && keys[i] != 0) { switch (keys[i]) { case CBUTTON_EXIT: exitGame(); break; case CBUTTON_ROTCCW: rotateCounterclockwise(); break; case CBUTTON_ROTCW: rotateClockwise(); break; case CBUTTON_DROP: dropDown(); break; case CBUTTON_LEFT: moveLeft(); break; case CBUTTON_DOWN: moveDown(); break; case CBUTTON_RIGHT: moveRight(); break; case CBUTTON_PAUSE: pauseGame(); break; case CBUTTON_NEWGAME: newGame(); break; case CBUTTON_STORAGE: storageFigure(); break; } i++; } if (!isGameOver && !isPaused && !(workCount%speed)) { if (!moveDown() && !isMoving) { deployFigure(); } } if (isMoving > 0) { isMoving--; } workCount++; } void setNewRotationClockwise(void) { int i; for (i = 1; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i] = rotatePoint(figureCellsPos[i], figureCellsPos[0], Clockwise); } updateShadowPosition(); isMoving = 15; fieldRedrawNeeded = 1; } void setNewRotationCounterclockwise(void) { int i; for (i = 1; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i] = rotatePoint(figureCellsPos[i], figureCellsPos[0], Counterclockwise); } updateShadowPosition(); isMoving = 15; fieldRedrawNeeded = 1; } Point rotatePoint(Point point, Point origin, Rotation direction) { Point retVal; retVal.x = origin.y - point.y; retVal.y = origin.x - point.x; switch (direction) { case Clockwise: retVal.y = -retVal.y; break; case Counterclockwise: retVal.x = -retVal.x; break; } retVal.x += origin.x; retVal.y += origin.y; return retVal; } void rotateClockwise(void) { if (isGameOver || isPaused) { return; } if (figure == TetrominoO) { return; } if (canBeRotatedClockwise()) { setNewRotationClockwise(); return; } moveLeft(); if (canBeRotatedClockwise()) { setNewRotationClockwise(); return; } moveRight(); moveRight(); if (canBeRotatedClockwise()) { setNewRotationClockwise(); return; } moveLeft(); moveUp(); if (canBeRotatedClockwise()) { setNewRotationClockwise(); return; } moveDown(); } void rotateCounterclockwise(void) { if (isGameOver || isPaused) { return; } if (figure == TetrominoO) { return; } if (canBeRotatedCounterclockwise()) { setNewRotationCounterclockwise(); return; } moveLeft(); if (canBeRotatedCounterclockwise()) { setNewRotationCounterclockwise(); return; } moveRight(); moveRight(); if (canBeRotatedCounterclockwise()) { setNewRotationCounterclockwise(); return; } moveLeft(); moveUp(); if (canBeRotatedCounterclockwise()) { setNewRotationCounterclockwise(); return; } moveDown(); } int canBeRotatedClockwise(void) { int i; for (i = 1; i < FIGURE_CELL_COUNT; i++) { Point p = rotatePoint(figureCellsPos[i], figureCellsPos[0], Clockwise); if (isCellFilled(p.x, p.y)) { return 0; } } return 1; } int canBeRotatedCounterclockwise(void) { int i; for (i = 1; i < FIGURE_CELL_COUNT; i++) { Point p = rotatePoint(figureCellsPos[i], figureCellsPos[0], Counterclockwise); if (isCellFilled(p.x, p.y)) { return 0; } } return 1; } int moveUp(void) { if (isGameOver || isPaused) { return 0; } if (canBeMovedUp()) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i].y--; } isMoving = 15; fieldRedrawNeeded = 1; return 1; } return 0; } int moveRight(void) { if (isGameOver || isPaused) { return 0; } if (canBeMovedRight()) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i].x++; } updateShadowPosition(); isMoving = 15; fieldRedrawNeeded = 1; return 1; } return 0; } int moveDown(void) { if (isGameOver || isPaused) { return 0; } if (canBeMovedDown()) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i].y++; } isMoving = 15; fieldRedrawNeeded = 1; return 1; } return 0; } int moveLeft(void) { if (isGameOver || isPaused) { return 0; } if (canBeMovedLeft()) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i].x--; } updateShadowPosition(); isMoving = 15; fieldRedrawNeeded = 1; return 1; } return 0; } void dropDown(void) { if (isGameOver || isPaused) { return; } if (isGameOver || isPaused) { return; } while (canBeMovedDown()) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { figureCellsPos[i].y++; } fieldRedrawNeeded = 1; } deployFigure(); } int canBeMovedUp(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (figureCellsPos[i].y-1 < 0) { return 0; } if (isCellFilled(figureCellsPos[i].x, figureCellsPos[i].y-1)) { return 0; } } return 1; } int canBeMovedRight(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (figureCellsPos[i].x+1 >= FIELD_WIDTH) { return 0; } if (isCellFilled(figureCellsPos[i].x+1, figureCellsPos[i].y)) { return 0; } } return 1; } int canBeMovedDown(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (figureCellsPos[i].y-1 >= FIELD_HEIGHT) { return 0; } if (isCellFilled(figureCellsPos[i].x, figureCellsPos[i].y+1)) { return 0; } } return 1; } int canBeMovedLeft(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (figureCellsPos[i].x-1 < 0) { return 0; } if (isCellFilled(figureCellsPos[i].x-1, figureCellsPos[i].y)) { return 0; } } return 1; } int keyWasPressed(int key) { int i = 0; while (i < MAX_KEY_COUNT && keys[i] != 0) { if (keys[i] == key) { return 1; } i++; } return 0; } void deployFigure(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { setCellFilling(figureCellsPos[i].x, figureCellsPos[i].y, figure); } storageUsed = 0; workCount = 0; checkForFilledLines(); newFigure(); } void newFigure(void) { if (nextFigure == TetrominoInit || nextFigure == TetrominoNone) { nextFigure = randomTetromino(); } figure = nextFigure; nextFigure = randomTetromino(); moveFigureToDefaultPosition(); updateShadowPosition(); fieldRedrawNeeded = 1; if (!canBeMovedDown()) { isGameOver = 1; } } Tetromino randomTetromino(void) { Tetromino tetramino = TetrominoNone; int total = 0; do { total = chanceI + chanceO + chanceT + chanceJ + chanceL + chanceS + chanceZ; if (total == 0) { chanceI = 15; chanceO = 15; chanceT = 15; chanceJ = 15; chanceL = 15; chanceS = 15; chanceZ = 15; } } while (total == 0); int value = rand()%total; chanceI += 2; chanceO += 2; chanceT += 2; chanceJ += 2; chanceL += 2; chanceS += 2; chanceZ += 2; int dChance = 0; int *pChance = NULL; if (value < chanceI) { tetramino = TetrominoI; pChance = &chanceI; } else if (value < (chanceI + chanceO)) { tetramino = TetrominoO; pChance = &chanceO; } else if (value < (chanceI + chanceO + chanceT)) { tetramino = TetrominoT; pChance = &chanceT; } else if (value < (chanceI + chanceO + chanceT + chanceJ)) { tetramino = TetrominoJ; pChance = &chanceJ; } else if (value < (chanceI + chanceO + chanceT + chanceJ + chanceL)) { tetramino = TetrominoL; pChance = &chanceL; } else if (value < (chanceI + chanceO + chanceT + chanceJ + chanceL + chanceS)) { tetramino = TetrominoS; pChance = &chanceS; } else if (value < (chanceI + chanceO + chanceT + chanceJ + chanceL + chanceS + chanceZ)) { tetramino = TetrominoZ; pChance = &chanceZ; } if (pChance != NULL) { *pChance -= 14; if (*pChance < 0) { dChance = -*pChance; *pChance = 0; } } while (dChance) { int i = rand()%7; if (*chances[i] > 0) { (*chances[i])--; dChance--; } } return tetramino; } void checkForFilledLines(void) { int filledCount = 0; int x; int y; for (y = FIELD_HEIGHT-2; y > 0; y--) { int filled = 1; for (x = 1; x < FIELD_WIDTH-1; x++) { if (!isCellFilled(x, y)) { filled = 0; break; } } if (filled) { filledCount++; int xx; int yy; for (yy = y; yy > 0; yy--) { for (xx = 1; xx < FIELD_WIDTH-1; xx++) { setCellFilling(xx, yy, isCellFilled(xx, yy-1)); } } y++; } } switch (filledCount) { case 1: score += 1; updateSpeed(); break; case 2: score += 3; updateSpeed(); break; case 3: score += 7; updateSpeed(); break; case 4: score += 15; updateSpeed(); break; default: break; } fieldRedrawNeeded = 1; } void updateSpeed(void) { int i; for (i = 0; i < SPEEDS_COUNT; i++) { if (score > scoreList[i]) { speed = speedList[i]; } else { break; } } } int isCellFilled(int x, int y) { if (x < 0 || y < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT) { return 0; } return filledCells[x][y]; } void setCellFilling(int x, int y, int filling) { if (x < 0 || y < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT) { return; } filledCells[x][y] = filling; } void newGame(void) { memset(filledCells, 0, sizeof(**filledCells)*(FIELD_WIDTH*FIELD_HEIGHT)); int x; int y; for (x = 0; x < FIELD_WIDTH; x++) { setCellFilling(x, FIELD_HEIGHT-1, -1); } for (y = 0; y < FIELD_HEIGHT; y++) { setCellFilling(0, y, -1); setCellFilling(FIELD_WIDTH-1, y, -1); } figure = TetrominoInit; nextFigure = TetrominoInit; storedFigure = TetrominoInit; isGameOver = 0; isPaused = 0; speed = 25; score = 0; fieldRedrawNeeded = 1; isMoving = 0; workCount = 0; chanceI = 0; chanceO = 0; chanceT = 0; chanceJ = 0; chanceL = 0; chanceS = 0; chanceZ = 0; storageUsed = 0; newFigure(); } void storageFigure(void) { if (!storageUsed) { Tetromino tmp = figure; figure = storedFigure; storedFigure = tmp; if (figure == TetrominoInit || figure == TetrominoNone) { figure = randomTetromino(); } moveFigureToDefaultPosition(); updateShadowPosition(); fieldRedrawNeeded = 1; storageUsed = 1; workCount = 0; } } int moveFigureToDefaultPosition(void) { switch (figure) { case TetrominoI: figureCellsPos[0].x = 5; figureCellsPos[0].y = 0; figureCellsPos[1].x = 4; figureCellsPos[1].y = 0; figureCellsPos[2].x = 6; figureCellsPos[2].y = 0; figureCellsPos[3].x = 7; figureCellsPos[3].y = 0; break; case TetrominoO: figureCellsPos[0].x = 5; figureCellsPos[0].y = 0; figureCellsPos[1].x = 6; figureCellsPos[1].y = 0; figureCellsPos[2].x = 5; figureCellsPos[2].y = -1; figureCellsPos[3].x = 6; figureCellsPos[3].y = -1; break; case TetrominoT: figureCellsPos[0].x = 6; figureCellsPos[0].y = 0; figureCellsPos[1].x = 5; figureCellsPos[1].y = 0; figureCellsPos[2].x = 7; figureCellsPos[2].y = 0; figureCellsPos[3].x = 6; figureCellsPos[3].y = -1; break; case TetrominoJ: figureCellsPos[0].x = 6; figureCellsPos[0].y = 0; figureCellsPos[1].x = 5; figureCellsPos[1].y = 0; figureCellsPos[2].x = 7; figureCellsPos[2].y = 0; figureCellsPos[3].x = 5; figureCellsPos[3].y = -1; break; case TetrominoL: figureCellsPos[0].x = 6; figureCellsPos[0].y = 0; figureCellsPos[1].x = 5; figureCellsPos[1].y = 0; figureCellsPos[2].x = 7; figureCellsPos[2].y = 0; figureCellsPos[3].x = 7; figureCellsPos[3].y = -1; break; case TetrominoS: figureCellsPos[0].x = 6; figureCellsPos[0].y = 0; figureCellsPos[1].x = 5; figureCellsPos[1].y = 0; figureCellsPos[2].x = 6; figureCellsPos[2].y = -1; figureCellsPos[3].x = 7; figureCellsPos[3].y = -1; break; case TetrominoZ: figureCellsPos[0].x = 6; figureCellsPos[0].y = 0; figureCellsPos[1].x = 7; figureCellsPos[1].y = 0; figureCellsPos[2].x = 6; figureCellsPos[2].y = -1; figureCellsPos[3].x = 5; figureCellsPos[3].y = -1; break; case TetrominoNone: case TetrominoInit: break; } int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (isCellFilled(figureCellsPos[i].x, figureCellsPos[i].y)) { return 0; } } return 1; } void updateShadowPosition(void) { int i; for (i = 0; i < FIGURE_CELL_COUNT; i++) { shadowCellsPos[i] = figureCellsPos[i]; } while (shadowCellsPos[0].y < FIELD_HEIGHT) { for (i = 0; i < FIGURE_CELL_COUNT; i++) { shadowCellsPos[i].y++; } int isFilled = 0; for (i = 0; i < FIGURE_CELL_COUNT; i++) { if (isCellFilled(shadowCellsPos[i].x, shadowCellsPos[i].y)) { isFilled = 1; } } if (isFilled) { break; } } for (i = 0; i < FIGURE_CELL_COUNT; i++) { shadowCellsPos[i].y--; } } void pauseGame(void) { isPaused = !isPaused; fieldRedrawNeeded = 1; } void exitGame(void) { wclear(wField); wrefresh(wField); wclear(wScore); wrefresh(wScore); wclear(wSpeed); wrefresh(wSpeed); wclear(wNextFigure); wrefresh(wNextFigure); wclear(wStoredFigure); wrefresh(wStoredFigure); endwin(); exit(0); }