Initial commit

This commit is contained in:
2026-01-09 14:03:09 +01:00
commit 461139835f
9 changed files with 668 additions and 0 deletions

25
CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.16)
project(CelebratingTetris LANGUAGES CXX)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
include(FetchContent)
FetchContent_Declare(SFML
GIT_REPOSITORY https://github.com/SFML/SFML.git
GIT_TAG 2.6.x)
FetchContent_MakeAvailable(SFML)
add_executable(CelebratingTetris src/main.cpp src/Board.cpp src/Game.cpp)
target_link_libraries(CelebratingTetris PRIVATE sfml-graphics)
target_compile_features(CelebratingTetris PRIVATE cxx_std_17)
if(WIN32)
add_custom_command(
TARGET CelebratingTetris
COMMENT "Copy OpenAL DLL"
PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${SFML_SOURCE_DIR}/extlibs/bin/$<IF:$<EQUAL:${CMAKE_SIZEOF_VOID_P},8>,x64,x86>/openal32.dll $<TARGET_FILE_DIR:CelebratingTetris>
VERBATIM)
endif()
install(TARGETS CelebratingTetris)

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
SRCS = ./src/main.cpp \
./src/Board.cpp \
./src/Game.cpp
NAME = CelebratingTetris
FLAGS = -lsfml-graphics -lsfml-window -lsfml-system
OBJS = $(SRCS:.cpp=.o)
COMPILER = clang++
all: $(NAME)
$(NAME): $(OBJS)
$(COMPILER) -o $(NAME) $(FLAGS) $(OBJS)
clean:
rm -fr $(OBJS)
fclean: clean
rm -fr $(NAME)
re: fclean all

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# Celebrating Tetris
This is a little trial to see if I could make a tetris game within 24 hours.
Linux dependencies :
* libxrandr-dev
* libxcursor-dev
* libudev-dev
* libfreetype-dev
* libopenal-dev
* libflac-dev
* libvorbis-dev
* libgl1-mesa-dev
* libegl1-mesa-dev
## Building
```bash
mkdir build
cd build
cmake ..
make
./build/CelebratingTetris
```

22
includes/Board.hpp Normal file
View File

@@ -0,0 +1,22 @@
// Celebrating tetris by Ley 2024
#include "./Game.hpp"
#pragma once
class Board {
public:
Board();
~Board();
void Draw(sf::RenderWindow &win);
void Clear();
int DoesFit(const Tetrominos::Names name, const int rot,
const sf::Vector2<int> pos);
void PlaceTetromino(const Tetrominos::Names name, const int rot,
const sf::Vector2<int> pos);
void CheckLines();
protected:
int isInBoard(const sf::Vector2<int> pos);
private:
int buffer_[10][20];
};

14
includes/Game.hpp Normal file
View File

@@ -0,0 +1,14 @@
// Celebrating tetris by Ley 2024
#pragma once
// SMFL headers
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
// Cxx headers
#include <iostream>
#include "./Tetrominos.hpp"
sf::Color getTetrominoColor(const Tetrominos::Names &name);

212
includes/Tetrominos.hpp Normal file
View File

@@ -0,0 +1,212 @@
// Celebrating tetris by Ley 2024
#pragma once
#include <SFML/Graphics/Color.hpp>
struct Tetrominos {
enum Names {
I,
J,
L,
O,
S,
T,
Z,
None
};
// [shape name][rot pos][y][z]
constexpr static int shapes[8][4][4][4] = {
{ // I
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0}
},
{
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0}
},
{
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0}
}
},
{ // J
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 1},
{0, 0, 0, 1}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 1, 1, 0}
},
{
{0, 0, 0, 0},
{0, 1, 0, 0},
{0, 1, 1, 1},
{0, 0, 0, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 0, 1, 0},
{0, 0, 1, 0}
}
},
{ // L
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 1},
{0, 1, 0, 0}
},
{
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 0, 1},
{0, 1, 1, 1},
{0, 0, 0, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 1}
}
},
{ // 0
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 0, 1, 1}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 0, 1, 1}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 0, 1, 1}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 0, 1, 1}
}
},
{ // S
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 1, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 1, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1}
}
},
{ // T
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 1},
{0, 0, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 1, 1, 0},
{0, 0, 1, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 1, 1, 1},
{0, 0, 0, 0}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 1, 0}
}
},
{ // Z
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 1}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 1, 1, 0},
{0, 1, 0, 0}
},
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 1}
},
{
{0, 0, 0, 0},
{0, 0, 1, 0},
{0, 1, 1, 0},
{0, 1, 0, 0}
}
},
{ // None
{
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}
}
}
};
};

103
src/Board.cpp Normal file
View File

@@ -0,0 +1,103 @@
// Celebrating tetris by Ley 2024
#include "../includes/Board.hpp"
Board::Board() {
Clear();
}
Board::~Board() {
}
void Board::Draw(sf::RenderWindow &win) {
sf::RectangleShape board({320.f, 640.f});
board.setOutlineThickness(4.f);
board.setOutlineColor({});
sf::RectangleShape cell({28.f, 28.f});
cell.setOutlineThickness(4.f);
cell.setOutlineColor({});
// Draw board
board.setFillColor(sf::Color());
board.setPosition(16.f, 16.f);
win.draw(board);
// Draw cells
for (int y = 0; y < 20; y++) {
for (int x = 0; x < 10; x++) {
cell.setFillColor(getTetrominoColor(static_cast<Tetrominos::Names>(buffer_[x][y])));
cell.setPosition(x * 34.f + 16.f, y * 34.f + 16.f);
win.draw(cell);
}
}
}
void Board::Clear() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 20; j++) {
buffer_[i][j] = Tetrominos::Names::None;
}
}
}
int Board::isInBoard(const sf::Vector2<int> pos) {
if (pos.y >= 20) {
return 1;
}
if (pos.x < 0 || pos.x >= 10) {
return 1;
}
return 0;
}
int Board::DoesFit(const Tetrominos::Names name, const int rot,
const sf::Vector2<int> pos) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (Tetrominos::shapes[name][rot][y][x] == 1) {
if (y + pos.y >= 0) {
if (isInBoard({x + pos.x, y + pos.y})) {
return 0;
}
if (buffer_[x + pos.x][y + pos.y] != Tetrominos::Names::None){
return 0;
}
}
}
}
}
return 1;
}
void Board::PlaceTetromino(const Tetrominos::Names name, const int rot,
const sf::Vector2<int> pos) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (Tetrominos::shapes[name][rot][y][x] == 1) {
buffer_[x + pos.x][y + pos.y] = name;
}
}
}
}
void Board::CheckLines() {
bool is_line;
for (int j = 0; j < 20; j++) {
is_line = true;
for (int i = 0; i < 10; i++) {
if (buffer_[i][j] == Tetrominos::Names::None) {
is_line = false;
}
}
if (is_line) {
for (int y = j; y > 0; y--) {
for (int x = 0; x < 10; x++) {
buffer_[x][y] = buffer_[x][y - 1];
}
}
CheckLines();
return;
}
}
}

35
src/Game.cpp Normal file
View File

@@ -0,0 +1,35 @@
// Celebrating tetris by Ley 2024
#include "../includes/Game.hpp"
sf::Color getTetrominoColor(const Tetrominos::Names &name) {
switch (name) {
case Tetrominos::Names::I:
return(sf::Color::Cyan);
break;
case Tetrominos::Names::J:
return(sf::Color::Blue);
break;
case Tetrominos::Names::L:
return(sf::Color(255, 127, 0, 255));
break;
case Tetrominos::Names::O:
return(sf::Color::Yellow);
break;
case Tetrominos::Names::S:
return(sf::Color::Green);
break;
case Tetrominos::Names::T:
return(sf::Color::Magenta);
break;
case Tetrominos::Names::Z:
return(sf::Color::Red);
break;
case Tetrominos::Names::None:
return(sf::Color::Black);
break;
default:
break;
};
return sf::Color::Black;
}

206
src/main.cpp Normal file
View File

@@ -0,0 +1,206 @@
// Celebrating tetris by Ley 2024
#include "../includes/Game.hpp"
#include "../includes/Board.hpp"
#include <ctime>
#include <cstdlib>
void draw_tetromino(sf::RenderWindow &window, const Tetrominos::Names &name,
const sf::Vector2<float> &pos, int anim) {
sf::RectangleShape cell({28.f, 28.f});
cell.setOutlineThickness(4.f);
cell.setOutlineColor({});
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (Tetrominos::shapes[name][anim][y][x] == 1) {
cell.setPosition(x * 34.f + (16.f + pos.x * 34.f),
y * 34.f + (16.f + pos.y * 34.f));
cell.setFillColor(getTetrominoColor(name));
window.draw(cell);
}
}
}
}
int main(int ac, char **av) {
(void)ac;
sf::Clock clock;
sf::Clock input_clock;
bool left_rotating = false;
bool right_rotating = false;
sf::RectangleShape top_hider({400.f, 12.f});
sf::RectangleShape pred_box({155.f, 77.f});
pred_box.setPosition(365.f, 586.f);
sf::RenderWindow window(sf::VideoMode(520, 700), av[0]+2);
Board board;
top_hider.setFillColor({40, 40, 40, 255});
float descending_delay = 500.f;
float input_delay = 100.f;
sf::Vector2<int> pos = {3, -2};
int anim_id = 0;
std::srand(std::time(nullptr));
int pred_shape = static_cast<Tetrominos::Names>(std::rand() / ((RAND_MAX + 1u) / 7));
int shape = static_cast<Tetrominos::Names>(std::rand() / ((RAND_MAX + 1u) / 7));
while (window.isOpen())
{
auto ElapsedTime = clock.getElapsedTime();
auto KeyElapsedTime = input_clock.getElapsedTime();
sf::Joystick::update();
// Joystick
if (sf::Joystick::isConnected(0)) {
auto buttonCount = sf::Joystick::getButtonCount(0);
for (int i = 0; i <= buttonCount; i++) {
std::cout << i << " : " << sf::Joystick::isButtonPressed(0, i) << std::endl;
}
// Moving
if (KeyElapsedTime.asMilliseconds() > input_delay) {
// Left & Right
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::A) && sf::Keyboard::isKeyPressed(sf::Keyboard::D))) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x - 1, pos.y})) {
pos.x--;
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x + 1, pos.y})) {
pos.x++;
}
}
}
// Speeding down
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x, pos.y + 1})) {
pos.y++;
}
}
input_clock.restart();
}
// Rotating
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::K) && sf::Keyboard::isKeyPressed(sf::Keyboard::L))) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::K) && left_rotating == false) {
int next_anim = anim_id - 1;
if (next_anim < 0) {
next_anim = 3;
}
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), next_anim, {pos.x, pos.y})) {
anim_id = next_anim;
}
left_rotating = true;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::L) && right_rotating == false) {
int next_anim = anim_id + 1;
if (next_anim > 3) {
next_anim = 0;
}
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), next_anim, {pos.x, pos.y})) {
anim_id = next_anim;
}
right_rotating = true;
}
}
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::K) && left_rotating == true) {
left_rotating = false;
}
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::L) && right_rotating == true) {
right_rotating = false;
}
} else { // Keyboard
// Moving
if (KeyElapsedTime.asMilliseconds() > input_delay) {
// Left & Right
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::A) && sf::Keyboard::isKeyPressed(sf::Keyboard::D))) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x - 1, pos.y})) {
pos.x--;
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x + 1, pos.y})) {
pos.x++;
}
}
}
// Speeding down
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x, pos.y + 1})) {
pos.y++;
}
}
input_clock.restart();
}
// Rotating
if (!(sf::Keyboard::isKeyPressed(sf::Keyboard::K) && sf::Keyboard::isKeyPressed(sf::Keyboard::L))) {
if (sf::Keyboard::isKeyPressed(sf::Keyboard::K) && left_rotating == false) {
int next_anim = anim_id - 1;
if (next_anim < 0) {
next_anim = 3;
}
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), next_anim, {pos.x, pos.y})) {
anim_id = next_anim;
}
left_rotating = true;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::L) && right_rotating == false) {
int next_anim = anim_id + 1;
if (next_anim > 3) {
next_anim = 0;
}
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), next_anim, {pos.x, pos.y})) {
anim_id = next_anim;
}
right_rotating = true;
}
}
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::K) && left_rotating == true) {
left_rotating = false;
}
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::L) && right_rotating == true) {
right_rotating = false;
}
}
// Scrolling down
if (ElapsedTime.asMilliseconds() > descending_delay) {
if (board.DoesFit(static_cast<Tetrominos::Names>(shape), anim_id,
{pos.x, pos.y + 1})) {
pos.y++;
} else {
board.PlaceTetromino(static_cast<Tetrominos::Names>(shape), anim_id, {pos.x, pos.y});
board.CheckLines();
pos = {3, -2};
anim_id = 0;
shape = pred_shape;
pred_shape = static_cast<Tetrominos::Names>(std::rand() / ((RAND_MAX + 1u) / 7));
}
//anim_id++;
if (anim_id >= 4) {
anim_id = 0;
}
clock.restart();
}
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
// UI
window.clear(sf::Color(40, 40, 40, 255));
board.Draw(window);
window.draw(pred_box);
// Game
draw_tetromino(window, static_cast<Tetrominos::Names>(shape), {static_cast<float>(pos.x), static_cast<float>(pos.y)}, anim_id);
window.draw(top_hider);
draw_tetromino(window, static_cast<Tetrominos::Names>(pred_shape), {10.5f, 15.f}, 0);
window.display();
}
return 0;
}