I have made a code that checks for wins but i cannot seem to find a away to make it check for ties, any help will be useful! I also cannot find a way to make it so X or O's are not placed over in the same spot
I tried to make it so that if all the rows or columns did not equal to 60 or 90 it would determine it was a tie, but i later realized that the program would think its a tie after every turn as the rows or columns do not equal 60 or 90
import hsa.Console;
import java.awt.*;
import java.util.*;
public class tictactoe88
{ static Console c;
public static void main(String[]args)
{ c = new Console();
//Drawing the tic tac toe table
c.setColor(Color.black);
c.drawLine(450,250,450,475);
c.drawLine(550,250,550,475);
c.drawLine(350,325,650,325);
c.drawLine(350,400,650,400);
//Declaring variables
int location;
String name1, name2, endGame;
boolean stillPlaying = true;
String[][] names = new String[3][3];
int[][] chart =
{
{1,2,3},
{4,5,6},
{7,8,9},
};
//Showing players what number corresponds to tic tac toe board
c.println("1 2 3");
c.println("4 5 6");
c.println("7 8 9");
//Asking players for their names
c.print("Player One : ");
name1 = c.readLine ();
c.print("Player Two : ");
name2 = c.readLine ();
//Starting of main loop
while(stillPlaying)
{
c.setCursor(6,0);
c.println("Where would you like to place the 'X' " + name1);
//Making sure the number entered is valid
while(true)
{
try
{ String locationStr = c.readLine();
location = Integer.parseInt(locationStr);
break;
}
catch(NumberFormatException e)
{ c.println("Bad number, please try again.");
}
}
//Drawing the 'X' and adjusting the 2d array's correspomding to the number entered
{
if (location == 1)
{
c.drawLine(350,250,450,325);
c.drawLine(450,250,350,325);
chart [0][0] = 20;
}
else if (location == 2)
{
c.drawLine(450,250,550,325);
c.drawLine(550,250,450,325);
chart [0][1] = 20;
}
else if(location == 3)
{
c.drawLine(550,250,650,325);
c.drawLine(650,250,550,325);
chart [0][2] = 20;
}
else if (location == 4)
{
c.drawLine(350,325,450,400);
c.drawLine(350,400,450,325);
chart [1][0] = 20;
}
else if (location == 5)
{
c.drawLine(450,325,550,400);
c.drawLine(450,400,550,325);
chart [1][1] = 20;
}
else if (location == 6)
{
c.drawLine(550,325,650,400);
c.drawLine(550,400,650,325);
chart [1][2] = 20;
}
else if (location == 7)
{
c.drawLine(350,400,450,475);
c.drawLine(350,475,450,400);
chart [2][0] = 20;
}
else if (location == 8)
{
c.drawLine(450,400,550,475);
c.drawLine(450,475,550,400);
chart [2][1] = 20;
}
else if (location == 9)
{
c.drawLine(550,400,650,475);
c.drawLine(550,475,650,400);
chart [2][2] = 20;
}
}
This is currently how it checks for a winner,
//Checks the winner by all 8 possibilities
if (chart[0][0]+chart[0][1]+chart[0][2] == 60||
chart[1][0]+chart[1][1]+chart[0][2] == 60||
chart[2][0]+chart[2][1]+chart[2][2] == 60||
chart[0][0]+chart[1][1]+chart[2][2] == 60||
chart[2][0]+chart[1][1]+chart[0][2] == 60||
chart[0][0]+chart[1][0]+chart[2][0] == 60||
chart[0][1]+chart[1][1]+chart[2][1] == 60||
chart[0][2]+chart[1][2]+chart[2][2] == 60)
{break;}
c.setCursor(8,0);
c.println("Where would you like to place 'O' " + name2);
//Making sure the number entered is valid
while(true)
{
try
{ String locationStr = c.readLine();
location = Integer.parseInt(locationStr);
break;
}
catch(NumberFormatException e)
{ c.println("Bad number, please try again.");
}
}
{
//Drawing the 'O' and adjusting the 2d array's correspomding to the number entered
if (location == 1)
{
c.drawOval(350,250,100,75);
chart [0][0] = 30;
}
else if (location == 2)
{
c.drawOval(450,250,100,75);
chart [0][1] = 30;
}
else if(location == 3)
{
c.drawOval(550,250,100,75);
chart [0][2] = 30;
}
else if (location == 4)
{
c.drawOval(350,325,100,75);
chart [1][0] = 30;
}
else if (location == 5)
{
c.drawOval(450,325,100,75);
chart [1][1] = 30;
}
else if (location == 6)
{
c.drawOval(550,325,100,75);
chart [1][2] = 30;
}
else if (location == 7)
{
c.drawOval(350,400,100,75);
chart [2][0] = 30;
}
else if (location == 8)
{
c.drawOval(450,400,100,75);
chart [2][1] = 30;
}
else if (location == 9)
{
c.drawOval(550,400,100,75);
chart [2][2] = 30;
}
This is currently how it checks for a winner,
//Checks the winner by all 8 possibilities
if (chart[0][0]+chart[0][1]+chart[0][2] == 90||
chart[1][0]+chart[1][1]+chart[0][2] == 90||
chart[2][0]+chart[2][1]+chart[2][2] == 90||
chart[0][0]+chart[1][1]+chart[2][2] == 90||
chart[2][0]+chart[1][1]+chart[0][2] == 90||
chart[0][0]+chart[1][0]+chart[2][0] == 90||
chart[0][1]+chart[1][1]+chart[2][1] == 90||
chart[0][2]+chart[1][2]+chart[2][2] == 90)
{break;}
}
}
This is how it ends the game,
//Ends the game by saying who teh winner is
if (chart[0][0]+chart[0][1]+chart[0][2] == 60||
chart[1][0]+chart[1][1]+chart[0][2] == 60||
chart[2][0]+chart[2][1]+chart[2][2] == 60||
chart[0][0]+chart[1][1]+chart[2][2] == 60||
chart[2][0]+chart[1][1]+chart[0][2] == 60||
chart[0][0]+chart[1][0]+chart[2][0] == 60||
chart[0][1]+chart[1][1]+chart[2][1] == 60||
chart[0][2]+chart[1][2]+chart[2][2] == 60)
{c.setCursor(10,0);
c.println("Congratulations "+name1+" you are the winner!");}
else if (chart[0][0]+chart[0][1]+chart[0][2] == 90||
chart[1][0]+chart[1][1]+chart[0][2] == 90||
chart[2][0]+chart[2][1]+chart[2][2] == 90||
chart[0][0]+chart[1][1]+chart[2][2] == 90||
chart[2][0]+chart[1][1]+chart[0][2] == 90||
chart[0][0]+chart[1][0]+chart[2][0] == 90||
chart[0][1]+chart[1][1]+chart[2][1] == 90||
chart[0][2]+chart[1][2]+chart[2][2] == 90)
{c.setCursor(10,0);
c.println("Congratulations "+name2+" you are the winner!");}
Related
I have made this very obnoxious way of finding the winner in a tic tac toe game and I was just wondering if there is a simpler way of doing this.
This is the method I currently have, and as you can see it is very redundant and repetitive. Any tips to narrow this down would be amazing. I'm thinking maybe a nested for-loop might work but not entirely sure how to set that up within this.
public static boolean isGameOver(char[][] gameBoard) {
//Testing for Horizontal Win
if(gameBoard[0][0] == 'X' && gameBoard[0][2] == 'X' && gameBoard [0][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[0][0] == 'O' && gameBoard[0][2] == 'O' && gameBoard [0][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
if(gameBoard[2][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [2][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[2][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [2][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
if(gameBoard[4][0] == 'X' && gameBoard[4][2] == 'X' && gameBoard [4][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[4][0] == 'O' && gameBoard[4][2] == 'O' && gameBoard [4][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
//Testing for Vertical Win
if(gameBoard[0][0] == 'X' && gameBoard[2][0] == 'X' && gameBoard [4][0] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[0][0] == 'O' && gameBoard[2][0] == 'O' && gameBoard [4][0] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
if(gameBoard[0][2] == 'X' && gameBoard[2][2] == 'X' && gameBoard [4][2] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[0][2] == 'O' && gameBoard[2][2] == 'O' && gameBoard [4][2] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
if(gameBoard[0][4] == 'X' && gameBoard[2][4] == 'X' && gameBoard [4][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[0][4] == 'O' && gameBoard[2][4] == 'O' && gameBoard [4][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
//Testing for Diagonal Win
if(gameBoard[0][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [4][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[0][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [4][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
if(gameBoard[4][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [0][4] == 'X') {
System.out.println("Player Wins!\n");
playerScore++;
return true;
}
if(gameBoard[4][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [0][4] == 'O') {
System.out.println("CPU Wins!\n");
cpuScore++;
return true;
}
//Testing for Tie
if(gameBoard[0][0] != ' ' && gameBoard[0][2] != ' ' && gameBoard[0][4] != ' ' &&
gameBoard[2][0] != ' ' && gameBoard[2][2] != ' ' && gameBoard[2][4] != ' ' &&
gameBoard[4][0] != ' ' && gameBoard[4][2] != ' ' && gameBoard[4][4] != ' ') {
System.out.println("It's a tie!!!\n");
numOfTies++;
return true;
}
return false;
}
Ok, I got a little carried away. But perhaps you could use some ideas presented here. My main goal was to make it so the entire board need not be checked after each move. This is the type of issue that is best considered during the design phase of a program.
I created a TriGroup class (which is essentially a mutable string to hold successive moves.
Then a map is used to hold all the groupings which have a common coordinate.
when a move is made, those groupings have the current player appended.
and check is done to see if that player won.
this program will run by itself using random moves resulting in a victory or a tie.
Some border cases may have been overlooked.
public class TicTacToeCheck {
int moveCount = 0;
static int MAX_MOVES = 27;
class TriGroup {
public String group = "";
#Override
public String toString() {
return group;
}
}
TriGroup row1 = new TriGroup();
TriGroup row2 = new TriGroup();
TriGroup row3 = new TriGroup();
TriGroup col1 = new TriGroup();
TriGroup col2 = new TriGroup();
TriGroup col3 = new TriGroup();
TriGroup diag1 = new TriGroup();
TriGroup diag2 = new TriGroup();
Map<String, List<TriGroup>> commonGroupings = new HashMap<>();
{
commonGroupings.put("00", List.of(row1, col1, diag1));
commonGroupings.put("02", List.of(row1, col2));
commonGroupings.put("04", List.of(row1, col3));
commonGroupings.put("20", List.of(row2, col1));
commonGroupings.put("22", List.of(row2, col2, diag1, diag2));
commonGroupings.put("24", List.of(row2, col3));
commonGroupings.put("40", List.of(row3, col1, diag1));
commonGroupings.put("42", List.of(row3, col2));
commonGroupings.put("44", List.of(row3, col3));
}
public static void main(String[] args) {
new TicTacToeCheck().start();
}
public void start() {
char player = 'X';
Random r = new Random();
outer: while (moveCount < MAX_MOVES) {
commonGroupings.entrySet().forEach(System.out::println);
System.out.println();
int row = r.nextInt(3) * 2;
int col = r.nextInt(3) * 2;
System.out.println("Move: " + row + ", " + col);
player = player == 'X' ? 'O' : 'X';
char val;
switch (val = recordMove(row, col, player)) {
case 'X' -> {
System.out.println("X wins!");
break outer;
}
case 'O' -> {
System.out.println("O wins!");
break outer;
}
case 0 -> {
System.out.println("Tie!");
break outer;
}
default -> {
}
}
}
commonGroupings.entrySet().forEach(System.out::println);
}
public char recordMove(int row, int col, char c) {
moveCount++;
for (TriGroup tri : commonGroupings.get(row + "" + col)) {
if (tri.group.length() > 2) {
// just ignore the row/col and try the next
continue;
}
// update group
tri.group += c;
if (tri.group.equals(c + "" + c + "" + c)) {
return c;
}
}
if (moveCount == MAX_MOVES) {
return 0;
}
return '#';
}
}
This version uses 2 auxiliary, private methods to make the code less repetitive, without changing the behavior of calling isGameOver. The main observation is that there is very little difference between checking for a win by O or checking for one by X - only a single character. So that becomes checkWins. The next observation is that there is a lot of repetition involved in checking 3 positions of the board that are next to each other. If you give me the char to expect, where to start, and where to look next (dcol and drow), that becomes allEqual.
Your code skips over uneven positions of the board; my code does not. I feel that it is an error to mix presentation ("how you show things to users") with model ("how you represent things internally"); so my code is not currently a drop-in replacement for yours, but can quickly be fixed to be such a replacement by tweaking values in checkWins (diagonal down would be 0, 0, 2, 2, and so on).
Note that, efficiency-wise, your code is probably faster. But I find this version to be much shorter, more readable, and therefore easier to debug & maintain.
private static boolean allEqual(char expected, char[][] b,
int row, int col, int drow, int dcol) {
for (int i=0; i<b[0].length; i++) {
if (b[row][col] != expected) return false;
row += drow;
col += dcol;
}
return true;
}
private static boolean checkWins(char playerChar, char[][]b) {
boolean win = allEqual(playerChar, b, 0, 0, 0, 1) // 1st row
|| allEqual(playerChar, b, 1, 0, 0, 1)
|| allEqual(playerChar, b, 2, 0, 0, 1) // 3rd row
|| allEqual(playerChar, b, 0, 0, 1, 0) // 1st col
|| allEqual(playerChar, b, 0, 1, 1, 0)
|| allEqual(playerChar, b, 0, 2, 1, 0) // 3rd col
|| allEqual(playerChar, b, 0, 0, 1, 1) // diagonal down
|| allEqual(playerChar, b, 2, 0, 1,-1); // diagonal up
return win;
}
public static boolean isGameOver(char[][] gameBoard) {
if (checkWins('X', gameBoard)) {
System.out.println("Player Wins!\n");
playerScore ++;
return true;
} else if (checkWins('O', gameBoard)) {
System.out.println("CPU Wins!\n");
cpuScore ++;
return true;
} else {
return false;
}
}
Take a look at this:
https://www.geeksforgeeks.org/tic-tac-toe-game-in-java/
Add the strings of gameBoard[x][y] and check them in a switch statement.
If the compound strings are equals to XXX or OOO, you can return the winner.
For your code something like this:
for (int a = 0; a < 8; a++) {
String line = null;
switch (a) {
case 0:
line = gameBoard[0][0] + gameBoard[0][1] + gameBoard[0][2];
break;
case 1:
line = gameBoard[1][0] + gameBoard[1][1] + gameBoard[1][2];
break;
case 2:
line = gameBoard[2][0] + gameBoard[2][1] + gameBoard[2][2];
break;
case 3:
line = gameBoard[0][0] + gameBoard[1][0] + gameBoard[2][0];
break;
case 4:
line = gameBoard[0][1] + gameBoard[1][1] + gameBoard[2][1];
break;
case 5:
line = gameBoard[0][2] + gameBoard[1][2] + gameBoard[2][2];
break;
case 6:
line = gameBoard[0][0] + gameBoard[1][1] + gameBoard[2][2];
break;
case 7:
line = gameBoard[0][2] + gameBoard[1][1] + gameBoard[2][0];
break;
}
//For X winner
if (line.equals("XXX")) {
return "X";
}
// For O winner
else if (line.equals("OOO")) {
return "O";
}
}
UPDATE: I found that the problem is in the Mouse() method. It will run and take values until you let go of the mouse. I need a way to pause the program or stop it until someone releases the mouse.
I'm working on a Tic Tac Toe game and I think I got it to only one problem left. I got it to work perfect, but then I messed with it to try and make the messages appear on the actual game instead of just the java interaction pane(excuse me if these aren't the right terms to use)
For some reason when I run, it will take the first move correctly and then it acts as if someone keeps clicking on the same place on the board and gives me an invalid input error multiple times. I'm not sure what to do to fix this problem, but I'm guessing the error is in the Fill() method. Any tips or hints would be greatly appreciated.
Thanks!
/*
* Tic Tac Toe Game
*/
import java.util.*;
import java.awt.*;
public class Game {
//Create Variables
public static int x;
public static int y;
public static double a;
public static double b;
public static int empty = 0;
public static int Cross = 1;
public static int Oh = -1;
public static double[][] board = new double[3][3];
public static int currentPlayer;
public static int p = 1;
//public static boolean v = false;
//Main method: draws the board and then begins game
public static void main(String args[]) {
initalize();
drawBoard();
// System.out.println("X's turn.");
for(int w = 0; w < 9; w++) {
turn();
Fill();
}
}
/*
* this method creates the graphic as a 9x9 board with the 'hashtag' for playing
*/
public static void drawBoard() {
StdDraw.setXscale(0, 9);
StdDraw.setYscale(0, 9);
StdDraw.setPenRadius(.01);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0, 3, 9, 3);
StdDraw.line(0, 6, 9, 6);
StdDraw.line(3, 0, 3, 9);
StdDraw.line(6, 0, 6, 9);
} //end draw board
public static void initalize() {
for(int j = 0; j <= 2; j++) {
for(int k = 0; k <= 2; k++) {
board[j][k] = 0;
}
}
}
/*
* this method will take the coordinates of the mouse when clicked as a,b coordinates
* the it will take the a,b coordinates and turn them into an x,y board position (0-2)
*/
public static void Mouse() {
while(true) {
if(StdDraw.mousePressed()) {
a = StdDraw.mouseX();
b = StdDraw.mouseY();
//System.out.println( a + " " + b);
//board location
//set column
if(0 <= a && a < 3) {
x = 0;
}
if(3 <= a && a < 6) {
x = 1;
}
if(6 <= a && a < 9) {
x = 2;
}
//set row
if(0 <= b && b < 3) {
y = 0;
}
if(3 <= b && b < 6) {
y = 1;
}
if(6 <= b && b < 9) {
y = 2;
}
//System.out.println("You clicked in Row" + x + "and column" +y);
break;
}
}
}//ends Mouse
/*
* Runs CurrentPlayer() to deicde who the current player is and then prints a message with whose turn it is
*/
public static void turn() {
CurrentPlayer();
if(currentPlayer != Cross) {
System.out.println("O's turn.");
}
if(currentPlayer != Oh) {
System.out.println("X's turn.");
}
}
/*
* this method determines if it is player X or player O
* it flip flops each time it is called and reurns the value of the 'currentPlayer' (-1 or 1)
*/
public static int CurrentPlayer() {
if(p % 2 == 0) {
currentPlayer = Oh;
} else {
currentPlayer = Cross;
}
return currentPlayer;
}//ends CurrentPlayer
/*
* this method is the actual 'playing'
* it calls Mouse() to get coordinates/board location
* determines if it is a valid move by making sure its within the board range and also checking to make sure that array spot of the board it empty(0)
* it then fills the array with the value of the currentplayer, prints the board, checks the game status for a win, adds a turn, and restarts if it is valid.
* if its not vaild it will tell you and then restart to get a valid move
*/
public static void Fill() {
Mouse();
//check if valid
if(0 <= x && x <= 2 && 0 <= y && y <= 2 && board[x][y] == 0) {
//switch player
p++;
//fill array spot
board[x][y] = currentPlayer;
//check game status and print board
PrintBoard();
GameStatus();
return;//exit method
}
//move is not valid
else {
System.out.println("Invalid. Try again.");
Fill();
}
}//ends Fill()
/*
*scrolls through board array to check for moves.
* if array value=0, its empty and does nothing
* if array value=1, its X and prints an X in that board position
* if array value=-1, its O and prints an O in that board position
*/
public static void PrintBoard() {
for(int j = 0; j <= 2; j++) {
for(int k = 0; k <= 2; k++) {
if(board[j][k] == 0) {
//do nothing leave empty
}
Font mark = new Font("Arial", Font.BOLD, 60);
if(board[j][k] == 1) {
double l = ((j + 1) * 3) - 1.5;
double m = ((k + 1) * 3) - 1.5;
//print x
StdDraw.setFont(mark);
StdDraw.setPenColor(StdDraw.RED);
StdDraw.text(l, m, "X");
}
if(board[j][k] == -1) {
double l = ((j + 1) * 3) - 1.5;
double m = ((k + 1) * 3) - 1.5;
//print O
StdDraw.setFont(mark);
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.text(l, m, "O");
}
}
}
return;
}//Ends PrintBoard()
/*
* this method checks if there is a win, cats game, or if the game is still in play
* if its a win it determines if it is an X win or O win and prints out a winners message coorespondingnto who won
* if its a cats game, it prints out cats game
* it the game is still in play it says keep playing and goes back to the Fill() method
*/
public static void GameStatus() {
Font win = new Font("Arial", Font.BOLD, 100);
Font cat = new Font("Arial", Font.BOLD, 80);
//check for win
if(// First column
board[0][0] == currentPlayer && board[0][1] == currentPlayer && board[0][2] == currentPlayer
//second column
|| board[1][0] == currentPlayer && board[1][1] == currentPlayer && board[1][2] == currentPlayer
//third column
|| board[2][0] == currentPlayer && board[2][1] == currentPlayer && board[2][2] == currentPlayer
//first row
|| board[0][0] == currentPlayer && board[1][0] == currentPlayer && board[2][0] == currentPlayer
//second row
|| board[0][1] == currentPlayer && board[1][1] == currentPlayer && board[2][1] == currentPlayer
//third row
|| board[0][2] == currentPlayer && board[1][2] == currentPlayer && board[2][2] == currentPlayer
//diagonal 1
|| board[0][2] == currentPlayer && board[1][1] == currentPlayer && board[2][0] == currentPlayer
// diagonal 2
|| board[2][2] == currentPlayer && board[1][1] == currentPlayer && board[0][0] == currentPlayer) {
//X win
while(currentPlayer == 1) {
StdDraw.setFont(win);
StdDraw.setPenColor(StdDraw.GREEN);
StdDraw.text(4.5, 4.5, "X Won!");
}
//O win
while(currentPlayer == -1) {
StdDraw.setFont(win);
StdDraw.setPenColor(StdDraw.GREEN);
StdDraw.text(4.5, 4.5, "O Won!");
}
return;
}
//cats game
else if(board[0][0] != 0 && board[0][1] != 0 && board[0][2] != 0 && board[1][0] != 0 && board[1][1] != 0 && board[1][2] != 0 && board[2][0] != 0 && board[2][1] != 0 && board[2][2] != 0) {
StdDraw.setFont(cat);
StdDraw.setPenColor(StdDraw.YELLOW);
StdDraw.text(4.5, 4.5, "Cat's Game!");
return;
}
//still playing
else {
System.out.println("Keep Playing.");
return;
}
}//Ends GameStatus
}// End Class Game
I am a beginner java programmer and I am making a simple TicTacToe game using 2D arrays and these are my if statements to check if player 1, or player 2 has won. I believe that this can be simplified by using for loop(s) however I do not understand how to use that method.
if ((grid[0][0] == 1 && grid[0][1] == 1 && grid[0][2] == 1)
|| (grid[1][0] == 1 && grid[1][1] == 1 && grid[1][2] == 1)
|| (grid[2][0] == 1 && grid[2][1] == 1 && grid[2][2] == 1)
|| (grid[0][0] == 1 && grid[1][1] == 1 && grid[2][2] == 1)
|| (grid[0][2] == 1 && grid[1][1] == 1 && grid[2][0] == 1)
|| (grid[0][0] == 1 && grid[1][0] == 1 && grid[2][0] == 1)
|| (grid[0][1] == 1 && grid[1][1] == 1 && grid[2][1] == 1)
|| (grid[0][2] == 1 && grid[1][2] == 1 && grid[2][2] == 1)
&& won == false) {
title.setText("X wins!");
won = true;
} else if ((grid[0][0] == 2 && grid[0][1] == 2 && grid[0][2] == 2)
|| (grid[1][0] == 2 && grid[1][1] == 2 && grid[1][2] == 2)
|| (grid[2][0] == 2 && grid[2][1] == 2 && grid[2][2] == 2)
|| (grid[0][0] == 2 && grid[1][1] == 2 && grid[2][2] == 2)
|| (grid[0][2] == 2 && grid[1][1] == 2 && grid[2][0] == 2)
|| (grid[0][0] == 2 && grid[1][0] == 2 && grid[2][0] == 2)
|| (grid[0][1] == 2 && grid[1][1] == 2 && grid[2][1] == 2)
|| (grid[0][2] == 2 && grid[1][2] == 2 && grid[2][2] == 2)
&& won == false) {
title.setText("O wins!");
won = true;
}
Below is the modified code that uses far less if statments and conditions.
public static boolean hasWon(int[][] grid) {
for (int a = 1; a <= 2; a++) {
for (int b = 0; b < grid.length; b++) {
// Checking for win in horizontal, then vertical, then diagonal
if (grid[b][0] == a && grid[b][1] == a && grid[b][2] == a) {
won = true;
} else if (grid[0][b] == a && grid[1][b] == a && grid[2][b] == a) {
won = true;
} else if ((grid[0][0] == a && grid[1][1] == a && grid[2][2] == a
|| (grid[0][2] == a && grid[1][1] == a && grid[2][0] == a))) {
won = true;
}
}
}
}
In order to help you reach a solution on your own I'll give you some hints for now.
Hint #1: Think about what it means to win. A player must get 3 of their tokens in a row - horizontal, vertical, or diagonal. Think about how that can be represented in your program.
Hint #2: Think about how you can break the problem into smaller more manageable pieces. Think about what each winning scenario has in common and separate that logic into a method that you can call multiple times.
Hint #3: Consider what makes each winning scenario unique and how you might use your grid to produce a representation of the spaces you want to examine that is easier to check for a win.
If you're not sure about how for loops work or other aspects of the Java Language you can find tutorials on Oracle's site
Yes you are right. For loops are the way to go. Here is one way you could implement it.
public class tictactoe {
public static void main(String[] args) {
int[][] grid = {{1, 2, 1},
{1, 2, 1},
{2, 0, 1}};
boolean won = hasWon(grid);
}
public static boolean hasWon(int[][] grid){
for (int player = 1; player <= 2; player++){
boolean playerWon = false;
for(int i = 0; i < 3; i++){
//Horizonal win
playerWon = (grid[i][0] == player && grid[i][1] == player && grid[i][2] == player) || playerWon;
//Vertical Win
playerWon = (grid[0][i] == player && grid[1][i] == player && grid[i][2] == player) || playerWon;
}
//Diagonal Win
playerWon = (grid[0][0] == player && grid[1][1] == player && grid[2][2] == player) || playerWon;
playerWon = (grid[0][2] == player && grid[1][1] == player && grid[2][0] == player) || playerWon;
if(playerWon){
if(player == 1){
System.out.println("X wins!");
return true;
}
else{
System.out.println("O wins!");
return true;
}
}
}
//neither have won
return false;
}
}
Not a direct answer for the question. (as this is not "check all at once" style)
To simplify,
1. Check when a cell is clicked.
2. Condition depends on the place of a cell which is clicked and who clicked the cell.
3. If someone wins, end the game.
code sample
// Part of codes.(not tested.)
// Each cell has three states (0, 1, or 2)
int player = 1; // (Not written here but) switch this each turn (1 or 2)
// In some place (activity.onCreate() etc)
{
// For on click event(0, 0)
cell_0_0.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
grid[0][0] = player;
final boolean isEnd = checkEnd_0_0();
if (isEnd) {
// Call some function to end the game.
// Calling title.setText() in game end function maybe good.
// (as not needed to write many times.)
if (player == 1) {
title.setText("X wins!");
} else {
title.setText("O wins!");
}
} else {
switchPlayer(); // Not written in this code.
}
}
};
);
...
}
// Call this on cell(0, 0) click event
// Returns true if someone wins.
boolean checkEnd_0_0() {
// Omit checking grid[0][0] is 1 or 2 as it is clear.
// Check horizontal.
if (grid[0][1] == player) {
if (grid[0][2] == player) {
return true; // This is the case shown in question.
}
}
// Check other cases (vertical, diagonal)
...
// No match.
return false;
}
In class, our assignment is to create a two-dimensional array and create a tic-tac-toe game around it. I have everything done except displaying when the whole board is full and the game is a draw. I have tried a few things but I have not found the solution and I need some help... Here is my code:
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int row, column;
char player = 'X';
//create 2 dimensional array for tic tac toe board
char[][] board = new char[3][3];
char ch = '1';
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++) {
board[i][j] = ch++;
}
}
displayBoard(board);
while(!winner(board) == true){
//get input for row/column
System.out.println("Enter a row and column (0, 1, or 2); for player " + player + ":");
row = in.nextInt();
column = in.nextInt();
//occupied
while (board[row][column] == 'X' || board[row][column] == 'O') {
System.out.println("This spot is occupied. Please try again");
}
//place the X
board[row][column] = player;
displayBoard(board);
if (winner(board)){
System.out.println("Player " + player + " is the winner!");
}
//time to swap players after each go.
if (player == 'O') {
player = 'X';
}
else {
player = 'O';
}
if (winner(board) == false) {
System.out.println("The game is a draw. Please try again.");
}
}
private static void displayBoard(char[][] board) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (j == board[i].length - 1) System.out.print(board[i][j]);
else System.out.print( board[i][j] + " | ");
}
System.out.println();
}
}
//method to determine whether there is an x or an o in the spot
public static Boolean winner(char[][] board){
for (int i = 0; i< board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == 'O' || board[i][j] == 'X') {
return false;
}
}
}
return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
(board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
(board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
(board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
(board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
(board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
(board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
(board[1][0] == board [1][1] && board[1][0] == board [1][2]);
}
}
I want output saying that the board is full when it's full but I get nothing. This is the last line of my output and as you can see, my current strategy is not working as it continues to ask for input. -->
Enter a row and column (0, 1, or 2); for player X:
2 0
X | O | X
O | O | X
X | X | O
Enter a row and column (0, 1, or 2); for player O:
First off:
while (board[row][column] == 'X' || board[row][column] == 'O') {
System.out.println("This spot is occupied. Please try again");
}
This will create a infinite loop because row and column shouldn't change you should ask for new input!
Also
public static Boolean winner(char[][] board){
for (int i = 0; i< board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == 'O' || board[i][j] == 'X') {
return false;
}
}
}
As soon you hit 'O' or 'X' you will exit the Method with a false (no winner)
What you probably want to check is if every spot is occupied
public static Boolean winner(char[][] board){
//Boolean which is true until there is a empty spot
boolean occupied = true;
//loop and check if there is empty space or if its a draw
for (int i = 0; i< board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
//Check if spot is not 'O' or not 'X' => empty
if (board[i][j] != 'O' || board[i][j] != 'X') {
occupied = false;
}
}
}
if(occupied)
return false;
//Check if someone won
return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
(board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
(board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
(board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
(board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
(board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
(board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
(board[1][0] == board [1][1] && board[1][0] == board [1][2]);
}
This would now check if there is a winner or its a tie
Occupied == true == tie == return false
Winner == return true
But you have three states:
Win
Tie
NotFinished
With the changed Method you will NOT finish the game until you win.
Reason:
while(!winner(board) == true)
This makes the game run as long as there is NO winner
(winner() will be false because everything is occupied or there is no winner)
while(!false==true) => while(true)
You could write a method similar to winner but it only checks if the board has empty spots:
public static Boolean hasEmptySpot(char[][] board){
//loop and check if there is empty space
for (int i = 0; i< board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] != 'O' && board[i][j] != 'X') {
return true;
}
}
}
return false;
}
//New code
while(hasEmptySpot(board) || !winner(board)){
//Your code for the game here
....
}
this would end the game when there is no empty spot left
After you finished the game you can call winner(board) and it will return if you tied or won!
By creating hasEmptySpot() you could change your winner method to
public static Boolean winner(char[][] board){
return (board[0][0] == board [0][1] && board[0][0] == board [0][2]) ||
(board[0][0] == board [1][1] && board[0][0] == board [2][2]) ||
(board[0][0] == board [1][0] && board[0][0] == board [2][0]) ||
(board[2][0] == board [2][1] && board[2][0] == board [2][2]) ||
(board[2][0] == board [1][1] && board[0][0] == board [0][2]) ||
(board[0][2] == board [1][2] && board[0][2] == board [2][2]) ||
(board[0][1] == board [1][1] && board[0][1] == board [2][1]) ||
(board[1][0] == board [1][1] && board[1][0] == board [1][2]);
}
Why?
Because you finished the game and you know there are only two possible outcomes Win or Tie.
I hope this helped you a little bit.
EDIT
Had a logic error myself!
First mistake:
you still need to check if there is a winner while the game is running forgot that point!
while(hasEmptySpot(board) || !winner(board)){
}
Now this will quit the game loop when there is a winner or no empty spots is left
Second mistake:
In hasEmptySpot()
if (board[i][j] != 'O' && board[i][j] != 'X') {
return true;
not
if (board[i][j] != 'O' || board[i][j] != 'X') {
return true;
Fixed it in the upper examples.
I'm sorry for the inconvenience!
The most efficient way to do this is to keep a running count of how many spaces have been filled previously and increment that count each time a space is occupied. The board can be considered full when that count reaches 9.
If you're familiar with object-oriented programming, I think you'll find this easier to implement if you wrap your 2D array in a Board class.
Example:
public static class Board {
private char[][] spaces = new char[3][3];
private int numMoves = 0;
public void makeMove(int row, int col, char player) {
if (spaces[row][col] == 'X' || spaces[row][col] == 'O') {
System.out.println("This spot is occupied. Please try again");
} else {
spaces[row][col] = player;
numMoves++;
}
}
public boolean isFull() {
return numMoves == 9;
}
public boolean hasWinner() {
...
}
public void display() {
...
}
}
You could try to incorporate a new method such as the following:
public Boolean boardFull()
{
short count = 0;
for(short i = 0; i < 3; i++){
for(short j = 0; j < 3; j++){
if(board[i][j] == ‘O’ || board[i][j] == ’X’){
count++;
} else {
continue;
}
}
}
if(count == 9){
return true;
} else {
return false;
}
}
You could use an if statement to see if it returns true and then print something out if it does.
Solution
The code that's not working is your winner() method. It is always returning false if there is at least one cell occupied. You could proceed based on the last part of Nordiii's answer.
Extra problems
Cell-checking loop
Your code to check if a cell is occupied is going infinitely. You need to use an 'if' statement instead of a 'while' loop:
if(board[row][column] == 'X' || board[row][column] == 'O'){
System.out.println("This spot is occupied. Please try again");
continue;
}
Your old code got stuck always checking if 1 cell was occupied and it always returned true, which kept the loop alive and flooded your console. The continue statement will exit the current iteration of your other 'while' loop and start a new iteration, thus asking for new input.
Exceptions
Man, that's a lot of uncaught exceptions! If I mess up my input, pow! The whole thing fails. Just put a try block for your input-checking code:
try {
row = in.nextInt();
column = in.nextInt();
// Attempt to place player (an ArrayOutOfBoundsException could be thrown)
if(board[row][column] == 'X' || board[row][column] == 'O'){
System.out.println("This spot is occupied. Please try again");
continue;
}
board[row][column] = player;
} catch(Exception e){
System.out.println("I'm sorry, I didn't get that.");
continue;
}
This attempts to execute the code inside the try statement, and if someone inputs something incorrect, the exception gets 'caught' and a new iteration is created. Genius!
Although there are already some great answers I'd like to post another solution that is more generic in its logic to determine the winner. Currently you've hard-coded your some of the possible winning scenarios when you could write more generic logic for this.
As other answers have pointed out you want a method to check for unoccupied spaces in the board and this will tell you if there is a tie. I have implemented such a method in the code below along with the more generic winner logic.
Note that some methods are public to make it easier to test, they do not necessarily have to remain public.
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int row, column;
char player = 'X';
//create 2 dimensional array for tic tac toe board
char[][] board = new char[3][3];
char ch = '1';
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++) {
board[i][j] = ch++;
}
}
displayBoard(board);
while(!winner(board) == true){
//get input for row/column
System.out.println("Enter a row and column (0, 1, or 2); for player " + player + ":");
row = in.nextInt();
column = in.nextInt();
//occupied
while (board[row][column] == 'X' || board[row][column] == 'O') {
System.out.println("This spot is occupied. Please try again");
}
//place the X
board[row][column] = player;
displayBoard(board);
if (winner(board)){
System.out.println("Player " + player + " is the winner!");
}
//time to swap players after each go.
if (player == 'O') {
player = 'X';
}
else {
player = 'O';
}
if (winner(board) == false && !hasFreeSpace(board)) {
System.out.println("The game is a draw. Please try again.");
}
}
//Don't forget to close the scanner.
in.close();
}
public static void displayBoard(char[][] board) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (j == board[i].length - 1) System.out.print(board[i][j]);
else System.out.print( board[i][j] + " | ");
}
System.out.println();
}
}
/**
* Determines whether the board is completely occupied by X and O characters
* #param board the board to search through
* #return true if entire board is populated by X or O, false otherwise.
*/
public static boolean hasFreeSpace(char[][] board){
for (int i = 0; i< board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] != 'O' && board[i][j] != 'X') {
return true;
}
}
}
return false;
}
//method to determine whether there is a winner
public static boolean winner(char[][] board){
return isHorizontalWin(board) || isVerticalWin(board) || isDiagonalWin(board);
}
/**
* Determines if there is a winner by checking each row for consecutive
* matching tokens.
* #return true if there is a winner horizontally, false otherwise.
*/
private static boolean isHorizontalWin(char[][] board) {
for(int row = 0; row < board.length; row++){
if(isWin(board[row]))
return true;
}
return false;
}
/**
* Determines whether all of the buttons in the specified array have the
* same text and that the text is not empty string.
* #param lineToProcess an array of buttons representing a line in the grid
* #return true if all buttons in the array have the same non-empty text, false otherwise.
*/
private static boolean isWin(char[] lineToProcess) {
boolean foundWin = true;
char prevChar = '-';
for(char character: lineToProcess) {
if(prevChar == '-')
prevChar = character;
if ('O' != character && 'X' != character) {
foundWin = false;
break;
} else if (prevChar != character) {
foundWin = false;
break;
}
}
return foundWin;
}
/**
* Determines whether there is a winner by checking column for consecutive
* matching tokens.
* #return true if there is a vertical winner, false otherwise.
*/
private static boolean isVerticalWin(char[][] board) {
char[] column = null;
//assuming all rows have same legnth (same number of cols in each row), use first row
for(int col = 0; col < board[0].length; col++){
column = new char[board[0].length];
for(int row = 0; row < column.length; row++){
column[row] = board[row][col];
}
if(isWin(column))
return true;
}
return false;
}
/**
* Determines if there is a winner by checking each diagonal for consecutive
* matching tokens.
* #return true if a diagonal winner exists, false otherwise.
*/
private static boolean isDiagonalWin(char[][] board) {
int row = 0, col = 0;
int cols = board.length;
int rows = board[0].length; //assuming all rows are equal length so just use the first one
//Create a one-dimensional array to represent the diagonal. Use the lesser
// of the rows or columns to set its size. If the grid is rectangular then
// a diagonal will always be the size of the lesser of its two dimensions.
int size = rows < cols ? rows : cols;
char[] diagonal = new char[size];
//Since we know the grid is a square we really could just check one of
// these - either row or col, but I left both in here anyway.
while (row < rows && col < cols) {
diagonal[col] = board[row][col];
row++;
col++;
}
if (isWin(diagonal)) {
return true;
}
row = rows - 1;
col = 0;
diagonal = new char[size];
while (row >=0 && col < cols) {
diagonal[col] = board[row][col];
row--;
col++;
}
return isWin(diagonal);
}
}
I'm working in artificial intelligence project to develop a TicTacToe 4X4 using Minimax algorithm
I have this existing program that run Minimax algorithm in 3x3 TicTacToe board.
I want to extend it to 4x4 TicTacToe
but I couldn't any idea how I can do it ??
import java.util.*;
//defines the point where to place 1 or 2
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public String toString() {
return "[" + x + ", " + y + "]";
}
}
//defines the score per point -1,0,1
class PointsAndScores {
int score;
Point point;
PointsAndScores(int score, Point point) {
this.score = score;
this.point = point;
}
}
//defince the game board
class Board {
List<Point> availablePoints;
Scanner scan = new Scanner(System.in);
int[][] board = new int[3][3];
public Board() {
}
public boolean isGameOver() {
//Game is over is someone has won, or board is full (draw)
return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
}
//check if X have won represented by 1
public boolean hasXWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
//System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
//check if O has won represented by 2
public boolean hasOWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
// System.out.println("X Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
// System.out.println("X Row or Column win");
return true;
}
}
return false;
}
//check available states in the board
public List<Point> getAvailableStates() {
availablePoints = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
//put player move in the board
public void placeAMove(Point point, int player) {
board[point.x][point.y] = player; //player = 1 for O, 2 for X..
}
//get best movement according to the board state
public Point returnBestMove() {
int MAX = -100000;
int best = -1;
for (int i = 0; i < rootsChildrenScores.size(); ++i) {
if (MAX < rootsChildrenScores.get(i).score) {
MAX = rootsChildrenScores.get(i).score;
best = i;
}
}
return rootsChildrenScores.get(best).point;
}
//accepts input from user
void takeHumanInput() {
System.out.println("Your move: ");
int x = scan.nextInt();
int y = scan.nextInt();
Point point = new Point(x, y);
placeAMove(point, 2);
}
//display current board
public void displayBoard() {
System.out.println();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
//get min value
public int returnMin(List<Integer> list) {
int min = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) < min) {
min = list.get(i);
index = i;
}
}
return list.get(index);
}
//get max value
public int returnMax(List<Integer> list) {
int max = Integer.MIN_VALUE;
int index = -1;
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) > max) {
max = list.get(i);
index = i;
}
}
return list.get(index);
}
//declares a list for scores
List<PointsAndScores> rootsChildrenScores;
//excutes minimax algorithm
public void callMinimax(int depth, int turn){
rootsChildrenScores = new ArrayList<>();
minimax(depth, turn);
}
//minimax algorithm
public int minimax(int depth, int turn) {
if (hasXWon()) return +1;
if (hasOWon()) return -1;
//get available states from the board
List<Point> pointsAvailable = getAvailableStates();
if (pointsAvailable.isEmpty()) return 0;
//stores scores
List<Integer> scores = new ArrayList<>();
for (int i = 0; i < pointsAvailable.size(); ++i) {
Point point = pointsAvailable.get(i);
if (turn == 1) { //O's turn select the highest from below minimax() call
placeAMove(point, 1);
int currentScore = minimax(depth + 1, 2);
scores.add(currentScore);//add scores to the list
if (depth == 0)
rootsChildrenScores.add(new PointsAndScores(currentScore, point));
} else if (turn == 2) {//X's turn select the lowest from below minimax() call
placeAMove(point, 2);
scores.add(minimax(depth + 1, 1));
}
board[point.x][point.y] = 0; //Reset this point
}
return turn == 1 ? returnMax(scores) : returnMin(scores);
}
}
//main class
public class TicTacToe {
public static void main(String[] args) {
Board b = new Board();//instantiate board
Random rand = new Random();//instantiate random value
b.displayBoard();//display board
System.out.println("Who's gonna move first? (1)Computer (2)User: ");
int choice = b.scan.nextInt();
if(choice == 1){
Point p = new Point(rand.nextInt(3), rand.nextInt(3));
b.placeAMove(p, 1);
b.displayBoard();
}
while (!b.isGameOver()) {
System.out.println("Your move: ");
Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());
b.placeAMove(userMove, 2); //2 for X and X is the user
b.displayBoard();
if (b.isGameOver()) {
break;
}
b.callMinimax(0, 1);
for (PointsAndScores pas : b.rootsChildrenScores) {
System.out.println("Point: " + pas.point + " Score: " + pas.score);
}
b.placeAMove(b.returnBestMove(), 1);
b.displayBoard();
}
if (b.hasXWon()) {
System.out.println("Unfortunately, you lost!");
} else if (b.hasOWon()) {
System.out.println("You win! This is not going to get printed.");
} else {
System.out.println("It's a draw!");
}
}
}
You need to introduce an integer variable, let's say n, which will contain the size of the board(i.e. # of cells = n*n). Before a game starts, the player will be asked for the preferred board size, 3 or 4, and the corresponding board will be created. In order for your program to work with 4x4 like it did with 3x3, we will need to place the variable n wherever we have a method or loop that needs to traverse through the board. In other words, instead of 3 we will place n. This will "generalize" our program. For example, within the display method we have 2 for loops that run as long as i and j are smaller than 3. To "generalize" our program to a semi-arbitrary board size(3 or 4), we place an n in place of the 3 so that the loop will run as long as i and j are smaller than n(3 or 4). This change from 3 to n will apply wherever we have a 3 that indicates the size of the board. Another thing we will need to change are the checking methods hasXWon() and hasOWon(). Here, we will need to add an extra section for when the board size is 4 and not 3. This will look more or less the same as it's counterpart with the only difference being that it will check the extra row, column and longer diagonals that exist in a 4x4 board. After inserting these changes, the program now looks like this:
import java.util.*;
//defines the point where to place 1 or 2
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public String toString() {
return "[" + x + ", " + y + "]";
}
}
//defines the score per point -1,0,1
class PointsAndScores {
int score;
Point point;
PointsAndScores(int score, Point point) {
this.score = score;
this.point = point;
}
}
//defince the game board
class board {
List<Point> availablePoints;
Scanner scan = new Scanner(System.in);
public int n;
int[][] board;
public board(int n) {
this.n = n;
board = new int[n][n];
}
public boolean isGameOver() {
//Game is over is someone has won, or board is full (draw)
return (hasXWon() || hasOWon() || getAvailableStates().isEmpty());
}
//check if X have won represented by 1
public boolean hasXWon() {
if(n==3){
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
//System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < n; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
else {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == board[3][3]&& board[0][0] == 1) || (board[0][3] == board[1][2] && board[0][3] == board[2][1] && board[0][3] == board[3][0] && board[0][3] == 1)) {
//System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < n; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
}
//check if O has won represented by 2
public boolean hasOWon() {
if(n==3){
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
//System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < n; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2))) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
else {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == board[3][3]&& board[0][0] == 2) || (board[0][3] == board[1][2] && board[0][3] == board[2][1] && board[0][3] == board[3][0] && board[0][3] == 2)) {
//System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < n; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2))) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
}
//check available states in the board
public List<Point> getAvailableStates() {
availablePoints = new ArrayList<>();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (board[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
//put player move in the board
public void placeAMove(Point point, int player) {
board[point.x][point.y] = player; //player = 1 for O, 2 for X..
}
//get best movement according to the board state
public Point returnBestMove() {
int MAX = -100000;
int best = -1;
for (int i = 0; i < rootsChildrenScores.size(); ++i) {
if (MAX < rootsChildrenScores.get(i).score) {
MAX = rootsChildrenScores.get(i).score;
best = i;
}
}
return rootsChildrenScores.get(best).point;
}
//accepts input from user
void takeHumanInput() {
System.out.println("Your move: ");
int x = scan.nextInt();
int y = scan.nextInt();
Point point = new Point(x, y);
placeAMove(point, 2);
}
//display current board
public void displayboard() {
System.out.println();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
//get min value
public int returnMin(List<Integer> list) {
int min = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) < min) {
min = list.get(i);
index = i;
}
}
return list.get(index);
}
//get max value
public int returnMax(List<Integer> list) {
int max = Integer.MIN_VALUE;
int index = -1;
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) > max) {
max = list.get(i);
index = i;
}
}
return list.get(index);
}
//declares a list for scores
List<PointsAndScores> rootsChildrenScores;
//excutes minimax algorithm
public void callMinimax(int depth, int turn){
rootsChildrenScores = new ArrayList<>();
minimax(depth, turn);
}
//minimax algorithm
public int minimax(int depth, int turn) {
if (hasXWon()) return +1;
if (hasOWon()) return -1;
//get available states from the board
List<Point> pointsAvailable = getAvailableStates();
if (pointsAvailable.isEmpty()) return 0;
//stores scores
List<Integer> scores = new ArrayList<>();
for (int i = 0; i < pointsAvailable.size(); ++i) {
Point point = pointsAvailable.get(i);
if (turn == 1) { //O's turn select the highest from below minimax() call
placeAMove(point, 1);
int currentScore = minimax(depth + 1, 2);
scores.add(currentScore);//add scores to the list
if (depth == 0)
rootsChildrenScores.add(new PointsAndScores(currentScore, point));
} else if (turn == 2) {//X's turn select the lowest from below minimax() call
placeAMove(point, 2);
scores.add(minimax(depth + 1, 1));
}
board[point.x][point.y] = 0; //Reset this point
}
return turn == 1 ? returnMax(scores) : returnMin(scores);
}
}
//main class
public class TicTacToe {
public static void main(String[] args) {
//board b = new board();//instantiate board
Random rand = new Random();//instantiate random value
Point p;
Scanner s = new Scanner(System.in);
System.out.println("Choose board size: 3 or 4?");
int n=s.nextInt();
board b = new board(n); //Instantiating board after value of n has been read. This is important because the value of n is required for the instantiation to take place.
System.out.println(b.n);
b.displayboard();//display board
System.out.println("Who's gonna move first? (1)Computer (2)User: ");
int choice = b.scan.nextInt();
if(choice == 1){
if(b.n==3)
p = new Point(rand.nextInt(3), rand.nextInt(3));
else
p = new Point(rand.nextInt(4), rand.nextInt(4));
b.placeAMove(p, 1);
b.displayboard();
}
while (!b.isGameOver()) {
System.out.println("Your move: ");
Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());
b.placeAMove(userMove, 2); //2 for X and X is the user
b.displayboard();
if (b.isGameOver()) {
break;
}
b.callMinimax(0, 1);
for (PointsAndScores pas : b.rootsChildrenScores) {
System.out.println("Point: " + pas.point + " Score: " + pas.score);
}
b.placeAMove(b.returnBestMove(), 1);
b.displayboard();
}
if (b.hasXWon()) {
System.out.println("Unfortunately, you lost!");
} else if (b.hasOWon()) {
System.out.println("You win! This is not going to get printed.");
} else {
System.out.println("It's a draw!");
}
}
}
Your program is now able to create 4x4 games. There are however two things that you need to do. First, your placeAMove(...) method doesn't check if a cell is occupied before filling it. This may lead to cells being overwritten, so you must change that by checking if a cell is occupied before trying to fill it. The second and more important thing is that although the program is now able to create 4x4 games, it will not be able to carryout a proper game. The reason for this is that the number of nodes(states) created by minimax to find the next move, although manageable in 3x3 games, rises dramatically for 4x4 games. This means that it would take your computer A LOT of time, and by a lot I mean hours, to calculate the computer's second move. Even when I rigged the game(i.e. manually inserted random 1s and 2s in the cells before allowing minimax to be called to choose the computer's move) it still took the computer over 30 seconds to calculate it's move. This renders your game, of course, unplayable in 4x4 mode. There are, of course, ways, such as Memoization or Alpha-Beta Pruning or both together, to overcome this and speed up your algorithm. Here is a previously asked question to get you started on these topics. The chess programming wiki is also a good source of information on such topics, if a bit more complex in it's structure. Here's their entry on Memoization(Here called transposition tables).