All pastes #974502 Raw Edit

Miscellany

public text v1 · immutable
·published 2008-04-06 20:35 UTC
rendered paste body
#include <iostream>
#include <string>
using namespace std;

class Board {
	int boardstate[9];		// describes the state of the board
	int boardhistory[9];		// note: 0 is a board position, so 9 is 'no event'
	
	public:
	Board() { for(int i = 0; i < 9; i++) { boardstate[i] = 0; boardhistory[i] = 9; } }
	void reset() { for(int i = 0; i < 9; i++) { boardstate[i] = 0; boardhistory[i] = 9; } }
	void draw();
	int history(int index) { if (index >= 0 && index < 9) { return boardhistory[index]; } else { return 9; } }
	int last();
	int state(int coord) { if (coord >= 0 && coord < 9) { return boardstate[coord]; } else {return 0; } }
	void change(int player, int coord) { boardstate[coord] = player; boardhistory[this->last() + 1] = coord; }
	bool wouldwin(int, int);
	int endgame();
	void undo();
};

class Player {
	bool is_ai;
	int player_num, player_score;
	
	public:
	Player(int n): player_num(n), player_score(0), is_ai(false) {}
	int score() { return player_score; }
	void won();
	void set_ai(bool ai) { is_ai = ai; }
	void go(Board&);		// ask for valid move, perform move
};

inline char mark(int num) {
	switch (num) {
		case 1: return 'X'; break;
		case 2: return 'O'; break;
		default: return ' '; break;
	}
}

int main() {
	Board board;
	Player player1(1), player2(2);
	bool again = true;
	string play_again;
	int games = 1;
	
	do {
		// New game
		system("clear");
		cout << "Tic-Tac-Toe\n\nGame " << games << endl;
		cout << "Player 1 score: " << player1.score() << endl;
		cout << "Player 2 score: " << player2.score() << endl << endl;
		
		// Determine if any players are AI
		int ai = 0; player1.set_ai(false); player2.set_ai(false);
		do { cout << "How many human players? "; cin >> ai; } while (ai < 1 || ai > 2);
		if (ai == 1) {
			do { cout << "Which player is AI? "; cin >> ai; } while (ai < 1 || ai > 2);
			if (ai == 1) { player1.set_ai(true); } else { player2.set_ai(true); }
		}
		
		// Begin game loop
		while (board.endgame() == 0) {
			// go Player 1
			board.draw();
			player1.go(board);
			if (board.endgame() != 0) { break; }
			
			// go Player 2
			board.draw();
			player2.go(board);
		}
		
		board.draw();
		switch ( board.endgame() ) {
			case 1: player1.won(); break;
			case 2: player2.won(); break;
			case 3: cout << "Draw!\n"; break;
		}
		games++;
		board.reset();
		cout << "New game? ";
		cin >> play_again;
		if (play_again[0] == 'n' || play_again[0] == 'N') { again = false; }
	} while (again);
	
	return 0;
}

void Board::draw() {
	system("clear");
	cout << " " << mark(boardstate[0]) << " | " << mark(boardstate[1]) << " | " << mark(boardstate[2]) << endl
	     << "-----------" << endl
	     << " " << mark(boardstate[3]) << " | " << mark(boardstate[4]) << " | " << mark(boardstate[5]) << endl
	     << "-----------" << endl
	     << " " << mark(boardstate[6]) << " | " << mark(boardstate[7]) << " | " << mark(boardstate[8]) << endl << endl;
}

int Board::endgame() {
	/* returns...
	   0 if unfinished
	   1 if player 1 won
	   2 if player 2 won
	   3 if draw */
	
	// check horizontals and verticals
	for(int i = 0; i < 3; i++) {
		if ( ( (boardstate[3*i] == boardstate[3*i+1]) && (boardstate[3*i] == boardstate[3*i+2]) && (boardstate[3*i] != 0) )
		  || ( (boardstate[i] == boardstate[i+3] ) && (boardstate[i]  == boardstate[i+6]) && (boardstate[i] != 0) ) )
			{ return boardstate[4*i]; }
	}
	
	// check diagonals
	if ( ( (boardstate[0] == boardstate[4]) && (boardstate[0] == boardstate[8]) && (boardstate[0] != 0) )
	  || ( (boardstate[2] == boardstate[4]) && (boardstate[2] == boardstate[6]) && (boardstate[2] != 0) ) )
		{ return boardstate[4]; }
	
	// check for draw
	bool draw = true;
	for (int i = 0; i < 9; i++) { if (boardstate[i] == 0) { draw = false; } }
	if (draw) { return 3; }
	
	// if none of these conditions are met, the game is not over
	return 0;
}

bool Board::wouldwin(int player, int coord) {
	Board hypothetical = *this;
	hypothetical.change(player, coord);
	if (hypothetical.endgame() == player) { return true; }
	return false;
}

int Board::last() {
	int last = 0;
	for(int i = 0; i < 9; i++) {
		if (boardhistory[i] == 9) { last = i - 1; break; }
	}
	return last;
}

void Board::undo() {
	if (last() > 0) {
		boardstate[ boardhistory[this->last()] ] = 0;
		boardhistory[this->last()] = 9;
		boardstate[ boardhistory[this->last()] ] = 0;
		boardhistory[this->last()] = 9;
	}
}

void Player::go(Board &board) {
	if (is_ai) {
		// check for obvious situations: winning moves and blocking moves
		for(int i = 0; i < 9; i++) {
			if (board.state(i) == 0 && board.wouldwin(player_num, i))
				{ board.change(player_num, i); return; }
		}
		// check if any coordinate will block
		for(int i = 0; i < 9; i++) {
			if (board.state(i) == 0 && board.wouldwin(3 - player_num, i))
				{ board.change(player_num, i); return; }
		}
		
		// check for special situations
		if (player_num == 1) {
			if (board.last() == -1) { board.change(1, 4); return; }
			else if (board.last() == 1) {
				int lastmove = board.history(1);
				if (lastmove % 2 == 0 && lastmove != 4)
					{ board.change(1, 8 - lastmove); return; }
				else { board.change(1, (lastmove < 4 ? 0 : 8)); return; } }
			else if (board.last() == 3) {
				if (board.history(3) % 2 == 0 || board.state(4) == 1) {
					if (board.state(0) == 1) { board.change(1, 1 + board.state(1)); return; }
					if (board.state(2) == 1) { board.change(1, 1 + 2 * board.state(1)); return; }
					if (board.state(6) == 1) { board.change(1, 3 + 2 * board.state(3)); return; }
					if (board.state(8) == 1) { board.change(1, 5 + board.state(5)); return; }
		}	}	}
		else {
			if (board.last() == 0) {
				if (board.state(4) == 1) { board.change(2, 0); return; }
				else { board.change(2, 4); return; } }
			else if (board.last() == 2) {
				if (board.state(4) == 1 && board.state(8) == 1) { board.change(2, 2); return; }
				if ((board.state(0) == 1 && board.state(8) == 1)
				      || (board.state(2) == 1 && board.state(6) == 1))
					{ board.change(2, 1); return; }
				if (board.state(0) == 1 && (board.state(5) == 1 || board.state(7) == 1)) { board.change(2, 8); return; }
				if (board.state(2) == 1 && (board.state(3) == 1 || board.state(7) == 1)) { board.change(2, 6); return; }
				if (board.state(6) == 1 && (board.state(1) == 1 || board.state(5) == 1)) { board.change(2, 2); return; }
				if (board.state(8) == 1 && (board.state(1) == 1 || board.state(3) == 1)) { board.change(2, 0); return; }
				if (board.state(1) == 1 && board.state(3) == 1) { board.change(2, 0); return; }
				if (board.state(1) == 1 && board.state(5) == 1) { board.change(2, 2); return; }
				if (board.state(3) == 1 && board.state(7) == 1) { board.change(2, 6); return; }
				if (board.state(5) == 1 && board.state(7) == 1) { board.change(2, 8); return; }
		}	}
		
		// special conditions out of the way, proceed with threatening
		for(int i = 0; i < 9; i++) {
			if (board.state(i) == 0 && board.state(8 - i) == 0)
				{ board.change(player_num, i); return; }
		}
		
		// all strategy aside, pick any spot
		for(int i = 0; i < 9; i++) {
			if (board.state(i) == 0) { board.change(player_num, i); return; }
		}
	} else {
		bool again;
		string input;
		const char *r_p, *c_p;
		int row = 0, col = 0;
		do {
			again = false;
			do {
				cout << "Player " << player_num << ", \"row,col\": ";
				cin >> input;
				if (input == "undo") { break; }
				if (input.size() > 1) {
					r_p = input.substr(0, 1).c_str(); row = (int)*r_p - 48;
					c_p = input.substr(2, 1).c_str(); col = (int)*c_p - 48; }
			} while ( row < 1 || row > 3 || col < 1 || col > 3 );
			
			// take care of "undo"
			if (input == "undo") { board.undo(); board.draw(); again = true; }
			else {
				if (board.state(3*(row-1)+col-1) == 0) { board.change(player_num, 3*(row-1)+col-1); }
				else { board.draw(); again = true; }
			}
		} while (again);
	}
}

void Player::won() {
	player_score++;
	cout << "Player " << player_num << " (" << mark(player_num) << ") wins!\n";
}