![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 630
Rep Power: 4
![]() |
Tic Tac Toe progress
I don't know what happened to the Tic Toe contest (I guess there was not enough interest) but I though I would try working on a Tic Tac Toe game. It currently uses text, but after getting enough experience with Qt, I'd like to try to implement it with a GUI.
It also only uses a random position for the AI, because I haven't developed the AI makeMove() function yet. Keep in mind, this is the equivalent of a rough draft. I'm sure there are hundreds of improvements. tic.h //tic.h -- TicTacToe declarations
#ifndef TICTACTOE_H_
#define TICTACTOE_H_
class Board {
private:
static char board[3][3];
char player;
char otherPlayer;
char *checkBoard(int);
public:
Board(char,char);
void reset();
virtual bool makeMove(int);
bool checkWin();
void show() const;
virtual ~Board();
};
/*class CBoard : public Board { //TODO
public:
CBoard(char);
virtual bool makeMove(int);
};*/
#endiftic.cpp
//tic.cpp -- implementation of class
#include <iostream>
#include "tic.h"
char Board::board[3][3];
Board::Board(char icon, char otherIcon) {
//std::cout << "constructor called" << std::endl;
player = icon;
otherPlayer = otherIcon;
}
/*void Board::set() {
//std::cout << "set() called" << std::endl;
if(boardSet == false) {
//std::cout << "Board being set..." << std::endl;
for(int x = 0; x < 3; x++)
for(int y = 0; y < 3; y++)
Board::board[x][y] = ' ';
}
else {
std::cout << "board previously set" << std::endl;
std::cout << "use reset() to reset game" << std::endl;
}
boardSet = true;
}*/
void Board::reset() {
for(int x = 0; x < 3; x++)
for(int y = 0; y < 3; y++)
Board::board[x][y] = ' ';
}
bool Board::makeMove(int space) {
//board setup is like that of numpad
if(space < 1 || space > 9) {
std::cout << "invalid range!" << std::endl;
return false;
}
int x, y; //x-axis, y-axis
switch(space) {
case 7:
x = 0;
y = 0;
break;
case 8:
x = 1;
y = 0;
break;
case 9:
x = 2;
y = 0;
break;
case 4:
x = 0;
y = 1;
break;
case 5:
x = 1;
y = 1;
break;
case 6:
x = 2;
y = 1;
break;
case 1:
x = 0;
y = 2;
break;
case 2:
x = 1;
y = 2;
break;
case 3:
x = 2;
y = 2;
break;
}
if((Board::board[x][y] == otherPlayer) || (Board::board[x][y] == player)) //if space is taken...
return false; //move failed. return false.
else {
Board::board[x][y] = player; //otherwise, make move
return true; //return success
}
}
char *Board::checkBoard(int part) {
//0, 1, 2 ==> cols
//3, 4, 5 ==> rows
//6, 7 ==> diagonals
char arr[3];
char *temp = arr;
int space;
switch(part) {
//columns
case 0:
case 1:
case 2:
for(int a = 0; a < 3; a++)
temp[a] = Board::board[part][a];
break;
//rows
case 3:
space = 0;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
case 4:
space = 1;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
case 5:
space = 2;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
//diagonals
case 6:
temp[0] = Board::board[0][2];
temp[1] = Board::board[1][1];
temp[2] = Board::board[2][0];
break;
case 7:
temp[0] = Board::board[0][0];
temp[1] = Board::board[1][1];
temp[2] = Board::board[2][2];
}
return temp;
}
bool Board::checkWin() {
int count = 0;
bool winner = false;
//checking rows
for(int x = 0; x < 3; x++) { //for each row
char *row = checkBoard(x);//get the row
count = 0;
for(int a = 0; a < 3; a++) //check each space in row
if(*(row + a) == player) //if space is taken
count++; //increase count
if(count == 3) //if all three taken on row...
winner = true; //set winner to true;
}
//checking columns
for(int x = 3; x < 6; x++) { //for each column
char *col = checkBoard(x);
count = 0; //starting count again
for(int a = 0; a < 3; a++)
if(*(col + a) == player) //if space is taken
count++; //increase count
if(count == 3)
winner = true;
}
//checking diagonals
count = 0;
char *dia1 = checkBoard(6);
for(int x = 0; x < 3; x++) {
if(*(dia1 + x) == player)
count++;
}
if(count == 3)
winner = true;
//second diagonal
count = 0;
char *dia2 = checkBoard(7);
for(int x = 0; x < 3; x++)
if(*(dia2 + x) == player)
count++;
if(count == 3)
winner = true;
return winner; //returns false if no wine, true if winner
}
void Board::show() const {
std::cout << "---------";
for(int a = 0; a < 3; a++) {
std::cout << std::endl;
std::cout << "|";
for(int b = 0; b < 3; b++)
std::cout << " " << Board::board[b][a];
std::cout.put(' ');
std::cout << "|";
}
std::cout << "\n---------" << std::endl;
}
Board::~Board() {
}main.cpp #include "tic.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
Board b1('x', 'o');
Board b2('o', 'x');
bool done = false;
int move;
while(!done) {
b1.reset();
while(1) {
cout << "Enter space with numpad: ";
cin >> move;
if(!b1.makeMove(move)) {
cout << "bad move" << endl;
continue;
}
b1.show();
if(b1.checkWin()) {
cout << "x wins!" << endl;
done = true;
break;
}
move = rand() % 8 + 1;
while(1) {
if(!b2.makeMove(move)) {
continue;
}
else
break;
}
if(b2.checkWin()) {
cout << "o wins!" << endl;
done = true;
break;
}
b1.show();
}
}
cout << "Done!" << endl;
return 0;
}EDIT:fixed a large bug EDIT2: I realize that it sometimes lags becuase the random moves keep getting rejected. This problem will disappear once I have written a good AI
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! Last edited by Jessehk; Dec 9th, 2005 at 10:07 PM. |
|
|
|
|
|
#2 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 630
Rep Power: 4
![]() |
new main.cpp
#include "tic.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
Board b1('x', 'o');
Board b2('o', 'x');
bool done = false;
int move;
while(!done) {
b1.reset();
while(1) {
cout << "Enter space with numpad: ";
cin >> move;
if(!b1.makeMove(move)) {
cout << "bad move" << endl;
continue;
}
b1.show();
if(b1.checkWin()) {
cout << "x wins!" << endl;
done = true;
break;
}
while(1) {
move = rand() % 9 + 1;
if(!b2.makeMove(move)) {
continue;
}
else
break;
}
b2.show();
if(b2.checkWin()) {
cout << "o wins!" << endl;
done = true;
break;
}
}
}
cout << "Done!" << endl;
return 0;
}
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! Last edited by Jessehk; Dec 9th, 2005 at 10:44 PM. |
|
|
|
|
|
#3 |
|
Hobbyist Programmer
|
You should try making an expanded tic-tac-toe where the user decides how many rows and columns on which to play. Then you can't make the AI so specific. Makes for a nice challenge.
|
|
|
|
|
|
#4 | |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 630
Rep Power: 4
![]() |
Quote:
![]() Here is the second rough version. The computer will now block you from winning. Next I'd like to add a offensive function with a 50% chance of running (so the computer is still beatable) anyway, here it is: tic.h
//tic.h -- TicTacToe declarations
#ifndef TICTACTOE_H_
#define TICTACTOE_H_
class Board
{
private:
char *checkBoard(int);
protected:
static char board[3][3];
char player;
char otherPlayer;
public:
Board(char,char);
void reset();
virtual bool makeMove(int);
bool checkWin();
void show() const;
virtual ~Board();
bool checkCats() const;
};
class CBoard : public Board
{
public:
CBoard(char, char);
virtual bool makeMove();
private:
int *checkDefence();
void makeRandomMove();
// int checkOffence();
};
#endiftic.cpp
//tic.cpp -- implementation of class
#include <iostream>
#include "tic.h"
char Board::board[3][3];
Board::Board(char icon, char otherIcon)
{
//std::cout << "constructor called" << std::endl;
player = icon;
otherPlayer = otherIcon;
}
void Board::reset()
{
for(int x = 0; x < 3; x++)
for(int y = 0; y < 3; y++)
Board::board[x][y] = ' ';
}
bool Board::makeMove(int space)
{
//board setup is like that of numpad
if(space < 1 || space > 9)
{
std::cout << "invalid range!" << std::endl;
return false;
}
int x, y; //x-axis, y-axis
x = y = 0;
switch(space) {
case 7:
x = 0;
y = 0;
break;
case 8:
x = 1;
y = 0;
break;
case 9:
x = 2;
y = 0;
break;
case 4:
x = 0;
y = 1;
break;
case 5:
x = 1;
y = 1;
break;
case 6:
x = 2;
y = 1;
break;
case 1:
x = 0;
y = 2;
break;
case 2:
x = 1;
y = 2;
break;
case 3:
x = 2;
y = 2;
break;
}
if((Board::board[x][y] == otherPlayer) || (Board::board[x][y] == player)) //if space is taken...
return false; //move failed. return false.
else
{
Board::board[x][y] = player; //otherwise, make move
return true; //return success
}
}
char *Board::checkBoard(int part)
{
//0, 1, 2 ==> cols
//3, 4, 5 ==> rows
//6, 7 ==> diagonals
char *temp = new char[3];
int space;
switch(part)
{
//columns
case 0:
case 1:
case 2:
for(int a = 0; a < 3; a++)
temp[a] = Board::board[part][a];
break;
//rows
case 3:
space = 0;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
case 4:
space = 1;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
case 5:
space = 2;
for(int a = 0; a < 3; a++)
temp[a] = Board::board[a][space];
break;
//diagonals
case 6:
temp[0] = Board::board[0][2];
temp[1] = Board::board[1][1];
temp[2] = Board::board[2][0];
break;
case 7:
temp[0] = Board::board[0][0];
//std::cout << "from checkBoard -- diagonal 2 " << Board::board[0][0] << std::endl;
temp[1] = Board::board[1][1];
//std::cout << "from checkBoard -- diagonal 2 " << Board::board[1][1] << std::endl;
temp[2] = Board::board[2][2];
//std::cout << "from checkBoard -- diagonal 2" << Board::board[2][2] << std::endl;
break;
}
return temp;
}
bool Board::checkWin()
{
int count = 0;
bool winner = false;
//checking columns
for(int x = 0; x < 3; x++)
{ //for each colums
char *col = checkBoard(x);//get the column
count = 0;
for(int a = 0; a < 3; a++) //check each space in column
if(*(col + a) == player) //if space is taken
count++; //increase count
if(count == 3)
{ //if all three taken on column...
winner = true; //set winner to true;
//std::cout << "column winner" << std::endl;
}
delete [] col;
}
//checking rows
for(int x = 3; x < 6; x++)
{ //for each rows
char *row = checkBoard(x);
count = 0; //starting count again
for(int a = 0; a < 3; a++)
if(*(row + a) == player) //if space is taken
count++; //increase count
if(count == 3)
{
winner = true;
//std::cout << "row winner" << std::endl;
}
delete [] row;
}
//checking diagonals
count = 0;
char *dia1 = checkBoard(6);
//for(int a = 0; a < 3; a++)
// std::cout << "diagonal[" << a << "] = " << dia1[a] << std::endl;
for(int x = 0; x < 3; x++)
{
if(*(dia1 + x) == player)
count++;
}
if(count == 3)
{
//std::cout << "diagonal 1 winner" << std::endl << std::endl;
winner = true;
}
delete [] dia1;
//second diagonal
count = 0;
char *dia2 = checkBoard(7);
//for(int a = 0; a < 3; a++)
// std::cout << "diagonal[" << a << "] = " << dia2[a] << std::endl;
for(int x = 0; x < 3; x++)
if(*(dia2 + x) == player)
count++;
if(count == 3)
{
//std::cout << "diagonal 2 winner" << std::endl;
winner = true;
}
delete [] dia2;
return winner; //returns false if no win, true if winner
}
void Board::show() const
{
std::cout << "---------";
for(int a = 0; a < 3; a++)
{
std::cout << std::endl;
std::cout << "|";
for(int c = 0; c < 3; c++)
std::cout << " " << Board::board[c][a];
std::cout.put(' ');
std::cout << "|";
}
std::cout << "\n---------" << std::endl;
}
bool Board::checkCats() const
{
int taken = 0;
for(int x = 0; x < 3; x++)
for(int y = 0; y < 3; y++)
if(Board::board[x][y] != ' ')
taken++;
/*if(taken == 9)
std::cout << "return true" << std::endl;
else
std::cout << "return false" << std::endl;
return (taken == 9);*/
return taken == 9;
}
Board::~Board()
{
}
//CBoard method definitions
CBoard::CBoard(char icon, char otherIcon)
: Board(icon, otherIcon)
{
player = icon;
otherPlayer = otherIcon;
}
int *CBoard::checkDefence()
{
//look for any spots where the human player will win
//first look for rows
int *spot = new int[3];
bool found = false;
int count = 0;
for(int x = 0; x < 3; x++)
{
count = 0;
for(int y = 0; y < 3; y++)
{
if(Board::board[x][y] == otherPlayer && Board::board[x][y] != player)
count++;
if(count == 2)
{
for(int c = 0; c < 3; c++)
{
if(Board::board[x][c] != otherPlayer && Board::board[x][c] != player)
{
spot[0] = x;
spot[1] = c;
found = true;
}
}
}
}
}
if(found)
return spot;
//then check for columns
count = 0;
for(int x = 0; x < 3; x++)
{
count = 0;
for(int y = 0; y < 3; y++)
{
if(Board::board[y][x] == otherPlayer && Board::board[y][x] != player)
count++;
if(count == 2)
{
for(int c = 0; c < 3; c++)
{
if(Board::board[c][x] != otherPlayer && Board::board[c][x] != player)
{
spot[0] = c;
spot[1] = x;
found = true;
}
}
}
}
}
if(found)
return spot;
//check diagonals
count = 0;
//first diagonal: lower left to upper right
if(Board::board[0][2] == otherPlayer && Board::board[1][1] == otherPlayer
&& Board::board[2][0] != player)
{
spot[0] = 2; //2
spot[1] = 0; //0
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
else if(Board::board[1][1] == otherPlayer && Board::board[2][0] == otherPlayer
&& Board::board[0][2] != player)
{
spot[0] = 0; //0
spot[1] = 2; //2
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
else if(Board::board[0][2] == otherPlayer && Board::board[2][0] == otherPlayer
&& Board::board[1][1] != player)
{
spot[0] = 1; //1
spot[1] = 1; //1
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
if(found)
return spot;
//second diagonal
if(Board::board[0][0] == otherPlayer && Board::board[1][1] == otherPlayer
&& Board::board[2][2] != player)
{
spot[0] = 2; //2
spot[1] = 2; //2
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
else if(Board::board[1][1] == otherPlayer && Board::board[2][2] == otherPlayer
&& Board::board[0][0] != player)
{
spot[0] = 0; //0
spot[1] = 0; //0
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
else if(Board::board[0][0] == otherPlayer && Board::board[2][2] == otherPlayer
&& Board::board[1][1] != player)
{
spot[0] = 1; //1
spot[1] = 1; //1
found = true;
//std::cout << "diagonal should be blocked" << std::endl;
}
if(found)
return spot;
return NULL;
}
void CBoard::makeRandomMove()
{
srand(time(0));
while(1)
{
int move = rand() % 9 + 1;
if(Board::makeMove(move)) //if move is good
break;
else
continue; //otherwise, make another move
}
}
bool CBoard::makeMove()
{
int *spot = checkDefence();
if(spot != NULL)
{
//std::cout << "spot[0] = " << spot[0] << std::endl;
//std::cout << "spot[1] = " << spot[1] << std::endl;
Board::board[spot[0]][spot[1]] = player;
}
else
{
srand(time(0));
makeRandomMove();
}
delete [] spot;
return true;
}main.cpp
#include "tic.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
Board b1('x', 'o');
CBoard b2('o', 'x');
bool done = false;
b2.reset();
while(!done)
{
int move;
while(1)
{
cout << "Enter move: ";
cin >> move;
if(!b1.makeMove(move)) //bad move
cout << "bad move" << endl;
else
break;
}
//cout << "out of first whiel loop" << endl;
b1.show();
if(b1.checkWin())
{
cout << "x wins!" << endl;
done = true;
continue;
}
if(b1.checkCats())
{
cout << "cat's game" << endl;
done = true;
continue;
}
//cout << "b2.makeMove()" << endl;
b2.makeMove();
b2.show();
if(b2.checkWin())
{
cout << "o wins!" << endl;
done = true;
continue;
}
if(b2.checkCats())
{
cout << "cat's game" << endl;
done = true;
continue;
}
}
return 0;
}
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
|
|
|
#5 |
|
Professional Programmer
|
I have some tic-tac-toe code lying around somewhere that implements minimax, if you're interested in seeing it.
Edit: It's actually a minimax variant called negamax. Really it's the same thing, it just eliminates the need for dual recursive functions and replaces it with one function. |
|
|
|
|
|
#6 | |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 630
Rep Power: 4
![]() |
Quote:
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
|
|
|
#7 |
|
Professional Programmer
|
Long story short, you can't win against my tic-tac-toe.
|
|
|
|
|
|
#8 | |
|
Programming Guru
![]() Join Date: Jun 2005
Location: elemental plane
Posts: 1,429
Rep Power: 5
![]() |
Quote:
__________________
"Employ your time in improving yourself by other men's writings, so that you shall gain easily what others have labored hard for." -- Socrates |
|
|
|
|
|
|
#9 |
|
The Oblivious One
Join Date: May 2005
Location: Ontario, Canada
Posts: 630
Rep Power: 4
![]() |
Well, I don't think it's pretty, but it works.
The AI is not supposed to be unbeatable. The human player should win about 50% of the time. I did not use an algorithm to make moves. Instead, the computer will make winning moves, and block you. All other moves are random. I also realise that if you enter in non-numerical input for a move, an endless loop will ensure. This will be fixed shortly. Thanks for looking ![]() EDIT: I will probably also change the AI so that after offensive, and defensive moves, the computer will attempt to make a move in the corners, and then the center, and then a random move will take place. it's too easy to beat the computer when it makes stupid moves.
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS! |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|