From c492078867413a4e9b5645e50ed56aa79d678964 Mon Sep 17 00:00:00 2001 From: IRUSlan92I Date: Thu, 31 Mar 2022 16:42:51 +0300 Subject: [PATCH] First version --- tetris.c | 1475 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1475 insertions(+) create mode 100644 tetris.c diff --git a/tetris.c b/tetris.c new file mode 100644 index 0000000..66b2304 --- /dev/null +++ b/tetris.c @@ -0,0 +1,1475 @@ +#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); +}