Browse Source

continue rewrite and introduce separate vertical and horizontal speeds to make the snake move consistently

master
Constantin Fürst 6 months ago
parent
commit
444af158aa
  1. 1
      components/Point.cpp
  2. 4
      components/Snack.cpp
  3. 43
      components/Snake.cpp
  4. 5
      game/Controller.cpp
  5. 2
      game/Game.cpp
  6. 14
      input-output/Graphics.cpp
  7. 20
      input-output/Graphics.h

1
components/Point.cpp

@ -47,4 +47,5 @@ void Point::print() const {
void Point::clear() {
img_ = ' ';
print();
}

4
components/Snack.cpp

@ -5,8 +5,8 @@
void generateSnack(Point* snack){
std::random_device dev;
static std::mt19937 prng(dev());
static std::uniform_int_distribution<std::mt19937::result_type> distX(0, GAME_RIGHT_WALL_X);
static std::uniform_int_distribution<std::mt19937::result_type> distY(0, GAME_BOTTOM_WALL_Y);
static std::uniform_int_distribution<uint32_t> distX(0, GAME_RIGHT_WALL_X);
static std::uniform_int_distribution<uint32_t> distY(0, GAME_BOTTOM_WALL_Y);
const uint32_t x = distX(prng);
const uint32_t y = distY(prng);

43
components/Snake.cpp

@ -1,13 +1,11 @@
#include "Snake.h"
#include <iostream>
Snake::Snake(uint32_t headY, uint32_t headX)
:direction_{LEFT}
{
snake_.push_back(Point{headY, headX, '>'});
for (uint32_t i = 1; i <= SNAKE_DEFAULT_SIZE - 1; i++) {
for (uint32_t i = 1; i <= SNAKE_DEFAULT_SIZE; i++) {
snake_.push_back(Point{headY, headX + i, SNAKE_BODY_CHAR});
}
}
@ -15,8 +13,11 @@ Snake::Snake(uint32_t headY, uint32_t headX)
bool Snake::isBitten() const {
const Point& head = snake_.front();
for (const Point& part : snake_) {
if (part.getX() == head.getX() && part.getY() == head.getY()) {
// use manual iterator loop instead of
// range-based for as we need to skip head
for (auto it = std::next(snake_.begin()); it != snake_.end(); it++) {
if (it->getX() == head.getX() && it->getY() == head.getY()) {
return true;
}
}
@ -110,26 +111,30 @@ void Snake::printSnake() const {
}
void Snake::move() {
// updates the tail by clearing it since
// the snake moved forward (overwrites with ' ')
snake_.back().clear();
auto head = snake_.begin();
auto second = std::next(snake_.begin());
// iterate through the snake step by step
// using iterators so that we can get prev
// and iterate from back to front as we want to
// update each element with the value of the next
// and would otherwise overwrite the next before
// and not updating first as head is handled separately
// clear the tail - overwrites with ' '
snake_.back().clear();
for (auto it = std::prev(snake_.end()); it != snake_.begin(); it--) {
const auto prev = std::prev(it);
it->setPoint(prev->getY(), prev->getX());
// update all nodes by iterating from the back
// and copying the previous nodes values in
// until the second-to-first one
auto iter = std::prev(snake_.end());
while (iter != second) {
auto prev = std::prev(iter);
*iter = *prev;
iter = prev;
}
// update the head depending on movement
// update the previous to head node
// by copying from head and setting
// the image to be body instead of head
*second = *head;
iter->setImg(SNAKE_BODY_CHAR);
updateHead();
// print the updated snake
printSnake();
}

5
game/Controller.cpp

@ -1,6 +1,7 @@
#include "Controller.h"
#include <string>
#include <iostream>
Controller::Controller()
: input_{0}, score_{0}, snack_{Point(0,0,0)}
@ -36,15 +37,19 @@ int Controller::act() {
switch (input_) {
case UP:
snake_.moveUp();
Graphics::get().setVertical(true);
break;
case DOWN:
snake_.moveDown();
Graphics::get().setVertical(true);
break;
case LEFT:
snake_.moveLeft();
Graphics::get().setVertical(false);
break;
case RIGHT:
snake_.moveRight();
Graphics::get().setVertical(false);
break;
default:
snake_.move();

2
game/Game.cpp

@ -16,7 +16,7 @@ const std::string& SnakeGame::getBestPlayer() const {
}
void SnakeGame::play(const std::string& name){
auto find = std::find(players_.begin(), players_.end(), [&name](const Player& p){ return p.getName() == name; });
auto find = std::find_if(players_.begin(), players_.end(), [&name](const Player& p){ return p.getName() == name; });
if (find == players_.end()) {
players_.emplace_back(Player(name));

14
input-output/Graphics.cpp

@ -41,10 +41,10 @@ void Graphics::finalize() {
}
void Graphics::refreshScreen() {
using namespace std::this_thread;
using namespace std::chrono;
const float window_factor = (static_cast<float>(LINES) * 5.25f) / static_cast<float>(COLS);
const uint32_t vertical_sleep_time = window_factor * sleep_time_;
sleep_for(nanoseconds(sleep_time_));
std::this_thread::sleep_for(std::chrono::microseconds(vertical_ ? vertical_sleep_time : sleep_time_));
refresh();
wrefresh(box_);
@ -81,8 +81,8 @@ int Graphics::readInpt() {
}
void Graphics::advanceDifficulty() {
if(sleep_time_ > 28000000) {
sleep_time_ -= 1000000;
if (sleep_time_ > DIFFICULTY_CAP) {
sleep_time_ -= DIFFICULTY_CHANGE;
}
}
@ -90,3 +90,7 @@ Graphics& Graphics::get() {
static Graphics graphics;
return graphics;
}
void Graphics::setVertical(bool value) {
vertical_ = value;
}

20
input-output/Graphics.h

@ -9,17 +9,22 @@ static constexpr int LEFT = KEY_LEFT;
static constexpr int RIGHT = KEY_RIGHT;
static constexpr int EXIT_GAME = 'q';
static constexpr int GAME_TOP_WALL_Y = 1;
const int GAME_BOTTOM_WALL_Y = LINES - 4;
static constexpr int GAME_LEFT_WALL_X = 1;
const int GAME_RIGHT_WALL_X = COLS - 2;
static constexpr uint32_t GAME_TOP_WALL_Y = 1;
static constexpr uint32_t GAME_LEFT_WALL_X = 1;
#define GAME_BOTTOM_WALL_Y (LINES - 4)
#define GAME_RIGHT_WALL_X (COLS - 2)
static constexpr uint32_t DIFFICULTY_BEGIN = 40000;
static constexpr uint32_t DIFFICULTY_CAP = 28000;
static constexpr uint32_t DIFFICULTY_CHANGE = 1000;
class Graphics {
private:
unsigned int sleep_time_ = 40000000;
WINDOW* box_ = NULL;
bool vertical_ = false;
uint32_t sleep_time_ = DIFFICULTY_BEGIN;
WINDOW* box_ = nullptr;
Graphics();
Graphics() = default;
void createBox();
void destroyBox();
@ -35,5 +40,6 @@ public:
int readInpt();
void advanceDifficulty();
void setVertical(bool value);
};
Loading…
Cancel
Save