Sign in to follow this  
Dr1fter

TicTacToe array problem

Recommended Posts

Hi there, im reading through java for students and in the 2d array chapter one of the exercises is to create a tic tac toe game. My problem is that i am writing a method to initialise the array and then another method to find out the winner as below

private void init() {
		String str = "";
		
		for (int i = 0; i <=2; i++) {
			for (int j=0; j<=2; j++) {
				grid[i][j] = str;
			}
		}
	}

private boolean winningCombinations() {
		
		if (grid[0][0] == grid[0][1] && grid[0][1] == grid[0][2]) {
			win = true;
		}
		if (grid[0][0] == grid[1][0] && grid[1][0] == grid[2][0]) {
			win = true;
		}
		if (grid[0][1] == grid[1][1] && grid[1][1] == grid[2][1]) {
			win = true;
		}
		if (grid[0][2] == grid[1][2] && grid[1][2] == grid[2][2]) {
			win = true;
		}
		if (grid[1][0] == grid[1][1] && grid[1][1] == grid[1][2]) {
			win = true;
		}
		if (grid[2][0] == grid[2][1] && grid[2][1] == grid[2][2]) {
			win = true;
		}
		if (grid[0][0] == grid[1][1] && grid[1][1] == grid[2][2]) {
			win = true;
		}
		if (grid[2][0] == grid[1][1] && grid[1][1] == grid[0][2]) {
			win = true;
		}
		
		else {
			win = false;
		}
		
		return win;
	}


Now my problem is when i initialise the array before the game starts all the data in the array is equal so the method winningCombinations is returning true and declaring a win. Should i not be initialising the array in this way or is it just my logic that is incorrect for the method. Thanks a lot for your help in this.

Share this post


Link to post
Share on other sites
Each grid element needs to hold three distinct states, "Empty", "X and "O". An enumeration would be perfect, but you might want to use null strings to represent empty elements for the moment.

Your winningCombinations method is incorrect through. You should use String#equals, not == for comparing strings. Otherwise you can get situations where the comparison returns false for strings with identical contents. That, and you would need to check that the elements are not "empty", depending on how you want to represent them.

It is idiomatic to write loops like this:

for(int i = 0 ; i < N ; ++i) {
// ...
}

Rather than testing if "loopCounter <= N - 1".

Share this post


Link to post
Share on other sites
Quote:
Original post by Dr1fter
Now my problem is when i initialise the array before the game starts all the data in the array is equal so the method winningCombinations is returning true and declaring a win. Should i not be initialising the array in this way or is it just my logic that is incorrect for the method.
After you check for equality check the contents of the array to see if one of those array elements contains a valid move. An empty string is not valid. Presumably "X" or "O" is though.

edit: I don't know Java didn't let you compare strings like that mentioned by rip-off so that would need to be fixed as well.

Share this post


Link to post
Share on other sites

if (grid[2][0] == grid[1][1] && grid[1][1] == grid[0][2]) {
win = true;
}

else {
win = false;
}

Under which circumstances do you think the else body will be executed?

Share this post


Link to post
Share on other sites
thank you for the quick replies.

@rip-off, thanks i will change it to .equals since i am comparing strings (rookie error) :).

@DevFred, i assume the else will be executed if the if statement before it is not true, what i was trying to achieve is the else statement to be executed if all the other if's are not true. Would i need to put an else in each of the if statements to achieve that? i was hoping for it to check each if statement and if none are true then the else would be called.

Share this post


Link to post
Share on other sites
DevFred is trying to say that if everything is equal at startup, regardless if it is " ", "X", "O".

Change your win checks to if grid[][] == "X" && grid[][] == "X".

To save you some time, allow your function to have a string passed to it. So pass in "X" and then "0" and just do grid[][] == str && grid[][] == str

Share this post


Link to post
Share on other sites
Quote:
Original post by Dr1fter
i assume the else will be executed if the if statement before it is not true

Correct.

Quote:
Original post by Dr1fter
Would i need to put an else in each of the if statements to achieve that?

You can simply replace each "if" (except the first) with "else if". But may I recommend a different solution?

private boolean winningCombinations()
{
boolean win = false;
if (...)
{
win = true;
}
if (...)
{
win = true;
}
if (...)
{
win = true;
}
...
return win;
}

Note the complete absence of else.

Share this post


Link to post
Share on other sites
You could use "return true" whenever you find out that the game is won:

private boolean winningCombinations()
{
if (...)
{
return true;
}

if (...)
{
return true;
}

if (...)
{
return true;
}

...

return false;
}

Share this post


Link to post
Share on other sites
And here is a more data-driven approach:

// Can you guess what these numbers mean?
private static final int[][][] winning_lines = {
{{0, 0}, {0, 1}, {0, 2}},
{{1, 0}, {1, 1}, {1, 2}},
{{2, 0}, {2, 1}, {2, 2}},

{{0, 0}, {1, 0}, {2, 0}},
{{0, 1}, {1, 1}, {2, 1}},
{{0, 2}, {1, 2}, {2, 2}},

{{0, 0}, {1, 1}, {2, 2}},
{{2, 0}, {1, 1}, {0, 2}}
};

private boolean winningCombinations()
{
// These two constants just increase readability in the coming loop
final int x = 0;
final int y = 1;

// The following loop will run eight times,
// Once for every possible winning line
for (int[][] pos: winning_lines)
{
String a = grid[pos[0][y]][pos[0][x]];
String b = grid[pos[1][y]][pos[1][x]];
String c = grid[pos[2][y]][pos[2][x]];

// If all three are equal and not empty, we have a winner!
if (!a.isEmpty() && a.equals(b) && b.equals(c)) return true;
}
// We tried every combination and could not determine a winner, thus:
return false;
}


It still looks overly complex to my eyes which have been spoiled by Haskell :)

Share this post


Link to post
Share on other sites
I have it working now thank you all for the replies, i obviously still have a lot of work to do practicing the fundamentals.

DevFred i like the data-driven approach it seems a lot tidier than my list of if statements and i think i will do another version with this approach to try and help my understanding further.

Share this post


Link to post
Share on other sites
Quote:
Original post by nobodynews
edit: I don't know Java didn't let you compare strings like that mentioned by rip-off so that would need to be fixed as well.


Java string literals actually end up as String objects, so == compares their addresses just like it does for other objects. This is not a problem if both strings being compared were created as a string literal, because all string literals in Java are automatically interned - two string literals with the same text will always point to the same String object.

Share this post


Link to post
Share on other sites
Quote:
Original post by lightbringer
Quote:
Original post by nobodynews
edit: I don't know Java didn't let you compare strings like that mentioned by rip-off so that would need to be fixed as well.


Java string literals actually end up as String objects, so == compares their addresses just like it does for other objects. This is not a problem if both strings being compared were created as a string literal, because all string literals in Java are automatically interned - two string literals with the same text will always point to the same String object.

Even still, you shouldn't use == to compare strings unless you can establish they are both interned as an invariant. In a situation like this where the data may end up coming from a file or somewhere more bizarre I would side with caution.

An enum would be the superior solution even to an interned string, they are by design always interned, as well as type safe.

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
Even still, you shouldn't use == to compare strings unless you can establish they are both interned as an invariant. In a situation like this where the data may end up coming from a file or somewhere more bizarre I would side with caution.


Wasn't trying to recommend it, just explaining what's going on. Interned Strings are great to use with IdentityHashMap for constant-time lookups for instance. I agree that enum makes more sense here, especially since the domain is well-defined (x, o, null, that's it). For situations where you need a lot of flexibility (for example you want to let the end user pass in his own message types in script without modifying the system), strings of course are a better choice.

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