Reputation: 1073
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();
}
Upvotes: 2
Views: 5556
Reputation: 23
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.
Upvotes: 0
Reputation: 180245
*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>
.
Upvotes: 2
Reputation: 5449
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:
sizeof
or a countof
function or macrostd::vector
or similar which stores the current number of items it holdsUpvotes: 1