From 5719c6c8ca184582e0fea9e216746c0e19e76144 Mon Sep 17 00:00:00 2001 From: felixalbrigtsen <64613093+felixalbrigtsen@users.noreply.github.com> Date: Mon, 4 May 2020 23:17:22 +0200 Subject: [PATCH] Add files via upload --- Pixelmania.ttf | Bin 0 -> 6844 bytes main.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++++++ makefile | 13 ++++ tetris.cpp | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ tetris.hpp | 92 ++++++++++++++++++++++ 5 files changed, 512 insertions(+) create mode 100644 Pixelmania.ttf create mode 100644 main.cpp create mode 100644 makefile create mode 100644 tetris.cpp create mode 100644 tetris.hpp diff --git a/Pixelmania.ttf b/Pixelmania.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0c20d6a4d4e48857343ae1506d11327aa629e246 GIT binary patch literal 6844 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 literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..dcfca37 --- /dev/null +++ b/main.cpp @@ -0,0 +1,201 @@ +//Felix Albrigtsen 2020 +//Graphics and loops are contained in this file. +//All game logic is defined in tetris.cpp + +#include "tetris.hpp" +#include +#include +#include +#include + +#define SCALE 50 +#define MENUWIDTH SCALE*BOARDWIDTH +#define MENUMARGIN 80 +#define MENUTEXT 30 +#define NPIECESIZE (MENUWIDTH-(2*MENUMARGIN)) + +#define MOVES_PER_TICK 2 + +int target_delay = 650; //Miliseconds between each fall + +sf::Color colors[] = { + sf::Color::Black, + sf::Color::Cyan, + sf::Color::Blue, + sf::Color(255,128,0), + sf::Color::Yellow, + sf::Color::Green, + sf::Color::Magenta, + sf::Color::Red +}; + +sf::RenderWindow window(sf::VideoMode(BOARDWIDTH*SCALE + MENUWIDTH, BOARDHEIGHT*SCALE),"Tetris"); +sf::Font font; +sf::Text txt_level; +sf::Text txt_lines; +sf::Text txt_score; +sf::Text txt_next; +sf::RectangleShape nextSquare; +sf::RectangleShape NPieceRect; + +static int NPieceBlockSize = NPIECESIZE / 6; + +void drawBoard(std::array board) { + for (int i = 0; i < BOARDSIZE; i++) { + int x = i % BOARDWIDTH; + int y = (i - x) / BOARDWIDTH; + + sf::RectangleShape square(sf::Vector2f(SCALE, SCALE)); + if (board[i] != 0) { + square.setOutlineThickness(-3); + } else { + square.setOutlineThickness(-1); + } + + square.setOutlineColor(sf::Color(50,50,50)); + + square.setPosition(x*SCALE, y*SCALE); + + square.setFillColor(colors[board[i]]); + window.draw(square); + } +} + +void drawInfo(std::array info) { + // In order: Level, Lines, Score, Next piece + int level = info[0]; + int lines = info[1]; + int points = info[2]; + int nextPiece = info[3]; + + target_delay = (650 * pow(0.92, level)); + + txt_level.setString("LEVEL " + std::to_string(level)); + txt_lines.setString(std::to_string(lines) + " LINES"); + txt_score.setString(std::to_string(points) + " POINTS"); + + window.draw(txt_level); + window.draw(txt_lines); + window.draw(txt_score); + + window.draw(nextSquare); + + for (int i = 0; i < 4; i++) { + int x = TETROMINOES[info[3]][0][i][0]; + int y = TETROMINOES[info[3]][0][i][1]; + + NPieceRect.setFillColor(colors[info[3]+1]); + //NPieceRect.setPosition((BOARDWIDTH*SCALE)+MENUMARGIN+(x*NPieceBlockSize) + (NPIECESIZE/4), MENUMARGIN + (y*NPieceBlockSize)+ (NPIECESIZE/3)); + int xpos = (BOARDWIDTH*SCALE)+MENUMARGIN+(x*NPieceBlockSize); + int ypos = MENUMARGIN + (y*NPieceBlockSize); + + //Correct positioning + switch(info[3]) { + case 0: //I + xpos += NPieceBlockSize; + ypos += NPieceBlockSize*1.5; + break; + case 3: //Sqare / O + xpos += NPieceBlockSize; + ypos += NPieceBlockSize*2; + break; + default: + xpos += NPieceBlockSize*1.5; + ypos += NPieceBlockSize*2; + break; + } + + NPieceRect.setPosition(xpos, ypos); + window.draw(NPieceRect); + } +} + +void initText() { + font.loadFromFile("Pixelmania.ttf"); + + //Initialize text objects for displaying progress + txt_level.setFont(font); + txt_level.setCharacterSize(MENUTEXT); + txt_level.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*2); + txt_level.setFillColor(sf::Color::White); + + txt_lines.setFont(font); + txt_lines.setCharacterSize(MENUTEXT); + txt_lines.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*3); + txt_lines.setFillColor(sf::Color::White); + + txt_score.setFont(font); + txt_score.setCharacterSize(MENUTEXT); + txt_score.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*4); + txt_score.setFillColor(sf::Color::White); + + //Initialize the everything neccessary to display the next piece + nextSquare.setSize(sf::Vector2f(NPIECESIZE, NPIECESIZE)); + nextSquare.setPosition((BOARDWIDTH*SCALE)+MENUMARGIN, MENUMARGIN); + nextSquare.setOutlineThickness(-12); + nextSquare.setOutlineColor(sf::Color::White); + nextSquare.setFillColor(sf::Color::Black); + + NPieceRect.setSize(sf::Vector2f(NPieceBlockSize, NPieceBlockSize)); + NPieceRect.setOutlineThickness(-3); + NPieceRect.setOutlineColor(sf::Color(200,200,200)); + +} + +int main(int argc, char *argv[]) { + Tetris game; + game.init(); + initText(); + + drawBoard(game.getBoard()); + drawInfo(game.getInfo()); + + sf::Clock clock; + + int tickCount = 0; + + while (window.isOpen()) { + sf::Event event; + + while (window.pollEvent(event)) { + if (event.type == sf::Event::Closed) { + window.close(); + } + } + window.clear(sf::Color(100,100,100)); + + bool keys[] = { + sf::Keyboard::isKeyPressed(sf::Keyboard::Left), + sf::Keyboard::isKeyPressed(sf::Keyboard::Right), + sf::Keyboard::isKeyPressed(sf::Keyboard::Up), + sf::Keyboard::isKeyPressed(sf::Keyboard::Down), + sf::Keyboard::isKeyPressed(sf::Keyboard::Space) + }; + + game.tick(keys, (tickCount == MOVES_PER_TICK)); + + if (tickCount == MOVES_PER_TICK) { + tickCount = 0; + } else { + tickCount += 1; + } + + //Display game board and stats + drawBoard(game.getBoard()); + drawInfo(game.getInfo()); + + //Delay to maintain correct speed + sf::Time framePeriod = clock.getElapsedTime(); + while (framePeriod.asMilliseconds() < target_delay / MOVES_PER_TICK) { + framePeriod = clock.getElapsedTime(); + } + clock.restart(); + + + + window.display(); + } + + + return 0; +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..c424054 --- /dev/null +++ b/makefile @@ -0,0 +1,13 @@ +LINKER_FLAGS = -lsfml-graphics -lsfml-window -lsfml-system + +main: main.cpp + g++ -c main.cpp + +game: tetris.cpp tetris.hpp + g++ -c tetris.cpp -o game.o + +out: main game + g++ main.o game.o -o out $(LINKER_FLAGS) + +clean: + rm main.o game.o out diff --git a/tetris.cpp b/tetris.cpp new file mode 100644 index 0000000..edde035 --- /dev/null +++ b/tetris.cpp @@ -0,0 +1,206 @@ +//Felix Albrigtsen 2020 +//All game logic, but no graphics are defined in this file + +#include "tetris.hpp" +#include +#include +#include +#include + +void Tetris::newPiece() { + cury = 0; + curx = (BOARDWIDTH / 2)-1; + curr = 0; + + curPiece = nextPiece; + nextPiece = rand() % 7; + + if (staticBoard[BOARDWIDTH*1.5] != 0) { gameOver = true; } +} + +void Tetris::init() { + srand((unsigned) time(NULL)); + staticBoard.fill(0); + newPiece(); + lines = 0; + points = 0; + level = 0; + levelProgress = 0; + nextPiece = rand() % 7; +} + +std::array Tetris::getBoard() { + //Returns staticboard and the current piece + std::array board = staticBoard; + for (int i = 0; i < 4; i++) { + int xpos = curx + TETROMINOES[curPiece][curr][i][0]; + int ypos = cury + TETROMINOES[curPiece][curr][i][1]; + int index = (ypos * BOARDWIDTH) + xpos; + board[index] = curPiece+1; + } + return board; +} + +std::array Tetris::getInfo() { + //Return Level, Lines, Score, Next piece + std::array info = {level, lines, points, nextPiece }; + return info; +} + +void Tetris::clearRows() { + int prevlines = lines; + + for (int line = 0; line < BOARDHEIGHT; line++) { + bool full = true; + for (int i = 0; i < BOARDWIDTH; i++) { + if (staticBoard[ (line*BOARDWIDTH) + i ] == 0) { + full = false; + } + } + if (full) { + + lines += 1; + levelProgress += 1; + if (levelProgress > level) { + level += 1; + levelProgress = 0; + } + + //Move everything down one line: + int startIndex = (line*BOARDWIDTH) - 1; + for (int i = startIndex; i > 0; i--) { + staticBoard[i + BOARDWIDTH] = staticBoard[i]; + } + + } + } + + switch (lines - prevlines) { + case 1: + points += 40 * (level+1); + break; + case 2: + points += 100 * (level+1); + break; + case 3: + points += 300 * (level+1); + break; + case 4: + points += 1200 * (level+1); + break; + } +} + +bool Tetris::testLanded() { + //Returns true if a part of the tetromino is directly above another piece + for (int i = 0; i < 4; i++) { + int xpos = curx + TETROMINOES[curPiece][curr][i][0]; + int ypos = cury + TETROMINOES[curPiece][curr][i][1] + 1; + int index = (ypos * BOARDWIDTH) + xpos; + if (staticBoard[index] != 0 || ypos == BOARDHEIGHT) { + return true; + } + } + return false; +} + +void Tetris::lock() { + for (int i = 0; i < 4; i++) { + int xpos = curx + TETROMINOES[curPiece][curr][i][0]; + int ypos = cury + TETROMINOES[curPiece][curr][i][1]; + int index = (ypos * BOARDWIDTH) + xpos; + staticBoard[index] = curPiece+1; + } +} + +bool Tetris::move(bool left, bool right) { + //Returns true if the move was legal + int newx; + if (right && !left) { + newx = curx + 1; + } else if (!right && left) { + newx = curx - 1; + } else { + return true; + } + + bool possible = true; + for (int i = 0; i < 4; i++) { + int xpos = newx + TETROMINOES[curPiece][curr][i][0]; + int ypos = cury + TETROMINOES[curPiece][curr][i][1]; + int index = (ypos * BOARDWIDTH) + xpos; + if (staticBoard[index] != 0) { possible = false; } + if (xpos < 0 || xpos >= BOARDWIDTH) { possible = false; } + } + if (possible) { + curx = newx; + } + return possible; +} + +void Tetris::rotate() { + //Try to rotate + int newr = (curr == 3) ? 0 : curr + 1; //Add one, wrap 4 -> 0 + bool possible = true; + bool kickleft = false; + for (int i = 0; i < 4; i++) { + int xpos = curx + TETROMINOES[curPiece][newr][i][0]; + int ypos = cury + TETROMINOES[curPiece][newr][i][1]; + int index = (ypos * BOARDWIDTH) + xpos; + if (staticBoard[index] != 0 || xpos >= BOARDWIDTH || xpos < 0) { + possible = false; + } + } + if (possible) { + curr = newr; + } else { + //If normal rotation is not possible, try to kick off the edge + int oldr = curr; + curr = newr; + bool hasMoved = false; + + hasMoved |= move(true, false); + if (!hasMoved) { + hasMoved |= move(false, true); + } + if (!hasMoved) { //No kicking was possible + curr = oldr; + } + + } +} + +void Tetris::tick(bool keys[], bool fallfast) { + //Main Game Loop + //Takes these inputs: + // keys[] = boolean, true uf these keys are held down, in order: left, right, up, down, space + // fall = boolean, true if this is a "gravity tick". Otherwise, only right/left and rotation is performed + + if (gameOver) { return; } + + //Left/Right + move(keys[0], keys[1]); + + //Rotate + if (keys[2]) { rotate(); } + + //Instant fall + if (keys[4]) { + bool landed = testLanded(); + while (!landed) { + landed = testLanded(); + if (!landed) { cury += 1; } + } + } + + if (fallfast || keys[3]) { + bool landed = testLanded(); + if (landed) { + lock(); + clearRows(); + newPiece(); + } else { + cury += 1; + } + } +} \ No newline at end of file diff --git a/tetris.hpp b/tetris.hpp new file mode 100644 index 0000000..073f995 --- /dev/null +++ b/tetris.hpp @@ -0,0 +1,92 @@ +#ifndef TETRIS_H +#define TETRIS_H + +#include + +#define BOARDWIDTH 10 +#define BOARDHEIGHT 20 + +const int BOARDSIZE = BOARDWIDTH*BOARDHEIGHT; + +//According to the official SRS system +const uint_fast8_t TETROMINOES[7][4][4][2] = { //[Shape][Rotation][Block][0=x 1=y] + { /* I */ + { {0,1}, {1,1}, {2,1}, {3,1} }, + { {2,0}, {2,1}, {2,2}, {2,3} }, + { {0,2}, {1,2}, {2,2}, {3,2} }, + { {1,0}, {1,1}, {1,2}, {1,3} } + }, + { /* J */ + { {0,0}, {0,1}, {1,1}, {2,1} }, + { {2,0}, {1,0}, {1,1}, {1,2} }, + { {0,1}, {1,1}, {2,1}, {2,2} }, + { {1,0}, {1,1}, {1,2}, {0,2} } + }, + { /* L */ + { {0,1}, {1,1}, {2,1}, {2,0} }, + { {0,0}, {1,0}, {1,1}, {1,2} }, + { {0,2}, {0,1}, {1,1}, {2,1} }, + { {1,0}, {1,1}, {1,2}, {2,2} } + }, + { /* Square */ + { {1,0}, {1,1}, {2,0}, {2,1} }, + { {1,0}, {1,1}, {2,0}, {2,1} }, + { {1,0}, {1,1}, {2,0}, {2,1} }, + { {1,0}, {1,1}, {2,0}, {2,1} } + }, + { /* S */ + { {0,1}, {1,1}, {1,0}, {2,0} }, + { {1,0}, {1,1}, {2,1}, {2,2} }, + { {0,2}, {1,2}, {1,1}, {2,1} }, + { {0,0}, {0,1}, {1,1}, {1,2} } + }, + { /* Z */ + { {0,0}, {1,0}, {1,1}, {2,1} }, + { {2,0}, {2,1}, {1,1}, {1,2} }, + { {0,1}, {1,1}, {1,2}, {2,2} }, + { {1,0}, {1,1}, {0,1}, {0,2} } + }, + { /* T */ + { {0,1}, {1,1}, {2,1}, {1,0} }, + { {1,0}, {1,1}, {1,2}, {2,1} }, + { {0,1}, {1,1}, {2,1}, {1,2} }, + { {1,0}, {1,1}, {1,2}, {0,1} } + }, +}; + + +class Tetris { + private: + //std::array, BOARDHEIGHT> staticBoard; + std::array staticBoard; + + int curPiece; + int curx; + int cury; + int curr; + int nextPiece; + + int lines; + int points; + int level; + int levelProgress; + + bool gameOver; + + //Functions + void newPiece(); + bool testLanded(); + void clearRows(); + void lock(); + void rotate(); + bool move(bool right, bool left); + bool fall(); + + public: + std::array getBoard(); + void tick(bool keys[], bool fall); + void init(); + std::array getInfo(); +}; + +#endif \ No newline at end of file