Constantin Fürst
8 months ago
13 changed files with 30 additions and 503 deletions
-
35components/Point.cpp
-
6components/Point.h
-
17components/Snack.cpp
-
7components/Snack.h
-
163components/Snake.cpp
-
34components/Snake.h
-
74game/Controller.cpp
-
31game/Controller.h
-
64game/Game.cpp
-
27game/Game.h
-
32input-output/Player.cpp
-
21input-output/Player.h
-
22main.cpp
@ -1,17 +0,0 @@ |
|||||
#include "Snack.h"
|
|
||||
|
|
||||
#include <random>
|
|
||||
|
|
||||
void generateSnack(Point* snack){ |
|
||||
std::random_device dev; |
|
||||
static std::mt19937 prng(dev()); |
|
||||
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); |
|
||||
|
|
||||
snack->setImg(SNACK_CHAR); |
|
||||
snack->setPoint(y, x); |
|
||||
snack->print(); |
|
||||
} |
|
@ -1,7 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "Point.h" |
|
||||
|
|
||||
static constexpr int SNACK_CHAR = '*'; |
|
||||
|
|
||||
void generateSnack(Point* snack); |
|
@ -1,163 +0,0 @@ |
|||||
#include "Snake.h"
|
|
||||
|
|
||||
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; i++) { |
|
||||
snake_.push_back(Point{headY, headX + i, SNAKE_BODY_CHAR}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
bool Snake::isBitten() const { |
|
||||
const Point& head = snake_.front(); |
|
||||
|
|
||||
// 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 false; |
|
||||
} |
|
||||
|
|
||||
bool Snake::hasBitSnack(uint32_t snackY, uint32_t snackX) const { |
|
||||
return snake_.front().getY() == snackY && snake_.front().getX() == snackX; |
|
||||
} |
|
||||
|
|
||||
bool Snake::hasCrashedWall() const { |
|
||||
const Point& head = snake_.front(); |
|
||||
|
|
||||
return (head.getY() < GAME_TOP_WALL_Y) || |
|
||||
(head.getY() > GAME_BOTTOM_WALL_Y) || |
|
||||
(head.getX() < GAME_LEFT_WALL_X) || |
|
||||
(head.getX() > GAME_RIGHT_WALL_X); |
|
||||
} |
|
||||
|
|
||||
uint32_t Snake::getSize() const { |
|
||||
return snake_.size(); |
|
||||
} |
|
||||
|
|
||||
void Snake::incSize(){ |
|
||||
const auto tail = std::prev(snake_.end()); |
|
||||
const uint32_t tailX = tail->getX(); |
|
||||
const uint32_t tailY = tail->getY(); |
|
||||
|
|
||||
const auto prev = std::prev(tail); |
|
||||
const uint32_t prevX = prev->getX(); |
|
||||
const uint32_t prevY = prev->getY(); |
|
||||
|
|
||||
if (prevY == tailY){ |
|
||||
// if the two last parts are on the same 'height' (horizontal tail direction)
|
|
||||
|
|
||||
if (prevX < tailX) { |
|
||||
// if the tail continues to the left:
|
|
||||
// add one part to the right of the tail
|
|
||||
snake_.push_back(Point{tailY, tailX + 1, SNAKE_BODY_CHAR}); |
|
||||
} |
|
||||
else { |
|
||||
// if the tail continues to the right:
|
|
||||
// add one part to the left of the tail
|
|
||||
snake_.push_back(Point{tailY, tailX - 1, SNAKE_BODY_CHAR}); |
|
||||
} |
|
||||
} |
|
||||
else { |
|
||||
// if the two last parts are on the same 'width' (vertical tail direction)
|
|
||||
|
|
||||
if (prevY < tailY) { |
|
||||
// if the tail continues to the upper side:
|
|
||||
// add one part facing down
|
|
||||
snake_.push_back(Point{tailY + 1, tailX, SNAKE_BODY_CHAR}); |
|
||||
} |
|
||||
else { |
|
||||
// if the tail continues to the lower side:
|
|
||||
// add one part facing up
|
|
||||
snake_.push_back(Point{tailY - 1, tailX, SNAKE_BODY_CHAR}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void Snake::updateHead(){ |
|
||||
switch (direction_) { |
|
||||
case UP: |
|
||||
snake_.front().moveUp(); |
|
||||
break; |
|
||||
|
|
||||
case DOWN: |
|
||||
snake_.front().moveDown(); |
|
||||
break; |
|
||||
|
|
||||
case LEFT: |
|
||||
snake_.front().moveLeft(); |
|
||||
break; |
|
||||
|
|
||||
case RIGHT: |
|
||||
snake_.front().moveRight(); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void Snake::printSnake() const { |
|
||||
//We print each element of the snake-list
|
|
||||
for (const Point& part : snake_){ |
|
||||
part.print(); |
|
||||
} |
|
||||
|
|
||||
Graphics::get().refreshScreen(); |
|
||||
} |
|
||||
|
|
||||
void Snake::move() { |
|
||||
auto head = snake_.begin(); |
|
||||
auto second = std::next(snake_.begin()); |
|
||||
|
|
||||
// clear the tail - overwrites with ' '
|
|
||||
snake_.back().clear(); |
|
||||
|
|
||||
// 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 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(); |
|
||||
|
|
||||
printSnake(); |
|
||||
} |
|
||||
|
|
||||
void Snake::moveUp(){ |
|
||||
snake_.front().setImg('v'); |
|
||||
direction_ = UP; |
|
||||
move(); |
|
||||
} |
|
||||
|
|
||||
void Snake::moveDown(){ |
|
||||
snake_.front().setImg('^'); |
|
||||
direction_ = DOWN; |
|
||||
move(); |
|
||||
} |
|
||||
|
|
||||
void Snake::moveLeft(){ |
|
||||
snake_.front().setImg('>'); |
|
||||
direction_ = LEFT; |
|
||||
move(); |
|
||||
} |
|
||||
|
|
||||
void Snake::moveRight(){ |
|
||||
snake_.front().setImg('<'); |
|
||||
direction_ = RIGHT; |
|
||||
move(); |
|
||||
} |
|
@ -1,34 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <vector> |
|
||||
|
|
||||
#include "Point.h" |
|
||||
#include "Snack.h" |
|
||||
|
|
||||
static constexpr int SNAKE_BODY_CHAR = 'o'; |
|
||||
static constexpr uint32_t SNAKE_DEFAULT_SIZE = 4; |
|
||||
|
|
||||
class Snake{ |
|
||||
private: |
|
||||
std::vector<Point> snake_; |
|
||||
int direction_; |
|
||||
|
|
||||
void updateHead(); |
|
||||
void printSnake() const; |
|
||||
|
|
||||
public: |
|
||||
Snake(uint32_t headY = LINES/2, uint32_t headX = COLS/2); |
|
||||
|
|
||||
void moveUp(); |
|
||||
void moveDown(); |
|
||||
void moveLeft(); |
|
||||
void moveRight(); |
|
||||
void move(); |
|
||||
|
|
||||
bool isBitten() const; |
|
||||
bool hasBitSnack(uint32_t snackY, uint32_t snackX) const; |
|
||||
bool hasCrashedWall() const; |
|
||||
|
|
||||
uint32_t getSize() const; |
|
||||
void incSize(); |
|
||||
}; |
|
@ -1,74 +0,0 @@ |
|||||
#include "Controller.h"
|
|
||||
|
|
||||
#include <string>
|
|
||||
#include <iostream>
|
|
||||
|
|
||||
Controller::Controller() |
|
||||
: input_{0}, score_{0}, snack_{Point(0,0,0)} |
|
||||
{ |
|
||||
generateSnack(&snack_); |
|
||||
} |
|
||||
|
|
||||
uint32_t Controller::getCurrScore() const { |
|
||||
return score_; |
|
||||
} |
|
||||
|
|
||||
void Controller::resetScore() { |
|
||||
score_ = 0; |
|
||||
} |
|
||||
|
|
||||
void Controller::printScore(uint32_t score) const { |
|
||||
const std::string str = "Score: " + std::to_string(score); |
|
||||
// locate message at (-1,-1) because otherwise it'll be printed inside the game box
|
|
||||
Graphics::get().printMsg(-1, -1, str); |
|
||||
} |
|
||||
|
|
||||
int Controller::act() { |
|
||||
if (snake_.hasBitSnack(snack_.getY(), snack_.getX())) { |
|
||||
score_ += 10; |
|
||||
snake_.incSize(); |
|
||||
|
|
||||
generateSnack(&snack_); |
|
||||
Graphics::get().advanceDifficulty(); |
|
||||
|
|
||||
printScore(score_); |
|
||||
} |
|
||||
|
|
||||
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(); |
|
||||
} |
|
||||
|
|
||||
Graphics::get().refreshScreen(); |
|
||||
|
|
||||
if (snake_.isBitten() || snake_.hasCrashedWall()) { |
|
||||
return DEFEAT; |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
int Controller::readInput() { |
|
||||
input_ = Graphics::get().readInpt(); |
|
||||
return input_; |
|
||||
} |
|
||||
|
|
||||
bool Controller::wantsToQuit() const { |
|
||||
return input_ == EXIT_GAME; |
|
||||
} |
|
@ -1,31 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <memory> |
|
||||
|
|
||||
#include "../input-output/Graphics.h" |
|
||||
#include "../components/Snake.h" |
|
||||
#include "../components/Point.h" |
|
||||
|
|
||||
static constexpr int DEFEAT = -1; |
|
||||
|
|
||||
class Controller { |
|
||||
private: |
|
||||
Snake snake_; |
|
||||
Point snack_; |
|
||||
|
|
||||
int input_; |
|
||||
uint32_t score_; |
|
||||
|
|
||||
void printScore(uint32_t score) const; |
|
||||
|
|
||||
public: |
|
||||
Controller(); |
|
||||
|
|
||||
uint32_t getCurrScore() const; |
|
||||
|
|
||||
void resetScore(); |
|
||||
int readInput(); |
|
||||
int act(); |
|
||||
|
|
||||
bool wantsToQuit() const; |
|
||||
}; |
|
@ -1,64 +0,0 @@ |
|||||
#include "Game.h"
|
|
||||
|
|
||||
#include <algorithm>
|
|
||||
#include <limits.h>
|
|
||||
|
|
||||
void SnakeGame::addPlayer(const std::string& name){ |
|
||||
players_.emplace_back(Player{name}); |
|
||||
} |
|
||||
|
|
||||
uint32_t SnakeGame::getHighScore() const { |
|
||||
return high_score_; |
|
||||
} |
|
||||
|
|
||||
const std::string& SnakeGame::getBestPlayer() const { |
|
||||
return best_player_; |
|
||||
} |
|
||||
|
|
||||
void SnakeGame::play(const std::string& 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)); |
|
||||
find = std::prev(players_.end()); |
|
||||
} |
|
||||
|
|
||||
Graphics::get().init(game_name_); |
|
||||
|
|
||||
find->play(); |
|
||||
|
|
||||
Graphics::get().finalize(); |
|
||||
|
|
||||
|
|
||||
if (find->getHighScore() > high_score_){ |
|
||||
high_score_ = find->getHighScore(); |
|
||||
best_player_ = find->getName(); |
|
||||
} |
|
||||
|
|
||||
std::cout << "Highscore: " << high_score_ << " by " << best_player_ << std::endl; |
|
||||
} |
|
||||
|
|
||||
void SnakeGame::play(){ |
|
||||
while(1) { |
|
||||
std::string name; |
|
||||
std::cout << "Who's playing: "; |
|
||||
std::cin >> name; |
|
||||
std::cout << std::endl; |
|
||||
|
|
||||
|
|
||||
play(name); |
|
||||
std::cin.clear(); |
|
||||
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); |
|
||||
std::cout << "Do you or someone else want to play again? (yes or no): "; |
|
||||
std::string ans; |
|
||||
|
|
||||
std::cin >> ans; |
|
||||
|
|
||||
if (ans != "yes") { |
|
||||
std::cout << "Exiting ..." << std::endl; |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
std::cout << "Perfect..." << std::endl; |
|
||||
} |
|
||||
} |
|
@ -1,27 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <string> |
|
||||
#include <vector> |
|
||||
|
|
||||
#include "Controller.h" |
|
||||
#include "../components/Snake.h" |
|
||||
#include "../input-output/Player.h" |
|
||||
|
|
||||
class SnakeGame{ |
|
||||
private: |
|
||||
const std::string game_name_ = "Snake Game for C++ Course"; |
|
||||
std::vector<Player> players_; |
|
||||
|
|
||||
uint32_t high_score_ = 0; |
|
||||
std::string best_player_ = "None"; |
|
||||
|
|
||||
void play(const std::string& name); |
|
||||
void addPlayer(const std::string& name); |
|
||||
|
|
||||
public: |
|
||||
uint32_t getHighScore() const; |
|
||||
const std::string& getBestPlayer() const; |
|
||||
void printGameStatistics() const; |
|
||||
|
|
||||
void play(); |
|
||||
}; |
|
@ -1,32 +0,0 @@ |
|||||
#include "Player.h"
|
|
||||
|
|
||||
Player::Player(std::string name) |
|
||||
: name_{name} |
|
||||
{} |
|
||||
|
|
||||
const std::string& Player::getName() const { |
|
||||
return name_; |
|
||||
} |
|
||||
|
|
||||
uint32_t Player::getHighScore() const { |
|
||||
return high_score_; |
|
||||
} |
|
||||
|
|
||||
void Player::play(){ |
|
||||
Controller controller; |
|
||||
|
|
||||
while (controller.wantsToQuit() == false) { |
|
||||
controller.readInput(); |
|
||||
|
|
||||
if (controller.act() == DEFEAT) { |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const uint32_t score = controller.getCurrScore(); |
|
||||
controller.resetScore(); |
|
||||
|
|
||||
if (score > high_score_) { |
|
||||
high_score_ = score; |
|
||||
} |
|
||||
} |
|
@ -1,21 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "../game/Controller.h" |
|
||||
|
|
||||
#include <iostream> |
|
||||
#include <string> |
|
||||
|
|
||||
class Player{ |
|
||||
private: |
|
||||
std::string name_; |
|
||||
uint32_t high_score_ = 0; |
|
||||
uint32_t timesPlayed_ = 0; |
|
||||
|
|
||||
public: |
|
||||
Player(std::string name); |
|
||||
|
|
||||
void play(); |
|
||||
|
|
||||
uint32_t getHighScore() const; |
|
||||
const std::string& getName() const; |
|
||||
}; |
|
@ -1,11 +1,23 @@ |
|||||
|
|
||||
#include <ncurses.h>
|
|
||||
#include <iostream>
|
#include <iostream>
|
||||
|
#include <thread>
|
||||
|
#include <chrono>
|
||||
|
|
||||
|
#include "components/Point.h"
|
||||
|
#include "input-output/Graphics.h"
|
||||
|
|
||||
|
void mainL01() { |
||||
|
Graphics::get().init("Learners Helper"); |
||||
|
|
||||
|
Point p(10,10,'X'); |
||||
|
|
||||
#include "game/Game.h"
|
|
||||
|
std::this_thread::sleep_for(std::chrono::seconds(10)); |
||||
|
|
||||
|
Graphics::get().finalize(); |
||||
|
|
||||
|
std::cout << "Helper QUIT" << std::endl; |
||||
|
} |
||||
|
|
||||
int main() { |
int main() { |
||||
SnakeGame game; |
|
||||
game.play(); |
|
||||
|
mainL01(); |
||||
return 0; |
return 0; |
||||
} |
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue