Sign in to follow this  
sheep19

Tic Tac Toe with AI :) + 1 more question

Recommended Posts

sheep19    494
I've finally finished it!Here it is:
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
using namespace std;

const char PLAYER_SYMBOL = 'X';
const char PC_SYMBOL = 'O';
const char DEFAULT_SYMBOL = '-';

class TicTacToe
{
private:
	vector <int> freeNodes; // the free nodes
	vector <int> usedNodes; // the used nodes
	char board [3] [3]; // set the board

public:
	void setBoard () // set the board to empty
	{
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				board [i] [j] = '-';
			}
		}
	}

	void setFreeNodes ()
	{
		for (int i = 1; i <= 9; i++)
		{
			freeNodes.push_back (i);
		}
	}

	void printBoard () // print the board on the screen
	{
		cout << "\n";
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				cout << board [i] [j] << " ";
			} 
			cout << "\n";
		}
	}
	int getNode ()
	{	
		int node;
		cout << "\n" << "Enter a number from 1-9: ";
		cin >> node;
		return node;
	}
	bool checkInput (int node)
	{
		vector <int>::iterator it;

		for (it = freeNodes.begin(); it < freeNodes.end(); it++)
		{
			if (node == *it)
			{
				return true;
			}
		}
		cout << "\n" << "Invalid input.";
		return false;
	}
	void applyInputToBoard (int node, bool playsP1, bool playsPC)
	{
		int i, j;
		if (node % 3 != 0)
		{
			i = node / 3;
			j = node % 3 - 1;
		}
		else
		{
			i = node / 3 - 1;
			j = 2;
		}

		if (playsP1 == true)
			board [i] [j] = PLAYER_SYMBOL; // ('X')
		else
			board [i] [j] = PC_SYMBOL; // ('O')
	}
	void undoInputToBoard (int node) // undo the pc's move (see pcNode)
	{
		int i, j;
		if (node % 3 != 0)
		{
			i = node / 3;
			j = node % 3 - 1;
		}
		else
		{
			i = node / 3 - 1;
			j = 2;
		}
		
		// undo the move
		board [i] [j] = DEFAULT_SYMBOL; // ('-')
	}
	void addRemoveNodes (int node)
	{
		usedNodes.push_back (node); // add the node in usedNodes
		sort (usedNodes.begin(), usedNodes.end() );

		//remove the node from freeNodes
		int position = 0;
		vector <int>::const_iterator it;
		for (it = freeNodes.begin(); it < freeNodes.end(); it++)
		{
			if (*it == node)
			{
				break;
			}

			position ++; // position is the position of the number (node) in the vector
		}
		
		/*when (*it = node), position isn't increased, because it breaks the loop. But that doesn't matter because when  erase, I have to delete -1
		 because it starts from the the first element. position is already -1 so I don't have to subtract anything.
		eg it should have been "freeNodes.erase (freeNodes.begin() + position - 1);", but position is -1 from the 
		node's position, so, it's ok.*/
		
		freeNodes.erase (freeNodes.begin() + position);
		sort (freeNodes.begin(), freeNodes.end() );
	}
	int checkWin ()
	{
		enum Won
		{
			none = 0,
			winsP1 = 1,
			winsPC = 2,
			tie = 3
		};

		/* This is how the board is.
		 (i, j)		 (i, j+1)	 (i, j+2) 
		(i+1, j)	(i+1, j+1)	(i+1, j+2)
		(i+2, j)	(i+2, j+1)	(i+2, j+2)
		*/

		//horizontal
		for (int i = 0; i < 3; i++)
		{
			int j = 0;
				
			if (board [i][j] == board [i][j+1] && board [i][j] == board [i][j+2])
			{
				if (board [i][j] == PLAYER_SYMBOL)
					return winsP1;
				else if (board [i][j] == PC_SYMBOL)
					return winsPC;
			}
		}

		//vertical
		for (int j = 0; j < 3; j++)
		{
			int i = 0;

			if (board [i][j] == board [i+1][j] && board [i][j] == board [i+2][j])
			{
				if (board [i][j] == PLAYER_SYMBOL)
					return winsP1;
				else if (board [i][j] == PC_SYMBOL)
					return winsPC;
			}
		}

		//diagonal
		int i = 0, j = 0;
		// 1 - 5 - 9
		if (board [i][j] == board [i+1][j+1] && board [i][j] == board [i+2][j+2])
		{
			if (board [i][j] == PLAYER_SYMBOL)
				return winsP1;
			else if (board [i][j] == PC_SYMBOL)
				return winsPC;
		}
		// 3 - 5 - 7
		if (board [i][j+2] == board [i+1][j+1] && board [i][j+2] == board [i+2][j])
		{
			if (board [i][j+2] == PLAYER_SYMBOL)
				return winsP1;
			else if (board [i][j+2] == PC_SYMBOL)
				return winsPC;
		}

		if (freeNodes.empty() == true)
			return tie;
		else
			return none;
	}
	int pcNode ()
	{
		vector <int>::iterator it = freeNodes.begin();

		// for every free node, check IF CAN WIN
		for (it = freeNodes.begin(); it < freeNodes.end(); it++)
		{
			bool winsP1, winsPC;

			applyInputToBoard (*it, false, true);

			int win = checkWin(); // check if this node is a winning node
			if (win == 1)
				winsP1 = true;
			else if (win == 2)
				winsPC = true;

			if (winsPC == true) // if this is a winning move for the pc, return the value
				return *it;
			else // if not, undo the move :)
			{
				undoInputToBoard (*it);
			}
		}
		/* -------------------------------------------------------------------------------------------------------------*/
		srand (static_cast <unsigned int> (time(NULL)));	
		// for every free node, check IF THE PLAYER CAN WIN (if he can, block him)
		int block;
		if (usedNodes.size() <= 4) //the pc will block until x nodes are used, then will have 60% to block			
			block = 1; //it will always block when <= x nodes have been used
		else if (usedNodes.size() <= 6)
			block = 1 + rand() % 80; // when the nodes used are 5 or 6, the pc has more chance to block than after
		else
			block = 1 + rand() % 100;

		int blockChance;
		if (board [1][1] == PLAYER_SYMBOL)
			blockChance = 70; // if the player has the middle node, the PC has more chance to block him (plays smarter)
		else
			blockChance = 60;

		//->
		if (block <= blockChance)
		{
			for (it = freeNodes.begin(); it < freeNodes.end(); it++)
			{
				bool winsP1, winsPC;

				applyInputToBoard (*it, true, false);

				int win = checkWin(); // check if this node is a winning node
				if (win == 1)
					winsP1 = true;
				else if (win == 2)
					winsPC = true;

				if (winsP1 == true) // if this is a winning move for the player, block him
					return *it;
				else // if not, undo the move :)
				{
					undoInputToBoard (*it);
				}
			}
		}
		// -----------------------------------------------------------------------------------------------------------
		//if the middle node is free, apply node
		int chance = 1 + rand() % 100;
		if (usedNodes.size() == 1)
		{
			if (chance <= 60)
			{
				for (it = freeNodes.begin(); it < freeNodes.end(); it++)
				{
					if (*it == 5)
						return *it;
				}
			}
		}
		else // if usedNodes > 1
		{
			for (it = freeNodes.begin(); it < freeNodes.end(); it++)
			{
				if (*it == 5)
					return *it;
			}
		}
		
		// if the player chooses the middle node, then the pc chooses a node in a corner
		random_shuffle (freeNodes.begin(), freeNodes.end() );
		if (board [1][1] == PLAYER_SYMBOL) 
		{
			for (it = freeNodes.begin(); it < freeNodes.end(); it++)
			{
				if ( (*it == 1) || (*it == 3) || (*it == 7) || (*it == 9) )
					return *it;
			}
		}

		//-----------------------------------------------------------------------------------------------------------
		//else choose a random node
		int node;
		int randomNode = 1 + rand() % 9;
		
		it = freeNodes.begin();

		while (it < freeNodes.end() )
		{
			if (randomNode == *it) // if node is free
			{
				int i = randomNode;
				return randomNode;
			}
			
			it++;

			if (it == freeNodes.end() ) // if the node is not in freeNodes (if it's not free), repeat again
			{
				it = freeNodes.begin();
				randomNode = 1 + rand () % 9;
			}
		}

		node = randomNode;
		return node;
	}
	void print()
	{
		cout << "\n" << "Used nodes: ";
		vector <int>::const_iterator it;
		for (it = usedNodes.begin(); it < usedNodes.end(); it++)
		{
			cout << *it << " ";
		}

		cout << "\n" << "Free nodes: ";
		for (it = freeNodes.begin(); it < freeNodes.end(); it++)
		{
			cout << *it << " ";
		}

		cout << "\n";
	}
};

int main()
{
	cout << "Tic Tac Toe" << "\t" << "[by Minas Mina]"
		 << "\n" << "You: X" << "\t" << "PC: O" << "\n";

	TicTacToe game; // create an object for the class

	game.setBoard(); // set the board
	game.setFreeNodes();

	bool playsP1 = true, playsPC = false;
	bool winsP1 = false, winsPC = false, tie = false;

	while ( (winsP1 == false) && (winsPC == false) && (tie == false) )
	{
		if (playsP1 == true)
		{
			int node;
			bool acceptInput = false;
			
			while (acceptInput == false)
			{	
				node = game.getNode();
				acceptInput = game.checkInput (node); // check if the node is free	
			}
			game.addRemoveNodes (node);
			game.applyInputToBoard (node, playsP1, playsPC); //update the board	
			
			int win = game.checkWin();
			if (win == 1)
				winsP1 = true;
			else if (win == 3)
				tie = true;

			game.printBoard();
			playsP1 = false;
			playsPC = true;
			//game.print(); // // prints the vectors
		}

		if (winsP1 == true || tie == true)
			break;
		cout << "\n--------------------------------------------------------------------------\n";
		
		if (playsPC == true)
		{	
			int node = game.pcNode();
			cout << "\n" << "PC plays " << node << "\n";
			game.addRemoveNodes (node);
			game.applyInputToBoard (node, playsP1, playsPC); //update the board

			int win = game.checkWin();

			if (win == 1)
				winsP1 = true;
			else if (win == 2)
				winsPC = true;
			else if (win == 3)
				tie = true;

			game.printBoard();
			playsP1 = true;
			playsPC = false;
			//game.print(); // prints the vectors
			cout << "\n--------------------------------------------------------------------------\n";
		}
	}

	if (winsP1 == true)
		cout << "\n" << "->You win!!";
	else if (winsPC == true)
		cout << "\n" << "->You lose..";
	else
		cout << "\n" << "->Tie..";

	cout << "\n";
	system ("PAUSE");
	return 0;
}

The AI can be found in the pcNode function. Is it good? [Edited by - sheep19 on November 25, 2007 3:07:57 PM]

Share this post


Link to post
Share on other sites
ukdeveloper    264
You really shouldn't have your functions fleshed out inside your class like that. Best to declare the methods in the class and implement them separately in separate files.

Let's call your header TicTacToe.h. You can then #include this in TicTacToe.cpp and actually define your functions within that instead of inside your header file. It keeps things a lot neater and it's much better practice because you can then include the header file in other files without running into multiple declaration linker errors.

As follows:


// TicTacToe.h

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
using namespace std;

const char PLAYER_SYMBOL = 'X';
const char PC_SYMBOL = 'O';
const char DEFAULT_SYMBOL = '-';

class TicTacToe
{
private:
vector <int> freeNodes; // the free nodes
vector <int> usedNodes; // the used nodes
char board [3] [3]; // set the board

public:
void setBoard();
void setFreeNodes();
void PrintBoard();
int getNode();
bool checkInput(int node);
void applyInputToBoard(int node, bool playsP1, bool playsPC);

// and so on....




// TicTacToe.cpp

#include "TicTacToe.h"

void TicTacToe::setBoard()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board [i] [j] = '-';
}
}
}

void TicTacToe::setFreeNodes()
{
for (int i = 1; i <= 9; i++)
{
freeNodes.push_back (i);
}
}

// and so on...



I don't have time to look over your code properly right now, but I feel there are a lot of parts which could be refactored and slimmed down in some way. You could also slip in a couple of reuseable functions here and there as well to avoid repeating yourself and repeating code unnecessarily.

In the meantime, please bear in mind my advice above regarding organisation of things. It's the preferred way of doing things and that's the way you'll see in basically every book you read about C++.



Share this post


Link to post
Share on other sites
sheep19    494
Quote:
Original post by ICUP
hey, i'm getting a runtime error.

It says that winPC is being used without being initialized.


That's strange, because it works fine for me. Which compiler are you using? (I'm using VC++ 2005 EE)

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by sheep19
Quote:
Original post by ICUP
hey, i'm getting a runtime error.

It says that winPC is being used without being initialized.


That's strange, because it works fine for me. Which compiler are you using? (I'm using VC++ 2005 EE)


It's most likely debug build, or the run-time checks turned on.

You have a bug:
bool winsP1, winsPC;

applyInputToBoard (*it, false, true);
int win = checkWin(); // check if this node is a winning node
if (win == 1)
winsP1 = true;
else if (win == 2)
winsPC = true;

if (winsPC == true) // if this is a winning move for the pc, return the value
return *it;



What happens if win is not 1 or 2? winsPC after the test is in undefined state.

While you may consider this a minor issue, you have just made a very common mistake in C++ - using uninitialized variable, source of many problems.

Every time you declare a variable, initialize it to proper value, or restructure the if statement above to always set the proper value.

Share this post


Link to post
Share on other sites
sheep19    494
Quote:
Original post by Antheus

You have a bug:*** Source Snippet Removed ***

What happens if win is not 1 or 2? winsPC after the test is in undefined state.

While you may consider this a minor issue, you have just made a very common mistake in C++ - using uninitialized variable, source of many problems.

Every time you declare a variable, initialize it to proper value, or restructure the if statement above to always set the proper value.


Actually it's not a bug. If win is not 1 or 2, then it won't return *it. It checks if it's true, if it's uninitialized then it's not true. (If I had put
else if (winsPC == false) then it would have been a problem.
The thing is it should have given a warning. (I tried it in a simple program and it did).
I'm going to give them a value of 0 though, because it's not good practice not to.
Thank you, I haven't really noticed it.

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
It checks if it's true, if it's uninitialized then it's not true


Whoa, careful. This is C++, not Java or C#. The value will be anything, either true or false.

bool winsP1, winsPC;

applyInputToBoard (*it, false, true);
int win = checkWin(); // check if this node is a winning node
if (win == 1)
winsP1 = true;
else if (win == 2)
winsPC = true;

if (winsPC == true) // for win == 3, winsPC can be anything. True or false.


Unitialized winsPC can have value of true. Or false. Depending on the garbage which happens to be located on the stack. Or perhaps not if it's stored in registers. Point is - you do not know what at that point winsPC is.

This is undefined behavior, and as such the (winsPC == true) is non-deterministic.

Quote:
Actually it's not a bug.


This is a very serious bug, which can lead to very frustrating random failures that are very difficult to debug.

Share this post


Link to post
Share on other sites
Nyarlath    104
To avoid uninitialized variables:

int win = checkWin(); // check if this node is a winning node
winsP1 = (win == 1);
winsPC = (win == 2);

if (winsPC) // no need to test == true, since true == true
return *it;




You write:

int chance = 1 + rand() % 100;
if (usedNodes.size() == 1)
{
if (chance <= 60)
{
for (it = freeNodes.begin(); it < freeNodes.end(); it++)
{
if (*it == 5)
return *it;
}
}
}



Better (no loop, does not initialize unused variable):

if (usedNodes.size() == 1)
{
int chance = 1 + rand() % 100;
if (chance <= 60 && board[1][1] == DEFAULT_SYMBOL) {
return 5;
}
}


Share this post


Link to post
Share on other sites
sheep19    494
Quote:
Original post by dperfors
Quote:
Original post by sheep19
where do I put int main() ? In the header or in the source file?


in the source file :)


thanks


bool winsP1 = false, winsPC = false;
applyInputToBoard (*it, false, true);
/*and so on...*/



Is this ok ? :)

Share this post


Link to post
Share on other sites
Nyarlath    104
Since the combination (true, true) is not possible, it would be better to use an

enum {
player_wins,
pc_wins,
no_one_wins
};

(Do not convert your variable "win" into bools.)

Share this post


Link to post
Share on other sites
sheep19    494
The new question is, does this program make me "eligible" to move to something more complicated? (like D3D). I know some SDL as well, but I really want to program with real graphics in. Also, I know nothing about pointers and their real uses (just some iterators, which I think are similar). What do you suggest to do?

Thanks in advance.

Share this post


Link to post
Share on other sites
ICUP    140
woah...no, not yet.

If you don't know much about pointers then you should learn about them well. Also, you really should get a good hang of the language, not only syntax but, learning how to program in OOP. Using a single class is not using OOP. Also, learning about data structures (queues, trees, linked lists), file processing, STL, operator overloading, etc. are all important. They may not all be used in game development but they will help you to get a strong hold in learning the language which will in turn help you to build better games.

Do you have an actual book on C++ programming? If you don't I suggest you pick one up. I recommed C++ How to Program (6th edition). It's pricey but it is an excellent book. If you do get it, make sure you get the 6th edition. Near the end of the book there is a chapter on how to make a graphical version of Pong.

Share this post


Link to post
Share on other sites
Drigovas    509
Only you can decide your 'eligibility' for some arbitrary next step. Graphics aren't about knowing certain data structures anyway. Graphics present a project with an increase in code complexity [the parts that deal with the graphics and the resources thereof] and an increased reliance on tools [created by you or others] in exchange for having another medium to work with. Graphics are not 'hard', they just introduce more stuff. If you're ready to take more things into consideration, and have your project expand accordingly, then go for it.

Just keep in mind though that programs that use graphics range in complexity and 'hard'ness similarly to how programs without graphics range in complexity and 'hard'ness. Tic Tac Toe isn't complicated, and a simple graphic-using program won't be either. 'Text' projects can be just as complicated as graphics projects, or at least complicated to the extent that the complexity difference that would be introduced by having the 'Text-only' program be a graphic using program instead would be negligible.

In short, if you want to work with graphics, take a look at some API's, and then make your own decision about your readiness. Half of learning something is being able to judge your own skill and capacity accurately.

Share this post


Link to post
Share on other sites
superpig    1825
I've looked in this thread a bunch of times, and each time I start writing a big reply doing a full critique of your code, and each time I stop before I finish because I worry that what I'm saying will be too many changes for you to digest in one go.

The code you've written works, but there are some major problems with its design that imply a certain degree of confusion about the design. Also, putting the whole thing into one big class is something of a misuse of classes.

I'd recommend exploring the separation of the program into multiple classes, first and foremost - at the very least you could pull out one class for the Board, one class each for the Human and AI players and a base class that they both derive from.

Learn how to isolate the parts of the program from each other, formulate your invariants and learn how to guarantee them. Refactor what you've got here into something that is beautifully designed, and it will help you significantly when you move on to larger projects.

Share this post


Link to post
Share on other sites
sheep19    494
How about this as my header file ?

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>

const char PLAYER_SYMBOL = 'X';
const char PC_SYMBOL = 'O';
const char DEFAULT_SYMBOL = '-';

class TicTacToe
{
private:
std::vector <int> freeNodes; // the free nodes
std::vector <int> usedNodes; // the used nodes
char board [3] [3]; // set the board

class board
{
void setBoard();
void setFreeNodes();
void printBoard();
};

class player
{
int getNode();
bool checkInput (int node);
void applyInputToBoard (int node, bool playsP1, bool playsPC);
void addRemoveNodes (int node);
int checkWin();
};

class pc
{
int pcNode();
void applyInputToBoard (int node, bool playsP1, bool playsPC);
void undoInputToBoard (int node);
void addRemoveNodes (int node);
int checkWin();
};
void print();
};



Also, do I have to use multiple source files ?

Share this post


Link to post
Share on other sites
nobodynews    3126
You don't have to use multiple source files, but you don't have to use for and while loops either (ie, goto). But as your project gets larger you will definitely want to break up your program into modules. Read this until you understand it and you'll do fine.

Share this post


Link to post
Share on other sites
Andorien    139
Quote:
Original post by sheep19
How about this as my header file ?

*** Source Snippet Removed ***
Also, do I have to use multiple source files ?


Your nested classes' members aren't public (class members are private by default), so your TicTacToe class would be able to use them. You need to either declare them public, or use structs instead.

Share this post


Link to post
Share on other sites
superpig    1825
Simply grouping functions together into nested classes isn't how classes are supposed to be used. You're supposed to be modelling an object, not just grouping functions.

The idea is to force interactions in the system to go through well-defined channels. At the moment, the print() function could mess freely with usedNodes or something - and sure, you probably wouldn't write code that did that, but the idea is to set it up such that you can't write code that does that (for example, because the usedNodes variable is private inside the 'board' class and can only be updated by calling functions on the board class).

Share this post


Link to post
Share on other sites
sheep19    494
Quote:
Original post by superpig
Simply grouping functions together into nested classes isn't how classes are supposed to be used. You're supposed to be modelling an object, not just grouping functions.

The idea is to force interactions in the system to go through well-defined channels. At the moment, the print() function could mess freely with usedNodes or something - and sure, you probably wouldn't write code that did that, but the idea is to set it up such that you can't write code that does that (for example, because the usedNodes variable is private inside the 'board' class and can only be updated by calling functions on the board class).


Erm.. How do I do that??

I have tried to do something, that caused 63 errors. Wow. I should have done that at the beginning.. I think I will keep it and try to use Classes and stuff well in the next project..

Share this post


Link to post
Share on other sites
superpig    1825
Well, here's an example. Let's consider the Tic-Tac-Toe board itself:

  • We want to store the contents of the 9 cells on the board.

  • We want to be able to put a piece into a cell (if that cell is empty).

  • We want to be able to check which piece is in a cell.

  • We want to be able to refer to cells using a pair of (x,y) coordinates, where 1 <= x,y <= 3.

  • We want to be able to clear the board.

  • If the user tries to do something that they're not supposed to be able to do, we want the board to respond by throwing an exception. (If we wanted, we could make it pop up a dialog box, crash, or just ignore it instead, but an exception's a pretty good way to handle it).


That's all the board needs to do; it's a fairly passive thing that just has pieces put into it. Things like checking for a winner, figuring out where the AI player should play a piece, etc, can all be done by other classes. The ways in which we interact with the board are laid out in that list; we don't want people to be able to play a piece into a cell that is already taken, for example, and we don't want people to be able to remove individual pieces (they have to clear the board). As a class, it could look something like this:


class Board
{
private:
// Fields (variables) and methods (functions) declared in a 'private' section
// cannot be accessed by functions that are not members of the class.
// We'll put the actual 3x3 array here, because we don't want people to be able
// to access it arbitrarily.
char board[3][3];

public:
// Stuff in a 'public' section can be accessed by any code. This is where we
// provide the methods of interaction that we want to allow.

void PutPieceIn(int x, int y, char piece);
char GetPieceAt(int x, int y);

void Clear();
};

// That's the class definition. Let's define the three functions we declared:

void Board::PutPieceIn(int x, int y, char piece)
{
// x and y should be between 1 and 3. Let's just check that:
if ( x < 1 || x > 3 || y < 1 || y > 3 )
throw "Invalid board position.";

// Let's also check that the position isn't already taken:
if (board[x-1][y-1] != DEFAULT_SYMBOL)
throw "Attempted to place a piece in a slot that is already occupied."

// If we wanted, we could check other things here - for example,
// we could check that (piece == PLAYER_SYMBOL || piece == PC_SYMBOL)
// to stop people putting completely arbitrary characters on the board.
// It wasn't on our list of requirements, though, so let's not worry about
// it for now and just get on with placing the piece:

board[x-1][y-1] = piece;
}

char Board::GetPieceAt(int x, int y)
{
// Just as before, let's check that x and y are in the expected range:
if ( x < 1 || x > 3 || y < 1 || y > 3 )
throw "Invalid board position.";

return board[x-1][y-1];
}

void Board::Clear()
{
// We'll use the standard library function 'fill' to clear the board.
// It's in the <algorithm> header if you want to look it up.
std::fill(&board[0][0], &board[2][2], DEFAULT_SYMBOL);
}




Voila. Now, if you use "Board board" instead of "char board[3][3]" in your TicTacToe class, you'll find it's impossible to screw with the board in any way other than going through the PutPieceIn and GetPieceAt functions - forcing it to do things like check that x and y are in range every time you do.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this