Need help with my boardgame program! Thank you!

I wrote a program for a boardgame called Santorini, the game play part is okay, but the robot doesnt work, here is the source code:

main.cpp:
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <GL/glu.h>

#include <stdlib.h>
#include <math.h>
#include "santoriniRule.h"
#include "robot.h"

#define PI 3.14159265


santorini mygame;
int cursorX;
int cursorY;

GLUquadric* gluNewQuadric();

/* GLUT callback Handlers */
void polarView(GLdouble R, GLdouble revolve, GLdouble degree){
gluLookAt(0.0, R * sin (degree*PI/180), R * cos (degree*PI/180), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glRotated(-revolve, 0.0, 1.0, 0.0);
}

void confirmMove(){

}

static void resize(int width, int height)
{
const float ar = (float) width / (float) height;

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-ar, ar, -1.0, 1.0, 2.0, 100.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity() ;
}

static void display(void)
{


glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();//for mygame.board

//draw mygame.board and tile:
glTranslated(-4.5, 0.0, -4.5);
glColor3f(0.7, 0.7, 0.7);
for (int i = 0; i < 5; i++){
for (int j = 0; j < 5; j++){
for (int l = 0; l <= mygame.board[i][j]; l++){
glPushMatrix(); //for tile
if ((i == cursorX) && (j == cursorY)){
switch(l){
case 0:
glColor3f(0.9, 0.9, 0.6);
glScalef(1, 0.2, 1);
glTranslated(2*i, 1.5*l, 2*j);
glutSolidCube(1.0);
break;

case 4:
glColor3f(0.9, 0.9, 0.6);
glTranslated(2*i, 0.9, 2*j);
glutSolidSphere(0.5, 12, 12);
break;

default:
glColor3f(0.9, 0.9, 0.6);
glScalef(1, 0.2, 1);
glTranslated(2*i, 1.5*l, 2*j);
glutSolidCube(1.0);
break;
}
} else {
switch(l){
case 0:
glColor3f(0.2, 0.2, 0.5);
glScalef(1, 0.2, 1);
glTranslated(2*i, 1.5*l, 2*j);
glutSolidCube(1.0);
break;

case 4:
glColor3f(0.7, 0.7, 0.7);
glTranslated(2*i, 0.9, 2*j);
glutSolidSphere(0.5, 12, 12);
break;

default:
glColor3f(0.7, 0.7, 0.7);
glScalef(1, 0.2, 1);
glTranslated(2*i, 1.5*l, 2*j);
glutSolidCube(1.0);
break;
}
}
glPopMatrix(); //for tile
}



}
}

//draw mygame.pieces:
glPushMatrix(); //for mygame.piece
for(int a = 0; a < 2; a++){
if (mygame.piece[a][0] < 5){
int x = mygame.piece[a][0];
int y = mygame.piece[a][1];
glPushMatrix();
if (mygame.selectedX==x && mygame.selectedY==y &&
(glutGet(GLUT_ELAPSED_TIME))%1000 > 500) {
glColor3f(1,1,0.5);
} else { glColor3f(0.7, 0.7, 0.7);}
glTranslated(2*x, 0.3*mygame.board[x][y], 2*y);
glRotatef(-90, 1, 0, 0);
gluCylinder(gluNewQuadric(), 0.4, 0.4, 0.7, 6, 1);
glTranslated(0.0, 0.0, 0.7);
gluDisk(gluNewQuadric(), 0, 0.4, 6, 1);
glPopMatrix();
}

if (mygame.piece[a+2][0] < 5){
int x = mygame.piece[a+2][0];
int y = mygame.piece[a+2][1];
glPushMatrix();
if (mygame.selectedX==x && mygame.selectedY==y &&
(glutGet(GLUT_ELAPSED_TIME))%1000 > 500) {
glColor3f(1,1,0.5);
} else { glColor3f(0.7, 0.7, 0.7);}
glTranslated(2*x, 0.5+0.3*mygame.board[x][y], 2*y);
glRotatef(37, 0, 1, 0);
glutSolidCube(0.63);
glPopMatrix();
}
}
glPopMatrix(); //for mygame.piece




glPopMatrix();//for mygame.board

glLoadIdentity();
polarView(12.0, 0, 42.0);
glutSwapBuffers();
}

void botMove(int array[], santorini G){
G.move(array[0],array[1],2);
G.move(array[2],array[3],2);
G.move(array[4],array[5],2);
}

static void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27 :
exit(0);
break;
case 32 :
mygame.move(cursorX, cursorY, 1);
break;
case 'b' :
botMove(robot(mygame,2,2),mygame);
break;
case 'w' :
cursorY = (cursorY+4)%5;
break;
case 's' :
cursorY = (cursorY+1)%5;
break;
case 'a' :
cursorX = (cursorX+4)%5;
break;
case 'd' :
cursorX = (cursorX+1)%5;
break;
case 'p' :
//cout << "hello!" << endl;
break;

}
glutPostRedisplay();
}

static void idle(void)
{
glutPostRedisplay();
}

const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };

/* Program entry point */

int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(10,10);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

glutCreateWindow("Santorini");

glutReshapeFunc(resize);
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutIdleFunc(idle);

glClearColor(0,0,0,0);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

glEnable(GL_LIGHT0);
glEnable(GL_NORMALIZE);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);

glShadeModel (GL_SMOOTH);

glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);

glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

glutMainLoop();

return EXIT_SUCCESS;
}


santoriniRule.h:

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#ifndef SANTORINIRULE_H_INCLUDED
#define SANTORINIRULE_H_INCLUDED

class santorini {



public:
    byte board[5][5];
    byte piece[4][2];
    byte walkable[5][5];
    byte state;
    int numOfMove;
    int selectedX, selectedY;

    public: santorini(){
        for (int i=0; i<4; i++){
            piece[i][0] = 10; piece[i][1] = 10;
        }
        state = 0;
        numOfMove = 1;
        selectedX = 10;
        selectedY = 10;

    }

    bool Compare(santorini A, santorini B){
        for (int i=0;i<=4;i++){
            for (int j=0;j<=4;j++){
                if (A.board[i][j]!=B.board[i][j]){
                    return false;
                }
            }
        }
        for (int i=0;i<=3;i++){
            if (A.board[i][0]!=B.board[i][0] ||
                A.board[i][1]!=B.board[i][1]){
                return false;
            }
        }

        return true;
    }

    int endOfGame(){
        byte movelist[][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
        //when one piece is on the level 3
        int who = 2-(numOfMove % 2)*2; //player1's turn = 0,  player2's turn = 2
        int legalMove = 16;

        if (numOfMove>4) {
            for (int a = 0; a <= 1; a++)
            {
                int x = piece[a+who][0];
                int y = piece[a+who][1];

                //check if there is a legal move for current player
                for (int i=0; i<=7; i++){
                    int tx = x + movelist[i][0];
                    int ty = y + movelist[i][1];
                    if (x>=0 && x<=4 && y>=0 && y<=4){
                        if (walkable[tx][ty] - walkable[x][y]>1){
                            legalMove--;
                        }
                    } else {legalMove--;}
                }

                if (legalMove==0){
                    return 2-(who/2);
                } else if (board[x][y]==3)
                {
                    selectedX = x;
                    selectedY = y;
                    return 1+(who/2);
                }
            }
        }
    }


    void checkWalkable(){
        for (int m=0; m<5; m++){
            for (int n=0; n<5; n++){
                if (board[m][n] == 4){
                    walkable[m][n] = 10;
                } else {
                walkable[m][n] = board[m][n];
                }
            }
        }

        for (int a=0; a<4; a++){
            int x = piece[a][0];
            int y = piece[a][1];
            walkable[x][y] += 10;
        }
    }

    void move(int x, int y, int player){
        checkWalkable();
        if (numOfMove <= 4){
            switch (numOfMove){
                case 1: case 2:
                    if (player == 1 && walkable[x][y] == 0){
                        piece[numOfMove-1][0] = x;
                        piece[numOfMove-1][1] = y;
                        numOfMove++;
                    }
                break;

                case 3: case 4:
                    if (player == 2 && walkable[x][y] == 0){
                        piece[numOfMove-1][0] = x;
                        piece[numOfMove-1][1] = y;
                        numOfMove++;
                    }
                break;
            }
        } else if (numOfMove%2 == player%2){

            switch(state){
                case 0:
                    if ((piece[player*2-1][0]==x && piece[player*2-1][1]==y) ||
                        (piece[player*2-2][0]==x && piece[player*2-2][1]==y)) //make sure this is my piece
                        {
                            selectedX = x;
                            selectedY = y;
                            state = 1;
                        }
                break;

                case 1:
                    if ((piece[player*2-1][0]==x && piece[player*2-1][1]==y) ||
                        (piece[player*2-2][0]==x && piece[player*2-2][1]==y)) //make sure this is my piece
                        {
                            selectedX = x;
                            selectedY = y;
                            state = 1;
                    } else if (walkable[x][y] < 4 &&
                               board[x][y] - board[selectedX][selectedY]<=1 &&
                               -1 <= selectedX - x &&
                               1 >= selectedX - x &&
                               -1 <= selectedY - y &&
                               1 >= selectedY - y)
                    {
                        if (piece[player*2-2][0]==selectedX &&
                            piece[player*2-2][1]==selectedY)
                        {
                            piece[player*2-2][0]=x;
                            piece[player*2-2][1]=y;
                            selectedX = x;
                            selectedY = y;
                            state = 2;
                        } else if (piece[player*2-1][0]==selectedX &&
                                   piece[player*2-1][1]==selectedY)
                        {
                            piece[player*2-1][0]=x;
                            piece[player*2-1][1]=y;
                            selectedX = x;
                            selectedY = y;
                            state = 2;
                        }
                    }
                break;
                case 2:
                    if (selectedX - x <= 1 &&
                        selectedX - x >= -1 &&
                        selectedY - y <= 1 &&
                        selectedY - y >= -1 &&
                        walkable[x][y] < 4 &&
                        (selectedX!=x || selectedY!=y))
                    {
                        board[x][y]++;
                        state = 0;
                        numOfMove++;
                    }
                break;
            }

            if (endOfGame()==1){
                for (int i=2; i<=3; i++){
                    piece[i][0] = 10; piece[i][1] = 10;
                }
            } else if (endOfGame()==2){
                for (int i=0; i<=1; i++){
                    piece[i][0] = 10; piece[i][1] = 10;
                }
            }
        }
    }


};

#endif // SANTORINIRULE_H_INCLUDED



minimax.h:

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
#ifndef MINIMAX_H_INCLUDED
#define MINIMAX_H_INCLUDED

int evaluate (santorini game, int player){
    int whosTurn = 2-(game.numOfMove%2);
    int x, y;
    int value;
    int height;

    byte movelist[][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
    for (int a=0; a<=1; a++){
        for (int i=0; i<=7; i++){
            x = game.piece[a][0];
            y = game.piece[a][1];
            height = game.board[x][y];
            x+=movelist[i][0];
            y+=movelist[i][1];
            if (x>=0 && x<=4 && y>=0 && y<=4){
                value+=game.board[x][y];
            }
        }
    }
    return 0;
}


int minimax(santorini root, int depth, int min, int max, int player){

    byte movelist[][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
    int who = 2-(root.numOfMove % 2)*2; //player1's turn = 0,  player2's turn = 2
    int currentPlayer = 1+who/2;

    if(root.endOfGame()>0 || depth == 0){
        if (root.endOfGame()==player){
            return 1000;
        } else if (root.endOfGame()!=player){
            return -1000;
        } else {
            return evaluate(root, player);
        }
    } else if (currentPlayer==player){  //a max node

        int v = min;
        santorini botgame = root;

        for(int a=0; a<=1; a++){
            for (int i=0; i<=7; i++){
                int x = botgame.piece[a+who][0];
                int y = botgame.piece[a+who][1];
                botgame.move(x, y, player);
                if (root.Compare(root,botgame)){
                    santorini botgame2 = botgame;
                    for (int j=0; j<=7; j++){
                        x += movelist[j][0];
                        y += movelist[j][1];
                        botgame.move(x, y, player);
                        if (botgame.Compare(botgame,botgame2)){
                            santorini botgame3 = botgame;
                            for (int k=0; k<=7; k++){
                                x += movelist[k][0];
                                y += movelist[k][1];
                                botgame.move(x, y, player);
                                if (botgame.Compare(botgame,botgame3)){
                                    int v1 = minimax(botgame, --depth, v, max, player);
                                    v = (v1>v ? v1 : v);
                                    if (v>max) {return max;}
                                }
                            }
                        }
                    }
                }
            }
        }
        return v;
    } else if (currentPlayer!=player){
        int v = max;
        santorini botgame = root;

        for(int a=0; a<=1; a++){
            for (int i=0; i<=7; i++){
                int x = botgame.piece[a+who][0];
                int y = botgame.piece[a+who][1];
                botgame.move(x, y, player);
                if (root.Compare(botgame,root)){
                    santorini botgame2 = botgame;
                    for (int j=0; j<=7; j++){
                        x += movelist[j][0];
                        y += movelist[j][1];
                        botgame.move(x, y, player);
                        if (botgame.Compare(botgame,botgame2)){
                            santorini botgame3 = botgame;
                            for (int k=0; k<=7; k++){
                                x += movelist[k][0];
                                y += movelist[k][1];
                                botgame.move(x, y, player);
                                if (botgame.Compare(botgame,botgame3)){
                                    int v1 = minimax(botgame, --depth, min, v, player);
                                    v = (v1<v ? v1 : v);
                                    if (v<min){return min;}
                                }
                            }
                        }
                    }
                }
            }
        }
        return v;
    }
}

#endif // MINIMAX_H_INCLUDED


robot.h:

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
#ifndef ROBOT_H_INCLUDED
#define ROBOT_H_INCLUDED

#include "minimax.h"

const int min = -10000;
const int max = 10000;
int* robot(santorini game, int Depth, int player){
    byte movelist[][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
    int value, v;
    int moves[6];
    int movesToTake[6];
    int who = 2-(game.numOfMove%2)*2; //player1's turn = 0,  player2's turn = 2
    if (2-game.numOfMove%2==player){
        santorini botgame = game;
        for(int a=0; a<=1; a++){
            for (int i=0; i<=7; i++){
                int x = botgame.piece[a+who][0];
                int y = botgame.piece[a+who][1];
                botgame.move(x, y, player);
                if (botgame.Compare(botgame,game)){
                    moves[0]=x;
                    moves[1]=y;
                    santorini botgame2 = botgame;
                    for (int j=0; j<=7; j++){
                        x += movelist[j][0];
                        y += movelist[j][1];
                        botgame.move(x, y, player);
                        if (botgame.Compare(botgame,botgame2)){
                            moves[2]=x;
                            moves[3]=y;
                            santorini botgame3 = botgame;
                            for (int k=0; k<=7; k++){
                                x += movelist[k][0];
                                y += movelist[k][1];
                                botgame.move(x, y, player);
                                if (botgame.Compare(botgame,botgame3)){
                                    moves[4]=x;
                                    moves[5]=y;
                                    v = minimax(botgame, Depth, min, max, player);
                                    if (v>value){
                                        value = v;
                                        for (int z=0;z<=5;z++){
                                            movesToTake[z]=moves[z];
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return movesToTake;
    }
}

#endif // ROBOT_H_INCLUDED

Tell us your primary variables and functions.
Some main organisational points about you code.

You should not have cpp code in headers. Put the class definition only in the header. The class functions definitions go in a header .cpp file with the same name as the class. Put the scope resolution before the function names. Example

1
2
3
4
5
double MyClass::MYClassFunction() {
double ADouble;

return ADouble;
}


Include the class header file in whichever .cpp file needs to use an object of that type.

If you are using an IDE, you should be able to get the class wizard to do the outline of these things for you (At least it does on mine)

In the file with main() in it - declare the functions before main, then put the definitions of them after main. This way we don't have to scroll down to see what main does.

That should help a great deal with the organisation of your files.

As far as trying to see where the problem is, I am not that keen to go through 500 lines of code to try and see a problem. Learn to use the debugger. If it doesn't compile, then post the compiler output in full.

Also in general, you might need to think about the design of your code. Having lots of nesting may mean there is a problem with that. And the use of functions. If there are more than 5 lines of code in a switch case - it might be worth putting that in a function - even if it is just for ease of reading. Same for loops and other control-flow statements. There is a bit of an unwritten rule that functions should not be longer than 80 lines and lines not more than 80 chars for readability.

Get back to us when you have made all these changes so we can see how you went. Hope all goes well.
Last edited on
Hello TheIdeasMan (1524)!

Thank you soooo much for your suggestions, they are staight to the point.

Sorry for my messy code, you know...I am just a beginner, so I really need advices on how to structure my program.

BTW:
(1)In a game program lik this, we usually need to make a class that represent all gameplays, game board and pieces right, where should I instantiate that game class?
(2)In a game program like this, what class should I make? A class that represent all gameplays and any what else?javascript:editbox1.editSend()
(3)How to debug? (I know this question is too broad, but I really have no idea how to ask)

I really appreciate your help!

Best,

Kai
First, I edited my last post a little. Class member functions go in a .cpp file.

Your questions:

1. A Game object could be could be instantiated in any .cpp file that is separate to the class function definitions, so this probably means in the main() function for this project.

2. Class design is trickier than what a lot of people think - it really depends on exactly what you want to do. There are several relationships: "IS A" (Implies inheritance); "HAS A" (One class contains another object as a member variable of that type); "USES A" (An object of a class type is passed as an argument to a class function). You should do some research of your own, by Googling these topics - and have a good read. Generally, you should separate things into 'Objects' like they would be in the real world, then have a think about how they are going to interrelate, not just for the data they store, but also for the functions they have - "The Interface". This whole thing is a big topic - entire books have been written about it.

3. If you have an IDE, it should be easy. There should be a Debug Menu. You can select breakpoints where the code stops. Set a watch list so you can track the value of all the variables & see how they change as you step through the code 1 line at a time. You can then deduce where your logic is screwed up. However I think you need to restructure your code quite a bit first.

If you compile from the shell, then it is a bit trickier to learn a command line debugger like gdb, which works with g++. There is an article in the documentation section of this website, on the top left of this page. Also lots of reference info you should read.

I have been thinking about how I would do this myself, I would like to be able to do plugins, so that I can add new things without redoing a bunch of code, no RTTI or dynamic casting, no overloading of functions, and minimisation of coupling by using design patterns. I will be making extensive use of polymorphic virtual functions.

One observation: Game programming can get very complex quickly - if you are a brand new beginner, you might consider keeping everything very simple to start with. Get the simple thing working first, then add complexity gradually, making sure it all works for each new thing. My program design is only going to have Players, Enemies, Resources (such as ammo, medi packs etc), Wepons, Actions and Tiles, and it already has some advanced ideas (Well IMO anyway)

Hope this all helps - Good Luck !!!
Last edited on
Topic archived. No new replies allowed.