2

I'm a little new to c++ right now and I have to write a console Tank game. I did lots of the part by myself and there's nothing wrong with most of the parts. Here is my Tank class:

    class Tank  {
    protected:
         string name;
         static int number;
         static int row;
         static int col;
         static char direction;

public:
    Tank(int _number, string _name, int _row, int _col, char _direction){
        this->name = _name;
        this->number = _number;
        setRow(_row);
        setCol(_col);
        setDirection(_direction);
    }

    ///////////////////////////////
    ///----GETTERS FUNCTIONS----///
    ///////////////////////////////
    char getdirection(){ return direction; }
    int getnumber(){ return number; }
    int getCol(){ return col; }
    int getRow(){ return row; }

    void setDirection(char _direction)
    {
        direction = _direction;
    }
    void setCol(int _col)
    {
        col = _col;
    }
    void setRow(int _row)
    {
        row = _row;
    }
    ////////////////////////
    ///To String Function///
    ////////////////////////
    string _toString(){
        string r = "";
        r = ("Tank No. " + toString(number + 1) + " " + name + " At (" + toString(row) + "," + toString(col) + ") " + direction);
        return r;
    }
};

int Tank::col = 0;
int Tank::row = 0;
int Tank::number = 0;
char Tank::direction = '\0';

the problem is when I create array of objects like:

Tank *players[4];

Because I need a four player game and one Tank object for every one of them it Only uses the last object. Here is the code I want to use for each turn, for each player to say what they're going to do:

static char action;
        static int turn = 1;
        static Game game(*players, map);
        for (int count = 0; players[count] != NULL; count++)
        {
            clr_screen();
            gotoxy(100, 15); cout << "Turn " << turn;
            gotoxy(100, 16); cout << "Player " << players[count]->getnumber() + 1 << " Action: ";
            cin >> action;
            game.move(action, count);
        }
        turn++;

And as you may see there is a problem with the code.

And as you may want to know, here is the Game class:

class Game{
private:
    Tank *p_tank[5];
    Map map;
public:
    Game(Tank *tanks, Map _map)
    {
        *p_tank = tanks; map = _map;
    }

    //Tank getTank(){ return p_tank; }
    Map getMap(){ return map; }

If you need the full code here it is:

    #include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <fstream>
#include <sstream>

using namespace std;

void SetWindow(int Width, int Height)
{
    _COORD coord;
    coord.X = Width;
    coord.Y = Height;

    _SMALL_RECT Rect;
    Rect.Top = 0;
    Rect.Left = 0;
    Rect.Bottom = Height - 1;
    Rect.Right = Width - 1;

    HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);      // Get Handle
    SetConsoleScreenBufferSize(Handle, coord);            // Set Buffer Size
    SetConsoleWindowInfo(Handle, TRUE, &Rect);            // Set Window Size
}

void gotoxy(int x, int y)
{
    static HANDLE h = NULL;
    if (!h)
        h = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD c = { x, y };
    SetConsoleCursorPosition(h, c);
}

string toString(int x) {
    stringstream ss;
    ss << x;
    return ss.str();
}
class Tank  {
protected:
    string name;
     static int number;
     static int row;
     static int col;
     static char direction;

public:
    Tank(int _number, string _name, int _row, int _col, char _direction){
        this->name = _name;
        this->number = _number;
        setRow(_row);
        setCol(_col);
        setDirection(_direction);
    }

    ///////////////////////////////
    ///----GETTERS FUNCTIONS----///
    ///////////////////////////////
    char getdirection(){ return direction; }
    int getnumber(){ return number; }
    int getCol(){ return col; }
    int getRow(){ return row; }

    void setDirection(char _direction)
    {
        direction = _direction;
    }
    void setCol(int _col)
    {
        col = _col;
    }
    void setRow(int _row)
    {
        row = _row;
    }
    ////////////////////////
    ///To String Function///
    ////////////////////////
    string _toString(){
        string r = "";
        r = ("Tank No. " + toString(number + 1) + " " + name + " At (" + toString(row) + "," + toString(col) + ") " + direction);
        return r;
    }
};

int Tank::col = 0;
int Tank::row = 0;
int Tank::number = 0;
char Tank::direction = '\0';

class Bullet : public Tank{
private:
    Tank tank;
public:
    Tank getTank(){ return tank; }
    void setRow(int _row){ row = _row; }
    void setCol(int _col){ col = _col; }
    char getDirection(){ return direction; }
    string __toString(){
        string r = "";
        r = ("Bullet At (" + toString(row) + "," + toString(col) + ") From Tank No " + toString(number));
        return r;
    }
};

class Block{//Characters: according to legends Lines: 316 - 322
protected:
    int row;
    int col;
    char character;
public:
    Block(char block)
    {
        this->character = block;
    }
    virtual string _toString(){
        string r = "";
        r = (character + (" at (" + toString(row) + toString(col) + "). "));
        return r;
    }
    int getRow(){ return row; }
    int getCol(){ return col; }
    void setRow(int _row){ row = _row; }
    void setCol(int _col) { row = _col; }
    virtual char getCharacter() { return character; }
};

/*class Ground : public Block{
public:
Ground(){
character = '.';
Block block;
block.getCharacter();
}
};

class Wall : public Block{
public:
Wall(){
character = 'W';
Block block;
block.getCharacter();
}
};

class Box : public Block{
public:
Box(){
character = 'B';
Block block;
block.getCharacter();
}
};

class Ice : public Block{
public:
Ice(){
character = 'I';
Block block;
block.getCharacter();
}
};

class Trap : public Block{
public:
Trap(){
character = 'T';
Block block;
block.getCharacter();
}
};

class Random : public Block{
public:
Random(){
character = '?';
Block block;
block.getCharacter();
}
};

class Tblock : public Block{
public:
Tblock(){
character = 'T';
Block block;
block.getCharacter();
}
};*/

class Map{
private:
    int rows;
    int cols;
    static const int MaxSize = 100;
    Block *map[MaxSize][MaxSize];
public:
    Map(int row = MaxSize, int col = MaxSize){
        this->rows = row;
        this->cols = col;
        for (int i = 0; i < row; i++){
            for (int j = 0; j < col; j++)
                map[i][j] = new Block(' ');
        }
    }

    void edit(int row, int col, char legend)
    {
        if (legend == '%')//bullet sign
            map[row][col] = new Block('O');
        else if (legend == '0')
            map[row][col] = new Block('1');
        else if (legend == '1')
            map[row][col] = new Block('2');
        else if (legend == '2')
            map[row][col] = new Block('3');
        else if (legend == '3')
            map[row][col] = new Block('4');
        else if (legend == 'G' || legend == 'g')
            map[row][col] = new Block(' ');
        else if (legend == 'w' || legend == 'W')
            map[row][col] = new Block('W');
        else if (legend == '?')
            map[row][col] = new Block('?');
        else if (legend == 'B' || legend == 'b')
            map[row][col] = new Block('B');
        else if (legend == 'X' || legend == 'x')
            map[row][col] = new Block('X');
        else if (legend == 'I' || legend == 'i')
            map[row][col] = new Block('I');
        else
            map[row][col] = new Block('B');
        _toGUI(row, col);
    }

    void _toGUI(int x, int y)
    {
        int X = 7 + 8 * x;
        int Y = 6 + 4 * y;
        gotoxy(X, Y);
        cout << map[x][y]->getCharacter();
    }

    bool moveOk(int r, int c, char mov)
    {
        switch (mov)
        {
        case 'l':
            if (r - 1 < 0) return false;
            else return true;
            break;

        case 'r':
            if (r > rows - 2) return false;
            else return true;
            break;

        case 'd':
            if (c < cols - 1) return true;
            else return false;
            break;

        case 'u':
            if (c > 0) return true;
            else return false;
            break;
        }
    }

    int getRows(){ return cols; }
    int getCols(){ return rows; }

};

class Game{
private:
    Tank *p_tank[5];
    Map map;
public:
    Game(Tank *tanks, Map _map)
    {
        *p_tank = tanks; map = _map;
    }

    //Tank getTank(){ return p_tank; }
    Map getMap(){ return map; }
    void move(char move, int whoToPlay)
    {
        string wtp;
        wtp = toString(whoToPlay);
        int m_row, m_col;
        switch (move)
        {
        case 'l':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'l')){
                map.edit(m_row, m_col, 'G');
                map.edit(--m_row, m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'r':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'r')){
                map.edit(m_row, m_col, 'G');
                map.edit(++m_row, m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'd':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'd')){
                map.edit(m_row, m_col, 'G');
                map.edit(m_row, ++m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;

        case 'u':
            m_row = p_tank[whoToPlay]->getRow() - 1;
            m_col = p_tank[whoToPlay]->getCol() - 1;
            if (map.moveOk(m_row, m_col, 'u')){
                map.edit(m_row, m_col, 'G');
                map.edit(m_row, --m_col, wtp[0]);
                p_tank[whoToPlay]->setRow(m_row + 1);
                p_tank[whoToPlay]->setCol(m_col + 1);
            }
            break;
        }
    }
};

void draw_field(Map map)
{
    ////////////////////////
    //-------Border-------//
    ////////////////////////
    for (int i = 3; i <= 2 * (4 * map.getRows() + 3); i++)
    {
        gotoxy(i, 3);
        printf("%c", 219);
        gotoxy(i, (4 * map.getCols() + 7));
        printf("%c", 219);
    }

    for (int i = 6; i < (4 * map.getCols() + 7); i++){
        gotoxy(3, i);
        printf("%c", 219);
        gotoxy(2 * (4 * map.getRows() + 3), i);
        printf("%c", 219);
    }

    gotoxy(3, 5);
    printf("%c", 219);
    gotoxy(3, 4);
    printf("%c", 219);
    gotoxy(3, 4 * map.getCols() + 7);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), 4);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), 5);
    printf("%c", 219);
    gotoxy(2 * (4 * map.getRows() + 3), map.getCols() + 7);
    printf("%c", 219);
    //end of BORDER

    //****************\\
    //---COORD GUI----\\
    //****************\\

    //numbers:
    int j = 1;
    int z = 1;
    for (int i = 8; i < 2 * (4 * map.getRows() + 3); i += 8)
    {
        gotoxy(i, 2); cout << j;
        j++;
    }

    for (int i = 6; i < (4 * map.getCols() + 6); i += 4)
    {
        gotoxy(1, i);
        cout << z;
        z++;
    }
    //lines:
    for (int j = 8; j < (4 * map.getCols() + 6); j += 4)
    for (int i = 4; i < 2 * (4 * map.getRows() + 3); i++)
    {
        gotoxy(i, j);
        cout << "-";
    }
    for (int j = 12, c = 0; c < 4 * map.getRows() / 4 - 1; j += 8, c++)
    for (int i = 4, t = 0; t < 4 * map.getCols() + 1; i++, t++)
    {
        Sleep(10);
        gotoxy(j, i);
        cout << "|";
    }

}

void clr_screen()
{
    for (int i = 100; i <= 136; i++){
        for (int j = 15; j <= 55; j++){
            gotoxy(i, j); cout << " ";
        }
    }
}

void legend()
{
    gotoxy(100, 2); cout << "Legends:             ";
    gotoxy(100, 4); cout << "Tanks: #\tBullet: *";
    gotoxy(100, 5); cout << "Walls: W\tRandom: ?";
    gotoxy(100, 6); cout << "Box: B\tIce: I";
    gotoxy(100, 7); cout << "Trap: X\t";
    gotoxy(100, 9); cout << "Actions:";
    gotoxy(100, 10); cout << "U D L R\tFire: F";
    gotoxy(100, 11); cout << "----------------------";
}

void action_display(string str, Map &map)
{
    Tank *players[5];
    char reply = 'n';

    if (str == "%edit%" || str == "%Edit%"){
        while (reply != 'y'){
            char legend; int c; int r;
            clr_screen();
            gotoxy(100, 14);
            cout << "Actions:";
            gotoxy(100, 15);
            cout << "Edit Section:";
            gotoxy(100, 16); cout << "Enter the legend: ";
            gotoxy(100, 17); cin >> legend;
            if (legend == '1' || legend == '2' || legend == '3' || legend == '4')
                legend = '!';
            gotoxy(100, 18); cout << "Col: "; cin >> r;
            gotoxy(100, 19); cout << "Row: "; cin >> c;
            map.edit(r - 1, c - 1, legend);
            gotoxy(100, 20); cout << "Done?(Y|N)"; cin >> reply;
        }
    }

    if (str == "%NewGame%")
    {
        static int P;
        string name;
        int r, c;
        clr_screen();
        gotoxy(100, 15);
        cout << "How many players?";
        cin >> P;
        if (P >= 4) P = 4;
        for (int i = 0; i < P; i++)
        {
            clr_screen();
            gotoxy(100, 16); cout << "Enter player " << i + 1 << " start: ";
            gotoxy(100, 17); cout << "col: "; cin >> r;
            gotoxy(100, 18); cout << "row: "; cin >> c;
            gotoxy(100, 19); cout << "Name: "; cin >> name;
            players[i] = new Tank(i, name, r, c, 'u');
            string a;
            a = toString(i);
            map.edit(r - 1, c - 1, a[0]);
            clr_screen();
            gotoxy(100, 15); cout << players[i]->_toString();
            _getch();
        }
    }

    if (str == "%%MovementDuringGame%%")
    {
        static char action;
        static int turn = 1;
        static Game game(*players, map);
        for (int count = 0; players[count] != NULL; count++)
        {
            clr_screen();
            gotoxy(100, 15); cout << "Turn " << turn;
            gotoxy(100, 16); cout << "Player " << players[count]->getnumber() + 1 << " Action: ";
            cin >> action;
            game.move(action, count);
        }
        turn++;
        clr_screen();
    }

}
void menu()
{
    cout << "Welcome to Tank CPP Game.\n";
    cout << "At first, Please Enter the field's Size.\n!!Attention!! We Recommend 10x10. Max Row Size: 11 and Max Column Size: 11\n\npress Any Key to continue...";
    _getch();
    int r; int c;
    system("cls");
    cout << "Row:"; cin >> r; cout << "Col:"; cin >> c;
    if (r >= 11) r = 11;
    if (c >= 11) c = 11;
    SetWindow(138, 60);
    system("cls");
    Map map(r, c);
    draw_field(map);
    _getch();
    clr_screen();
    legend();

    //**********************//
    //****** Editing *******//
    //**********************//
    action_display("%edit%", map);
    clr_screen();

    /****************************
    ******GAME START-Players*****
    ****************************/
    action_display("%NewGame%", map);
    _getch();

    /************************
    ******* MOVEMENTS *******
    *************************/
    while (1)
        action_display("%%MovementDuringGame%%", map);
    _getch();
}

void main()
{
    menu();
}
Mani Mirjavadi
  • 973
  • 9
  • 19
  • 1
    At what point does the problem occur? And what kind of behavior do you experience? – Michał Szydłowski Jan 13 '15 at 07:43
  • It's not very obvious what's wrong with the code you show. There is *one* obvious problem with it, and that's when you have four players then all entries of the `player` array will be non-null, so you will loop out of bounds. Other than that you need to provide more information! One way to get that information is to use a debugger, and step through the code line by line to see what really happens (the problem I told you about would be very obvious if you did that), and try to fix eventual problems. If there's some thing you don't know how to fix, then you can come back here and ask about it. – Some programmer dude Jan 13 '15 at 07:46
  • The main problem is, in console when I want to use the first object like players[0] if the user just entered 1 player game, it works fine. But when I use the two player, it Only moves and wants the actions for players[1]. And in the code I posted the main problem is on action_display function. – Mani Mirjavadi Jan 13 '15 at 07:48
  • 1
    I used the debugger and it pointed out the Tank Class. If i don't use the static for protected elements then I have a different type of problem, which is overloading. And I really don't know what to do. Because At first the problem was overloading, but after using static elements there is a new problem which I told above. Thank you for your time. – Mani Mirjavadi Jan 13 '15 at 07:52
  • You should really learn how to write actual C++ code. Stop writing C and pick up [a good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). –  Jan 13 '15 at 07:54
  • Hint: `*p_tank = tanks;` is the same as `p_tank[0] = tanks;`. – molbdnilo Jan 13 '15 at 08:12

3 Answers3

2

*p_tank = tanks; is the line which fails, and you should have told us that.

Now I could tell you how to fix just that, but you really should not be using raw pointers and arrays. The whole problem goes away when you use std::vector<Tank>.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Thank you. But Why is that? What's the problem with it? And unfortunately we're not allowed to use vector. If I could this project would be an easy one. – Mani Mirjavadi Jan 13 '15 at 07:58
  • 1
    That is very unfortunate, and a sign of a very poor teacher. Use the technique your teacher showed you. We are here to tell you the smart thing to do, and your teacher is looking for dumb. – MSalters Jan 13 '15 at 08:03
1

This is very dangerous:

for (int count = 0; players[count] != NULL; count++)

There is no guarantee that players[4] or any other memory outside the original array is null as it might be used by something different or just holds old and uninitialized memory values, causing your program to continue unexpectedly and causing the problems you experience.

Improvements:

  • Store the amount of players hard coded in a constant.
  • Retrieve the number of items in the array using sizeof or a countof function or macro
  • Use a std::vector or similar which stores the current number of items it holds
Alex
  • 5,240
  • 1
  • 31
  • 38
0

I didn't read the full code sample but I've noticed you're using static member variables to save the classes state. Of course this can't work because if you have multiple objects they share the same memory for number, row, col and direction. This means if you have an array of Tank objects and you change only one member of any of these array objects they will all be set to the very same value. This means you have all your tanks always on top of each other, looking in the same direction. There may be four of them but you can only see one of them.

I don't say that this is the problem for sure, but this is something you might consider.

Noxoreos
  • 23
  • 5