mirror of
https://github.com/ProgramSnail/snake_2024.git
synced 2025-12-08 23:48:42 +00:00
initial game
This commit is contained in:
parent
4570d6e593
commit
adabb50a9e
10 changed files with 200 additions and 132 deletions
|
|
@ -1,12 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
#
|
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "Utils.hpp"
|
#include "Vec.hpp"
|
||||||
|
|
||||||
enum class Color : uint32_t {
|
enum class Color : uint32_t {
|
||||||
BLACK = 0x000000ff,
|
BLACK = 0x000000ff,
|
||||||
|
|
@ -25,99 +22,27 @@ constexpr Color CL_BG = Color::BLACK;
|
||||||
|
|
||||||
using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
class CanvasObject {
|
namespace canvas {
|
||||||
public:
|
|
||||||
struct Config {
|
|
||||||
Veci pos;
|
|
||||||
int size;
|
|
||||||
Color color = Color ::WHITE;
|
|
||||||
};
|
|
||||||
CanvasObject(Config config)
|
|
||||||
: pos_(config.pos), size_(config.size), color_(config.color),
|
|
||||||
shown_(false) {}
|
|
||||||
|
|
||||||
virtual void move_to(Veci) = 0;
|
struct Object {
|
||||||
virtual void show() = 0;
|
Veci pos;
|
||||||
virtual void hide() = 0;
|
Color color;
|
||||||
|
|
||||||
virtual ~CanvasObject() {
|
|
||||||
if (shown_) {
|
|
||||||
// hide(); // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Veci pos_;
|
|
||||||
int size_;
|
|
||||||
Color color_;
|
|
||||||
bool shown_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatelessCanvasObject : public CanvasObject {
|
struct Square : public Object {
|
||||||
public:
|
int side;
|
||||||
using CanvasObject::CanvasObject;
|
|
||||||
|
|
||||||
void move_to(Veci pos) override {
|
|
||||||
if (not shown_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hide();
|
|
||||||
pos_ = pos;
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void show() override { fill(color_); }
|
|
||||||
|
|
||||||
void hide() override { fill(CL_BG); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void fill(Color color) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Square : public StatelessCanvasObject {
|
struct Circle : public Object {
|
||||||
public:
|
int radius;
|
||||||
using StatelessCanvasObject::StatelessCanvasObject;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void fill(Color color) override {
|
|
||||||
for (int x = pos_.x; x < pos_.x + size_; ++x) {
|
|
||||||
std::fill(screen_at(x, pos_.y), screen_at(x, pos_.y + size_),
|
|
||||||
static_cast<uint32_t>(color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Circle : public StatelessCanvasObject {
|
} // namespace canvas
|
||||||
public:
|
|
||||||
using StatelessCanvasObject::StatelessCanvasObject;
|
|
||||||
|
|
||||||
protected:
|
namespace paint {
|
||||||
void fill(Color color) override {
|
|
||||||
for (int x = -size_; x < size_; ++x) {
|
|
||||||
int size_y = std::sqrt(size_ * size_ - std::abs(x) * std::abs(x));
|
|
||||||
std::fill(screen_at(pos_.x + x, pos_.y - size_y),
|
|
||||||
screen_at(pos_.x + x, pos_.y + size_y),
|
|
||||||
static_cast<uint32_t>(color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Worm : public CanvasObject {
|
void square(const canvas::Square &s);
|
||||||
Worm(CanvasObject::Config config, size_t max_track_size)
|
|
||||||
: CanvasObject(config), max_track_size_(max_track_size) {}
|
|
||||||
|
|
||||||
protected:
|
void circle(const canvas::Circle &c);
|
||||||
const size_t max_track_size_;
|
|
||||||
std::vector<Circle> track_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// class Canvas {
|
} // namespace paint
|
||||||
// public:
|
|
||||||
// Canvas(Screen &screen) : screen_(screen) {}
|
|
||||||
|
|
||||||
// size_t insert(CanvasObject obj) {}
|
|
||||||
// void erase(size_t id) {}
|
|
||||||
|
|
||||||
// private:
|
|
||||||
// Screen &screen_;
|
|
||||||
// };
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
|
#include "Canvas.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Vec.hpp"
|
#include "Vec.hpp"
|
||||||
|
|
||||||
|
|
@ -13,17 +14,23 @@ class Map : public GameObject {
|
||||||
bool eaten = false;
|
bool eaten = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr double GEN_INTERVAL = 1.0;
|
|
||||||
static constexpr size_t FOOD_EXISTS_GENS = 10;
|
|
||||||
static constexpr size_t GEN_FOOD_COUNT = 1000;
|
|
||||||
static constexpr int SIZE_X = 20000;
|
|
||||||
static constexpr int SIZE_Y = 20000;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct Config {
|
||||||
|
double gen_interval;
|
||||||
|
size_t food_exists_gens;
|
||||||
|
size_t gen_food_count;
|
||||||
|
Veci size;
|
||||||
|
int min_food_weight;
|
||||||
|
int max_food_weight;
|
||||||
|
Color food_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
Map(Config config) : config_(config) {}
|
||||||
|
|
||||||
void act(float dt) override {
|
void act(float dt) override {
|
||||||
time_from_last_gen_ += dt;
|
time_from_last_gen_ += dt;
|
||||||
if (time_from_last_gen_ > GEN_INTERVAL) {
|
if (time_from_last_gen_ > config_.gen_interval) {
|
||||||
time_from_last_gen_ -= GEN_INTERVAL;
|
time_from_last_gen_ -= config_.gen_interval;
|
||||||
generate();
|
generate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -39,23 +46,36 @@ public:
|
||||||
return eaten_weight;
|
return eaten_weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() override {} // TODO
|
void draw(Veci offset) override {
|
||||||
|
for (const auto &food : food_) {
|
||||||
private:
|
Veci food_pos = food.pos - offset;
|
||||||
void generate() {
|
if (utils::is_valid_pos(food_pos) and not food.eaten) {
|
||||||
++current_gen_;
|
paint::circle(
|
||||||
while (food_.front().gen + FOOD_EXISTS_GENS < current_gen_) {
|
{{.pos = food_pos, .color = config_.food_color}, food.weight * 3});
|
||||||
food_.pop_front();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < GEN_FOOD_COUNT; ++i) {
|
|
||||||
food_.push_back({.gen = current_gen_,
|
|
||||||
.pos = {.x = rand() % SIZE_X, .y = rand() % SIZE_Y},
|
|
||||||
.weight = 1});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void generate() {
|
||||||
|
++current_gen_;
|
||||||
|
while (food_.front().gen + config_.food_exists_gens < current_gen_) {
|
||||||
|
food_.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < config_.gen_food_count; ++i) {
|
||||||
|
food_.push_back({
|
||||||
|
.gen = current_gen_,
|
||||||
|
.pos = {.x = rand() % config_.size.x, .y = rand() % config_.size.y},
|
||||||
|
.weight = config_.min_food_weight +
|
||||||
|
rand() % std::abs(config_.max_food_weight -
|
||||||
|
config_.min_food_weight),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Config config_;
|
||||||
double time_from_last_gen_ = 0;
|
double time_from_last_gen_ = 0;
|
||||||
size_t current_gen_ = 0;
|
size_t current_gen_ = 0;
|
||||||
std::deque<Food> food_ = {};
|
std::deque<Food> food_ = {};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class Player {
|
#include "Vec.hpp"
|
||||||
public:
|
|
||||||
private:
|
struct Player {
|
||||||
|
Vecf pos;
|
||||||
|
Vecf direction;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include "Canvas.hpp"
|
||||||
|
|
||||||
|
namespace canvas {
|
||||||
|
|
||||||
|
struct WormObject : public Object {
|
||||||
|
size_t length;
|
||||||
|
int radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
|
||||||
|
class Worm : protected canvas::WormObject {
|
||||||
|
public:
|
||||||
|
Worm(canvas::WormObject obj) : WormObject(obj), track_{pos} {}
|
||||||
|
|
||||||
|
void add(Veci pos);
|
||||||
|
|
||||||
|
void draw(Veci offset = {}) const;
|
||||||
|
|
||||||
|
size_t get_length() { return length; }
|
||||||
|
|
||||||
|
void set_length(size_t length) { this->length = length; }
|
||||||
|
|
||||||
|
void inc_length(int inc) {
|
||||||
|
if (-inc > static_cast<int>(length)) {
|
||||||
|
length = 0;
|
||||||
|
} else {
|
||||||
|
length += inc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<Veci> track_;
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "Vec.hpp"
|
#include "Vec.hpp"
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
namespace safe {
|
namespace safe {
|
||||||
|
|
||||||
inline int lx(int x) { return std::max(0, x); }
|
inline int lx(int x) { return std::max(0, x); }
|
||||||
|
|
@ -30,12 +32,18 @@ template <typename T> inline bool is_valid_pos(Vec<T> pos) {
|
||||||
|
|
||||||
inline Veci get_cursor() { return {.x = get_cursor_y(), .y = get_cursor_x()}; }
|
inline Veci get_cursor() { return {.x = get_cursor_y(), .y = get_cursor_x()}; }
|
||||||
|
|
||||||
|
inline constexpr Veci get_center() {
|
||||||
|
return {.x = SCREEN_HEIGHT / 2, .y = SCREEN_WIDTH / 2};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
class GameObject {
|
class GameObject {
|
||||||
public:
|
public:
|
||||||
virtual void act(float dt) = 0;
|
virtual void act(float dt) = 0;
|
||||||
virtual void draw() = 0;
|
virtual void draw(Veci offset) = 0;
|
||||||
|
|
||||||
virtual ~GameObject() {}
|
virtual ~GameObject() {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
template <typename T> struct Vec {
|
template <typename T> struct Vec {
|
||||||
T x;
|
T x = {};
|
||||||
T y;
|
T y = {};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
template <typename U> explicit operator Vec<U>() {
|
template <typename U> explicit operator Vec<U>() {
|
||||||
return Vec<U>{.x = x, .y = y};
|
return Vec<U>{.x = static_cast<U>(x), .y = static_cast<U>(y)};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
9
include/World.hpp
Normal file
9
include/World.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Vec.hpp"
|
||||||
|
|
||||||
|
struct World {
|
||||||
|
float game_time = {};
|
||||||
|
Veci prev_cursor = {};
|
||||||
|
Veci cursor = {};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "Canvas.hpp"
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
namespace paint {
|
||||||
|
|
||||||
|
void square(const canvas::Square &s) {
|
||||||
|
for (int x = s.pos.x; x < s.pos.x + s.side; ++x) {
|
||||||
|
std::fill(screen_at(x, s.pos.y), screen_at(x, s.pos.y + s.side),
|
||||||
|
static_cast<uint32_t>(s.color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void circle(const canvas::Circle &c) {
|
||||||
|
for (int x = -c.radius; x < c.radius; ++x) {
|
||||||
|
int size_y = std::sqrt(c.radius * c.radius - std::abs(x) * std::abs(x));
|
||||||
|
std::fill(screen_at(c.pos.x + x, c.pos.y - size_y),
|
||||||
|
screen_at(c.pos.x + x, c.pos.y + size_y),
|
||||||
|
static_cast<uint32_t>(c.color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace paint
|
||||||
57
src/Game.cpp
57
src/Game.cpp
|
|
@ -6,6 +6,11 @@
|
||||||
// #include <stdio.h>
|
// #include <stdio.h>
|
||||||
|
|
||||||
#include "Canvas.hpp"
|
#include "Canvas.hpp"
|
||||||
|
#include "Map.hpp"
|
||||||
|
#include "Player.hpp"
|
||||||
|
#include "Snake.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#include "World.hpp"
|
||||||
|
|
||||||
constexpr double MIN_CONTROL_DISTANCE = 10;
|
constexpr double MIN_CONTROL_DISTANCE = 10;
|
||||||
|
|
||||||
|
|
@ -22,12 +27,28 @@ constexpr double MIN_CONTROL_DISTANCE = 10;
|
||||||
// left button, 1 - right button) schedule_quit_game() - quit game after act()
|
// left button, 1 - right button) schedule_quit_game() - quit game after act()
|
||||||
|
|
||||||
// initialize game data in this function
|
// initialize game data in this function
|
||||||
void initialize() {}
|
void initialize() { srand(time(0)); }
|
||||||
|
|
||||||
float game_time = 0.0f;
|
float game_time = 0.0f;
|
||||||
Vecf pos = {.x = 20, .y = 20};
|
|
||||||
Vecf direction{.x = 1.0, .y = 0.0};
|
Player player{
|
||||||
Veci prev_cursor = {.x = 0, .y = 0};
|
.pos = {.x = 20, .y = 20},
|
||||||
|
.direction = {.x = 1.0, .y = 0.0},
|
||||||
|
};
|
||||||
|
|
||||||
|
World world;
|
||||||
|
|
||||||
|
Map map{{
|
||||||
|
.gen_interval = 1.0,
|
||||||
|
.food_exists_gens = 10,
|
||||||
|
.gen_food_count = 1000,
|
||||||
|
.size = {.x = 20000, .y = 20000},
|
||||||
|
.min_food_weight = 1,
|
||||||
|
.max_food_weight = 10,
|
||||||
|
.food_color = Color::RED,
|
||||||
|
}};
|
||||||
|
|
||||||
|
auto snake = Worm({{.pos = Veci(player.pos), .color = Color::GREEN}, 100, 10});
|
||||||
|
|
||||||
// this function is called to update game data,
|
// this function is called to update game data,
|
||||||
// dt - time elapsed since the previous update (in seconds)
|
// dt - time elapsed since the previous update (in seconds)
|
||||||
|
|
@ -35,23 +56,26 @@ void act(float dt) {
|
||||||
if (is_key_pressed(VK_ESCAPE))
|
if (is_key_pressed(VK_ESCAPE))
|
||||||
schedule_quit_game();
|
schedule_quit_game();
|
||||||
|
|
||||||
Veci cursor = get_cursor();
|
world.cursor = utils::get_cursor();
|
||||||
if (cursor != prev_cursor and is_valid_pos(cursor)) {
|
if (world.cursor != world.prev_cursor and utils::is_valid_pos(world.cursor)) {
|
||||||
Vecf diff = Vecf(cursor) - pos;
|
Vecf diff(world.cursor - utils::get_center()); // - pos;
|
||||||
|
|
||||||
if (diff.len() > MIN_CONTROL_DISTANCE) {
|
if (diff.len() > MIN_CONTROL_DISTANCE) {
|
||||||
direction = diff.norm();
|
player.direction = diff.norm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int speed = 200;
|
int speed = 200;
|
||||||
pos += direction * dt * speed;
|
player.pos += player.direction * dt * speed;
|
||||||
|
|
||||||
game_time += dt;
|
game_time += dt;
|
||||||
prev_cursor = cursor;
|
world.prev_cursor = world.cursor;
|
||||||
// std::cout << "pos: " << pos.x << ' ' << pos.y << std::endl;
|
|
||||||
// std::cout << "curs: " << get_cursor().x << ' ' << get_cursor().y <<
|
snake.add(Veci(player.pos));
|
||||||
// std::endl;
|
|
||||||
|
snake.inc_length(map.eat(Veci(player.pos), 30));
|
||||||
|
|
||||||
|
map.act(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill buffer in this function
|
// fill buffer in this function
|
||||||
|
|
@ -61,8 +85,11 @@ void draw() {
|
||||||
// clear backbuffer
|
// clear backbuffer
|
||||||
memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
||||||
|
|
||||||
Circle sq({.pos = Veci(pos), .size = 20});
|
Veci map_offset = Veci(player.pos) - utils::get_center();
|
||||||
sq.show();
|
|
||||||
|
snake.draw(map_offset);
|
||||||
|
map.draw(map_offset);
|
||||||
|
// paint::circle({{.pos = Veci(pos), .color = Color::ORANGE}, 20});
|
||||||
}
|
}
|
||||||
|
|
||||||
// free game data in this function
|
// free game data in this function
|
||||||
|
|
|
||||||
14
src/Snake.cpp
Normal file
14
src/Snake.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Snake.hpp"
|
||||||
|
|
||||||
|
void Worm::add(Veci pos) {
|
||||||
|
track_.push_back(pos);
|
||||||
|
if (track_.size() > length) {
|
||||||
|
track_.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worm::draw(Veci offset) const {
|
||||||
|
for (const auto &pos : track_) {
|
||||||
|
paint::circle({{.pos = pos - offset, .color = color}, radius});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue