Context
Was inspired from this LeetCode post to do my own System Design exercise of the common Snake and Ladders problem. As I was writing the code Mario Party kept coming to mind so I added a little fun theme to it.
Feedback
Did I make the appropriate system design choices in terms of the classes I've implemented the functionality expressed within them? Let me know of any improvements I can make to the implementation of inheritance and polymorphism with my Space
Abstract Class. I have Smart Pointers in place for possible future requirements to take this board from a linear data structure to something you would see in an actual Mario Party game. Any other general feedback would be appreciated too.
Code
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
// stars ✩ space ○
class Player
{
private:
int m_current_space = 1;
public:
Player() {}
void role_dice() {
m_current_space += floor( (rand()%10 + 1) / 3 );
}
int const get_current_space() {
if(m_current_space > 9) {
return 9;
}
return m_current_space;
}
void set_current_space(int current_space) {
m_current_space = current_space;
}
};
class Space
{
protected:
int m_id;
std::vector<std::shared_ptr<Space>> m_paths;
public:
Space() {} // requied to use [] operator in map
Space(int id) : m_id(id) {}
/*
POSSIBLE SEG FAUL HERE, BE CARFUL
*/
void add_path(std::shared_ptr<Space> s) {
m_paths.push_back(s);
}
int get_id() {
return m_id;
}
virtual void event(Player& p) = 0;
virtual std::string class_type() = 0;
};
class Empty : public Space
{
public:
Empty(int id) : Space(id) {}
void event(Player& p) {}
std::string class_type() {
return "Empty";
}
};
class Ladder : public Space
{
public:
Ladder(int id) : Space(id) {}
virtual void event(Player& p) {
std::cout << "Waha!" << '\n';
p.set_current_space(5);
}
std::string class_type() {
return "Ladder";
}
};
class Snake : public Space
{
public:
Snake(int id) : Space(id) {}
virtual void event(Player& p) {
std::cout << "Mumba Mia!" << '\n';
p.set_current_space(4);
}
std::string class_type() {
return "Snake";
}
};
class Board
{
private:
std::map<int, std::shared_ptr<Space>> m_board;
public:
void add_space(std::shared_ptr<Space> s) {
m_board[s->get_id()] = s;
}
void draw_board(int position) {
int i = 1;
std::string line = "\n";
for(auto const& space : m_board) {
if(i%3 == 0) { line = "\n"; }
else { line = " "; }
if (space.first == position) {
std::cout << "●" + line;
}
else if(space.second->class_type() == "Snake" ||
space.second->class_type() == "Ladder") {
std::cout << "\x1B[32m○\033[0m" + line;
}
else { std::cout << "○" + line; }
++i;
}
}
const std::map<int, std::shared_ptr<Space>> get_board() {
return m_board;
}
friend std::ostream &operator<<(std::ostream& os, const Board& b) {
return os;
}
};
class GameStateManager
{
private:
std::string m_state = "game over";
bool m_playing = false;
public:
std::string const get_state() {
return m_state;
}
void set_state(std::string state) {
m_state = state;
}
};
int main()
{
std::cout << "Welcome to Bowser's 9 board game\n";
std::cout << "Start? y(yes) n(no)\n";
char wants_to_play;
std::cin >> wants_to_play;
if (wants_to_play == 'y' || wants_to_play == 'Y') {
GameStateManager game_manager;
game_manager.set_state("playing");
std::cout << "Let's a go!\nPress Enter to role.\n";
std::cin.get();
auto space1 = std::make_shared<Empty>(1);
auto space2 = std::make_shared<Empty>(2);
auto space3 = std::make_shared<Ladder>(3);
auto space4 = std::make_shared<Empty>(4);
auto space5 = std::make_shared<Empty>(5);
auto space6 = std::make_shared<Empty>(6);
auto space7 = std::make_shared<Snake>(7);
auto space8 = std::make_shared<Empty>(8);
auto space9 = std::make_shared<Empty>(9);
std::vector<std::shared_ptr<Space>> spaces {
space1, space2, space3,
space4, space5, space6,
space7, space8, space9
};
Board bowsers_bigbad_laddersnake;
for(auto space : spaces) {
bowsers_bigbad_laddersnake.add_space(space);
}
Player mario;
bowsers_bigbad_laddersnake.draw_board(mario.get_current_space());
int turn = 0;
while(game_manager.get_state() == "playing") {
std::cin.get();
std::cout << "-- Turn " << ++turn << " --" << '\n';
mario.role_dice();
auto board = bowsers_bigbad_laddersnake.get_board();
std::shared_ptr<Space> s = board[mario.get_current_space()];
s->event(mario);
bowsers_bigbad_laddersnake.draw_board(mario.get_current_space());
std::cout << mario.get_current_space() << '\n';
if (mario.get_current_space() >= 9) {
game_manager.set_state("game over");
}
}
std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
std::cin.get();
} else if (wants_to_play == 'n' || wants_to_play == 'N') {
std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
std::cin.get();
}
return 0;
}