commit 982624bb5bbd94f37803457f6ed4a9d0de1bb49a Author: Cole Date: Thu Jan 11 12:55:43 2024 -0600 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75041ec --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +./src/tetris-clone \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..837cb22 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "~/src/raylib/src/**", + "/usr/include/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c99", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a12df9c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.m": "c", + "tetromino.h": "c" + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9535ae1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e8ee92 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Tetris Clone + +Creating a Tetris clone in C to practice C programming and learn more about Raylib (awesome library btw!). + +## Description + +This game is a clone of the popular game Tetris. This clone features what you would expect from a simplistic version of Tetris, namely: falling blocks, which upon completion of a row disappear, score keeping and levels with incremental difficulty. + +I hope you enjoy! + +## Getting Started + +### Dependencies + +* Describe any prerequisites, libraries, OS version, etc., needed before installing program. + +### Installing + +* How/where to download your program +* Any modifications needed to be made to files/folders + +### Executing program + +* How to run the program +* Step-by-step bullets +``` +code blocks for commands +``` + +## Help + +## Authors + +Contributors names and contact info + +John Landers [jcolelanders@gmail.com](mailto:jcolelanders@gmail.com) + +## Version History + +* 0.1 + * Initial Release + +## License + +This project is licensed under the MIT License - see the LICENSE file for details + +## Acknowledgments + +Inspiration, code snippets, etc. +* [Tetris](https://en.wikipedia.org/wiki/Tetris) +* [raylib](https://github.com/raysan5/raylib) \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..037ca3f --- /dev/null +++ b/TODO @@ -0,0 +1,6 @@ +- Rotate (Partially Implemented/Buggy. Should try to understand Rotation Matrix better.) +- Empty rows that are full should "break" +- Shift rows down after row "breaks" +- Track and Display Score. During and After game +- display Upcoming block +- Introduce levels/speed up with every level \ No newline at end of file diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..8d1f2bc --- /dev/null +++ b/src/Makefile @@ -0,0 +1,13 @@ +CC=clang +CFLAGS=-Wall -Wextra -include raylib.h +LIBS=-lraylib -lGL -lm -lpthread -ldl -lrt -lX11 + +tetris-clone: main.c manager.c renderer.c app.c + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean + +clean: + rm -f ./tetris-clone + rm -f ./*.o + rm -f ./*.gch diff --git a/src/app.c b/src/app.c new file mode 100644 index 0000000..b994642 --- /dev/null +++ b/src/app.c @@ -0,0 +1,71 @@ +#include + +#include "raylib.h" +#include "manager.h" +#include "renderer.h" +#include "tetromino.h" + +#define TARGET_FPS 60 + +void EmptyGameBoard(void) { + EmptyAllBlocks(); + + int numberOfUpdatedBlocks = 0; + struct TetrominoBlock** updatedBlocks = GetUpdatedBlocks(&numberOfUpdatedBlocks); +} + +void InitRaylib(void) { + InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Tetris Clone"); + + SetTargetFPS(TARGET_FPS); +} + +void Init(void) { + InitRaylib(); + InitManager(); + InitRenderer(); + EmptyGameBoard(); +} + +void ProcessInput(void) { + if (IsKeyPressed(KEY_SPACE)) { + RequestRotate(); + } + if (IsKeyDown(KEY_A)) { + RequestMoveLeft(); + } else if (IsKeyDown(KEY_D)) { + RequestMoveRight(); + } +} + +void Loop(void) { + int tickRate = 0; // Guaranteed a better way to do this XD + while(!WindowShouldClose()) { + while(!CantSpawnBlock()) { + ProcessInput(); + if (tickRate == 0) { + Update(); + tickRate = 5; + } + + int numberOfUpdatedBlocks = 0; + struct TetrominoBlock** updatedBlocks = GetUpdatedBlocks(&numberOfUpdatedBlocks); + + RenderBlocks(updatedBlocks, numberOfUpdatedBlocks); + tickRate--; + } + // TODO present Game over message and score + RenderGameOver(); + } +} + +void Cleanup() { + CleanupManager(); + CloseWindow(); +} + +void start(void) { + Init(); + Loop(); + Cleanup(); +} \ No newline at end of file diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..c23d9a1 --- /dev/null +++ b/src/app.h @@ -0,0 +1,6 @@ +#ifndef TETRIS_CLONE_APP_H_ +#define TETRIS_CLONE_APP_H_ + +void start(void); + +#endif // TETRIS_CLONE_APP_H_ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..977982c --- /dev/null +++ b/src/main.c @@ -0,0 +1,7 @@ +#include "app.h" + +int main(void) { + start(); + + return 0; +} diff --git a/src/manager.c b/src/manager.c new file mode 100644 index 0000000..29d50d1 --- /dev/null +++ b/src/manager.c @@ -0,0 +1,418 @@ +#include +#include +#include +#define __USE_MISC +#include + +#include "manager.h" + +#define FIELD_WIDTH 10 +#define FIELD_HEIGHT 20 +#define BLOCKS_WITHIN_A_TETROMINO 4 + +// should probably call these offsets +struct Offset { + int xOffset; + int yOffset; +}; + +struct Point { + int x; + int y; +}; + +struct Tetromino { + enum TetrominoType type; + struct TetrominoBlock* blocks[BLOCKS_WITHIN_A_TETROMINO]; +}; + + +// To find block (x,y) index = (x % FIELD_WIDTH) + (y * FIELD_WIDTH) +// Height doesn't matter but height is just the factor of how tall the board is. +// And in the end each row is just the next representation of width * units, height just determines row count. +// Doesn't factor into determining X,Y +struct TetrominoBlock* _blocks[FIELD_HEIGHT * FIELD_WIDTH]; +struct TetrominoBlock* _updatedBlocks[FIELD_HEIGHT * FIELD_WIDTH] = { NULL }; +int _updatedBlockLength = 0; +bool shouldSpawnTetromino; +struct Tetromino fallingTetromino; +const char kSpawnX = FIELD_WIDTH / 2 - 1; +const struct Offset kShiftDownOffset = { .xOffset = 0, .yOffset = 1 }; +const struct Offset kShiftLeftOffset = { .xOffset = -1, .yOffset = 0 }; +const struct Offset kShiftRightOffset = { .xOffset = 1, .yOffset = 0 }; + +bool moveLeft = false; +bool moveRight = false; +bool rotate = false; +bool cannotSpawn = false; + +void CleanupManager(void) { + for(int i = 0; i < sizeof(_blocks) / sizeof(struct TetrominoBlock*); i++) { + free(_blocks[i]); + } +} + +struct TetrominoBlock** GetUpdatedBlocks(int* numberOfUpdatedBlocks) { + *numberOfUpdatedBlocks = _updatedBlockLength; + + return &_updatedBlocks[0]; +} + +void RequestRotate(void) { + rotate = true; +} + +void RequestMoveLeft(void) { + moveLeft = true; + moveRight = false; +} + +void RequestMoveRight(void) { + moveRight = true; + moveLeft = false; +} + +struct TetrominoBlock* GetBlockAtPoint(struct Point point) { + return _blocks[(point.x % FIELD_WIDTH) + (point.y * FIELD_WIDTH)]; +} + +void RegisterUpdatedBlock(struct TetrominoBlock* pTetrominoBlock) { + if (_updatedBlockLength < FIELD_HEIGHT * FIELD_WIDTH) { + _updatedBlocks[_updatedBlockLength] = pTetrominoBlock; + _updatedBlockLength++; + } +} + +void UpdateBlockType(struct TetrominoBlock* pTetrominoBlock, enum TetrominoType type) { + pTetrominoBlock->type = type; + RegisterUpdatedBlock(pTetrominoBlock); +} + +void ShiftFallingTetrominoByOffset(struct Offset offset) { + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + UpdateBlockType(fallingTetromino.blocks[i], EMPTY); + RegisterUpdatedBlock(fallingTetromino.blocks[i]); + } + + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + struct Point updatedPoint = { .x = fallingTetromino.blocks[i]->x + offset.xOffset, .y = fallingTetromino.blocks[i]->y + offset.yOffset}; + fallingTetromino.blocks[i] = GetBlockAtPoint(updatedPoint); + + UpdateBlockType(fallingTetromino.blocks[i], fallingTetromino.type); + RegisterUpdatedBlock(fallingTetromino.blocks[i]); + } +} + +// I HATE THIS NAME... but it's verbose for now TODO - RENAME +bool DoesPointIntersectNonFallingBlock(struct Point point) { + bool result = false; + + struct TetrominoBlock* blockAtPoint = GetBlockAtPoint(point); + + if (blockAtPoint->type != EMPTY) { + result = true; + + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + if (fallingTetromino.blocks[i] == blockAtPoint) { + result = false; + break; + } + } + } + + return result; +} + +bool CanBlockFall(struct TetrominoBlock block) { + struct Point newBlockPoint = { .x = block.x, .y = block.y + 1}; + if (newBlockPoint.y >= FIELD_HEIGHT) { + return false; + } else if (DoesPointIntersectNonFallingBlock(newBlockPoint)) { + return false; + } else { + return true; + } +} + +bool FallingTetrominoCanFall(void) { + bool result = true; + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + if (!CanBlockFall(*(fallingTetromino.blocks[i]))) { + result = false; + break; + } + } + + return result; +} + +void UpdateFallingTetromino(void) { + if(FallingTetrominoCanFall()) { + ShiftFallingTetrominoByOffset(kShiftDownOffset); + } else { + shouldSpawnTetromino = true; + } +} + +bool CanBlockMoveLeft(struct TetrominoBlock block) { + struct Point newBlockPoint = { .x = block.x - 1, .y = block.y }; + if (newBlockPoint.x < 0) { + return false; + } else if (DoesPointIntersectNonFallingBlock(newBlockPoint)) { + return false; + } else { + return true; + } +} + +bool FallingTetrominoCanMoveLeft(void) { + bool result = true; + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + if (!CanBlockMoveLeft(*(fallingTetromino.blocks[i]))) { + result = false; + break; + } + } + + return result; +} + +bool CanBlockMoveRight(struct TetrominoBlock block) { + struct Point newBlockPoint = { .x = block.x + 1, .y = block.y }; + if (newBlockPoint.x >= FIELD_WIDTH) { + return false; + } else if (DoesPointIntersectNonFallingBlock(newBlockPoint)) { + return false; + } else { + return true; + } +} + +bool FallingTetrominoCanMoveRight(void) { + bool result = true; + for(int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + if (!CanBlockMoveRight(*(fallingTetromino.blocks[i]))) { + result = false; + break; + } + } + + return result; +} + +struct Point GetRotatedPointForBlock(struct Point origin, struct TetrominoBlock block) { + // Only doing counter clockwise for now + struct Point newPoint; + newPoint.x = (block.x - origin.x) * cos(M_PI_2) - (block.y - origin.y) * sin(M_PI_2); + newPoint.y = (block.x - origin.x) * sin(M_PI_2) + (block.y - origin.y) * cos(M_PI_2); + + return newPoint; +} + +void RotateFallingTetromino(void) { + // Will be a bit buggy because i'm not checking for intersections + struct Point origin = { .x = fallingTetromino.blocks[0]->x, .y = fallingTetromino.blocks[0]->y }; + + for (int i = 0; i < BLOCKS_WITHIN_A_TETROMINO; i++) { + // Empty the old + UpdateBlockType(fallingTetromino.blocks[i], EMPTY); + RegisterUpdatedBlock(fallingTetromino.blocks[i]); + + // Update the new + struct Point rotatedPointAdjustedForOrigin = GetRotatedPointForBlock(origin, *fallingTetromino.blocks[i]); + struct Point newRotatedPoint = { .x = rotatedPointAdjustedForOrigin.x + origin.x, .y = rotatedPointAdjustedForOrigin.y + origin.y }; + fallingTetromino.blocks[i] = GetBlockAtPoint(newRotatedPoint); + + UpdateBlockType(fallingTetromino.blocks[i], fallingTetromino.type); + RegisterUpdatedBlock(fallingTetromino.blocks[i]); + } +} + +void TryRotateFallingTetromino(void) { + // TODO IMPLEMENT + if (fallingTetromino.type == O_TYPE) { + return; + } else { + RotateFallingTetromino(); + } +} + +void SpawnBlocksAtPoints(enum TetrominoType type, struct Offset* offsets) { + fallingTetromino.type = type; + int index = 0; + while(index < BLOCKS_WITHIN_A_TETROMINO) { + struct Point spawnPoint; + spawnPoint.x = kSpawnX + offsets->xOffset; + spawnPoint.y = offsets->yOffset; + + struct TetrominoBlock* spawnBlock = GetBlockAtPoint(spawnPoint); + if (spawnBlock->type != EMPTY) { + cannotSpawn = true; + return; + } + UpdateBlockType(spawnBlock, type); + + // I'm not sure if this makes more sense to be here or return a list of spawnedBlocks to be handled in Parent function + fallingTetromino.blocks[index] = spawnBlock; + + index++; + offsets++; + } +} + +enum TetrominoType GenerateRandomTetrominoType() { + srand(time(NULL)); + return (enum TetrominoType) rand() % 6; +} + +void SpawnTetromino() { + enum TetrominoType type = GenerateRandomTetrominoType(); + + struct Offset offsets[BLOCKS_WITHIN_A_TETROMINO]; + + if (type == O_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 0; + + offsets[1].xOffset = 1; + offsets[1].yOffset = 0; + + offsets[2].xOffset = 0; + offsets[2].yOffset = 1; + + offsets[3].xOffset = 1; + offsets[3].yOffset = 1; + } else if (type == J_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 0; + + offsets[1].xOffset = 0; + offsets[1].yOffset = 1; + + offsets[2].xOffset = 1; + offsets[2].yOffset = 1; + + offsets[3].xOffset = 2; + offsets[3].yOffset = 1; + } else if (type == L_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 1; + + offsets[1].xOffset = 1; + offsets[1].yOffset = 1; + + offsets[2].xOffset = 2; + offsets[2].yOffset = 1; + + offsets[3].xOffset = 2; + offsets[3].yOffset = 0; + } else if (type == I_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 0; + + offsets[1].xOffset = 0; + offsets[1].yOffset = 1; + + offsets[2].xOffset = 0; + offsets[2].yOffset = 2; + + offsets[3].xOffset = 0; + offsets[3].yOffset = 3; + } else if (type == S_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 1; + + offsets[1].xOffset = 1; + offsets[1].yOffset = 1; + + offsets[2].xOffset = 1; + offsets[2].yOffset = 0; + + offsets[3].xOffset = 2; + offsets[3].yOffset = 0; + } else if (type == Z_TYPE) { + offsets[0].xOffset = 0; + offsets[0].yOffset = 0; + + offsets[1].xOffset = 1; + offsets[1].yOffset = 0; + + offsets[2].xOffset = 1; + offsets[2].yOffset = 1; + + offsets[3].xOffset = 2; + offsets[3].yOffset = 1; + } else if (type == T_TYPE) { + offsets[0].xOffset = 1; + offsets[0].yOffset = 0; + + offsets[1].xOffset = 0; + offsets[1].yOffset = 1; + + offsets[2].xOffset = 1; + offsets[2].yOffset = 1; + + offsets[3].xOffset = 2; + offsets[3].yOffset = 1; + } + + SpawnBlocksAtPoints(type, offsets); +} + +void DeregisterUpdatedBlocks() { + _updatedBlockLength = 0; +} + +void Update(void) { + DeregisterUpdatedBlocks(); + if (shouldSpawnTetromino) { + moveLeft = false; + moveRight = false; + SpawnTetromino(); + shouldSpawnTetromino = false; + } else { + // TODO move left and right refactor naming + + if(rotate) { + TryRotateFallingTetromino(); + rotate = false; + } + + if (moveRight && FallingTetrominoCanMoveRight()) { + ShiftFallingTetrominoByOffset(kShiftRightOffset); + moveRight = false; + } + + if (moveLeft && FallingTetrominoCanMoveLeft()) { + ShiftFallingTetrominoByOffset(kShiftLeftOffset); + moveLeft = false; + } + + UpdateFallingTetromino(); + } +} + +bool CantSpawnBlock(void) { + return cannotSpawn; +} + +void EmptyAllBlocks(void) { + for (int i = 0; i < FIELD_HEIGHT * FIELD_WIDTH; i++) { + UpdateBlockType(_blocks[i], EMPTY); + } +} + +void AllocBlocks(void) { + for (int i = 0; i < FIELD_HEIGHT * FIELD_WIDTH; i++) { + struct TetrominoBlock* block = calloc(1, sizeof(struct TetrominoBlock)); + block->x = i % FIELD_WIDTH; + block->y = i / FIELD_WIDTH; + + _blocks[i] = block; + } +} + +void InitManager(void) { + AllocBlocks(); + shouldSpawnTetromino = true; +} \ No newline at end of file diff --git a/src/manager.h b/src/manager.h new file mode 100644 index 0000000..db6086a --- /dev/null +++ b/src/manager.h @@ -0,0 +1,18 @@ +#ifndef TETRIS_CLONE_MANAGER_H_ +#define TETRIS_CLONE_MANAGER_H_ + +#include + +#include "tetromino.h" + +void InitManager(void); +void EmptyAllBlocks(void); +bool CantSpawnBlock(void); +void Update(void); +void RequestRotate(void); +void RequestMoveLeft(void); +void RequestMoveRight(void); +struct TetrominoBlock** GetUpdatedBlocks(int* lengthOfBlocksUpdated); +void CleanupManager(void); + +#endif diff --git a/src/renderer.c b/src/renderer.c new file mode 100644 index 0000000..0de371a --- /dev/null +++ b/src/renderer.c @@ -0,0 +1,76 @@ +#include +#include "raylib.h" + +#include "renderer.h" + +#define PNG_RES(name) "resources/" #name ".png" +#define BLOCK_SIZE 32 + +Texture2D yellowTexture; +Texture2D blueTexture; +Texture2D lightBlueTexture; +Texture2D redTexture; +Texture2D purpleTexture; +Texture2D greenTexture; +Texture2D orangeTexture; +Texture2D emptyTexture; +Texture2D gameOverTexture; + +void InitRenderer(void) { + yellowTexture = LoadTexture(PNG_RES(yellow)); + blueTexture = LoadTexture(PNG_RES(blue)); + lightBlueTexture = LoadTexture(PNG_RES(light-blue)); + redTexture = LoadTexture(PNG_RES(red)); + purpleTexture = LoadTexture(PNG_RES(purple)); + greenTexture = LoadTexture(PNG_RES(green)); + orangeTexture = LoadTexture(PNG_RES(orange)); + emptyTexture = LoadTexture(PNG_RES(black)); + gameOverTexture = LoadTexture(PNG_RES(gameOver)); +} + +Texture2D* GetTextureFromTetrominoType(enum TetrominoType type) { + switch (type) { + case O_TYPE: + return &yellowTexture; + case I_TYPE: + return &lightBlueTexture; + case T_TYPE: + return &purpleTexture; + case J_TYPE: + return &blueTexture; + case L_TYPE: + return &orangeTexture; + case S_TYPE: + return &greenTexture; + case Z_TYPE: + return &redTexture; + default: + return &emptyTexture; + } +} + +void RenderTetrominoBlock(struct TetrominoBlock blockToRender) { + Texture2D* textureToRender = GetTextureFromTetrominoType(blockToRender.type); + DrawTexture(*textureToRender, blockToRender.x * BLOCK_SIZE, blockToRender.y * BLOCK_SIZE, WHITE); +} + +void RenderBlocks(struct TetrominoBlock** updatedBlocks, int length) { + BeginDrawing(); + + while(length--) { + RenderTetrominoBlock(*(*updatedBlocks)); + updatedBlocks++; + } + + EndDrawing(); +} + +void RenderGameOver(void) { + BeginDrawing(); + + ClearBackground(BLACK); + DrawText("Game Over", SCREEN_WIDTH / 2 - MeasureText("Game Over", 24) / 2, 200, 24, RED); + DrawText("Score: N/A", SCREEN_WIDTH / 2 - MeasureText("Score: N/A", 18) / 2, 232, 18, RED); + + EndDrawing(); +} \ No newline at end of file diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..3a9db69 --- /dev/null +++ b/src/renderer.h @@ -0,0 +1,13 @@ +#ifndef TETRIS_CLONE_RENDERER_H_ +#define TETRIS_CLONE_RENDERER_H_ + +#include "tetromino.h" + +#define SCREEN_HEIGHT 640 +#define SCREEN_WIDTH 320 + +void InitRenderer(void); +void RenderBlocks(struct TetrominoBlock** updatedBlocks, int length); +void RenderGameOver(void); + +#endif diff --git a/src/resources/black.png b/src/resources/black.png new file mode 100644 index 0000000..d0f2187 Binary files /dev/null and b/src/resources/black.png differ diff --git a/src/resources/blue.png b/src/resources/blue.png new file mode 100644 index 0000000..de06341 Binary files /dev/null and b/src/resources/blue.png differ diff --git a/src/resources/green.png b/src/resources/green.png new file mode 100644 index 0000000..1263e59 Binary files /dev/null and b/src/resources/green.png differ diff --git a/src/resources/light-blue.png b/src/resources/light-blue.png new file mode 100644 index 0000000..23c3f39 Binary files /dev/null and b/src/resources/light-blue.png differ diff --git a/src/resources/orange.png b/src/resources/orange.png new file mode 100644 index 0000000..f23b6d6 Binary files /dev/null and b/src/resources/orange.png differ diff --git a/src/resources/purple.png b/src/resources/purple.png new file mode 100644 index 0000000..19f10b4 Binary files /dev/null and b/src/resources/purple.png differ diff --git a/src/resources/red.png b/src/resources/red.png new file mode 100644 index 0000000..958e9a5 Binary files /dev/null and b/src/resources/red.png differ diff --git a/src/resources/yellow.png b/src/resources/yellow.png new file mode 100644 index 0000000..661058a Binary files /dev/null and b/src/resources/yellow.png differ diff --git a/src/tetris-clone b/src/tetris-clone new file mode 100755 index 0000000..22b581d Binary files /dev/null and b/src/tetris-clone differ diff --git a/src/tetromino.h b/src/tetromino.h new file mode 100644 index 0000000..9fa4019 --- /dev/null +++ b/src/tetromino.h @@ -0,0 +1,21 @@ +#ifndef TETRIS_CLONE_TETROMINO_H_ +#define TETRIS_CLONE_TETROMINO_H_ + +enum TetrominoType { + I_TYPE, + J_TYPE, + L_TYPE, + O_TYPE, + S_TYPE, + T_TYPE, + Z_TYPE, + EMPTY +}; + +struct TetrominoBlock { + unsigned char x; + unsigned char y; + enum TetrominoType type; +}; + +#endif