I'm currently studying Java by myself. The book I'm using (while very good)
lacks feedback (obviously). While trying to write this program, I found myself solving most of the problems I've encountered by trial and error and makeshift solutions, which I fear might have led to some haphazard code. A good example would be the initialization of several variables: bestline
and bestrow
in the Testing
class, and bestMove
in the Board
class.
The code also feels very cumbersome. I would really appreciate it if you could review my code and comment on where I can improve, best practices I should adopt, etc...
import java.util.Scanner;
public class Testing
{
public static void main(String[] args) {
int turn = 0; // turn is either 0 or 1 , changed with Math.abs(turn-1).
Board board = new Board ();
board.initializeBoard(board);
Scanner myScanner = new Scanner(System.in);
for( ; board.scoreBoard(board) == 1000 ; ){
if (turn == 0){ //X's turn
int line = myScanner.nextInt(); // get a line
int row = myScanner.nextInt(); // get a row
for ( ; board.Square[line][row] != 0 ;line = myScanner.nextInt() , row = myScanner.nextInt() ){ // run while the square at [line][row] is taken,print the line and pick another.
System.out.println("Square Taken , please pick another.");
}
board.Square[line][row] = 1; //make the move (1 means X)
board.printBoard(board);
}
if (turn == 1){ //O's turn
int lastboard = 100; // O's worst case scenario so it has somthing to compare in the first round. // this will keep the best score that player can get
int bestline=0; // no meaning because I had to initialize
int bestrow =0; // no meaning because I had to initialize
for(int line = 0 ; line <3 ; line++){ //
for(int row = 0 ; row < 3 ; row ++){ //go over the entire board
if(board.Square[line][row]==0){ //if (Square is empty)
board.Square[line][row] = 2; // place 2 (2 means O) , this "tries" a move.
int tmpboard = board.scoreBestMove(board, Math.abs(turn-1)); //"scores" the move just made, keeps it in a temporary int.
if(tmpboard<=lastboard){ // if the move just made is better than previous best move.
lastboard = tmpboard; // keep the new score as the best score.
System.out.println(line+" "+row+" this is " +board.scoreBestMove(board, Math.abs(turn-1))); // printing how it scores every move for debugging. // ignore.
bestline = line; // keeps the best moves line.
bestrow = row; // keeps the best moves row.
}
board.Square[line][row] = 0; // resets the square.
}
}
}
board.Square[bestline][bestrow] = 2; // once the loop is over , do the best move you found.
board.printBoard(board); // print the board.
}
turn = Math.abs(turn-1); // change the turn.
}
System.out.println(board.scoreBoard(board));
}
}
Board:
import java.math.*;
public class Board {
int Square[][] = new int [3][3];
Board(){
int Square[][] = new int [3][3];
}
// constructor for a new board , makes an int array size 3/3.
public void initializeBoard (Board board){
for(int i=0 ; i<3 ; i++)
for(int j=0 ; j<3 ; j++)
Square[i][j] = 0;
}
public int scoreBoard (Board board){
if( (board.Square[0][0] == 1 & board.Square[0][1] == 1 & board.Square[0][2] == 1) || // 1'st line X
(board.Square[1][0] == 1 & board.Square[1][1] == 1 & board.Square[1][2] == 1) || // 2'nd line X
(board.Square[2][0] == 1 & board.Square[2][1] == 1 & board.Square[2][2] == 1) || // 3'rd line X
(board.Square[0][0] == 1 & board.Square[1][0] == 1 & board.Square[2][0] == 1) || // 1'st row X
(board.Square[0][1] == 1 & board.Square[1][1] == 1 & board.Square[2][1] == 1) || // 2'nd row X
(board.Square[0][2] == 1 & board.Square[1][2] == 1 & board.Square[2][2] == 1) || // 3'rd row X
(board.Square[0][0] == 1 & board.Square[1][1] == 1 & board.Square[2][2] == 1) || // 1'st diag X
(board.Square[0][2] == 1 & board.Square[1][1] == 1 & board.Square[2][0] == 1) ){ // 2'nd diag X
return 100; //X win
}
if( (board.Square[0][0] == 2 & board.Square[0][1] == 2 & board.Square[0][2] == 2) || // 1'st line X
(board.Square[1][0] == 2 & board.Square[1][1] == 2 & board.Square[1][2] == 2) || // 2'nd line X
(board.Square[2][0] == 2 & board.Square[2][1] == 2 & board.Square[2][2] == 2) || // 3'rd line X
(board.Square[0][0] == 2 & board.Square[1][0] == 2 & board.Square[2][0] == 2) || // 1'st row X
(board.Square[0][1] == 2 & board.Square[1][1] == 2 & board.Square[2][1] == 2) || // 2'nd row X
(board.Square[0][2] == 2 & board.Square[1][2] == 2 & board.Square[2][2] == 2) || // 3'rd row X
(board.Square[0][0] == 2 & board.Square[1][1] == 2 & board.Square[2][2] == 2) || // 1'st diag X
(board.Square[0][2] == 2 & board.Square[1][1] == 2 & board.Square[2][0] == 2) ){ // 2'nd diag X
return 0; // O win
}
if ( board.Square[0][0] == 0 || // this "if" gets called if no player won yet, this checks to see if the game is over , it returns 1000 if the game is not over.
board.Square[0][1] == 0 ||
board.Square[0][2] == 0 ||
board.Square[1][0] == 0 ||
board.Square[1][1] == 0 ||
board.Square[1][2] == 0 ||
board.Square[2][0] == 0 ||
board.Square[2][1] == 0 ||
board.Square[2][2] == 0){
return 1000; // game not over
}
return 50; // draw , gets to this "if" only if no other if is called.
}
// get a board: returns 100 if X won , 0 if O won , 50 if draw and 1000 if game not over.
public void printBoard (Board board){
for(int i=0 ; i<3 ; i++){
for(int j=0 ; j<3 ; j++){
System.out.print("|"+ Square[i][j]+ "|");
}
System.out.println();
}
}
public int scoreBestMove (Board board , int turn){
if(board.scoreBoard(board)!= 1000){ // if(game over)
return board.scoreBoard(board); // return the score
}
int bestmove=50; // no meaning because I had to initialize
int tmpbestmove; // temoprary int to store the value the recursion returns , so I dont have to call it twice.
switch (turn){
case 0: //X's turn
bestmove = 0; // makes the bestmove's score X's worst case scenario.
break;
case 1://O's turn
bestmove = 100;// makes the bestmove's score O's worst case scenario.
break;
}
for(int i = 0 ; i <3 ; i++){ //
for (int j = 0 ; j <3 ; j++){ //go over the entire board
if(board.Square[i][j]==0){ //if (Square is empty)
switch(turn){
case 0: // X's turn
board.Square[i][j] = 1; // place 1 (1 means X) , this "tries" a move.
tmpbestmove = board.scoreBestMove(board, Math.abs(turn-1)); // call itself with the board and the other player's turn to rate the move it just made.
if(tmpbestmove>bestmove){ //if the move scores better (for X) than any previous moves
bestmove = tmpbestmove; // keep that score in bestmove.
}
board.Square[i][j] = 0; // reset that Square to empty.
break;
case 1: // O's turn
board.Square[i][j] = 2; // place 2 (2 means O) , this "tries" a move.
tmpbestmove = board.scoreBestMove(board, Math.abs(turn-1));// call itself with the board and the other player's turn to rate the move it just made.
if(tmpbestmove<bestmove){ //if the move scores better(for O) than any previous moves
bestmove = tmpbestmove; // keep that score in bestmove.
}
board.Square[i][j] = 0; // reset that Square to empty.
break;
}
}
}
}
return bestmove; // return the score for the best option , NOTE : this does not return a move , but the score for the callers move.
}
// gets a board and a turn , returns the score for the move.
}