Better code structure: snake game object, player game object, bot game object

This commit is contained in:
programsnail 2024-07-26 19:29:35 +03:00
parent a92c96f91e
commit 421574ab40
6 changed files with 155 additions and 100 deletions

View file

@ -5,29 +5,27 @@
#include "Map.hpp"
#include "Snake.hpp"
class Bot : public Snake, public GameObject {
class Bot : public Snake {
public:
struct Config {
float time_between_targets;
double speed;
float move_interval;
};
Bot(canvas::SnakeObject obj, Config config, Map &map)
: Snake(obj), config_(config), map_(map), real_pos_(obj.pos) {}
Bot(canvas::SnakeObject::Config canvas_config, Snake::Config snake_config,
Config bot_config, Map &map)
: Snake(canvas_config, snake_config, map), bot_config_(bot_config) {}
void act(float dt) override {
protected:
void change_direction(float dt) override {
time_from_target_set_ += dt;
int eaten = map_.eat(utils::to_world_coord(pos), 20);
inc_length(eaten);
if (not target_.has_value() or
time_from_target_set_ >= config_.time_between_targets or
(target_.value() - pos).len_sq() < radius * radius) {
time_from_target_set_ >= bot_config_.time_between_targets or
(target_.value() - get_pos()).len_sq() <
canvas_config_.radius * canvas_config_.radius) {
time_from_target_set_ = 0;
target_ = map_.find_nearest_food(pos);
target_ = map_.find_nearest_food(get_pos());
}
// check for case when no food found
@ -35,29 +33,11 @@ public:
direction_ = (Vecf(target_.value()) - real_pos_).norm();
// TODO: manually change direction
}
move(dt);
}
void draw(Veci offset) override { Snake::draw(offset); }
protected:
void move(float dt) {
real_pos_ += direction_ * dt * config_.speed;
move_time_delta_ += dt;
if (move_time_delta_ > config_.move_interval) {
move_time_delta_ -= config_.move_interval;
add(Veci(real_pos_));
}
}
const Config bot_config_;
private:
const Config config_;
Map &map_;
Vecf real_pos_;
Vecf direction_ = {};
std::optional<Veci> target_ = {};
float time_from_target_set_ = 0;
float move_time_delta_ = 0;
};

View file

@ -3,3 +3,5 @@
#include "Vec.hpp"
constexpr Veci WORLD_SIZE = {.x = 10000, .y = 10000};
constexpr double MIN_CONTROL_DISTANCE = 10;

View file

@ -1,11 +1,48 @@
#pragma once
#include "Vec.hpp"
#include "Snake.hpp"
struct Player {
Vecf pos;
Vecf direction;
double speed;
double move_interval;
double move_time_delta;
// struct Player {
// Vecf pos;
// Vecf direction;
// double speed;
// double move_interval;
// double move_time_delta;
// };
class Player : public Snake {
public:
using Snake::Snake;
void act(float dt) override {
int eaten = map_.eat(
utils::to_world_coord(get_pos() + utils::get_screen_center()), 20);
inc_length(eaten);
change_direction(dt);
move(dt);
}
void draw(Veci offset) override {
Snake::draw(offset - utils::get_screen_center());
}
protected:
void change_direction(float) override {
Veci cursor = utils::get_cursor();
if (cursor != prev_cursor_ and utils::is_on_screen(cursor)) {
Vecf diff(cursor - utils::get_screen_center()); // - pos;
if (diff.len() > MIN_CONTROL_DISTANCE) {
direction_ = diff.norm();
}
}
prev_cursor_ = cursor;
}
protected:
Veci prev_cursor_ = {};
};

View file

@ -3,19 +3,21 @@
#include <deque>
#include "Canvas.hpp"
#include "Map.hpp"
namespace canvas {
struct SnakeObject : public Object {
size_t length;
int radius;
};
} // namespace canvas
class Snake : protected canvas::SnakeObject {
class SnakeObject {
public:
Snake(canvas::SnakeObject obj) : SnakeObject(obj), track_{pos} {}
struct Config {
Object obj;
size_t initial_length;
int radius;
};
SnakeObject(Config config)
: canvas_config_(config), length_{config.initial_length},
track_{config.obj.pos} {}
void add(Veci pos);
@ -23,26 +25,80 @@ public:
//
Veci get_pos() { return pos; }
Veci get_pos() {
if (track_.empty()) {
throw std::exception();
}
return track_.back();
}
//
size_t get_length() { return length; }
size_t get_length() { return length_; }
void set_length(size_t length) { this->length = length; }
void set_length(size_t length) { length_ = length; }
void inc_length(int inc) {
if (-inc > static_cast<int>(length)) {
length = 0;
if (-inc > static_cast<int>(length_)) {
length_ = 1;
} else {
length += inc;
length_ += inc;
}
}
//
bool touches(const Snake &other);
bool touches(const SnakeObject &other);
protected:
const Config canvas_config_;
size_t length_;
std::deque<Veci> track_;
};
} // namespace canvas
class Snake : public canvas::SnakeObject, public GameObject {
public:
struct Config {
double speed;
float move_interval;
};
Snake(canvas::SnakeObject::Config canvas_config, Config snake_config,
Map &map)
: SnakeObject(canvas_config), snake_config_(snake_config), map_(map),
real_pos_(canvas_config.obj.pos) {}
void act(float dt) override {
int eaten = map_.eat(utils::to_world_coord(get_pos()), 20);
inc_length(eaten);
change_direction(dt);
move(dt);
}
void draw(Veci offset) override { SnakeObject::draw(offset); }
protected:
virtual void change_direction(float dt) = 0;
virtual void move(float dt) {
real_pos_ += direction_ * dt * snake_config_.speed;
move_time_delta_ += dt;
if (move_time_delta_ > snake_config_.move_interval) {
move_time_delta_ -= snake_config_.move_interval;
add(Veci(real_pos_));
}
}
protected:
const Config snake_config_;
Map &map_;
Vecf real_pos_;
Vecf direction_ = {};
float move_time_delta_ = 0;
};