diff options
author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2024-05-08 11:14:37 +0530 |
---|---|---|
committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2024-05-08 11:14:37 +0530 |
commit | a9797b80ca45cb40948ed0e682656c492c8a3639 (patch) | |
tree | 87205b76152e7119b98d9df1833449bb15f49a52 | |
parent | d11bd1d21269c3eb48ef154917e3d6efed3cf90c (diff) | |
download | snek-a9797b80ca45cb40948ed0e682656c492c8a3639.tar.gz snek-a9797b80ca45cb40948ed0e682656c492c8a3639.tar.bz2 snek-a9797b80ca45cb40948ed0e682656c492c8a3639.zip |
Introduced main game loop, a player character, fruits and walls
Game resets on player colliding with themselves or a wall.
Fruit increases size of player, and player's body follows the head.
There is text on the top left corner which shows time elapsed and
current score.
-rw-r--r-- | src/main.cpp | 255 |
1 files changed, 254 insertions, 1 deletions
diff --git a/src/main.cpp b/src/main.cpp index 68a69b9..fe2c2ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,19 +9,272 @@ * Description: Entrypoint */ +#include <algorithm> +#include <chrono> +#include <cstring> +#include <iostream> #include <raylib.h> +#include <sstream> +#include <string> +#include <vector> #define WIDTH 800 #define HEIGHT 600 +long mod(long a, long b) +{ + return (a % b + b) % b; +} + +enum class Type +{ + EMPTY, + WALL, + FRUIT, +}; + +enum class Direction +{ + UP = 1, + LEFT = 2, + RIGHT = -2, + DOWN = -1 +}; + +struct Point +{ + int x, y; + + bool operator==(const struct Point &p) const + { + return x == p.x && y == p.y; + } + + Point() : x{0}, y{0} + {} + + Point(int x, int y) : x{x}, y{y} + {} + + Point(Direction dir) + { + switch (dir) + { + case Direction::UP: + *this = Point{0, -1}; + break; + case Direction::DOWN: + *this = Point{0, 1}; + break; + case Direction::LEFT: + *this = Point{-1, 0}; + break; + case Direction::RIGHT: + *this = Point{1, 0}; + break; + } + } +}; + +template <size_t a, size_t b> +struct State +{ + static constexpr double mx = WIDTH / a; + static constexpr double my = HEIGHT / b; + static constexpr double square_size = mx < my ? mx : my; + + struct Player + { + std::vector<Point> points; + } player; + + Type grid[a][b]; + + bool is_player(size_t x, size_t y) const + { + return std::find(player.points.begin(), player.points.end(), + Point{(int)x, (int)y}) != player.points.end(); + } + + double rescale(size_t grid, bool smaller, double translation) const + { + double x = grid * square_size; + if (smaller) + x += translation; + return x; + } + + void draw_grid() const + { + for (size_t x = 0; x < a; ++x) + { + double x_ = rescale(x, HEIGHT < WIDTH, (WIDTH - HEIGHT) / 2); + for (size_t y = 0; y < b; ++y) + { + double y_ = rescale(y, WIDTH < HEIGHT, (HEIGHT - WIDTH) / 2); + + if (grid[x][y] == Type::EMPTY) + DrawRectangleLines(x_, y_, square_size, square_size, WHITE); + else if (grid[x][y] == Type::WALL) + DrawRectangle(x_, y_, square_size, square_size, WHITE); + else if (grid[x][y] == Type::FRUIT) + DrawRectangle(x_, y_, square_size, square_size, RED); + } + } + + for (const auto p : player.points) + { + double x_ = rescale(p.x, HEIGHT < WIDTH, (WIDTH - HEIGHT) / 2); + double y_ = rescale(p.y, WIDTH < HEIGHT, (HEIGHT - WIDTH) / 2); + DrawRectangle(x_, y_, square_size, square_size, GREEN); + } + } + + std::vector<Point>::iterator player_head(void) + { + return player.points.begin(); + } + + bool update_player_head(Point dir) + { + auto head = player_head(); + Point old_position = *head; + Point new_position = old_position; + new_position.y += dir.y; + new_position.x += dir.x; + new_position.x = mod(new_position.x, a); + new_position.y = mod(new_position.y, b); + + if (is_player(new_position.x, new_position.y) || + grid[new_position.x][new_position.y] == Type::WALL) + return true; + + *head = new_position; + + for (size_t i = 1; i < player.points.size(); ++i) + { + Point cpy = player.points[i]; + player.points[i] = old_position; + old_position = cpy; + } + return false; + } + + void player_fruit_collision(void) + { + const auto point = player_head(); + if (grid[point->x][point->y] == Type::FRUIT) + { + grid[point->x][point->y] = Type::EMPTY; + std::vector<Point>::iterator last = player.points.end() - 1; + player.points.push_back({last->x + 1, last->y}); + } + } + + void reset(void) + { + player.points.clear(); + player.points.push_back({a / 2, b / 2}); + memset(grid, 0, sizeof(Type) * a * b); + } +}; + +namespace chrono = std::chrono; + +struct Time +{ + size_t hours, minutes, seconds; + + Time(size_t s) + { + hours = s / 3600; + s -= (hours * 3600); + minutes = s / 60; + s -= (minutes * 60); + seconds = s; + } + + std::string to_str(void) + { + std::stringstream s; + if (hours < 10) + s << "0"; + s << std::to_string(hours) + ":"; + if (minutes < 10) + s << "0"; + s << std::to_string(minutes) + ":"; + if (seconds < 10) + s << "0"; + s << std::to_string(seconds); + return s.str(); + } +}; + +using Clock = chrono::steady_clock; int main(void) { + constexpr size_t X = 50, Y = 50; + State<X, Y> state; + state.player.points.push_back({15, 15}); + for (size_t i = 0; i < X; ++i) + for (size_t j = 0; j < Y; ++j) + state.grid[i][j] = Type::FRUIT; + + Direction dir = Direction::LEFT; InitWindow(WIDTH, HEIGHT, "snek"); + int fps = 10; + SetTargetFPS(fps); + + chrono::time_point<Clock> beg{Clock::now()}; while (!WindowShouldClose()) { + state.player_fruit_collision(); + + if (IsKeyDown(KEY_J) && (Direction)(-((int)dir)) != Direction::DOWN) + dir = Direction::DOWN; + else if (IsKeyDown(KEY_K) && (Direction)(-((int)dir)) != Direction::UP) + dir = Direction::UP; + else if (IsKeyDown(KEY_H) && (Direction)(-((int)dir)) != Direction::LEFT) + dir = Direction::LEFT; + else if (IsKeyDown(KEY_L) && (Direction)(-((int)dir)) != Direction::RIGHT) + dir = Direction::RIGHT; + else if (IsKeyDown(KEY_EQUAL)) + { + ++fps; + if (fps > 60) + fps = 60; + SetTargetFPS(fps); + } + else if (IsKeyDown(KEY_MINUS)) + { + --fps; + if (fps < 0) + fps = 1; + SetTargetFPS(fps); + } + + bool collide = state.update_player_head(dir); + if (collide) + { + state.reset(); + beg = Clock::now(); + } + BeginDrawing(); ClearBackground(BLACK); - DrawTriangle({400, 0}, {100, 300}, {700, 300}, RED); + state.draw_grid(); + DrawText("TIME", 0, 0, 25, YELLOW); + DrawText( + Time(chrono::duration_cast<chrono::seconds>(Clock::now() - beg).count()) + .to_str() + .c_str(), + 0, 25, 25, YELLOW); + + std::stringstream ss; + ss << "Score: "; + ss << state.player.points.size() - 1; + DrawText(ss.str().c_str(), 0, 50, 20, YELLOW); + EndDrawing(); } CloseWindow(); |