Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 789
  • Last Modified:

AI Pyramid Game Project

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!!

Open in new window

0
alscrimp
Asked:
alscrimp
  • 2
1 Solution
 
ozoCommented:
for ( j = 1; j <= stoneCount[row]; j++) std::cout << "*";
should be
 for (int j = 1; j <= stoneCount[row]; j++) std::cout << "*";
0
 
alscrimpAuthor Commented:
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
 
alscrimpAuthor Commented:
Anyone else have any ideas. I have to get this program up and running and your expert help is needed!
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now