aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAryadev Chavali <aryadev@aryadevchavali.com>2024-05-08 11:14:37 +0530
committerAryadev Chavali <aryadev@aryadevchavali.com>2024-05-08 11:14:37 +0530
commita9797b80ca45cb40948ed0e682656c492c8a3639 (patch)
tree87205b76152e7119b98d9df1833449bb15f49a52
parentd11bd1d21269c3eb48ef154917e3d6efed3cf90c (diff)
downloadsnek-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.cpp255
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();