Compare commits

...

12 Commits

  1. 69
      LESSONS.md
  2. 27
      components/Point.cpp
  3. 1
      components/Point.h
  4. 17
      components/Snack.cpp
  5. 0
      components/Snack.h
  6. 163
      components/Snake.cpp
  7. 34
      components/Snake.h
  8. 74
      game/Controller.cpp
  9. 31
      game/Controller.h
  10. 64
      game/Game.cpp
  11. 27
      game/Game.h
  12. 32
      input-output/Player.cpp
  13. 21
      input-output/Player.h
  14. 70
      main.cpp

69
LESSONS.md

@ -0,0 +1,69 @@
## Lesson 01
#### Prequisites
- Classes
- Functions
#### Files
- _components/Point.cpp_
- _components/Point.h_
- _main.cpp_
#### Task
1) With the point-class we represent a single dot on screen, giving it a position and character as "image". Begin by inspecting _components/Point.h_ and _components/Point.cpp_ and figure out what a header (.h) and code (.cpp) file contain and how they differ.
2) When an instance of a class in C++ is created, its constructor is used to initialize values. At some point, the created instance will get destroyed which uses the destructor. Read about these two and implement them in _components/Point.cpp_.
3) Select to call __mainL01__ in __main__ of _main.cpp_ and think or read about the purpose of this function as the applications entry point.
4) See what happens when you run the application now and play around with the point or maybe create multiple. You can also modify the destructor to do something special (like print to the command line).
5) Go to _components/Point.*_ again and look at the member variables x,y and img. Notice that they are defined in a "private" region. Try to modify these directly in the main function __mainL01__. If that does not work, you can implement the getters and setters marked with TODO in _components/Point.cpp_
6) Continue exploring around with the point and see what happens.
Once you're ready, you can _git merge lesson02_.
---
## Lesson 02
#### Prequisites
- Random Numbers
- Functions
- Pointers/References
#### Files
- _components/Snack.cpp_
- _components/Snack.h_
- _main.cpp_
#### Task
Every little snake needs to eat. And no snake will appear without food, which is why we begin by providing this essential item.
1) We want to implement the _Snack_ as an instance of the Point-Class from the previous Task. Therefore we will write a function that turns an ordinary Point into a delicious Snack. This will be called _generateSnack_ and we will implement it in _components/Snack.*_ where you can see the skeleton for the function.
2) A Snack must be placed at a random point when generating, so set its x and y value to a random value that fits on the playing field (see the comment in the function skeleton for a hint).
3) A Snack must also look enticing, so set its image to the one defined as SNACK_CHAR and then display the delicious snack.
4) Experiment and use __mainL02__ to test if your code works.
5) Notice anything interesting with how we pass the Point to the generateSnack-function? The function requests a pointer which is the address of an object instance. To get the address of an existing object, we use operator&. Read up on pointers and references.
Once you're ready, you can _git merge lesson03_.
---
## Lesson 03
#### Prequisites
- Loops
#### Files
- _components/Point.cpp_
- _components/Point.h_
- _main.cpp_
#### Task
This Task will be a little on the short side. We plan to implement our Snake as a collection of points on the screen and for that, each point should be able to move by itself. This is the objective of this lesson.
1) Some new functions have appeared in _components/Point.*_ which you should inspect and try to implement.
2) Go to _main.cpp_ and see the new __mainL03__ which will use the movement functions now implemented to make one point move across the screen diagonally. Try it out!
3) As you can see, we use a for-loop for this but C++ has some other types of loop which you should familiarize yourself with. Try and play around with the commented-out loops and get the same result by using while and do-while loops.
Once you're ready, you can _git merge lesson04_.
---

27
components/Point.cpp

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

1
components/Point.h

@ -10,6 +10,7 @@ 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;

17
components/Snack.cpp

@ -1,17 +1,8 @@
#include "Snack.h" #include "Snack.h"
#include <random>
void generateSnack(Point* snack){ 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();
// TODO: implement me
// should set a random position for the point, update its character/image and display the point
// x should be between (0, GAME_RIGHT_WALL_X)
// y should be between (0, GAME_BOTTOM_WALL_Y)
} }

0
components/Snack.h

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

70
main.cpp

@ -1,11 +1,71 @@
#include <ncurses.h>
#include <iostream> #include <iostream>
#include <thread>
#include <chrono>
#include "components/Point.h"
#include "components/Snack.h"
#include "input-output/Graphics.h"
void mainL01() {
Graphics::get().init("Learners Helper");
Point p(10,10,'X');
p.print();
Graphics::get().refreshScreen();
std::this_thread::sleep_for(std::chrono::seconds(10));
Graphics::get().finalize();
std::cout << "Helper QUIT" << std::endl;
}
void mainL02() {
Graphics::get().init("Learners Helper 02");
Point p(10,10);
generateSnack(&p);
Graphics::get().refreshScreen();
std::this_thread::sleep_for(std::chrono::seconds(10));
Graphics::get().finalize();
#include "game/Game.h"
std::cout << "Helper QUIT" << std::endl;
}
void mainL03() {
Graphics::get().init("Learners Helper 02");
Point p(10,10);
for (uint32_t i = 0; i < 100; i++) {
p.moveDown();
Graphics::get().refreshScreen();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
p.moveRight();
Graphics::get().refreshScreen();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
/*
while(...) {
// TODO: play with this
}
*/
/*
do {
// TODO: play with this
} while (...)
*/
Graphics::get().finalize();
std::cout << "Helper QUIT" << std::endl;
}
int main() { int main() {
SnakeGame game;
game.play();
mainL03();
return 0; return 0;
} }
Loading…
Cancel
Save