7
\$\begingroup\$

As a follow-up of my previous non-off-the-bat Ant Colony Simulator, we have this one that works off-the-bat... quite; the code for when the ants find and take the food isn't really done but it would be almost impossible to do it anyway, if someone wants to contribute directly to the code, please do it in GitHub.

scent.cpp

#ifndef SCENT_CPP_INCLUDED
#define SCENT_CPP_INCLUDED

class AntScent
{
private:
    short power;
    short angle;
    short ScentType;
public:
    short addPower()
    {
        power += 1;
        return power;
    }
    short affectAngle(short vel)
    {
        angle = (vel + angle) / 2;
        return angle;
    }
    void ChangeType(short NewType)
    {
        ScentType = NewType;
    }
    short decreasePower()
    {
        power -= 1;
        return power;
    }
};

#endif // SCENT_CPP_INCLUDED

antFactory.cpp

#pragma once
#include "scent.cpp"
#include "randbool.h"
#include <iostream>
#include <vector>
#include <cstdlib>
#include "foodFactory.cpp"
#include "board.cpp"
#include "tile.h"
#define DEBUG

#ifndef ANTFACTORY_CPP_INCLUDED
#define ANTFACTORY_CPP_INCLUDED

class Ant
{
private:
    int x;
    int y;
    Board* pertainingBoard;
    tile* Position;
    int index;
    AntScent NewDropScent;
    bool bFoundFood = false;
    short AntSize;
    bool bIsCarryingFood;
    std::pair<int, int> newPos;
    std::pair<bool, Food*> FoodPos;
public:
    Ant(int nx, int ny, int i, Board* pb)
    {
        x = nx;
        y = ny;
        index = i;
        pertainingBoard = pb;
        pertainingBoard->addAnt(this);
    }
    std::pair<int, int> moveThisAnt(std::pair<bool, bool> dir, std::pair<bool, bool> move)
    {
        if (move.first)
        {
            if (dir.first)
                x += 1;
            else
                x -= 1;
        }
        if (move.second)
        {
            if (dir.second)
                y += 1;
            else
                y -= 1;
        }
        #ifdef DEBUG
        std::cout << "Ant moved to x:" << x << ", y:" << y << std::endl;
        #endif // DEBUG
        std::pair<int, int> result;
        result.first = x;
        result.second = y;
        return result;
    }
    void TickThisAnt()
    {
        if (! bFoundFood)
        {
            Wander();
            if (! pertainingBoard->bPosHasTile(x, y))
            {
                pertainingBoard->addTile(Position);
            }
            Position = pertainingBoard->PosTile(x, y).second;
            FoodPos = pertainingBoard->posHasFood(x, y);
            if (FoodPos.first)
            {
                FoodPos.second->BeTaken(AntSize);
                bIsCarryingFood = true;
                bFoundFood = true;
            }
        }
        else
        {
            Wander();
        }
    }
    void FoundFood()
    {
        bFoundFood = true;
    }
    std::pair<int, int> Wander()
    {
        bool bool1 = MyRandomBoolean();
        bool bool2 = MyRandomBoolean();
        bool bool3 = MyRandomBoolean();
        bool bool4 = MyRandomBoolean();
        return moveThisAnt(std::pair<bool,bool>(bool1, bool2), std::pair<bool, bool>(bool3, bool4));
    }
    void resetFood()
    {
        bIsCarryingFood = false;
    }
};

class AntFactory
{
private:
    int foodAcumulated = 0;
    int XPos;
    int YPos;
    std::vector<Ant> AntGenerated;
    Board* MyBoard;
public:
    Ant* MakeAnt()
    {
        Ant z(XPos, YPos, AntGenerated.size() + 1, MyBoard);
        AntGenerated.push_back(z);
        return &AntGenerated[AntGenerated.size() - 1];
    }
    AntFactory(int nx, int ny, Board* PB)
    {
        MyBoard = PB;
        XPos = nx;
        YPos = ny;
        MyBoard->addAFactory(this);
    }
    Ant DelAnt(int AntIndex)
    {
        Ant theresult = AntGenerated[AntIndex];
        AntGenerated.erase(AntGenerated.begin() + AntIndex);
        return theresult;
    }
    void WeFoundFood()
    {
        for (unsigned int i = 0; i < AntGenerated.size(); i++)
            AntGenerated[i].FoundFood();
    }
    void TickAnts()
    {
        for (unsigned int i = 0; i < AntGenerated.size(); i++)
        {
            AntGenerated[i].TickThisAnt();
        }
    }
    void takeFood(int amount, Ant* giver) {
        foodAcumulated += amount;
        giver->resetFood();
        WeFoundFood();
    }
};

#endif //ANTFACTORY_CPP_INCLUDED

food.cpp

#pragma once
#include <utility>

class Food
{
private:
    short FoodSize;
    int x;
    int y;
    int index;
public:
    Food(int nx, int ny, int i, short ns)
    {
        x = nx;
        y = ny;
        index = i;
        FoodSize = ns;
    }
    short BeTaken(short BiteSize)
    {
        if (FoodSize > BiteSize)
        {
            FoodSize -= BiteSize;
            return BiteSize;
        }
        else
        {
            FoodSize = 0;
            return FoodSize;
        }
    }
    std::pair<int, int> getPos() {
        return std::pair<int, int>(x, y);
    }
};

foodFactory.cpp

#pragma once
#include <iostream>
#include <vector>
#include <cstdlib>
#include "food.cpp"
#include "board.cpp"
#define DEBUG

FoodFactory::FoodFactory(int nx, int ny, Board* PB)
{
    XPos = nx;
    YPos = ny;
    MyBoard = PB;
    MyBoard->addFFactory(this);
}
Food* FoodFactory::SplatFood()
{
    XPos = -200 + (rand() % (int)(200 - -200 + 1));
    YPos = -200 + (rand() % (int)(200 - -200 + 1));
    Food z(XPos, YPos, (FoodGenerated.size() + 1), (5 + (rand() % (int)(50 - 5 + 1))));
    FoodGenerated.push_back(z);
    #ifdef DEBUG
    std::cout << "Food created at x:" << XPos << ", y:" << YPos << std::endl;
    #endif
    return &FoodGenerated[FoodGenerated.size()];
}

int FoodFactory::HowMuchFoodGenerated()
{
    return FoodGenerated.size();
}

std::pair<bool, Food*> FoodFactory::FindFoodAt(int xp, int yp)
{
    for (unsigned int i = 0; i < FoodGenerated.size(); i++)
        if (FoodGenerated[i].getPos().first == xp && FoodGenerated[i].getPos().second == yp)
            return std::pair<bool, Food*>(true, &FoodGenerated[i]);
    return std::pair<bool, Food*>(false, &FoodGenerated[0]);
}

foodFactory.h

#ifndef FOODFACTORY_H_INCLUDED
#define FOODFACTORY_H_INCLUDED
#include "food.cpp"

class FoodFactory
{
private:
    int XPos;
    int YPos;
    std::vector<Food> FoodGenerated;
    Board* MyBoard;
public:
    FoodFactory(int nx, int ny, Board* PB);
    Food* SplatFood();
    int HowMuchFoodGenerated();
    std::pair<bool, Food*> FindFoodAt(int xp, int yp);
};

#endif // FOODFACTORY_H_INCLUDED

main.cpp

#include "scent.cpp"
#include "board.cpp"
#include "foodFactory.cpp"
#include "antFactory.cpp"
#include <fstream>
#include <cstdlib>

int main()
{
    Board Root;
    FoodFactory Feeder1(42, 21, &Root);
    Feeder1.SplatFood();
    AntFactory Anter1(-12, 95, &Root);
    Ant* Ant1 = Anter1.MakeAnt();
    Ant* Ant2 = Anter1.MakeAnt();
    Ant* Ant3 = Anter1.MakeAnt();
    while(true)
    {
        Anter1.TickAnts();
        if (! std::cin.good())
        {
            break;
        }
    }
    return 0;
}

tile.cpp

#pragma once
#include "board.cpp"
#include "antFactory.cpp"
#include "curses.h"

#ifndef TILE
#define TILE

class tile
{
private:
    int x;
    int y;
    AntScent tileScent;
    Board* MyBoard;
public:
    tile(int nx, int ny, Board* PB)
    {
        x = nx;
        y = ny;
        MyBoard = PB;
        MyBoard->addTile(this);
    }
    tile(Board* PB)
    {
        x = 0;
        y = 0;
        MyBoard = PB;
    }
    std::pair<int, int> getPosition()
    {
        return std::pair<int, int>(x, y)
    }
    void changeScent(AntScent newscent)
    {
        tileScent = newscent;
    }
};

#endif // TILE

randbool.cpp

#pragma once
#ifndef RANDBOOL
#define RANDBOOL

#include <cstdlib>

bool MyRandomBoolean() {
    return rand() % 2 == 1;
}

#endif // RANDBOOL

randbool.h

#ifndef RANDBOOL_H_INCLUDED
#define RANDBOOL_H_INCLUDED

bool MyRandomBoolean();

#endif // RANDBOOL_H_INCLUDED

tile.h

#ifndef TILE_H_INCLUDED
#define TILE_H_INCLUDED
#include "scent.cpp"

class Board;
class tile
{
private:
    int x;
    int y;
    AntScent tileScent;
    Board* MyBoard;
public:
    tile(int nx, int ny, Board* PB);
    tile(Board* PB);
    std::pair<int, int> getPosition();
    void changeScent(AntScent newscent);
};

#endif // TILE_H_INCLUDED

board.cpp

#pragma once
#include <vector>
#include "tile.h"
#include "foodFactory.h"

class AntFactory;
class Ant;

class Board
{
private:
    std::vector<tile*> BoardTiles;
    std::vector<FoodFactory*> FFactories;
    std::vector<AntFactory*> AFactories;
    std::vector<Ant*> BoardAnts;
public:
    void addAFactory(AntFactory* AFactory)
    {
        AFactories.push_back(AFactory);
    }
    bool bPosHasTile(int x, int y) {
        for (int i = 0; i < BoardTiles.size(); i++)
            if (BoardTiles[i]->getPosition().first == x && BoardTiles[i]->getPosition().second == y)
                return true;
        return false;
    }
    std::pair<bool, tile*> PosTile(int x, int y)
    {
        for (int i = 0; i < BoardTiles.size(); i++)
            if (BoardTiles[i]->getPosition().first == x && BoardTiles[i]->getPosition().second == y)
                return std::pair<bool, tile*>(true, BoardTiles[i]);
        return std::pair<bool, tile*>(false, BoardTiles[0]);
    }
    void addFFactory(FoodFactory* FFactory)
    {
        FFactories.push_back(FFactory);
    }
    void addTile(tile* Tl)
    {
        BoardTiles.push_back(Tl);
    }
    void addAnt(Ant* mAnt)
    {
        BoardAnts.push_back(mAnt);
    }
    tile* getTile(int i)
    {
        return BoardTiles[i];
    }
    std::pair<bool, Food*> posHasFood(int x, int y)
    {
        std::pair<bool, Food*> result;
        for (int n = 0; n < FFactories.size(); n++)
        {
            result = FFactories[n]->FindFoodAt(x, y);
            if (result.first)
                return result;
            return FFactories[0]->FindFoodAt(x, y);
        }
    }
};
\$\endgroup\$
1
  • \$\begingroup\$ Compiler error discovered! \$\endgroup\$ Jan 9, 2016 at 20:41

2 Answers 2

6
\$\begingroup\$

I see a number of things that could help you improve your program.

Don't use #pragma once

No doubt others will disagree on this one, but they can write their own reviews. In my projects, I use #include guards instead. The advantage is that it's specified in the standard, while any #pragma is, by definition, non-standard. Some claim that it's faster to compile when using #pragma once but I'd recommend that you actually measure it both ways and see for yourself.

Don't use #pragma once in a .cpp file

The use of #pragma once (or an #include guard) is for headers that might be included in multiple source files. Neither should be used in .cpp files.

Don't #include a .cpp file

Similar to the first two points, you shouldn't #include a .cpp file within a header file. It is sometimes OK to #include a .cpp file within another .cpp file as for certain kinds of autogenerated source code but this project does not have such special cases and shouldn't #include any .cpp files within either .h or .cpp files. To show how this should be done, here are scent.cpp and scent.h files rewritten to follow these conventions:

scent.h

#ifndef SCENT_H
#define SCENT_H 

class AntScent
{
private:
    short power;
    short angle;
    short ScentType;
public:
    short addPower();
    short affectAngle(short vel);
    void ChangeType(short NewType);
    short decreasePower();
};

#endif // SCENT_H

scent.cpp

#include "scent.h"

short AntScent::addPower()
{
    power += 1;
    return power;
}
short AntScent::affectAngle(short vel)
{
    angle = (vel + angle) / 2;
    return angle;
}
void AntScent::ChangeType(short NewType)
{
    ScentType = NewType;
}
short AntScent::decreasePower()
{
    power -= 1;
    return power;
}

Rethink your classes

There are a lot of classes here, which isn't necessarily a bad sign, but they all seem to rely on each other. In other words, the level of coupling is very high, which is a sign of trouble. For example, the Ant class has pointers to both the Board it is on and the tile it is on as well as its own x and y coordinates and also its own index within a vector which is a private member of the AntFactory class! That is just way too much information for a little Ant to track! If we think about actual ants, individual ants probably don't actually have much concept of these things. Instead, the Board should contain the collection of ants that is currently in the AntFactory class. Next, the AntFactory shoul only generate a new Ant and nothing else. The Board should also keep each Ant's position rather than the Ant itself. Similarly, a Board should include a collection of tile objects. The Board should keep track of the positions of each tile and not each tile.

Simplify your code

If we look at the AntScent::addPower() routine, it is currently this:

short AntScent::addPower()
{
    power += 1;
    return power;
}

However this could be both more idiomatic C++ and also shorter:

short AntScent::addPower()
{
    return ++power;
}

Use a better random number generator

Modern C++ has much better random number generators than rand(). A random variable that only has true or false values is called a Bernoulli distribution and it's already built into C++11's <random> header. Use this:

#include <random>
bool MyRandomBoolean() {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::bernoulli_distribution d;   // default is 50:50 distribution

    return d(gen);
}

There's more, but it's all I have time for at the moment.

\$\endgroup\$
1
  • \$\begingroup\$ Very good! But I can't use C++11 because Code::Blocks compiler says its suport is still in experimental stage \$\endgroup\$ Jan 9, 2016 at 18:19
2
\$\begingroup\$

Let me offer some general comments as well.

First, tile should probably be renamed to Tile for consistency. Moreover, inside tile, I don't think you expect the Board to change, so do not use a pointer to a Board. Instead, use a (const) reference to a board you set in the constructor.

I strongly dislike the following function (the same goes for its counterpart):

short addPower()
{
    power += 1;
    return power;
}

I suggest the function only increments the variable (++power), and does nothing more. A separate const member function should retrieve the value of power for the user of the interface. Indeed, each function/class should have one task, and one responsibility.

Finally, your constructors should use member initializer list. So consider the following:

Ant(int nx, int ny, int i, Board* pb)
{
    x = nx;
    y = ny;
    index = i;
    pertainingBoard = pb;
    pertainingBoard->addAnt(this);
}

This could be rewritten as

Ant(int nx, int ny, int i, Board* pb)
: x(nx), y(ny), index(i), pertainingBoard(pb)
{
    pertainingBoard->addAnt(this);
}

The reason is that if you don't use the member initializer list, the variables will get a default value (e.g., an int is initialized to zero). So it's better to initialize them to the values you wish, instead of first initializing everything to zero, and then setting them again.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Good review! Something that everyone must read. It has different points from the other one, which makes this unique. \$\endgroup\$ Jan 9, 2016 at 23:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.