(Rename) Console Pong

Is it possible to assign objects of different class types to one another? I'm trying to assign a member of the Ball class representing the 'ball' to an element on the grid which is an array in the Grid class. I'm trying to do this by overloading the assignment operator but it's not compiling.

The program I'm trying to write is a game of Pong. Here's the code so far:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED

class Grid
{
    public:
        Grid();
        Grid(const Grid&);
        ~Grid();

        void display();

    private:
        void create();

        int row, col;
        char *grid;
};
#endif // MAP_H_INCLUDED 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include "grid.h"

using namespace std;

Grid::Grid() : row(12), col(30)
{
    grid = new char[row * col];

    create();
}

Grid::Grid(const Grid &obj)
: row(obj.row), col(obj.col), grid(new char[obj.row * obj.col])
{
    create();
}

Grid::~Grid()
{
    delete[] grid;
    grid = 0;
}

void Grid::create()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            if(i == 0 || i == row - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else if(j == 0 || j == col - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else
            {
                *(grid + (i * col) + j) = ' ';
            }
        }
    }
}

void Grid::display()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            cout << *(grid + (i * col) + j);
        }

        cout << endl;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef BALL_H_INCLUDED
#define BALL_H_INCLUDED
#include "grid.h"

class Ball
{
    public:
        Ball operator=(const Ball&);

    private:
        Ball();

        void traverse(Grid&);

        char b;
};

#endif // BALL_H_INCLUDED 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include "ball.h"
#include "grid.h"

using namespace std;

Ball::Ball() : b('o') {}

Ball Ball::operator=(const Ball &b)
{
    return b;
}

void Ball::traverse(Grid &grid)
{
    *(grid + 5) = b;
}

I'm sure there's plenty wrong with the code so if someone would be willing to provide some guidance I'd appreciate it.
Last edited on
You wrote an assignment operator for Ball objects, but you are trying to assign a Ball to a *(grid + 5). Dereferencing with * means value pointed by. First C++ would need to know how to add a Grid with an integer, then it would need to know how to dereference that sum. Since derefencing is just for pointers, it probably would not compile. Even if it could be dereferenced, you would need an assignment operator in Grid that could accept a right hand side Ball.

In this situation I would choose for the Grid class to own a private member of type Ball. Of course, additional methods would be required to move the ball around in Grid's private character array. But if I really stop to think about Ball, why would I want to make a class for something that is really just a simple character? In the end, I would decide to accept a char in Grid's constructor and also have a default ball character in case they do not pass a ball character or they pass a character that is already used in the game.

In object oriented programming, the first thing you want to do is plan out how your classes are going to interact with each other and determine their relationships. When the program is complex enough UML diagrams can be a tremendous help, but even for simple programs they can still shed light. http://agilemodeling.com/artifacts/interactionOverviewDiagram.htm
Last edited on
Thanks kevin. I tried what you suggested and simplified things, so that the Grid class now contains chars to hold the ball and paddles.

When creating the grid, I've set the ball and paddles to certain positions on the grid. For the grid as it is now, it's fine, but if the dimensions are changed, the positions of the ball and paddles become skewed. This is because the positions they are set to are hard coded. Do you know how I might set the positions without hard coding them, so that if the grid dimensions are altered the ball and paddles are set to a new appropriate position?

Code so far:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "grid.h"

using namespace std;

int main()
{
    Grid grid;

    grid.display();

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED

class Grid
{
    public:
        Grid();
        Grid(const Grid&);
        ~Grid();

        void display();

    private:
        void create();

        int row, col;
        char ball, paddle;
        char *grid;
};

#endif // MAP_H_INCLUDED 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include "grid.h"

using namespace std;

Grid::Grid() : row(9), col(25), ball('o'), paddle('|')
{
    grid = new char[row * col];

    create();
}

Grid::Grid(const Grid &obj)
: row(obj.row), col(obj.col), ball(obj.ball), paddle(obj.paddle),
grid(new char[obj.row * obj.col])
{
    create();
}

Grid::~Grid()
{
    delete[] grid;
    grid = 0;
}

void Grid::create()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            if(i == 0 || i == row - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else if(j == 0 || j == col - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else
            {
                *(grid + (i * col) + j) = ' ';
            }
        }
    }
    
    // Set positions (hard coded)
    *(grid + (row * col / 2) - 10) = paddle;
    *(grid + (row * col / 2) + 10) = paddle;
    *(grid + row * col / 2) = ball;
}

void Grid::display()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            cout << *(grid + (i * col) + j);
        }

        cout << endl;
    }
}
Grid::row and Grid::col sound too much like an individual row or column. I would consider renaming them to Grid::nrows and Grid::ncols or something similar to reflect that they are counts and not specific indices.

For the grid as it is now, it's fine, but if the dimensions are changed

This is just basic pong, right? Why would the grid size need to be altered in the middle of a game? If you are trying to make some sort of Pong++ (my lack of creativity is indeed real), what would you like to happen to the game grid (including balls and paddles)? If you lack a vision of what you want the finished code to do, it is not possible for me to help you until you plan out your vision. All I can do is make guesses at what you want to happen when the gird resizes.
No, no, this is just basic pong. I don't want the grids dimensions to be altered during the game. I'm just looking for a way to set the ball and paddles offsets without hard coding those values, so that if the grid just so happens to be of a different size to what I have now, the ball and paddles won't end up being randomly (probably incorrectly) placed on the grid. My 'vision', as it were, is to make a bog standard game of pong in the console. Can you think of a way to place the ball and paddles without hard coding their positions?

Also, here's the updated code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include "grid.h"

using namespace std;

int main()
{
    Grid grid;

    while(1)
    {
        grid.clearScreen();

        grid.display();

        grid.movePaddle();
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED

class Grid
{
    public:
        Grid();
        Grid(const Grid&);
        ~Grid();

        void display();
        void movePaddle();
        void clearScreen();

    private:
        void create();

        int row, col, offset;
        char ball, paddle;
        char *grid;
};

#endif // MAP_H_INCLUDED 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <iostream>
#include "conio.h"
#include "grid.h"

using namespace std;

Grid::Grid() : row(9), col(25), offset(0), ball('o'), paddle('|'),
grid(new char[row * col])
{
    create();
}

Grid::Grid(const Grid &obj)
: row(obj.row), col(obj.col), offset(obj.offset), ball(obj.ball), paddle(obj.paddle),
grid(new char[obj.row * obj.col])
{
    create();
}

Grid::~Grid()
{
    delete[] grid;
    grid = 0;
}

void Grid::create()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            if(i == 0 || i == row - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else if(j == 0 || j == col - 1)
            {
                *(grid + (i * col) + j) = '0';
            }
            else
            {
                *(grid + (i * col) + j) = ' ';
            }
        }
    }

    *(grid + (row * col / 2) - 10) = paddle;
    offset = (row * col / 2) - 10;

    *(grid + (row * col / 2) + 10) = paddle;

    *(grid + row * col / 2) = ball;
}

void Grid::display()
{
    for(int i = 0; i < row; ++i)
    {
        for(int j = 0; j < col; ++j)
        {
            cout << *(grid + (i * col) + j);
        }

        cout << "\n";
    }
}

void Grid::movePaddle()
{
    char direction = ' ';
    const char up = 'w', down = 's';

    direction = getch();

    switch(direction)
    {
        case up:
        {
            if(*(grid + offset - col) == '0')
            {
                *(grid + offset) = paddle;
            }
            else
            {
                *(grid + offset) = ' ';

                *(grid + offset - col) = paddle;

                offset = offset - col;
            }

            break;
        }

        case down:
        {
            if(*(grid + offset + col) == '0')
            {
                *(grid + offset) = paddle;
            }
            else
            {
                *(grid + offset) = ' ';

                *(grid + offset + col) = paddle;

                offset = offset + col;
            }

            break;
        }
    }
}

void Grid::clearScreen()
{
    for(int i = 0; i < 25; ++i)
    {
        cout << "\n";
    }
}

I can now move the left (user) paddle up and down the grid. Awful flickering though. Do you know how I might improve that?
Learning a GUI library would be the best way to avoid flickering. There are some libraries that specifically target the console such as ncurses. You are already on the right track with "hard coding" the ball and paddles by using math formulas. Try using different grid sizes and tweaking your formulas until they suit your fancy.
I was hoping there might be some "one size fits all" formula, but oh well. If using a graphical interface is the best way to avoid flickering, maybe I should go one step further than ncurses and learn SFML. What do you think, would SFML be appropriate for this as well?
The first thing I did when I started learning SFML was make a pong game. Then two parts I found difficult was learning a new library and coding a pong game. You already have a pong game, so you could easily learn SFML.
Thanks Jims, that's very gracious of you to say that I already have a pong game, considering it's just a paddle moving up and down a grid so far! :D

But anyway yeah, I'll try and learn SFML and make pong or whatever else later. Thanks for the advice guys.
Topic archived. No new replies allowed.