mirror of
https://github.com/ProgramSnail/snake_2024.git
synced 2025-12-07 06:58:43 +00:00
modernization, better food rendering, consistent player moving, snake intersections
This commit is contained in:
parent
adabb50a9e
commit
b4509733da
8 changed files with 119 additions and 39 deletions
|
|
@ -3,23 +3,9 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
|
#include "Utils.hpp"
|
||||||
#include "Vec.hpp"
|
#include "Vec.hpp"
|
||||||
|
|
||||||
enum class Color : uint32_t {
|
|
||||||
BLACK = 0x000000ff,
|
|
||||||
WHITE = 0x00ff6000,
|
|
||||||
|
|
||||||
BLUE = 0x000000ff,
|
|
||||||
GREEN = 0x0000ff00,
|
|
||||||
CYAN = 0x0000ffff,
|
|
||||||
RED = 0x00ff0000,
|
|
||||||
MAGENTA = 0x00ff00ff,
|
|
||||||
YELLOW = 0x00ffff00,
|
|
||||||
GRAY = 0x001f1f1f,
|
|
||||||
ORANGE = 0x00ff6000,
|
|
||||||
};
|
|
||||||
constexpr Color CL_BG = Color::BLACK;
|
|
||||||
|
|
||||||
using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
namespace canvas {
|
namespace canvas {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "Canvas.hpp"
|
#include "Canvas.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
@ -50,8 +51,12 @@ public:
|
||||||
for (const auto &food : food_) {
|
for (const auto &food : food_) {
|
||||||
Veci food_pos = food.pos - offset;
|
Veci food_pos = food.pos - offset;
|
||||||
if (utils::is_valid_pos(food_pos) and not food.eaten) {
|
if (utils::is_valid_pos(food_pos) and not food.eaten) {
|
||||||
paint::circle(
|
paint::circle({{
|
||||||
{{.pos = food_pos, .color = config_.food_color}, food.weight * 3});
|
.pos = food_pos,
|
||||||
|
.color = color::scale(config_.food_color,
|
||||||
|
1.0 - food_lasted(food)),
|
||||||
|
},
|
||||||
|
food.weight * 2});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,21 +64,31 @@ public:
|
||||||
private:
|
private:
|
||||||
void generate() {
|
void generate() {
|
||||||
++current_gen_;
|
++current_gen_;
|
||||||
while (food_.front().gen + config_.food_exists_gens < current_gen_) {
|
while (food_.front().gen + config_.food_exists_gens <= current_gen_) {
|
||||||
food_.pop_front();
|
food_.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < config_.gen_food_count; ++i) {
|
for (size_t i = 0; i < config_.gen_food_count; ++i) {
|
||||||
food_.push_back({
|
food_.push_back({
|
||||||
.gen = current_gen_,
|
.gen = current_gen_,
|
||||||
.pos = {.x = rand() % config_.size.x, .y = rand() % config_.size.y},
|
.pos =
|
||||||
|
{
|
||||||
|
.x = std::rand() % config_.size.x,
|
||||||
|
.y = std::rand() % config_.size.y,
|
||||||
|
},
|
||||||
.weight = config_.min_food_weight +
|
.weight = config_.min_food_weight +
|
||||||
rand() % std::abs(config_.max_food_weight -
|
std::rand() % std::abs(config_.max_food_weight -
|
||||||
config_.min_food_weight),
|
config_.min_food_weight),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double food_lasted(const Food &food) {
|
||||||
|
return ((current_gen_ - food.gen) * config_.gen_interval +
|
||||||
|
time_from_last_gen_) /
|
||||||
|
(config_.food_exists_gens * config_.gen_interval);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config config_;
|
Config config_;
|
||||||
double time_from_last_gen_ = 0;
|
double time_from_last_gen_ = 0;
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,7 @@
|
||||||
struct Player {
|
struct Player {
|
||||||
Vecf pos;
|
Vecf pos;
|
||||||
Vecf direction;
|
Vecf direction;
|
||||||
|
double speed;
|
||||||
|
double move_interval;
|
||||||
|
double move_time_delta;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ public:
|
||||||
|
|
||||||
void draw(Veci offset = {}) const;
|
void draw(Veci offset = {}) const;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
Veci get_pos() { return pos; }
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
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) { this->length = length; }
|
||||||
|
|
@ -33,6 +39,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
bool touches(const Worm &other);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::deque<Veci> track_;
|
std::deque<Veci> track_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,41 @@ inline constexpr Veci get_center() {
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
|
namespace color {
|
||||||
|
|
||||||
|
enum FixedColor : uint32_t {
|
||||||
|
BLACK = 0x000000ff,
|
||||||
|
WHITE = 0x00ff6000,
|
||||||
|
|
||||||
|
BLUE = 0x000000ff,
|
||||||
|
GREEN = 0x0000ff00,
|
||||||
|
CYAN = 0x0000ffff,
|
||||||
|
RED = 0x00ff0000,
|
||||||
|
MAGENTA = 0x00ff00ff,
|
||||||
|
YELLOW = 0x00ffff00,
|
||||||
|
GRAY = 0x001f1f1f,
|
||||||
|
ORANGE = 0x00ff6000,
|
||||||
|
};
|
||||||
|
constexpr FixedColor BG = BLACK;
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
uint32_t v;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Color scale(Color color, double scale) {
|
||||||
|
static constexpr uint32_t COL_BITS = 8;
|
||||||
|
static constexpr uint32_t COMPONENTS = 4;
|
||||||
|
for (uint32_t i = COL_BITS; i < COMPONENTS * COL_BITS; i += COL_BITS) {
|
||||||
|
uint32_t comp = (color.v >> i) % (1 << COL_BITS);
|
||||||
|
color.v -= (comp << i);
|
||||||
|
color.v += (uint32_t(comp * scale) << i);
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace color
|
||||||
|
using color::Color;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
class GameObject {
|
class GameObject {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ namespace paint {
|
||||||
|
|
||||||
void square(const canvas::Square &s) {
|
void square(const canvas::Square &s) {
|
||||||
for (int x = s.pos.x; x < s.pos.x + s.side; ++x) {
|
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),
|
std::fill(screen_at(x, s.pos.y), screen_at(x, s.pos.y + s.side), s.color.v);
|
||||||
static_cast<uint32_t>(s.color));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,8 +16,7 @@ void circle(const canvas::Circle &c) {
|
||||||
for (int x = -c.radius; x < c.radius; ++x) {
|
for (int x = -c.radius; x < c.radius; ++x) {
|
||||||
int size_y = std::sqrt(c.radius * c.radius - std::abs(x) * std::abs(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),
|
std::fill(screen_at(c.pos.x + x, c.pos.y - size_y),
|
||||||
screen_at(c.pos.x + x, c.pos.y + size_y),
|
screen_at(c.pos.x + x, c.pos.y + size_y), c.color.v);
|
||||||
static_cast<uint32_t>(c.color));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
50
src/Game.cpp
50
src/Game.cpp
|
|
@ -1,11 +1,10 @@
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include <iostream>
|
#include <ctime>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
// #include <stdio.h>
|
// #include <stdio.h>
|
||||||
|
|
||||||
#include "Canvas.hpp"
|
|
||||||
#include "Map.hpp"
|
#include "Map.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "Snake.hpp"
|
#include "Snake.hpp"
|
||||||
|
|
@ -27,13 +26,16 @@ 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() { srand(time(0)); }
|
void initialize() { std::srand(std::time(0)); }
|
||||||
|
|
||||||
float game_time = 0.0f;
|
float game_time = 0.0f;
|
||||||
|
|
||||||
Player player{
|
Player player{
|
||||||
.pos = {.x = 20, .y = 20},
|
.pos = {.x = 20, .y = 20},
|
||||||
.direction = {.x = 1.0, .y = 0.0},
|
.direction = {.x = 1.0, .y = 0.0},
|
||||||
|
.speed = 200.0,
|
||||||
|
.move_interval = 0.01,
|
||||||
|
.move_time_delta = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
|
|
@ -44,17 +46,26 @@ Map map{{
|
||||||
.gen_food_count = 1000,
|
.gen_food_count = 1000,
|
||||||
.size = {.x = 20000, .y = 20000},
|
.size = {.x = 20000, .y = 20000},
|
||||||
.min_food_weight = 1,
|
.min_food_weight = 1,
|
||||||
.max_food_weight = 10,
|
.max_food_weight = 5,
|
||||||
.food_color = Color::RED,
|
.food_color = {color::RED},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
auto snake = Worm({{.pos = Veci(player.pos), .color = Color::GREEN}, 100, 10});
|
// std::vector<Worm> bots; // TODO
|
||||||
|
|
||||||
|
auto snake = Worm(canvas::WormObject{
|
||||||
|
{.pos = Veci(player.pos), .color = {color::GREEN}},
|
||||||
|
10,
|
||||||
|
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)
|
||||||
void act(float dt) {
|
void act(float dt) {
|
||||||
if (is_key_pressed(VK_ESCAPE))
|
if (is_key_pressed(VK_ESCAPE)) {
|
||||||
schedule_quit_game();
|
schedule_quit_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
map.act(dt);
|
||||||
|
|
||||||
world.cursor = utils::get_cursor();
|
world.cursor = utils::get_cursor();
|
||||||
if (world.cursor != world.prev_cursor and utils::is_valid_pos(world.cursor)) {
|
if (world.cursor != world.prev_cursor and utils::is_valid_pos(world.cursor)) {
|
||||||
|
|
@ -65,17 +76,29 @@ void act(float dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int speed = 200;
|
player.pos += player.direction * dt * player.speed;
|
||||||
player.pos += player.direction * dt * speed;
|
|
||||||
|
|
||||||
game_time += dt;
|
game_time += dt;
|
||||||
world.prev_cursor = world.cursor;
|
world.prev_cursor = world.cursor;
|
||||||
|
|
||||||
snake.add(Veci(player.pos));
|
player.move_time_delta += dt;
|
||||||
|
if (player.move_time_delta > player.move_interval) {
|
||||||
|
player.move_time_delta -= player.move_interval;
|
||||||
|
snake.add(Veci(player.pos));
|
||||||
|
}
|
||||||
|
|
||||||
snake.inc_length(map.eat(Veci(player.pos), 30));
|
int eaten = map.eat(Veci(player.pos), 20);
|
||||||
|
snake.inc_length(eaten);
|
||||||
|
|
||||||
map.act(dt);
|
// TODO
|
||||||
|
// for (const auto &bot : bots) {
|
||||||
|
// if (snake.touches(bot)) { // GAME OVER
|
||||||
|
// schedule_quit_game();
|
||||||
|
// }
|
||||||
|
// if (bot.touches(snake)) {
|
||||||
|
// // regen bot
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill buffer in this function
|
// fill buffer in this function
|
||||||
|
|
@ -85,11 +108,10 @@ 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));
|
||||||
|
|
||||||
Veci map_offset = Veci(player.pos) - utils::get_center();
|
Veci map_offset = Veci(/*player.pos*/ snake.get_pos()) - utils::get_center();
|
||||||
|
|
||||||
snake.draw(map_offset);
|
snake.draw(map_offset);
|
||||||
map.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
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Snake.hpp"
|
#include "Snake.hpp"
|
||||||
|
|
||||||
void Worm::add(Veci pos) {
|
void Worm::add(Veci pos) {
|
||||||
|
this->pos = pos;
|
||||||
track_.push_back(pos);
|
track_.push_back(pos);
|
||||||
if (track_.size() > length) {
|
if (track_.size() > length) {
|
||||||
track_.pop_front();
|
track_.pop_front();
|
||||||
|
|
@ -12,3 +13,13 @@ void Worm::draw(Veci offset) const {
|
||||||
paint::circle({{.pos = pos - offset, .color = color}, radius});
|
paint::circle({{.pos = pos - offset, .color = color}, radius});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Worm::touches(const Worm &other) {
|
||||||
|
int dist = radius + other.radius;
|
||||||
|
for (const auto &elem : other.track_) {
|
||||||
|
if ((pos - elem).len_sq() < dist * dist) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue