views:

709

answers:

12

I have a program I am writing that works on the principle of populating a two dimensional array to check the winning condition, it's noughts and crosses so the two dimensional array is populated on the click of a button, 1 for a circle, 2 for a cross, then the checkWin() will work on this principle, not the actual code...

if (myArray[0][0] == 1 && myArray[0][1] == 1 && myArray[0][2] == 1){
    setBoolWinVal = true;
} else {
    if(myArray[0][0] == 2 && myArray[0][1] == 2 && myArray[0][2] == 2){
    setBoolWinVal = true;
}

you can see immediately that for every winning condition this will be messy, is there any way of rewriting this check for win to shorten it a little?

+7  A: 

This looks like homework, so I won't give it away completely. But notice a few things:

  • You win when you have three of the same thing in a row, in a column, or along a diagonal.

  • It's not that you need to check whether there are three noughts or three crosses in a line, but rather that there are three of the same thing in a line.

You could start by writing a function which determines whether a row, column, or diagonal has three of the same kind of thing (and that thing isn't a blank cell). Then call that function once for each row, column, and diagonal.

John Feminella
A: 

Smells of homework.

Small hint - use for loops and it will work for any size you want. Bonus points if you use recursion instead.

Definition: Recursion -> see Recursion

Robin
A: 

If the arrays are all the same length, then the following should help.

Arrays.equals(myArray[0], {1,1,1})

Otherwise, write a function that loops through all the values in myArray[0].

Nikhil Chelliah
Doesn't work. What if the three winning elements are in a column instead of a row?
John Feminella
Didn't realize that was a possibility - both examples used rows. Thanks for the catch.
Nikhil Chelliah
+2  A: 

The obvious way is to loop over the 3 elements in a row, column or diagonal and check they are the same.

Another way is to use a more compact representation - for example, an int, where the 9 cells are represented by 2 bits each, or two shorts, one for each player. Then use a look-up table, or bitwise operations, to map the state to win lose.

If a single bit represents a cell, and you have one padding bit, each player's tiles are 3 hex digits 0-7.

diagonal line is:

cells & 0x421 == 0x421
cells & 0x124 == 0x124

vertical line is:

cells & (cells>>4) & (cells>>8) != 0

horizontal line

cells & (cells>>1) & (cells>>2) != 0

Similar techniques are used using 64bit patterns to represent possible moves in chess games.

Pete Kirkham
+3  A: 

Here's (untested) code. It's certainly not production quality :)

Note some of the optimisations from other answers:

  1. Use of for-loops
  2. We check for equal player values, not specific player values
  3. A diagonal win must pass through cell[1][1]

.

int find_winner(int[][] arr) {

    // check rows
    for (int i = 0; i < 3; ++i) {
        int player = arr[i][0];
        if (player < 1) continue; // nb: prior version didn't check for empty cells
        if (arr[i][1] == player && arr[i][2] == player) return player;
    }

    // check cols
    for (int i = 0; i < 3; ++i) {
        int player = arr[0][i];
        if (player < 1) continue;
        if (arr[1][i] == player && arr[2][i] == player) return player;
    }

    // check diagonals
    int player = arr[1][1];
    if (player < 1) return -1;

    if ((arr[0][0] == player && arr[2][2] == player) ||
        (arr[2][0] == player && arr[0][2] == player)) return player;

    // no winner found
    return -1;
}
Alnitak
-1 when it's homework, don't give full solutions...
basszero
@basszero - it wasn't tagged as homework when I started this answer
Alnitak
+1  A: 

Two funny ideas for you: (I assume underlying 1D array because it makes life easier.)

First: Encode the positions to be tested. Like if you have 1D array describing your field:

diag[] = { { 0, 1, 2}, {3, 4, 5}, {6, 7, 8},
           { 0, 3, 6}, {1, 4, 7}, {2, 5, 8},
           { 0, 4, 8}, {6, 4, 2}};

then loop through diag: for every element of it, test the three corresponding fields (use diag[i] as index of your field array).

Second: Use a bitfield to represent your field. Like java.util.BitSet. Then encode the solutions into bitfields like {1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0} and so on. Then you can test (loop over) if (field & solution[i] == solution[i]). For second player, just use !field in the if statement.

Fun, isn't it? p.s.: it is up to you to make working Java code out of this!

ypnos
A: 

So if you think about it, you are checking for conditions where:

www   xxx   xxx  wxx
xxx   www   xxx  xwx
xxx   xxx   www  xxw

etc

Where w are the ones to check for a win, and x are the ones to ignore.

(0,0),(0,1),(0,2) are the locations of the "w" in the first correct solution. 1,0, 1,1 and 1,2 are the second, etc.

This looks to me like it should be data instead of code. The trick is, how do you input and store the data efficiently.

Note that this data is starting to look like your array indexes.

So let's say you had an array of "Solutions" that had 8 x,y pairs in it (each indicating a winning solution combination)

Then all you'd have to do is iterate over this collection, extract the three x,y pairs and test them. so it would be similar to what you have, but use variables where you have numbers.

if(myArray[x0][y0] == 2 && myArray[x1][y1] == 2 && myArray[x2][y2] == 2){
    win();

As you iterate over it, the index variables will be replaced with the next line of your data.

Since you don't want to report a loss after you win, you're going to have to play with the logic a little to make sure your loop stops, but this is probably already too much help for a homework style problem.

Bill K
A: 

this really sounds like a bit of homework, but i will help with the simple question of going through the rows and columns, checking if they are all full, diagonal is slightly harder, and i leave it up to you. Again, i believe a nested for-loop would be the best idea:

java.util.Arrays;

public yourClass{

public void findAnswer{

int initialVal = 0;

int copy = 0;

int column = 0;

boolean columnVal = true;

boolean rowVal = false;

for (int j = 0; j< myArray.length; j++){ //presuming myArray and myArray[] are same length

boolean firstRun = true;

for (int i = 0; i <myArray.length; i++){

    initialVal = myArray[i][column];
    if (firstRun == true){
        copy = intialVal; //makes a copy for the first time, to compare
        firstRun = false;
    }
    if (intialVal != copy){
        columnVal = false; //realises this column isnt a full column i.e. 3 1's or 2's in column
        return;
    }else
        columnVal = true;

    }//end nested for loop

rowVal = findRow(j); 
if(rowVal = true)
return;

}

public boolean findRow(int row){

int[] tempRow1, tempRow2;
Arrays.fill(tempRow1, 1); 
Arrays.fill(tempRow2, 2); //fills temp arrays for comparison
boolean answer = false;
if (Arrays.equals(myArray[], tempRow1) || Arrays.equals(myArray[], tempRow2) //this tests either, || being logical OR
    answer = true;
return answer;

} }


n.b. this code isnt tested, and it was quickly coded, so doesnt look very good, it can be refined, and i also havent implemented diagonals. p.s. if it is homework, its probably best to look up stuff on the internet, and try to work it out on your own, rather than seeking help by asking others to do it for you.

A: 
  1. represent board as an int in 0..19683 (3 to the 9th)
  2. pre-generate winning boards and identify who is winner in each board, perhaps by hand
  3. at runtime look for the current board in your precalculated map
Ron
A: 

For n X n matrix...

//Check all horizontal rows
for(i=0; i<n && !setBoolWinVal; i++) //row
{
 setBoolWinVal = true;
 for(j=0; j<n-1; j++)
 {
  if( myArray[i][j] != myArray[i][j+1] ) //column
  {
   setBoolWinVal = false;
   break;
  }
 }

 if(setBoolWinVal) declareWinner(myArray[i][0]);
}

//Check all vertical columns
if(!setBoolWinVal)
{
 for(i=0; i<n && !setBoolWinVal; i++) //column
 {
  setBoolWinVal = true;
  for(j=0; j<n-1; j++) //row
  {
   if( myArray[j][i] != myArray[j+1][i] ) 
   {
    setBoolWinVal = false;
    break;
   }
  }
 }

 if(setBoolWinVal) declareWinner(myArray[0][i]);
}


//Check forward diagonal
if(!setBoolWinVal)
{
 setBoolWinVal = true;
 for(i=0; i<n-1; i++)
 {
  if( myArray[i][i] != myArray[i+1][i+1])
  {
   setBoolWinVal = false;
   break;
  }
 }

 if(setBoolWinVal) declareWinner(myArray[0][0]);
}

//Check reverse diagonal
if(!setBoolWinVal)
{
 setBoolWinVal = true;
 for(i=0, j=n-1; i<n-1 && j>0; i++, j--)
 {
  if( myArray[i][j] != myArray[i+1][j-1])
  {
   setBoolWinVal = false;
   break;
  }
 }

 if(setBoolWinVal) declareWinner(myArray[0][n-1]);
}
Chandan .
A: 

You can also solve this iteratively:

  1. Generate an array for each type of marker for each column, row, and diagonal. Initialize to zero.
  2. For each entry in the grid, if it has a marker, increment the appropriate column, row, and diagonal entries for that marker.
  3. If any of those entries is equal to the dimension of the board, that marker wins.
MSN
A: 

A simple yet elegant solution - you assign the numbers 1 through 9 to your board in the following way:

8 1 6
3 5 7
4 9 2

This square has the unique characteristic that every row, column, and diagonal sums to 15, and there is no way to get a sum of 15 from three cells unless they share a row, column or diagonal.

For each player you keep two boolean arrays:

boolean squarePairs[PAIRSMAXSUM+1];
boolean chosenSquares[NUMSQUARES+1];

where PAIRSMAXSUM = 9+8 and NUMSQUARES = 9

You can then easily check a winning move by a call to this function:

boolean hasWon(int squareIndex) {
    return squarePairs[WINNINGSUM - squareIndex];
}

where WINNINGSUM is 15. And if that function returns false, you update the boolean arrays by calling this function:

void update(int squareIndex) {
    for (int i = 1; i <= NUMSQUARES; i++) {
     if (chosenSquares[i]) {  {
         squarePairs[i + squareIndex] = true;
     }
    }
    chosenSquares[squareIndex] = true;
}


Simple as that!

JRL