Solved

# AI Pyramid Game Project

Posted on 2008-11-04
753 Views
4 of us are helping to design a AI pyramid game. We can't seem to get the program to compile and run. No error messages popup. Here are the rules of the game.

There are 3 rows of stones. In the bottom row are 7 stones, in the middle row are 5 stones, and in the upper row are 3 stones. 2 players take turns. During a turn , a player may remove any number of stones from one row. For example, during the first move, the player who moves first could remove from 1 to 7 stones from the bottom row, from 1 to 5 stones from the middle row,or from 1 to 3 stones from the top row. A player must remove at least one stone. Play continues until all stones in all rows are gone. The player who takes the last stone loses. So the objective is to force your opponent to take the last stone.

The project is structured as follows:
PYRAMID: a class that represents the game board and has methods for making moves and determining    whether the game is over.
MOVE: a simple struct to store a move.
PLAYERBASE: an abstract class representing a player.
HUMANPLAYER: a subclass of Playerbase, this class allows a human to play.
RANDOMPLAYER: A subclass of Playerbase, this class picks a random move each turn.
RANDOMTABLEPLAYER: A subclass of Playerbase, this class makes a table of random moves at the start of the game, then plays from the table during the game.

Here is the code we have thus far:

``````#include <iostream>

#include "pyramid.h"

// INCLUDE PLAYER CLASSES

#include "humanplayer.h"

#include "randomplayer.h"

#include "randomtableplayer.h"

using std::cout;

using std::endl;

using std::cin;

int main () {

Pyramid game;

const int opponentCount = 3;

PlayerBase* opponents[2];

// MODIFICATION NOTE:

// This is where the opponents in the game are assigned.

// This is the only place in this function where modifications

// are needed in this function when new player classes are added.

opponents[0] = new HumanPlayer();

opponents[1] = new RandomPlayer();

cout << "Pyramid.  Players in this game: " << endl;

cout << "1. ";

opponents[0]->outputName(&cout);

cout << endl << "2. ";

opponents[1]->outputName(&cout);

cout << endl << endl;

bool keepPlaying = true;

while (keepPlaying) {

cout << "Initializing game board." << endl;

game.newGame();

cout << "Initializing players." << endl;

cout << "Initializing player 1." << endl;

opponents[0]->initialize();

cout << "Initializing player 2." << endl;

opponents[1]->initialize();

cout << endl;

bool gameOver = false;

int currentPlayer = 0;

game.display();

while (!gameOver) {

cout << endl << "Player " << currentPlayer + 1 << " turn.  ";

opponents[currentPlayer]->outputName(&cout);

cout << "." << endl;

game.performMove(opponents[currentPlayer]->makeMove(game));

cout << "Resulting Board:" << endl;

game.display();

gameOver = game.gameOver();

// if currentPlayer was 0 it becomes 1 and vice versa

currentPlayer = 1 - currentPlayer;

}

cout << "Game Over." << endl;

cout << "Player " << currentPlayer+1 << ", a ";

opponents[currentPlayer]->outputName(&cout);

cout << ", has won." << endl << endl;

cout << "Enter 'y' to play again: ";

char response;

cin >> response;

keepPlaying = ((response == 'y') || (response == 'Y'));

}

return 0;

}

/////////////////////////////////////////////

#ifndef RANDOMTABLEPLAYER

#define RANDOMTABLEPLAYER

#include "playerBase.h"

// RandomTablePlayer

//    This player makes a random move each turn, but the moves are

//    preselected at the start of the game for each board configuration.

class RandomTablePlayer : public PlayerBase {

public:

virtual void initialize();

virtual void outputName(std::ostream*);

virtual Move makeMove(Pyramid p);

private:

Move movematrix[8][6][4]; // move table

};

#endif

//////////////////////////////

#include "RandomTablePlayer.h"

#include <stdlib.h>

#include <time.h>

void RandomTablePlayer::initialize() {

srand(time(0));

Move m;

int s[3]; // for the three subscripts in our loop

for (s[0] = 0; s[0] <= 7; s[0]++) {

for (s[1] = 0; s[1] <= 5; s[1]++) {

for (s[2] = 0; s[2] <= 3; s[2]++) {

// this "if" is needed because the 0,0,0 configuration

// has to be skipped, as there are no legal moves

if (!s[0] && !s[1] && !s[2]) continue;

// determine a random move. Loop is needed in case a row

// with 0 stones is selected

bool needMove = true;

while (needMove) {

m.level = rand() % 3;

if (s[m.level] != 0) {

m.count = rand() % s[m.level] + 1;

needMove = false;

}

}

movematrix[s[0]][s[1]][s[2]] = m;

}

}

}

}

void RandomTablePlayer::outputName(std::ostream* o) {

*o << "Random player using a table";

}

Move RandomTablePlayer::makeMove(Pyramid p) {

return movematrix[p.getCount(0)][p.getCount(1)][p.getCount(2)];

}

////////////////////////////////////////////////////////////////////

#ifndef RANDOMPLAYER

#define RANDOMPLAYER

#include "playerBase.h"

// RandomPlayer

//    This player makes a random move each turn.

class RandomPlayer : public PlayerBase {

public:

virtual void initialize();

virtual void outputName(std::ostream*);

virtual Move makeMove(Pyramid p);

};

#endif

/////////////////////////////////////////////////////////////

#include "RandomPlayer.h"

#include <stdlib.h>

#include <time.h>

void RandomPlayer::initialize() {

srand(time(0));

}

void RandomPlayer::outputName(std::ostream* o) {

*o << "Random player";

}

Move RandomPlayer::makeMove(Pyramid p) {

Move m;

p.resetLegalMoves();

int numMoves = p.getLegalMoveCount();

// generate a number from 1 to the number of legal moves

int randomMoveNumber = rand() % numMoves + 1;

// iterate through moves to that number

for (int i = 1; i <= randomMoveNumber; i++) {

m = p.getNextLegalMove();

}

return m;

}

/////////////////////////////////////////////////////////////

#ifndef PYRAMID

#define PYRAMID

#include "Move.h"

class Pyramid {

public:

Pyramid();

// newGame

//    Set initial stone counts.

void newGame();

// performMove

//	  Makes the given move and returns true, except if the move is illegal,

//    the pyramid will be unchanged and the function returns false.

bool performMove(Move m);

// goodMove

//    Returns true for a legal move for the current pyramid state, false

//    otherwise.

bool goodMove(Move m);

// getCount

//    Returns the number of stones on a certain level.

int getCount(int level);

// gameOver

//    Returns true if the game is over (all stones are gone), and false

//    otherwise.

bool gameOver();

// display

//    Displays the board to cout.

void display();

// resetLegalMoves

//    Must be called before using getNextLegalMove.

void resetLegalMoves();

// getNextLegalMove

//	  Calling this repeatedly allows all the legal moves to be

//	  enumerated.  This will return -1, -1 for the move if there

//    are no more legal moves.  This method assumes the game state for this

//    Pyramid hasn't changed since the last call to this function.  If it has,

//    you have to call reset and start over.

Move getNextLegalMove();

// getLegalMoveCount

//    Returns the count of legal moves for this state.

int getLegalMoveCount();

private:

int stoneCount[3];

// used by the legal move methods

int lastLegalRow;

int lastLegalCount;

};

#endif

///////////////////////////////////////////////////////////////

#include "pyramid.h"

#include <iostream>

Pyramid::Pyramid() {

newGame();

}

void Pyramid::newGame() {

stoneCount[0] = 7;

stoneCount[1] = 5;

stoneCount[2] = 3;

}

bool Pyramid::goodMove(Move m) {

if ((m.level < 0) || (m.level > 2)) return false;

if (m.count < 1) return false;

return (stoneCount[m.level] >= m.count);

}

bool Pyramid::performMove(Move m) {

if (!goodMove(m)) return false;

stoneCount[m.level] -= m.count;

return true;

}

bool Pyramid::gameOver() {

return (getLegalMoveCount() == 0);

}

int Pyramid::getCount(int level) {

if ((level < 0) || (level > 2)) return 0;

return stoneCount[level];

}

void Pyramid::display() {

// cheap attempt to make it look like a pyramid

for (int row = 2; row >= 0; row--) {

std::cout << "Level " << row+1 << ": ";

for (int j = (7 - stoneCount[row]) / 2; j > 0; j--) std::cout << " ";

for (j = 1; j <= stoneCount[row]; j++) std::cout << "*";

std::cout << std::endl;

}

}

void Pyramid::resetLegalMoves() {

lastLegalRow = 0;

lastLegalCount = 0;

}

Move Pyramid::getNextLegalMove() {

Move nextMove;

bool foundMove = false;

// set up default values, this is what the function

// will return if we can't find a move.

nextMove.count = -1;

nextMove.level = -1;

while ((!foundMove) && (lastLegalRow < 3)) {

lastLegalCount++;

if (stoneCount[lastLegalRow] >= lastLegalCount) {

nextMove.count = lastLegalCount;

nextMove.level = lastLegalRow;

foundMove = true;

} else {

lastLegalCount = 0;

lastLegalRow++;

}

}

return nextMove;

}

int Pyramid::getLegalMoveCount() {

int sum = 0;

for (int i = 0; i <= 2; i++) sum += stoneCount[i];

return sum;

}

/////////////////////////////////////////////////////////////////

#ifndef PLAYERBASE

#define PLAYERBASE

#include <iostream>

#include "move.h"

#include "pyramid.h"

// PlayerBase

//    Abstract class from which all player classes should descend

class PlayerBase {

public:

// initialize

//	  This function takes no parameters and will be called

//    at the start of every game for any initialization the

//    player needs to do (not all players will do anything

//    in this function).

virtual void initialize() = 0;

// outputName

//    Inserts the name of the player into the specified stream

virtual void outputName(std::ostream*) = 0;

// makeMove

//    Given the board state specified by the Pyramid parameter,

//    returns the Move to make.

virtual Move makeMove(Pyramid p) = 0;

};

#endif

/////////////////////////////////////////////////////////////

#ifndef MOVE

#define MOVE

struct Move {

int level; // level of pyramid 1 - 3

int count; // number of stones to remove

};

#endif

//////////////////////////////////////////////////////////

#ifndef HUMANPLAYER

#define HUMANPLAYER

#include "playerBase.h"

class HumanPlayer : public PlayerBase {

public:

virtual void initialize();

virtual void outputName(std::ostream*);

virtual Move makeMove(Pyramid p);

};

#endif

/////////////////////////////////////////////////////////////////

#include "HumanPlayer.h"

void HumanPlayer::initialize() {

// no initialization needed

}

void HumanPlayer::outputName(std::ostream* o) {

*o << "Human player";

}

Move HumanPlayer::makeMove(Pyramid p) {

Move m;

bool needInput = true;

while (needInput) {

std::cout << "Choose a level (1 - 3): ";

std::cin >> m.level;

// actual level is 0 - 2, we subtract one

// so user can enter level more naturally

m.level--;

std::cout << "Choose a count: ";

std::cin >> m.count;

needInput = !p.goodMove(m);

if (needInput) {

std::cout << "Bad input. " << std::endl;

}

}

return m;

}

Sorry for the long snippet!!
``````
0
Question by:alscrimp

LVL 84

Accepted Solution

for ( j = 1; j <= stoneCount[row]; j++) std::cout << "*";
should be
for (int j = 1; j <= stoneCount[row]; j++) std::cout << "*";
0

Author Comment

ozo,
I tried your solution, no success!  When I try to compile it I get a very brief DOS window for a few seconds, then nothing.
0

Author Comment

Anyone else have any ideas. I have to get this program up and running and your expert help is needed!
0

## Featured Post

### Suggested Solutions

In our object-oriented world the class is a minimal unit, a brick for constructing our applications. It is an abstraction and we know well how to use it. In well-designed software we are not usually interested in knowing how objects look in memory. …
Artificial Intelligence comes in many forms, and for game developers, Path-Finding is an important ability for making an NPC (Non-Playable Character) maneuver through terrain.  A* is a particularly easy way to approach it.  I’ll start with the algor…
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.