Determining a winner on a grid of 8x8 tic tac toe - java

I'm trying to get the diagonals of a matrix that is at least 5x5; trying to loop through new columns in multiple rows. The example I have is 8x8. I can't get it to trigger the middle section as recorded "hits" in a sequence diagonally. Here is my code. This is regarding button presses that then register a persons choice, based on a player color. It loops through fine on the edges like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int row = 0; row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
I am trying to solve it like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int loop = 0; loop < 5; loop++) {
for (int row = (0 + loop); row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
}

What I came up with is that I am always checking only from top to bottom and
Only to the right
Only to the bottom
Bottom down diagonally
Bottom left diagonally
So in my opinion this covers all possible wining configurations if the search is done field by field from the top.
I use 1D array to represent the gameboard and I always check if any of those checks can be performed or not, and skipping the one that.
Running example tries to randomly fill the board of given dimensions step by step and stops execution when winning condition is met (or it is a tie).
For checking winning conditions I just define the "field index progression" for the next field so I can do the ckeck as simple for loop.
public class TheGame {
static int side = 8;
static int fieldsCount = side * side;
static int[] fields = new int[fieldsCount];
static int toWin = 5;
public static void main(String[] args) {
//generate list of random picks
List<Integer> fieldsToBet = IntStream.range(0, fields.length).boxed().collect(Collectors.toList());
Collections.shuffle(fieldsToBet);
Iterator<Integer> it = fieldsToBet.iterator();
Arrays.fill(fields, ' ');
//place bets using list above
char nextBet = 0;
while (it.hasNext()) {
nextBet = nextBet == 'o' ? 'x' : 'o';
Integer idx = it.next();
fields[idx] = nextBet;
char winner=(char) gameOver(idx); //check the condition, if game is done, return the winner
if (winner > 0) {
System.out.println();
System.out.println("Winner is: " + nextBet);
break;
}
}
printout();
}
private static int gameOver(Integer i) {
if (fields[i] == ' ') {
return 0;
}
int column = i % side; //this can be ommited if using nested for loops
int row = i / side;
boolean canCheckRight = column + toWin <= side;
boolean canCheckDown = row <= side - toWin;
boolean canCheckLeft = column + 1 - toWin >= 0;
boolean canCheckRightDownDiagonal = canCheckDown && canCheckRight;
boolean canCheckLeftDownDiagonal = canCheckDown && canCheckLeft;
boolean itsEnd =
canCheckRight && check(i, toWin, idx -> idx += 1) || //every field to check is just "next field"
canCheckDown && check(i, toWin, idx -> idx += side) || // every field to check is in next row
canCheckRightDownDiagonal && check(i, toWin, idx -> idx += side + 1) || // next field to check is in next row skewed by 1 to the right
canCheckLeftDownDiagonal && check(i, toWin, idx -> idx += side - 1);// next field to check is in next row skewed by 1 to the left
return itsEnd ? fields[i]:0;
}
private static boolean check(int idx, int iterations, Function<Integer, Integer> nextCoordinateProducer) {
int figure = fields[idx];
if (figure == ' ') {
return false;
}
for (int i = 1; i < iterations; i++) {
idx = nextCoordinateProducer.apply(idx);
if (figure != fields[idx]) {
return false;
}
}
return true;
}
public static void printout() {
for (int i = 0; i < fieldsCount; i += side) {
System.out.println(Arrays.stream(fields)
.skip(i)
.limit(side)
.boxed()
.map(v -> Character.valueOf((char) v.intValue()).toString())
.collect(Collectors.joining("|")));
}
}
}

Related

Is there any COMPACT way to check winner in a simple tic-tac-toe game?

I am coding a simple tic-tac-toe for a high-school mini project, but I need it to be within a strict data volume (not more than 112 lines). I thought checking for each row, column and cross would be long, so is there any alternative to do so (You should see a [[[HERE]]] comment)? (Btw, I already know it looks awful) Thanks in advance!
public class TTTGame {
//OPTIONS v
public static final String draw = "DRAW"; // <- Definitions for different states
public static final String circles = "CIRCLES"; // BOT
public static final String crosses = "CROSSES"; // PLAYER
public static final String getCrosses = "X"; //<- Symbols to display
public static final String getCircles = "O";
//OPTIONS ^
//DO NOT MODIFY UNDER THIS LINE (Just kidding, do whatever u want) v
public static int[][] board = {
{0,0,0},
{0,0,0},
{0,0,0},
};
public static final int empty = 0; // Definition of the values
public static final int cross = 1;
public static final int circle = 2;
public static int turns = 0; //Just here to count turns, nothing special
public static void main(String[]args) { //Main process
board[1][1] = circle;
display();
while (true) {
PlayerTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
BotTurn();
if (checkStop()||checkWinner()!=null) {display();GStop();break;}
display();
turns += 1;
}
}
private static void GStop() { //Force stop the match function
System.out.println("Winner : " + checkWinner());
System.exit(1);
}
private static boolean checkStop() { //Check if match is already full / completed (Draw)
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
if (board[x][y]==empty) return false;
return true;
}
#Nullable
private static String checkWinner() { //Check Winner
// [[[ HERE ]]] ---------------
return null;
}
private static void PlayerTurn() { //Player turn
int x; Scanner c = new Scanner(System.in);
while (true) {
x = c.nextInt();
x = x-1;
if ((x>=0)&&(x < 9)) {
if (board[x / 3][x % 3] == empty) {
board[x / 3][x % 3] = cross;
break;
} else System.out.println("Already chosen");
} else System.out.println("Invalid");
}
}
private static void BotTurn() { //Bot turn -> (Modify these to change the AI behaviour, here's a very simple one);
boolean choose = true;
for (int y = 0; y < 3 ; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == empty&&choose) {
board[y][x] = circle;
choose = false;
}
}
private static void display() { //Display the board
int nn = 1;
String a = "z";
for (int y = 0; y < 3 ; y++) {
for (int x = 0; x < 3; x++) {
if (board[y][x] == 0) a = "*";
if (board[y][x] == 1) a = getCrosses;
if (board[y][x] == 2) a = getCircles;
System.out.print(a + " ");
}
System.out.print(" "); //Indications
for (int xn = 0; xn < 3; xn++) {
System.out.print(nn);
nn+=1;
System.out.print(" ");
}
System.out.println(" ");
}
}
}
How about this idea: (neither the only nor the best nor the most performant solution... just an idea)
You can use the sum of each row, diagonal and column to determine if the either player one (all 1s) or player two (all 2s) wins. Therefore you only need to set the empty field to be higher than 6.
For example let's say your board looks like this:
7 1 1 -> 7+1+1 = 9 // no one wins
2 2 2 -> 2+2+2 = 6 // player two wins, he has 3 * 2 in a row
1 7 2 -> 1+7+2 =10 // no win here
if all three numbers where 1s (sum == 3) your player one wins.
It is "cumbersome" to implement, but as I said it is just an idea:
// first we check every column
for( int x=0; x<board[y].length; x++){
int sum = 0;
for( int y=0; y<board.length; y++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// then every row
for( int y=0; y<board.length; y++){
int sum = 0;
for( int x=0; x<board[y].length; x++){
sum += board[y][x];
}
if(sum == 3 || sum == 6){
return true;
}
}
// and finally the diagonals (if we ever reach that part)
int sum= board[0][0] + board[1][1] + board[2][2];
if(sum == 3 || sum == 6){
return true;
}
sum= board[0][2] + board[1][1] + board[2][0];
if(sum == 3 || sum == 6){
return true;
}
you could also return 1 when the sum == 3 and the first player wins or 2 when player two wins.

How to fix "Index 5 out of bounds for length 5" error in Java

I am working on solution for the CCC 2018 Robo Thieves Problem, it a simplified version of the original problem. My problem is that it will give me this "Index 5 out of bounds for length 5" when I executed my code and I'm not sure why it is happening. Half of my program executes and then this error occurs.
import java.util.*;
public class RoboThieves {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
ArrayList<Integer> rowPos = new ArrayList<Integer>();
ArrayList<Integer> colPos = new ArrayList<Integer>();
int sRow = -1; // robot pos
int sCol = -1;
int dotCounter = 0;
int stepCounter = 0;
int rowSize = sc.nextInt();
int colSize = sc.nextInt();
char[][] factoryGrid = new char[rowSize][colSize];
for (int i = 0; i < rowSize; i++) {
String rowChars = sc.next().toUpperCase();
for (int j = 0; j < colSize; j++) {
factoryGrid[i][j] = rowChars.charAt(j);
}
}
// check to see if the grid was inputted properly (with square brackets)
/*
* for (char [] row: factoryGrid) { System.out.println(Arrays.toString(row)); }
*/
// check to see if the grid was inputted properly (as inputted)
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
System.out.print(factoryGrid[i][j]);
}
System.out.println();
}
// locate dots and store their row and col in arraylists
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (factoryGrid[i][j] == '.') {
rowPos.add(i);
colPos.add(j);
dotCounter++;
}
}
}
// print dot location to check
for (int i = 0; i < rowPos.size(); i++) {
System.out.println("Dot Position = " + "(" + rowPos.get(i) + "," + colPos.get(i) + ")");
}
// locate robot position
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
if (factoryGrid[i][j] == 'S')
sRow = i;
sCol = j;
}
}
// print camera location to check
System.out.println("Camera Position = " + "(" + sRow + "," + sCol + ")");
//System.out.println(dotCounter); // test to see if counter works
char above = getAbove(factoryGrid, sRow, sCol);
char right = getRight(factoryGrid, sRow, sCol);
char below = getBelow(factoryGrid, sRow, sCol);
char left = getLeft(factoryGrid, sRow, sCol);
if (above == '.') {
boolean canMove = check360(factoryGrid, sRow, sCol);
// check if camera is around dot
if (canMove == true) {
// set robot position to dot position and old position to W
sRow = sRow - 1;
// sCol = sCol;
factoryGrid[sRow][sCol] = 'W';
dotCounter--;
stepCounter++;
} else {
// this is if there is a camera in the 360 radius of the open space
System.out.println("You cannot move to the space beside because there is a camera in your sightline!");
}
} else if (right == '.') {
boolean canMove = check360(factoryGrid, sRow, sCol);
// check if camera is around dot
if (canMove == true) {
// set robot position to dot position and old position to W
// sRow = sRow;
sCol = sCol + 1;
factoryGrid[sRow][sCol] = 'W';
dotCounter--;
stepCounter++;
} else {
// this is if there is a camera in the 360 radius of the open space
System.out.println("You cannot move to the space beside because there is a camera in your sightline!");
}
} else if (below == '.') {
boolean canMove = check360(factoryGrid, sRow, sCol);
// check if camera is around dot
if (canMove == true) {
// set robot position to dot position and old position to W
sRow = sRow + 1;
// sCol = sCol;
factoryGrid[sRow][sCol] = 'W';
dotCounter--;
stepCounter++;
} else {
// this is if there is a camera in the 360 radius of the open space
System.out.println("You cannot move to the space beside because there is a camera in your sightline!");
}
} else if (left == '.') {
boolean canMove = check360(factoryGrid, sRow, sCol);
// check if camera is around dot
if (canMove == true) {
// set robot position to dot position and old position to W
// sRow = sRow;
sCol = sCol - 1;
factoryGrid[sRow][sCol] = 'W';
dotCounter--;
stepCounter++;
} else {
// this is if there is a camera in the 360 radius of the open space
System.out.println("You cannot move to the space beside because there is a camera in your sightline!");
}
} else {
System.out.println(
"The robot cannot move to any spaces try inputting a factory layout that can produce an answer.");
} // end if above dot (yes)
System.out.println(stepCounter);
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize; j++) {
System.out.print(factoryGrid[i][j]);
}
System.out.println();
}
} // end main method
public static char getLeft(char[][] factGrid, int cRow, int cCol) {
return factGrid[cRow][(cCol - 1)];
}
public static char getAbove(char[][] factGrid, int cRow, int cCol) {
return factGrid[(cRow - 1)][(cCol)];
}
public static char getBelow(char[][] factGrid, int cRow, int cCol) {
return factGrid[cRow + 1][cCol];
}
public static char getRight(char[][] factGrid, int cRow, int cCol) {
return factGrid[cRow][(cCol + 1)];
}
public static boolean check360(char[][] factGrid, int cRow, int cCol) {
boolean canMove = true;
char left = getLeft(factGrid, cRow, cCol);
char above = getAbove(factGrid, cRow, cCol);
char right = getRight(factGrid, cRow, cCol);
char below = getBelow(factGrid, cRow, cCol);
if (left == 'C' || above == 'C' || right == 'C' || below == 'C') {
canMove = false;
}
return canMove;
}
} // end main program
Upon first look, I expect that the issue may lie in your getLeft(), getAbove(), getRight(), and getBelow() methods.
In these methods, you give it a value for cRow and cCol and add or subtract 1. However, you need to make sure that the index you are querying does not exceed the size of the double array factGrid, or go below 0.
For example, in your getRight() method, you might try:
public static char getRight(char[][] factGrid, int cRow, int cCol) {
if(factGrid[0].length > (cCol + 1)) {
return factGrid[cRow][(cCol + 1)];
} else {
return '';
}
}

Java Connect Four in console - Horizontal and Vertical winning conditions

I'm working on a Connect Four game for the console in Java. I have problems with the winning conditions, as I don't know how to program them. Here is my code my Main:
public class Main {
public static char[] playerNumber = new char[]{'1', '2'};
public static char[] Badge = new char[]{'X', 'O'};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int moves = 7 * 6;
int whichPlayer = 0;
for (int i = 0; i < 10; i++) {
System.out.println(" FOUR IN A ROW");
System.out.println("-------------------------------------------------------");
System.out.println("Welcome to the amazing game Four In A Row:");
System.out.println("Enter a number between 0 and 6 for choosing a column.");
System.out.println();
Board board = new Board();
board.fillBoard();
board.presentBoard();
do {
// 1. get a badge
char Player = playerNumber[whichPlayer];
char badge = Badge[whichPlayer];
// 2. make a turn
board.makeTurn(badge, Player);
board.presentBoard();
// 3. Tjek om der er vinder
if (board.checkWinHorizontal() || board.checkWinVertical()) {
System.out.println("Player " + Player + " has won!");
break;
}
// 4. change the player
whichPlayer = 1 - whichPlayer;
// 5. decrease moves
--moves;
if (moves == 0) {
System.out.println("Game over, nobody has won.");
System.out.println("Do you want to play again? 'Y' or 'N':");
String newGame = scanner.nextLine();
if (newGame.equals("Y") || newGame.equals("y")) {
break;
}
if (newGame.equals("N") || newGame.equals("n")) {
System.out.println("Thanks for the game!");
return;
}
}
// 6. repeat
} while (true);
}
}
And here is my code for my Board class:
public class Board {
char[][] board = new char[6][7];
int column;
// Fills the empty spaces
public void fillBoard() {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
board[i][j] = ' ';
}
}
}
// Prints the board
public void presentBoard() {
for (int i = 0; i < 6; i++) {
System.out.print(" | ");
for (int j = 0; j < 7; j++) {
System.out.print(board[i][j] + " | ");
}
System.out.println();
System.out.print(" -----------------------------");
System.out.println();
}
}
// Turn
public void makeTurn(char badge, char Player) {
Scanner scanner = new Scanner(System.in);
do {
// 1. Ask for a column
System.out.println("Player " + Player + " turn: ");
column = scanner.nextInt();
// 2. Check if it's between 0 and 6
if (column > 6) {
System.out.println("That is not a valid number. Please enter a number between 0 and 6: ");
continue;
}
// 3. Place a badge
for (int i = 6 - 1; i >= 0; i--) {
if (board[i][column] == ' ') {
board[i][column] = badge;
return;
}
}
// If column is full
System.out.println("Column " + column + " is full. Try another column:");
} while (true);
}
// Check for vertical win
public boolean checkWinVertical() {
return verticalWin(5, column);
}
// Check for horizontal win
public boolean checkWinHorizontal() {
return horizontalWin(5,column);
}
// Conditions for vertical win
private boolean verticalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x-1][y] == charToCheck &&
board[x-2][y] == charToCheck &&
board[x-3][y] == charToCheck) {
return true;
}
return false;
}
// Conditions for horizontal win
private boolean horizontalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x][y+1] == charToCheck &&
board[x][y+2] == charToCheck &&
board[x][y+3] == charToCheck) {
return true;
}
return false;
}
I have succeeded in getting the game recognize a win horizontally and vertically at the bottom row of my array, but I don't know how to make the game recognize for the whole array. I'm only concentrating about the horizontal and vertical, as the diagonal is too complicated for me. And I don't know if this is the right approach or there is a better one.
Thanks!
Here's another solution. It's the same general idea as previously mentioned: loop through each row/column, checking for a streak of 4 in a row. Maybe this implementation will provide some other insight. Below, I've shown an example method checking the horizontal streaks. For vertical, you would iterate over the rows in the inner for loop instead.
public boolean checkWin(char badge) {
return checkHorizontalStreaks(board, badge)
|| checkVerticalStreaks(board, badge);
}
private boolean checkHorizontalStreaks(char[][] board, char badge) {
for (int row = 0; row < board.length; row++) {
// loop throught each row
int currentStreak = 0;
for (int col = 0; col < board[row].length; col++) {
// loop through each column in the row
if (board[row][col] == badge) {
// keep the streak of 'badge' going
currentStreak++;
if (currentStreak == 4) {
// winner
return true;
}
} else {
// restart the streak
currentStreak = 0;
}
}
}
return false;
}
And then update your Main class with
if (board.checkWin(badge)) {
System.out.println("Player " + Player + " has won!");
break;
}
I'd wager there is a more efficient way to determine a winner (perhaps by treating the grid as a graph and traversing it with some special logic). However, I suspect this may be enough for what you need. I'll spare you the output, but it worked with a few different test cases.
Possibly you could check all the adjacent fields around the last played field, so after the user did his turn. So for checking upwards you could do this:
public boolean checkUp(int rowPlayed, int columnPlayed){
boolean checked = false;
if(rowplayed + 1 <= maxrows){ //Checks if you didn't hit the top
if(board[rowPlayed+1][columnPlayed] != null){
if(board[rowPlayed+1][columnPlayed].getPlayer() == currentPlayer){
checked = true;
}
}
}
return checked;
}
and for example implemented like this:
public void checkWin(int rowPlayed, int columnPlayed){
boolean checkingWin = true;
int countWin = 0;
while(checkingWin){
if(checkUp(rowPlayed + countWin, columnPlayed)){
countWin++;
}
else{
checkingWin = false;
}
if(countWin == 4){
checkinWin = false;
//Insert win confirmation here
}
}
}
It's partially pseudo code because I don't know exactly how you handle things in your code, nor do I know if this is the best way to do it. But I hope it was of help for you.
This is a long answer and I'll go around the houses a bit so you can see how I reached my solution (which also expands to diagonal checking at the end).
I would use the last piece added as a starting point and work from there since checking all combinations is exhaustive and unnecessary.
Given the row and column of the last piece added I need to decide what I need to achieve.
I already know that the current row and column has the piece of the colour I'm looking for so I can ignore that.
For horizontal matching, I want to check I want to checking pieces to left and right in the same row have the same colour, and stop if the colour is different or there is no piece.
So imagine the following board (# = empty, R = Red piece, Y = Yellow piece:
6 # # # # # # # #
5 # # # # # # # #
4 # # # # # # # #
3 # # # # # # # #
2 # # # # # # # #
1 # # # # # # # #
0 Y R R R Y Y Y R
0 1 2 3 4 5 6 7
The last move was Yellow, row 0, col 4.
So I want to check left and right from [0][4] and see if the total number of consecutive pieces of the colour is 3, (not 4) since I know [0][4] is Yellow and can be discounted.
Based on this I can take a recursive approach where I check the adjacent to one side, then recursively do the same thing as long as I keep matching pieces of the same colour or do not encounter an empty slot.
I'll start of with a check to the right (to demonstrate):
private static final int COLS = 7;
private static final int ROWS = 6;
public enum Piece {RED, YELLOW}; // null is empty
private Piece[][] board = new Piece[ROWS][COLS]; // the board
private int checkRight(Piece piece, int row, int col) {
// assume valid row for now
col++; // moving col to the right
if (col >= COLS || board[row][col] != piece) {
// We're outside the limits of the column or the Piece doesn't match
return 0; // So return 0, nothing to add
} else {
// otherwise return 1 + the result of checkRight for the next col
return 1 + checkRight(piece, row, col);
}
}
Now I can perform the same to the left.
private int checkLeft(Piece piece, int row, int col) {
// assume valid row for now
col--; // moving col to the left
if (col < 0 || board[row][col] != piece) {
// We're outside the limits of the column or the Piece doesn't match
return 0; // So return 0, nothing to add
} else {
// otherwise return 1 + the result of checkLeft for the next col
return 1 + checkLeft(piece, row, col);
}
}
And to check a winner for horizontal, I could do this:
public boolean checkWinner(Piece piece, int row, int col) {
// if the sum is 3, we have a winner (horizontal only).
return checkRight(piece, row, col) + checkLeft(piece, row, col) == 3;
}
Ugh, there's a lot of repetition isn't there?
We can condense the two methods into one by introducing a new parameter direction which can change if we move col positive or negative through the values 1 and -1 respectively:
private int check(Piece piece, int row, int col, int direction) {
col += direction; // direction is either 1 (right) or -1 (left)
if (col < 0 || col >= COLS || board[row][col] != piece) {
return 0;
} else {
return 1 + check(piece, row, col);
}
}
Update checkWinner() for this new parameter:
private static final int POSITIVE = 1; // right at the moment
private static final int NEGATIVE = -1; // left at the moment
public boolean checkWinner(Piece piece, int row, int col) {
// if the sum is 3, we have a winner (horizontal only).
return check(piece, row, col, POSITIVE) + check(piece, row, col, NEGATIVE) == 3;
}
Now I could implement the same sort of logic for vertical, but instead stay on the same col and change the row. I will skip this part in detail and move onto a solution which includes this and diagonal checking.
This has been done using an enum called CheckType storing values for which row and col should change and is used by the check() method. e.g. for HORIZONTAL the column changes by 1 or -1 (depending upon the direction specified when check() is called) and the row remains 0.
public class Board {
public enum Piece {
RED, YELLOW
};
private enum CheckType {
HORIZONTAL(0, 1), VERTICAL(1, 0), DIAGNONAL_UP(1, 1), DIAGNONAL_DOWN(-1, 1);
int row;
int col;
CheckType(int row, int col) {
this.row = row;
this.col = col;
}
}
private static final int POSITIVE = 1;
private static final int NEGATIVE = -1;
private static final int ROWS = 6;
private static final int COLS = 7;
private Piece[][] board = new Piece[ROWS][COLS];
private boolean hasWinner = false;
public boolean hasWinner() {
return hasWinner;
}
private void checkWinner(Piece piece, int row, int col) {
// check all values of enum CheckType for a winner
// so HORIZONTAL, VERTICAL, etc..
int enumIndex = 0;
while (!hasWinner && enumIndex < CheckType.values().length) {
hasWinner = check(piece, row, col, POSITIVE, CheckType.values()[enumIndex])
+ check(piece, row, col, NEGATIVE, CheckType.values()[enumIndex]) == 3;
enumIndex++;
}
}
private int check(Piece piece, int row, int col, int direction, CheckType type) {
row += type.row * direction;
col += type.col * direction;
if (row >= ROWS || row < 0 || col >= COLS || col < 0 || board[row][col] != piece) {
return 0;
} else {
return 1 + check(piece, row, col, direction, type);
}
}
// for completeness, adding a Piece
public boolean add(Piece piece, int col) {
int row = 0;
while (row < ROWS && board[row][col] != null) {
row++;
}
if (row < ROWS) {
board[row][col] = piece;
// check for winner after successful add
checkWinner(piece, row, col);
}
return row < ROWS;
}
}
Hope this helps.

move to a new instance in an 2d array randomly once

so i a player on a 2d array, when i do an action i want the player to move to one of the 8 available blocks around him, the code below moves him randomly but does it twice
Map Before Moving
GrassGrassGrassGrass
Grass Rek GrassGrass
GrassGrassGrassGrass
GrassGrassGrassGrass
Random Movement
0 0
0 0 //This shouldn't be happening
Map After Moving
GrassGrassGrassGrass
GrassGrassGrassGrass
GrassGrassGrassGrass
GrassGrassGrass Rek
import java.util.Random;
public class command_Movment implements command_Move {
inSwamp map = new inSwamp();
inSwamp rek = new Rek();
Random random = new Random();
int row = random.nextInt(3);
int col = random.nextInt(3);
#Override
public Command move() {
for (int i = 0; i < map.grid.length; i++) {
for (int j = 0; j < map.grid[i].length; j++) {
if (map.grid[i][j] == rek.getName()) {
try {
map.grid[i][j] = "Grass";
if (row == 0) {
i++;
}
if (row == 1) {
i--;
}
if (col == 0) {
j++;
}
if (col == 1) {
j--;
}
map.grid[i][j] = rek.getName();
System.out.println(col + " " + row);
break;
} catch (ArrayIndexOutOfBoundsException exception) {
if (row == 0) {
i--;
}
if (row == 1) {
i++;
}
if (col == 0) {
j--;
}
if (col == 1) {
j++;
}
map.grid[i][j] = rek.getName();
System.out.println("Error");
break;
}
}
}
}
return null;
}
}
Firstly, you shouldn't use == to compare strings, you should use equals method. so replace if (map.grid[i][j] == rek.getName()) with if (map.grid[i][j].equals(rek.getName())).
Edit: PLEASE don't use label to break the modularity of the program!
Please don't use catching ArrayIndexOutofBound exception to determine if an array index is correct or not. The exception should NOT happen. You should check the index first.
I updated my program for your random move: basically I thin you want to:
1) randomly move up or move down from the original position 2) if move up or move down exceeds the boundary of the matrix, don't move in that direction.
The following program should move rek to one of its 8 neighbors randomly without causing any ArrayIndexOutOfBoundException.
public Command move() {
// randomly determine the moving direction
// -1 means move left, 1 means move right
int horizontal_direction = Math.random() > 0.5 ? -1 : 1;
// -1 means move up, 1 mean move down
int vertical_direction = Math.random() > 0.5 ? -1 : 1;
for (int i = 0; i < map.grid.length; i++) {
for (int j = 0; j < map.grid[i].length; j++) {
if (map.grid[i][j].equals(rek.getName())) {
map.grid[i][j] = "Grass"; // replace rek's current position with Grass\
// if the newRow exceeds the boundaries, don't move in that direction
int newRow = i + horizontal_direction;
if (newRow < 0 || newRow == map.grid.length)
newRow = i;
// if the newCol exceeds the boundaries, don't move in that direction
int newCol = j + vertical_direction;
if (newCol < 0 || newCol == map.grid[i].length)
newCol = j;
map.grid[newRow][newCol] = rek.getName(); // move rek to the new position
System.out.println(newRow + " " + newCol);
break;
}
}
}
return null;
}
Add a label like this to your outer loop:
outer:
for (int i = 0; i < map.grid.length; i++) {
......
}
And in the try block, break the loop this way:
map.grid[i][j] = rek.getName();
System.out.println(col + " " + row);
break outer;

Java - minimax algorithm issue

I'm working on a tictactoe board for practice making classes and i have ran into a problem with my algorithm. it seems to be returning the best move offensive, but it doesn't play defense. i dont know where i have messed up and cant seem to find it. i have looked over a lot of things on here about it and ive compared it to simular projects, but still can't seem to get it. here is my code.
package TicTacToe;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
public class Solution {
private static GameBoard currentBoard;
private static Player botPlayer;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String player;
System.out.println("ENTER bot: ");
player = in.next();
if(player.equalsIgnoreCase("X")) {
botPlayer = Player.X;}
else {botPlayer = Player.O;}
String board[] = new String[3];
for(int i = 0; i < 3; i++) {
System.out.println("ENTER board: ");
board[i] = in.next();
}
currentBoard = new GameBoard(3,3, board);
List<Space> OpenSpaces = getOpenSquares(currentBoard);
MakeMove(OpenSpaces);
System.exit(-1);
}
public static List<Space> getOpenSquares(GameBoard GB) {
List<Space> OpenSpaces = new ArrayList<Space>();
for(int r = 0; r < 3; r++) {
for(int c = 0; c < 3; c++) {
if(GB.squares[r][c] == Player.Open) {
OpenSpaces.add(new Space(r,c));
}
}
}
return OpenSpaces;
}
private static Space bestMove;
private static Space currentMove;
private static Space previousMove;
private static void MakeMove(List<Space> OpenSpaces) {
if(OpenSpaces.size() == currentBoard.Size) {
Random random = new Random();
bestMove = new Space(random.nextInt(2),2);
} else {
for(Space child: OpenSpaces) {
currentMove = GetBestMove(currentBoard,botPlayer);
if (currentMove != null){
}else{
continue;}
if(previousMove != null && previousMove.Rank < currentMove.Rank ||
previousMove == null && currentMove != null) {
bestMove = currentMove;
}
previousMove = currentMove;
}
}
if (bestMove != null) {
currentBoard.squares[bestMove.X][bestMove.Y] = botPlayer;
System.out.println("the best move is: " + currentMove.X + " " + currentMove.Y);
}
}
private static Space GetBestMove(GameBoard gb, Player p) {
Space bestSpace = null;
List<Space> cloneOpenSpaces = getOpenSquares(gb);
GameBoard cloneBoard = null;
cloneBoard = gb.Clone();
for(Space Open: cloneOpenSpaces) {
cloneBoard = gb.Clone();
Space newSpace = Open;
cloneBoard.squares[newSpace.X][newSpace.Y] = p;
if(cloneBoard.Winner == Player.Open && cloneOpenSpaces.size() > 0) {
Player InP;
if(p == Player.X) {
InP = Player.O;
}else {
InP = Player.X;
}
Space tempMove = GetBestMove(cloneBoard, InP);
if(tempMove != null){
newSpace.Rank = tempMove.Rank;
}
} else {
if(cloneBoard.Winner == Player.Open) {
newSpace.Rank = 0;
}else if(cloneBoard.Winner == Player.O) {
newSpace.Rank = -1;
}else if(cloneBoard.Winner == Player.X) {
newSpace.Rank = 1;
}
}
System.out.println(newSpace.Rank);
if(bestSpace == null ||
(p == Player.X && newSpace.Rank < ((Space)bestSpace).Rank)||
(p == Player.O && newSpace.Rank > ((Space)bestSpace).Rank)) {
bestSpace = newSpace;
}
}
return (Space)bestSpace;
}
public static enum Player {
X (1),
O (-1),
Open (0);
private final double value;
Player(double value){
this.value = value;
}
}
public static class Space {
public int X;
public int Y;
public double Rank;
public Space(int x, int y) {
this.X = x;
this.Y = y;
Rank = 0;
}
public Space() {
}
}
public static class GameBoard {
public int Rows;
public int getRows() {
return this.Rows;
}
public void setRows(int rows) {
Rows = rows;
}
public int Columns;
public int getColumns() {
return this.Columns;
}
public void setColumns(int columns) {
Columns = columns;
}
public Player[][] squares;
//public Player[x][y]
public Player getPlayer(int x, int y) {
return this.squares[x][y];
}
public void setPlayer(int x, int y, Player player) {
squares[x][y] = player;
}
public boolean Full;
public boolean isFull() {
for(int r = 0; r < 2; r++) {
for(int c = 0; c < 2; c++) {
if (squares[r][c] != Player.Open) {return false;}
}
}
return true;
}
public int Size;
public int getSize() {
return this.Size;
}
public void setSize(int size) {
Size = size;
}
public List<Space> OpenSquares;
public List<Space> getOpenSquares() {
List<Space> OpenSquares = new ArrayList<Space>();
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
if(squares[r][c] == Player.Open) {
OpenSquares.add(new Space(r,c));
}
}
}
return this.OpenSquares;
}
public Player Winner;
public Player getWinner() {
int count = 0;
//columns
for (int x = 0; x < Rows; x++)
{
count = 0;
for (int y = 0; y < Columns; y++) {
count += squares[x][y].value;
}
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
}
//rows
for (int x = 0; x < Rows; x++) {
count = 0;
for (int y = 0; y < Columns; y++) {
count += squares[y][x].value;
}
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
}
// Diagonals right to left
count = 0;
count += squares[0][0].value;
count += squares[1][1].value;
count += squares[2][2].value;
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
// Diagonals left to right
count = 0;
count += squares[0][2].value;
count += squares[1][1].value;
count += squares[2][0].value;
if (count == 3) {
return Player.X;
}else if (count == -3) {
return Player.O;
}
return Player.Open;
}
public GameBoard Clone() {
GameBoard b = new GameBoard(Rows,Columns);
b.squares = (Player[][])this.squares.clone();
b.Winner = getWinner();
return b;
}
// Class initializer
public GameBoard(int boardRows, int boardColumns, String[] board) {
// Set the dimensions
Rows = boardRows;
Columns = boardColumns;
// Create game spaces
squares = new Player[Rows][Columns];
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
//squares[i][n] = Player.Open;
if(board[r].charAt(c) == 'X') {
squares[r][c] = Player.X;
}
if(board[r].charAt(c) == 'O') {
squares[r][c] = Player.O;
}
if(board[r].charAt(c) == '_') {
squares[r][c] = Player.Open;
}
}
}
this.Winner = getWinner();
this.OpenSquares = getOpenSquares();
//Size of the board
this.Size = Rows * Columns;
}
// clone Class initializer
public GameBoard(int boardRows, int boardColumns) {
// Set the dimensions
Rows = boardRows;
Columns = boardColumns;
// Create game spaces
squares = new Player[Rows][Columns];
for(int r = 0; r < Rows; r++) {
for(int c = 0; c < Columns; c++) {
squares[r][c] = Player.Open;
}
}
this.Winner = getWinner();
this.OpenSquares = getOpenSquares();
//Size of the board
Size = Rows * Columns;
}
}
}
all of the classes are at the bottom. Thanks in advance for any help and corrections. :)
i made it recursive in the following code, although i still cant figure out the scoring.. if the value is either 1, 0, or -1 then if there are multipule moves with the same value it will just take the 1st one which may not be the best move "blocking.
private static Space GetBestMove(GameBoard gb, Player p) {
Space bestSpace = null;
List<Space> cloneOpenSpaces = getOpenSquares(gb);
GameBoard cloneBoard = null;
cloneBoard = gb.Clone();
for(Space Open: cloneOpenSpaces) {
cloneBoard = gb.Clone();
Space newSpace = Open;
cloneBoard.squares[newSpace.X][newSpace.Y] = p;
if(cloneBoard.Winner == Player.Open && cloneOpenSpaces.size() > 0) {
Player InP;
if(p == Player.X) {
InP = Player.O;
}else {
InP = Player.X;
}
***Space tempMove = GetBestMove(cloneBoard, InP);***
if(tempMove != null){
newSpace.Rank = tempMove.Rank;
}
the results of the test are as follows
test 1
ENTER bot:
O
ENTER board:
[ ][O][ ]
ENTER board:
[ ][ ][ ]
ENTER board:
[ ][X][X]
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
-1.0
the best move is: 0 2
test 2
ENTER bot:
O
ENTER board:
[ ][X][X]
ENTER board:
[ ][ ][ ]
ENTER board:
[ ][O][ ]
1.0
1.0
1.0
1.0
1.0
-1.0
1.0
-1.0
-1.0
1.0
-1.0
1.0
1.0
-1.0
-1.0
the best move is: 1 1
I haven't ran your code, but I think I may know why you are having issues. The minimax algorithm is recursive in nature. You look at each open space, and determine some sort of score for each one. I see this in your code. However, what I don't see is the recursion that equates to the logic "if I move here, then what options will my opponent have during his next turn". Notice that you can keep calling the same scoring function, but scoring both players' options. This is where the computation can get intensive, and where stuff like pruning comes into play. Say I want to look 3 moves ahead. Say there are initially 5 open spaces. For each of the 5 open spaces, I examine my options and give a score to each one. Then I pretend to move there, and send the new board through the scoring function, and assume my opponent will take the highest scoring move of the remaining 4 possible moves. Then I pretend he moves there, and I again run the board through the scoring function, now with 2 hypothetical moves on it. You continue this for a set "depth", or number of potential moves, and pick the move that results in the highest value, assuming the opponent will do what you calculated they would.
I realize this was long-winded, but I hope there was a little bit of value buried in there somewhere. Take a look at your code, figure out where you are scoring moves (if you see a win, take it; if you can block a win, take it; etc.). Then continue calling this function where you keep adding fake/potential moves (those with the highest value from your scoring function), and once you reach the depth, you can simply pick the move that is likely to give you the most valuable outcome.
Basically, in your code, you should call GetBestMove(...) once from MakeMove(...). However, GetBestMove(...) should repeatedly call itself, with a modified board each time; and each time, it will return the best move given a hypothetical (or real) board. What I don't see in your code is that recursive call to GetBestMove(...), and the necessary upkeep that goes along with it. This explains why you only get aggressive behavior; it only looks to see what the best immediate move is, without any regard to what your opponent might be able to do if you make that move!
If my assumptions are wrong, provide a test case where you expect some behavior, but are getting something different.

Categories