Browse Source

implement lesson01

lessons
Constantin Fürst 6 months ago
parent
commit
73c38c595f
  1. 35
      components/Point.cpp
  2. 6
      components/Point.h
  3. 17
      components/Snack.cpp
  4. 7
      components/Snack.h
  5. 163
      components/Snake.cpp
  6. 34
      components/Snake.h
  7. 74
      game/Controller.cpp
  8. 31
      game/Controller.h
  9. 64
      game/Game.cpp
  10. 27
      game/Game.h
  11. 32
      input-output/Player.cpp
  12. 21
      input-output/Player.h
  13. 22
      main.cpp

35
components/Point.cpp

@ -1,36 +1,25 @@
#include "Point.h" #include "Point.h"
Point::Point(uint32_t y, uint32_t x, int img)
: x_ {x}, y_ {y}, img_ {img}
{ }
void Point::setPoint(uint32_t y, uint32_t x) {
x_ = x;
y_ = y;
}
uint32_t Point::getX() const {
return x_;
Point::Point(uint32_t y, uint32_t x, int img) {
// TODO: implement constructor
// should copy x,y and image
} }
uint32_t Point::getY() const {
return y_;
Point::~Point() {
// TODO: implement destructor
// should hide the point (hint: function for this already exists)
} }
void Point::moveUp() {
y_--;
}
void Point::moveDown() {
y_++;
void Point::setPoint(uint32_t y, uint32_t x) {
// TODO: implement this setter
} }
void Point::moveLeft() {
x_--;
uint32_t Point::getX() const {
// TODO: implement this getter
} }
void Point::moveRight() {
x_++;
uint32_t Point::getY() const {
// TODO: implement this getter
} }
int Point::getImg() const { int Point::getImg() const {

6
components/Point.h

@ -10,16 +10,12 @@ private:
public: public:
Point(uint32_t y, uint32_t x, int img = '*'); Point(uint32_t y, uint32_t x, int img = '*');
~Point();
void setPoint(uint32_t y, uint32_t x); void setPoint(uint32_t y, uint32_t x);
uint32_t getX() const; uint32_t getX() const;
uint32_t getY() const; uint32_t getY() const;
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
int getImg() const; int getImg() const;
void setImg(int image); void setImg(int image);

17
components/Snack.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();
}

7
components/Snack.h

@ -1,7 +0,0 @@
#pragma once
#include "Point.h"
static constexpr int SNACK_CHAR = '*';
void generateSnack(Point* snack);

163
components/Snake.cpp

@ -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();
}

34
components/Snake.h

@ -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();
};

74
game/Controller.cpp

@ -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;
}

31
game/Controller.h

@ -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;
};

64
game/Game.cpp

@ -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;
}
}

27
game/Game.h

@ -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();
};

32
input-output/Player.cpp

@ -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;
}
}

21
input-output/Player.h

@ -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;
};

22
main.cpp

@ -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;
} }
Loading…
Cancel
Save