Browse Source

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

master
Constantin Fürst 8 months ago
parent
commit
444af158aa
  1. 3
      components/Point.cpp
  2. 4
      components/Snack.cpp
  3. 45
      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

3
components/Point.cpp

@ -45,6 +45,7 @@ void Point::print() const {
Graphics::get().printChar(y_, x_, img_); Graphics::get().printChar(y_, x_, img_);
} }
void Point::clear(){
void Point::clear() {
img_ = ' '; img_ = ' ';
print();
} }

4
components/Snack.cpp

@ -5,8 +5,8 @@
void generateSnack(Point* snack){ void generateSnack(Point* snack){
std::random_device dev; std::random_device dev;
static std::mt19937 prng(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 x = distX(prng);
const uint32_t y = distY(prng); const uint32_t y = distY(prng);

45
components/Snake.cpp

@ -1,13 +1,11 @@
#include "Snake.h" #include "Snake.h"
#include <iostream>
Snake::Snake(uint32_t headY, uint32_t headX) Snake::Snake(uint32_t headY, uint32_t headX)
:direction_{LEFT} :direction_{LEFT}
{ {
snake_.push_back(Point{headY, headX, '>'}); 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}); 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 { bool Snake::isBitten() const {
const Point& head = snake_.front(); 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; return true;
} }
} }
@ -109,27 +110,31 @@ void Snake::printSnake() const {
Graphics::get().refreshScreen(); Graphics::get().refreshScreen();
} }
void Snake::move(){
// updates the tail by clearing it since
// the snake moved forward (overwrites with ' ')
snake_.back().clear();
void Snake::move() {
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(); updateHead();
// print the updated snake
printSnake(); printSnake();
} }

5
game/Controller.cpp

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

2
game/Game.cpp

@ -16,7 +16,7 @@ const std::string& SnakeGame::getBestPlayer() const {
} }
void SnakeGame::play(const std::string& name){ 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()) { if (find == players_.end()) {
players_.emplace_back(Player(name)); players_.emplace_back(Player(name));

14
input-output/Graphics.cpp

@ -41,10 +41,10 @@ void Graphics::finalize() {
} }
void Graphics::refreshScreen() { 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(); refresh();
wrefresh(box_); wrefresh(box_);
@ -81,8 +81,8 @@ int Graphics::readInpt() {
} }
void Graphics::advanceDifficulty() { 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; static Graphics graphics;
return 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 RIGHT = KEY_RIGHT;
static constexpr int EXIT_GAME = 'q'; 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 { class Graphics {
private: 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 createBox();
void destroyBox(); void destroyBox();
@ -35,5 +40,6 @@ public:
int readInpt(); int readInpt();
void advanceDifficulty(); void advanceDifficulty();
void setVertical(bool value);
}; };
Loading…
Cancel
Save