jeudi 29 septembre 2022

C++: This world object constructor doesn't seem to be doing what I want it to and the play method is crashing the program with a seg fault error

I have a world class that creates a level* array, creating a new level for each spot in the level* array. After each level object is created a boss* element is added to the level object's Element* array(all elements in the array are derived classes of the element class), and a warpipe* element is added to the level object's level array for every level object except the last one. Additionally, a Mario* element is created and added to the first level's object array. For all additions to the level object array, after the level is created(Boss, Warpipe, Mario), a check is done to make sure the element in the array being replaced is a nothing element.

Once the world is created the play function is called. This method runs as long as Mario still has lives and the current level int exists as an index in the level* array. For each iteration of the loop, the type of element in the element* array, that Mario is to interact with, is determined, and based on that some action is taken. Afterward, the element Mario interacted with is either reallocated to the current element* in the array, or the current element* now points to a new nothing element. Then, Mario's coordinates are updated and the next type of element in the element* array, that Mario is to interact with, is determined.

I've noticed 2 problems(which may be related to each other), but I can't figure out the reason for either.

The first problem is that the level* array does not construct the level properly

The second problem is that the play method crashes with a seg fault.

Here is my code

World

#ifndef WORLD_H
#define WORLD_H

#include <random>
#include <iostream>
#include "Level.h"
#include "Mario.h"
#include "Boss.h"
#include "WarpPipe.h"

using namespace std;

class World{
public:
    World(int levs, int dim, int livs, int coinPrct, int goombaPcrt, int koopaPrct, int mushPcrt, int nothPcrt);

    ~World();

    void play();

    void nextElement();

    void replaceWithNothing();

    void putBack();

    void warp();

    friend ostream &operator<<(ostream &out, const World &world);
private:
    int m_currentLev;
    int m_totalLevs;
    int m_dim;
    Level **m_levels;
    Element *m_tempElement;
    Mario *m_mario;
};

#endif


#include "World.h"

World::World(int levs, int dim, int livs, int coinPrct, int goombaPcrt, int koopaPrct, int mushPcrt, int nothPcrt){
    
    mt19937 rand(time(nullptr));
    
    m_totalLevs = levs;
    m_currentLev = 0;
    m_dim = dim;
    m_levels = new Level*[m_totalLevs];

    for (int i = 0; i < levs; i++){
        m_levels[i] = new Level(dim, coinPrct, goombaPcrt, koopaPrct, mushPcrt, rand);

        m_tempElement = m_levels[i]->getElement((rand() % dim), (rand() % dim));//tempElement now points to the same element as a random element pointer in the array
        while ( (strcmp(typeid(*m_tempElement).name(), "7Nothing")) != 0){
            m_tempElement = m_levels[i]->getElement((rand() % dim), (rand() % dim));
        }
        m_levels[i]->repopulate(new Boss(m_tempElement->getRow(), m_tempElement->getCol()), m_tempElement->getRow(), m_tempElement->getCol());//the element pointer in the array now points to a new boss object. tempElement still points to the old object
        delete m_tempElement; //if the object is not gonna be placed back in the level array delete it

        
        if (i < levs - 1){

            m_tempElement = m_levels[i]->getElement((rand() % dim), (rand() % dim));//tempElement now points to the same element as a random element pointer in the array
            while ( (strcmp(typeid(*m_tempElement).name(), "7Nothing")) != 0){
                m_tempElement = m_levels[i]->getElement((rand() % dim), (rand() % dim));
            }
            m_levels[i]->repopulate(new WarpPipe(m_tempElement->getRow(), m_tempElement->getCol()), m_tempElement->getRow(), m_tempElement->getCol());
            delete m_tempElement;
        }
    }

    m_tempElement = m_levels[m_currentLev]->getElement((rand() % dim), (rand() % dim));//tempElement now points to the same element as a random element pointer in the array
    while ( (strcmp(typeid(*m_tempElement).name(), "7Nothing")) != 0){
        m_tempElement = m_levels[m_currentLev]->getElement((rand() % dim), (rand() % dim));
    }

    m_mario = new Mario(livs, m_tempElement->getRow(), m_tempElement->getCol());
    m_levels[m_currentLev]->repopulate(m_mario, m_mario->getRow(), m_mario->getCol());

    cout << *this << endl;

    cout << "construction over" << endl;
}

void World::play(){
    while (m_mario->getLives() > 0 && m_currentLev < m_totalLevs){
        cout << *m_levels[m_currentLev] << endl;
        string type = typeid(*m_tempElement).name();
        if (strcmp(type.c_str(),"4Coin")){
            m_mario->collectCoin();
            replaceWithNothing();
        } else if (strcmp(type.c_str(),"8Mushroom")){
            m_mario->increasePower();
            replaceWithNothing();
        } else if (strcmp(type.c_str(),"6Goomba")){
            //interatc with the enemy
            //if win point level array pointer to new nothing object delete what tempElement was pointing to
            //if lose(not including exception) point level array pointer back to object being pointed at by tempelement
            bool win = static_cast<Goomba*>(m_tempElement)->fight(m_mario);
            if (win){
                replaceWithNothing();
            } else {
                putBack();
            }
        } else if (strcmp(type.c_str(),"5Koopa")){
            //interatc with the enemy
            //if win point level array pointer to new nothing object delete what tempElement was pointing to
            //if lose(not including exception) point level array pointer back to object being pointed at by tempelement
            bool win = static_cast<Koopa*>(m_tempElement)->fight(m_mario);
            if (win){
                replaceWithNothing();
            } else {
                putBack();
            }
        } else if (strcmp(type.c_str(),"4Boss")){
            // interact with the boss
            bool win = static_cast<Boss*>(m_tempElement)->fight(m_mario);
            if (win){
                if (m_currentLev == m_totalLevs - 1){//did we beat the boss on the last level
                    replaceWithNothing();
                    m_currentLev++;
                } else {// if its not the last level
                    replaceWithNothing();
                    warp();
                }
            }
        } else if (strcmp(type.c_str(),"8WarpPipe")){
            //warp
            replaceWithNothing();
            warp();
        } else {//Nothing object
            //realoocating the pointer in the array back to the old object because it was a nothing object
            //temp element can now be realocated again
            putBack();
        }

        if (m_currentLev < m_totalLevs){
            nextElement();//tempelement is pointed at the the next object to interact with. the pointer in the array is now pointing at mario
        }
    }

}

void World::nextElement(){
    int next = rand() % 4;
    switch (next){
    case 0://up
        m_tempElement = m_levels[m_currentLev]->getElement((m_mario->getRow() - 1 + m_dim) % m_dim, m_mario->getCol());
        m_levels[m_currentLev]->repopulate(m_mario, m_tempElement->getRow(), m_tempElement->getCol());
        break;
    case 1://down
        m_tempElement = m_levels[m_currentLev]->getElement((m_mario->getRow() + 1 + m_dim) % m_dim, m_mario->getCol());
        m_levels[m_currentLev]->repopulate(m_mario, m_tempElement->getRow(), m_tempElement->getCol());
        break;
    case 3://left
        m_tempElement = m_levels[m_currentLev]->getElement(m_mario->getRow(), (m_mario->getCol() - 1 + m_dim) % m_dim);
        m_levels[m_currentLev]->repopulate(m_mario, m_tempElement->getRow(), m_tempElement->getCol());
        break;
    case 4://right
        m_tempElement = m_levels[m_currentLev]->getElement(m_mario->getRow(), (m_mario->getCol() + 1 + m_dim) % m_dim);
        m_levels[m_currentLev]->repopulate(m_mario, m_tempElement->getRow(), m_tempElement->getCol());
        break;
    default:
        break;
    }
}

void World::replaceWithNothing(){
    m_levels[m_currentLev]->repopulate(new Nothing(m_mario->getRow(), m_mario->getCol()), m_mario->getRow(), m_mario->getCol());
    delete m_tempElement;
}

void World::putBack(){
    m_levels[m_currentLev]->repopulate(m_tempElement, m_mario->getRow(), m_mario->getCol());
}

void World::warp(){
    m_currentLev++;
    m_mario->setCords((rand() % m_dim), (rand() % m_dim));
    while (!(strcmp(typeid(*m_levels[m_currentLev]->getElement(m_mario->getRow(), m_mario->getCol())).name(), "7Nothing"))){
        m_mario->setCords((rand() % m_dim), (rand() % m_dim));
    }
}

ostream &operator << (ostream &out, const World &world){
    for (int i = 0; i < world.m_totalLevs; i++){
        out << *world.m_levels[i] << endl;
    }
    return out;
}

World::~World(){
    delete[] m_levels;
}

int main(int argc, char const *argv[])
{
    World world1(3, 10, 3, 23, 23, 23, 23, 8);
    world1.play();
    //cout << world1 << endl;
    return 0;
}

Level

#ifndef LEVEL_H
#define LEVEL_H

#include <random>
#include <iostream>
#include <algorithm>
#include "Element.h"
#include "Mario.h"
#include "Coin.h"
#include "Mushroom.h"
#include "Goomba.h"
#include "Koopa.h"
#include "Nothing.h"

using namespace std;

//Class for level
class Level{
public:
    //Constructor for level, which takes in the array dimensions
    //Also takes in the percentages chosen by the file, for how many coins, nothing spaces, goomba and koopa's
    //are present in the array (world)
    Level(int dimension, int coinPrct, int goombaPcrt, int koopaPrct, int mushPcrt, mt19937& rand);
    
    //Default destructor
    ~Level();

    void repopulate(Element *elem, int row, int col);

    Element* getElement(int row, int col);

    int getDimension();

    friend ostream &operator<<(ostream &out, const Level &level);
    
//Private member variables
private:
    Element **m_levelAry;
    int m_coins;
    int m_goombas;
    int m_koopas;
    int m_mushrooms;
    int m_dimension;
};

#endif



#include "Level.h"

//Constructor handles dimensions of the array and odds of how frequent coins, enemies and nothing spawn
Level::Level(int dimension, int coinPrct, int goombaPcrt, int koopaPrct, int mushPcrt, mt19937& rand){
    
    m_levelAry = new Element* [dimension * dimension];
    m_coins = coinPrct * dimension * dimension / 100;
    m_goombas = goombaPcrt * dimension * dimension / 100;
    m_koopas = koopaPrct * dimension * dimension / 100;
    m_mushrooms = mushPcrt * dimension * dimension / 100;
    m_dimension = dimension;

    Element **memLoc = m_levelAry;

    for (int i = 0; i < m_dimension; i++){
        for (int j = 0; j < m_dimension; j++){
            if (m_coins != 0){
                *(m_levelAry + i * m_dimension + j) = new Coin(i, j);
                m_coins--;
            } else if (m_mushrooms != 0){
                *(m_levelAry + i * m_dimension + j) = new Mushroom(i, j);
                m_mushrooms--;
            }else if(m_goombas != 0){
                *(m_levelAry + i * m_dimension + j) = new Goomba(i, j);
                m_goombas--;
            } else if (m_koopas != 0){
                *(m_levelAry + i * m_dimension + j) = new Koopa(i, j);
                m_koopas--;
            } else{
                *(m_levelAry + i * m_dimension + j) = new Nothing(i, j);
            }
            memLoc++;
        }
    }

    shuffle(m_levelAry, memLoc, rand);
}

Level::~Level(){
    delete[] m_levelAry;
}

int Level::getDimension(){
    return m_dimension;
}

void Level::repopulate(Element * elem, int row, int col){
    *(m_levelAry + row * m_dimension + col) = elem;
    elem->setCords(row,col);
}

Element* Level::getElement(int row, int col){
    return *(m_levelAry + row * m_dimension + col);
}

ostream &operator << (ostream &out, const Level &level){
    for (int i = 0; i < level.m_dimension; i++){
        for (int j = 0; j < level.m_dimension; j++){
            out << (**(level.m_levelAry + i * level.m_dimension + j)) << " ";
        }
        out << endl;
    }
    return out;
}

Aucun commentaire:

Enregistrer un commentaire