From 1b63d1f246d41f963c5d975f174c454879f6c392 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 13:52:11 +0200 Subject: [PATCH 01/26] add package, reduce number of variables --- src/main/java/App.java | 90 ------------------------------ src/main/java/org/example/App.java | 87 +++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 90 deletions(-) delete mode 100644 src/main/java/App.java create mode 100644 src/main/java/org/example/App.java diff --git a/src/main/java/App.java b/src/main/java/App.java deleted file mode 100644 index 4f6ffd2..0000000 --- a/src/main/java/App.java +++ /dev/null @@ -1,90 +0,0 @@ -import java.util.Scanner; - -public class App { - - public static void main(String[] args) { - Scanner scan = new Scanner(System.in); - byte input; - byte rand; - byte i; - boolean boxAvailable = false; - byte winner = 0; - char box[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - System.out.println("Enter box number to select. Enjoy!\n"); - - boolean boxEmpty = false; - while (true) { - System.out.println("\n\n " + box[0] + " | " + box[1] + " | " + box[2] + " "); - System.out.println("-----------"); - System.out.println(" " + box[3] + " | " + box[4] + " | " + box[5] + " "); - System.out.println("-----------"); - System.out.println(" " + box[6] + " | " + box[7] + " | " + box[8] + " \n"); - if(!boxEmpty){ - for(i = 0; i < 9; i++) - box[i] = ' '; - boxEmpty = true; - } - - if(winner == 1){ - System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if(winner == 2){ - System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if(winner == 3){ - System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } - - while (true) { - input = scan.nextByte(); - if (input > 0 && input < 10) { - if (box[input - 1] == 'X' || box[input - 1] == 'O') - System.out.println("That one is already in use. Enter another."); - else { - box[input - 1] = 'X'; - break; - } - } - else - System.out.println("Invalid input. Enter again."); - } - - if((box[0]=='X' && box[1]=='X' && box[2]=='X') || (box[3]=='X' && box[4]=='X' && box[5]=='X') || (box[6]=='X' && box[7]=='X' && box[8]=='X') || - (box[0]=='X' && box[3]=='X' && box[6]=='X') || (box[1]=='X' && box[4]=='X' && box[7]=='X') || (box[2]=='X' && box[5]=='X' && box[8]=='X') || - (box[0]=='X' && box[4]=='X' && box[8]=='X') || (box[2]=='X' && box[4]=='X' && box[6]=='X')){ - winner = 1; - continue; - } - - boxAvailable = false; - for(i=0; i<9; i++){ - if(box[i] != 'X' && box[i] != 'O'){ - boxAvailable = true; - break; - } - } - - if(boxAvailable == false){ - winner = 3; - continue; - } - - while (true) { - rand = (byte) (Math.random() * (9 - 1 + 1) + 1); - if (box[rand - 1] != 'X' && box[rand - 1] != 'O') { - box[rand - 1] = 'O'; - break; - } - } - - if((box[0]=='O' && box[1]=='O' && box[2]=='O') || (box[3]=='O' && box[4]=='O' && box[5]=='O') || (box[6]=='O' && box[7]=='O' && box[8]=='O') || - (box[0]=='O' && box[3]=='O' && box[6]=='O') || (box[1]=='O' && box[4]=='O' && box[7]=='O') || (box[2]=='O' && box[5]=='O' && box[8]=='O') || - (box[0]=='O' && box[4]=='O' && box[8]=='O') || (box[2]=='O' && box[4]=='O' && box[6]=='O')){ - winner = 2; - continue; - } - } - - } -} \ No newline at end of file diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java new file mode 100644 index 0000000..878cc7c --- /dev/null +++ b/src/main/java/org/example/App.java @@ -0,0 +1,87 @@ +package org.example; + +import java.util.Scanner; + +public class App { + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + byte winner = 0; + char[] board = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + System.out.println("Enter board number to select. Enjoy!\n"); + + boolean boardEmpty = false; + while (true) { + System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); + System.out.println("-----------"); + System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); + System.out.println("-----------"); + System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); + if (!boardEmpty) { + for (byte i = 0; i < 9; i++) + board[i] = ' '; + boardEmpty = true; + } + + if (winner == 1) { + System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } else if (winner == 2) { + System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } else if (winner == 3) { + System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } + + while (true) { + byte input = scanner.nextByte(); + if (input > 0 && input < 10) { + if (board[input - 1] == 'X' || board[input - 1] == 'O') + System.out.println("That one is already in use. Enter another."); + else { + board[input - 1] = 'X'; + break; + } + } else + System.out.println("Invalid input. Enter again."); + } + + if ((board[0] == 'X' && board[1] == 'X' && board[2] == 'X') || (board[3] == 'X' && board[4] == 'X' && board[5] == 'X') || (board[6] == 'X' && board[7] == 'X' && board[8] == 'X') || + (board[0] == 'X' && board[3] == 'X' && board[6] == 'X') || (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') || (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') || + (board[0] == 'X' && board[4] == 'X' && board[8] == 'X') || (board[2] == 'X' && board[4] == 'X' && board[6] == 'X')) { + winner = 1; + continue; + } + + boolean boardAvailable = false; + for (byte i = 0; i < 9; i++) { + if (board[i] != 'X' && board[i] != 'O') { + boardAvailable = true; + break; + } + } + + if (!boardAvailable) { + winner = 3; + continue; + } + + byte rand; + while (true) { + rand = (byte) (Math.random() * (9 - 1 + 1) + 1); + if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { + board[rand - 1] = 'O'; + break; + } + } + + if ((board[0] == 'O' && board[1] == 'O' && board[2] == 'O') || (board[3] == 'O' && board[4] == 'O' && board[5] == 'O') || (board[6] == 'O' && board[7] == 'O' && board[8] == 'O') || + (board[0] == 'O' && board[3] == 'O' && board[6] == 'O') || (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') || (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') || + (board[0] == 'O' && board[4] == 'O' && board[8] == 'O') || (board[2] == 'O' && board[4] == 'O' && board[6] == 'O')) { + winner = 2; + } + } + + } +} \ No newline at end of file From 0d8ad54969776d837736a2d237a6d1a368e4b25c Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 14:46:24 +0200 Subject: [PATCH 02/26] extract game class --- src/main/java/org/example/App.java | 82 +----------------- .../java/org/example/game/TicTacToeGame.java | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 src/main/java/org/example/game/TicTacToeGame.java diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 878cc7c..844168e 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -1,87 +1,11 @@ package org.example; -import java.util.Scanner; +import org.example.game.TicTacToeGame; public class App { public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - byte winner = 0; - char[] board = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; - System.out.println("Enter board number to select. Enjoy!\n"); - - boolean boardEmpty = false; - while (true) { - System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); - System.out.println("-----------"); - System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); - System.out.println("-----------"); - System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); - if (!boardEmpty) { - for (byte i = 0; i < 9; i++) - board[i] = ' '; - boardEmpty = true; - } - - if (winner == 1) { - System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if (winner == 2) { - System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if (winner == 3) { - System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } - - while (true) { - byte input = scanner.nextByte(); - if (input > 0 && input < 10) { - if (board[input - 1] == 'X' || board[input - 1] == 'O') - System.out.println("That one is already in use. Enter another."); - else { - board[input - 1] = 'X'; - break; - } - } else - System.out.println("Invalid input. Enter again."); - } - - if ((board[0] == 'X' && board[1] == 'X' && board[2] == 'X') || (board[3] == 'X' && board[4] == 'X' && board[5] == 'X') || (board[6] == 'X' && board[7] == 'X' && board[8] == 'X') || - (board[0] == 'X' && board[3] == 'X' && board[6] == 'X') || (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') || (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') || - (board[0] == 'X' && board[4] == 'X' && board[8] == 'X') || (board[2] == 'X' && board[4] == 'X' && board[6] == 'X')) { - winner = 1; - continue; - } - - boolean boardAvailable = false; - for (byte i = 0; i < 9; i++) { - if (board[i] != 'X' && board[i] != 'O') { - boardAvailable = true; - break; - } - } - - if (!boardAvailable) { - winner = 3; - continue; - } - - byte rand; - while (true) { - rand = (byte) (Math.random() * (9 - 1 + 1) + 1); - if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { - board[rand - 1] = 'O'; - break; - } - } - - if ((board[0] == 'O' && board[1] == 'O' && board[2] == 'O') || (board[3] == 'O' && board[4] == 'O' && board[5] == 'O') || (board[6] == 'O' && board[7] == 'O' && board[8] == 'O') || - (board[0] == 'O' && board[3] == 'O' && board[6] == 'O') || (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') || (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') || - (board[0] == 'O' && board[4] == 'O' && board[8] == 'O') || (board[2] == 'O' && board[4] == 'O' && board[6] == 'O')) { - winner = 2; - } - } - + TicTacToeGame game = new TicTacToeGame(); + game.play(); } } \ No newline at end of file diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java new file mode 100644 index 0000000..6de4a26 --- /dev/null +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -0,0 +1,85 @@ +package org.example.game; + +import java.util.Scanner; + +public class TicTacToeGame { + public void play() { + Scanner scanner = new Scanner(System.in); + byte winner = 0; + char[] board = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + System.out.println("Enter board number to select. Enjoy!\n"); + + boolean boardEmpty = false; + while (true) { + System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); + System.out.println("-----------"); + System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); + System.out.println("-----------"); + System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); + if (!boardEmpty) { + for (byte i = 0; i < 9; i++) + board[i] = ' '; + boardEmpty = true; + } + + if (winner == 1) { + System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } else if (winner == 2) { + System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } else if (winner == 3) { + System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + } + + while (true) { + byte input = scanner.nextByte(); + if (input > 0 && input < 10) { + if (board[input - 1] == 'X' || board[input - 1] == 'O') + System.out.println("That one is already in use. Enter another."); + else { + board[input - 1] = 'X'; + break; + } + } else + System.out.println("Invalid input. Enter again."); + } + + if ((board[0] == 'X' && board[1] == 'X' && board[2] == 'X') || (board[3] == 'X' && board[4] == 'X' && board[5] == 'X') || (board[6] == 'X' && board[7] == 'X' && board[8] == 'X') || + (board[0] == 'X' && board[3] == 'X' && board[6] == 'X') || (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') || (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') || + (board[0] == 'X' && board[4] == 'X' && board[8] == 'X') || (board[2] == 'X' && board[4] == 'X' && board[6] == 'X')) { + winner = 1; + continue; + } + + boolean boardAvailable = false; + for (byte i = 0; i < 9; i++) { + if (board[i] != 'X' && board[i] != 'O') { + boardAvailable = true; + break; + } + } + + if (!boardAvailable) { + winner = 3; + continue; + } + + byte rand; + while (true) { + rand = (byte) (Math.random() * (9 - 1 + 1) + 1); + if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { + board[rand - 1] = 'O'; + break; + } + } + + if ((board[0] == 'O' && board[1] == 'O' && board[2] == 'O') || (board[3] == 'O' && board[4] == 'O' && board[5] == 'O') || (board[6] == 'O' && board[7] == 'O' && board[8] == 'O') || + (board[0] == 'O' && board[3] == 'O' && board[6] == 'O') || (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') || (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') || + (board[0] == 'O' && board[4] == 'O' && board[8] == 'O') || (board[2] == 'O' && board[4] == 'O' && board[6] == 'O')) { + winner = 2; + } + } + } +} From 63957013e7424cf10796209ae22bbff4ae92be60 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 15:20:15 +0200 Subject: [PATCH 03/26] extract methods --- .../java/org/example/game/TicTacToeGame.java | 122 +++++++++++------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 6de4a26..6799377 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -7,79 +7,103 @@ public void play() { Scanner scanner = new Scanner(System.in); byte winner = 0; char[] board = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; - System.out.println("Enter board number to select. Enjoy!\n"); - boolean boardEmpty = false; + while (true) { - System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); - System.out.println("-----------"); - System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); - System.out.println("-----------"); - System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); + printBoard(board); + if (!boardEmpty) { for (byte i = 0; i < 9; i++) board[i] = ' '; boardEmpty = true; } - if (winner == 1) { - System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if (winner == 2) { - System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - } else if (winner == 3) { - System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + if (winner != 0) { + displayResult(winner); break; } - while (true) { - byte input = scanner.nextByte(); - if (input > 0 && input < 10) { - if (board[input - 1] == 'X' || board[input - 1] == 'O') - System.out.println("That one is already in use. Enter another."); - else { - board[input - 1] = 'X'; - break; - } - } else - System.out.println("Invalid input. Enter again."); - } + byte userMove = getUserMove(scanner, board); + board[userMove - 1] = 'X'; - if ((board[0] == 'X' && board[1] == 'X' && board[2] == 'X') || (board[3] == 'X' && board[4] == 'X' && board[5] == 'X') || (board[6] == 'X' && board[7] == 'X' && board[8] == 'X') || - (board[0] == 'X' && board[3] == 'X' && board[6] == 'X') || (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') || (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') || - (board[0] == 'X' && board[4] == 'X' && board[8] == 'X') || (board[2] == 'X' && board[4] == 'X' && board[6] == 'X')) { + if (checkWin(board, 'X')) { winner = 1; continue; } - boolean boardAvailable = false; - for (byte i = 0; i < 9; i++) { - if (board[i] != 'X' && board[i] != 'O') { - boardAvailable = true; - break; - } - } - - if (!boardAvailable) { + if (checkDraw(board)) { winner = 3; continue; } - byte rand; - while (true) { - rand = (byte) (Math.random() * (9 - 1 + 1) + 1); - if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { - board[rand - 1] = 'O'; - break; + byte computerMove = getComputerMove(board); + board[computerMove - 1] = 'O'; + + if (checkWin(board, 'O')) { + winner = 2; + } + } + } + + public void printBoard(char[] board) { + System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); + System.out.println("-----------"); + System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); + System.out.println("-----------"); + System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); + } + + public void displayResult(byte winner) { + if (winner == 1) { + System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); + } else if (winner == 2) { + System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); + } else if (winner == 3) { + System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + } + } + + public byte getUserMove(Scanner scanner, char[] board) { + while (true) { + byte input = scanner.nextByte(); + if (input > 0 && input < 10) { + if (board[input - 1] == 'X' || board[input - 1] == 'O') + System.out.println("That one is already in use. Enter another."); + else { + return input; } + } else + System.out.println("Invalid input. Enter again."); + } + } + + public byte getComputerMove(char[] board) { + byte rand; + while (true) { + rand = (byte) (Math.random() * (9 - 1 + 1) + 1); + if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { + return rand; } + } + } - if ((board[0] == 'O' && board[1] == 'O' && board[2] == 'O') || (board[3] == 'O' && board[4] == 'O' && board[5] == 'O') || (board[6] == 'O' && board[7] == 'O' && board[8] == 'O') || - (board[0] == 'O' && board[3] == 'O' && board[6] == 'O') || (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') || (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') || - (board[0] == 'O' && board[4] == 'O' && board[8] == 'O') || (board[2] == 'O' && board[4] == 'O' && board[6] == 'O')) { - winner = 2; + public boolean checkWin(char[] board, char player) { + return (board[0] == player && board[1] == player && board[2] == player) || + (board[3] == player && board[4] == player && board[5] == player) || + (board[6] == player && board[7] == player && board[8] == player) || + (board[0] == player && board[3] == player && board[6] == player) || + (board[1] == player && board[4] == player && board[7] == player) || + (board[2] == player && board[5] == player && board[8] == player) || + (board[0] == player && board[4] == player && board[8] == player) || + (board[2] == player && board[4] == player && board[6] == player); + } + + public boolean checkDraw(char[] board) { + for (byte i = 0; i < 9; i++) { + if (board[i] != 'X' && board[i] != 'O') { + return false; } } + return true; } } From 69de45bd2ed52f9c126dff1674f1f83cada1d538 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 15:32:45 +0200 Subject: [PATCH 04/26] add constants --- .../java/org/example/game/TicTacToeGame.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 6799377..cef182a 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -3,6 +3,10 @@ import java.util.Scanner; public class TicTacToeGame { + private static final char HUMAN_MARKER = 'X'; + private static final char COMPUTER_MARKER = 'O'; + private static final int BOARD_SIZE = 9; + public void play() { Scanner scanner = new Scanner(System.in); byte winner = 0; @@ -13,7 +17,7 @@ public void play() { printBoard(board); if (!boardEmpty) { - for (byte i = 0; i < 9; i++) + for (byte i = 0; i < BOARD_SIZE; i++) board[i] = ' '; boardEmpty = true; } @@ -24,9 +28,9 @@ public void play() { } byte userMove = getUserMove(scanner, board); - board[userMove - 1] = 'X'; + board[userMove - 1] = HUMAN_MARKER; - if (checkWin(board, 'X')) { + if (checkWin(board, HUMAN_MARKER)) { winner = 1; continue; } @@ -37,9 +41,9 @@ public void play() { } byte computerMove = getComputerMove(board); - board[computerMove - 1] = 'O'; + board[computerMove - 1] = COMPUTER_MARKER; - if (checkWin(board, 'O')) { + if (checkWin(board, COMPUTER_MARKER)) { winner = 2; } } @@ -67,7 +71,7 @@ public byte getUserMove(Scanner scanner, char[] board) { while (true) { byte input = scanner.nextByte(); if (input > 0 && input < 10) { - if (board[input - 1] == 'X' || board[input - 1] == 'O') + if (board[input - 1] == HUMAN_MARKER || board[input - 1] == COMPUTER_MARKER) System.out.println("That one is already in use. Enter another."); else { return input; @@ -80,8 +84,8 @@ public byte getUserMove(Scanner scanner, char[] board) { public byte getComputerMove(char[] board) { byte rand; while (true) { - rand = (byte) (Math.random() * (9 - 1 + 1) + 1); - if (board[rand - 1] != 'X' && board[rand - 1] != 'O') { + rand = (byte) (Math.random() * BOARD_SIZE + 1); + if (board[rand - 1] != HUMAN_MARKER && board[rand - 1] != COMPUTER_MARKER) { return rand; } } @@ -99,8 +103,8 @@ public boolean checkWin(char[] board, char player) { } public boolean checkDraw(char[] board) { - for (byte i = 0; i < 9; i++) { - if (board[i] != 'X' && board[i] != 'O') { + for (byte i = 0; i < BOARD_SIZE; i++) { + if (board[i] != HUMAN_MARKER && board[i] != COMPUTER_MARKER) { return false; } } From 3276d7b57ec3d478b643fb7e1676a6d2bcd8635c Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 16:33:23 +0200 Subject: [PATCH 05/26] remove break and continue --- .../java/org/example/game/TicTacToeGame.java | 131 ++++++++++-------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index cef182a..7671439 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -3,53 +3,57 @@ import java.util.Scanner; public class TicTacToeGame { - private static final char HUMAN_MARKER = 'X'; - private static final char COMPUTER_MARKER = 'O'; + private static final char USER_MARK = 'X'; + private static final char COMPUTER_MARK = 'O'; private static final int BOARD_SIZE = 9; + private static final char EMPTY_CELL = ' '; - public void play() { - Scanner scanner = new Scanner(System.in); - byte winner = 0; - char[] board = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; - boolean boardEmpty = false; + private final char[] board = new char[BOARD_SIZE]; + private final Scanner scanner = new Scanner(System.in); + public void play() { + resetBoard(); while (true) { - printBoard(board); + printBoard(); - if (!boardEmpty) { - for (byte i = 0; i < BOARD_SIZE; i++) - board[i] = ' '; - boardEmpty = true; + if (checkWinner(USER_MARK)) { + displayResult(1); + return; } - if (winner != 0) { - displayResult(winner); - break; + if (checkDraw()) { + displayResult(3); + return; } - byte userMove = getUserMove(scanner, board); - board[userMove - 1] = HUMAN_MARKER; - - if (checkWin(board, HUMAN_MARKER)) { - winner = 1; - continue; + makeUserMove(); + if (checkWinner(USER_MARK)) { + printBoard(); + displayResult(1); + return; } - if (checkDraw(board)) { - winner = 3; - continue; + if (checkDraw()) { + printBoard(); + displayResult(3); + return; } - byte computerMove = getComputerMove(board); - board[computerMove - 1] = COMPUTER_MARKER; - - if (checkWin(board, COMPUTER_MARKER)) { - winner = 2; + makeComputerMove(); + if (checkWinner(COMPUTER_MARK)) { + displayResult(2); + return; } } } - public void printBoard(char[] board) { + private void resetBoard() { + for (int i = 0; i < BOARD_SIZE; i++) { + board[i] = EMPTY_CELL; + } + } + + public void printBoard() { System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); System.out.println("-----------"); System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); @@ -57,41 +61,50 @@ public void printBoard(char[] board) { System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); } - public void displayResult(byte winner) { - if (winner == 1) { - System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); - } else if (winner == 2) { - System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); - } else if (winner == 3) { - System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + + private void displayResult(int result) { + switch (result) { + case 1: + System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + case 2: + System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); + break; + case 3: + System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); + break; } } - public byte getUserMove(Scanner scanner, char[] board) { - while (true) { - byte input = scanner.nextByte(); - if (input > 0 && input < 10) { - if (board[input - 1] == HUMAN_MARKER || board[input - 1] == COMPUTER_MARKER) - System.out.println("That one is already in use. Enter another."); - else { - return input; - } - } else - System.out.println("Invalid input. Enter again."); - } + private void makeUserMove() { + byte move; + do { + move = getUserMove(); + } while (board[move - 1] == USER_MARK || board[move - 1] == COMPUTER_MARK); + board[move - 1] = USER_MARK; } - public byte getComputerMove(char[] board) { - byte rand; + private byte getUserMove() { + byte input; while (true) { - rand = (byte) (Math.random() * BOARD_SIZE + 1); - if (board[rand - 1] != HUMAN_MARKER && board[rand - 1] != COMPUTER_MARKER) { - return rand; + System.out.print("Enter your move (1-9): "); + input = scanner.nextByte(); + if (input >= 1 && input <= 9) { + return input; } + System.out.println("Invalid input. Enter again."); } } - public boolean checkWin(char[] board, char player) { + private void makeComputerMove() { + byte move; + do { + move = (byte) (Math.random() * BOARD_SIZE + 1); + } while (board[move - 1] == USER_MARK || board[move - 1] == COMPUTER_MARK); + board[move - 1] = COMPUTER_MARK; + } + + private boolean checkWinner(char player) { return (board[0] == player && board[1] == player && board[2] == player) || (board[3] == player && board[4] == player && board[5] == player) || (board[6] == player && board[7] == player && board[8] == player) || @@ -102,12 +115,12 @@ public boolean checkWin(char[] board, char player) { (board[2] == player && board[4] == player && board[6] == player); } - public boolean checkDraw(char[] board) { - for (byte i = 0; i < BOARD_SIZE; i++) { - if (board[i] != HUMAN_MARKER && board[i] != COMPUTER_MARKER) { + private boolean checkDraw() { + for (char cell : board) { + if (cell != USER_MARK && cell != COMPUTER_MARK) { return false; } } return true; } -} +} \ No newline at end of file From c06378e017b9609568046c2153f1495f73583979 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 17:22:14 +0200 Subject: [PATCH 06/26] fix initial board numbers --- .../java/org/example/game/TicTacToeGame.java | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 7671439..f86dd31 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -11,22 +11,35 @@ public class TicTacToeGame { private final char[] board = new char[BOARD_SIZE]; private final Scanner scanner = new Scanner(System.in); - public void play() { + public TicTacToeGame() { + initializeNumberedBoard(); + printBoard(); resetBoard(); + } + + private void initializeNumberedBoard() { + for (int i = 0; i < BOARD_SIZE; i++) { + board[i] = (char) ('1' + i); + } + } + + public void play() { while (true) { - printBoard(); if (checkWinner(USER_MARK)) { + printBoard(); displayResult(1); return; } if (checkDraw()) { + printBoard(); displayResult(3); return; } makeUserMove(); + if (checkWinner(USER_MARK)) { printBoard(); displayResult(1); @@ -41,9 +54,12 @@ public void play() { makeComputerMove(); if (checkWinner(COMPUTER_MARK)) { + printBoard(); displayResult(2); return; } + + printBoard(); } } @@ -61,7 +77,6 @@ public void printBoard() { System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); } - private void displayResult(int result) { switch (result) { case 1: @@ -73,6 +88,9 @@ private void displayResult(int result) { case 3: System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); break; + default: + System.out.println("Error"); + break; } } @@ -87,7 +105,7 @@ private void makeUserMove() { private byte getUserMove() { byte input; while (true) { - System.out.print("Enter your move (1-9): "); + System.out.print("Enter your move: "); input = scanner.nextByte(); if (input >= 1 && input <= 9) { return input; @@ -99,7 +117,7 @@ private byte getUserMove() { private void makeComputerMove() { byte move; do { - move = (byte) (Math.random() * BOARD_SIZE + 1); + move = (byte) ((Math.random() * BOARD_SIZE) + 1); } while (board[move - 1] == USER_MARK || board[move - 1] == COMPUTER_MARK); board[move - 1] = COMPUTER_MARK; } From 1f748364a7ace5f072e38384b3c48e26bc81d5d7 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 18:05:26 +0200 Subject: [PATCH 07/26] add result enum --- src/main/java/org/example/game/Result.java | 7 +++++++ .../java/org/example/game/TicTacToeGame.java | 19 +++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/example/game/Result.java diff --git a/src/main/java/org/example/game/Result.java b/src/main/java/org/example/game/Result.java new file mode 100644 index 0000000..8e37945 --- /dev/null +++ b/src/main/java/org/example/game/Result.java @@ -0,0 +1,7 @@ +package org.example.game; + +public enum Result { + USER_WON, + COMPUTER_WON, + DRAW +} diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index f86dd31..9cf3ae3 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -25,16 +25,15 @@ private void initializeNumberedBoard() { public void play() { while (true) { - if (checkWinner(USER_MARK)) { printBoard(); - displayResult(1); + displayResult(Result.USER_WON); return; } if (checkDraw()) { printBoard(); - displayResult(3); + displayResult(Result.DRAW); return; } @@ -42,20 +41,20 @@ public void play() { if (checkWinner(USER_MARK)) { printBoard(); - displayResult(1); + displayResult(Result.USER_WON); return; } if (checkDraw()) { printBoard(); - displayResult(3); + displayResult(Result.DRAW); return; } makeComputerMove(); if (checkWinner(COMPUTER_MARK)) { printBoard(); - displayResult(2); + displayResult(Result.COMPUTER_WON); return; } @@ -77,15 +76,15 @@ public void printBoard() { System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); } - private void displayResult(int result) { + private void displayResult(Result result) { switch (result) { - case 1: + case USER_WON: System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); break; - case 2: + case COMPUTER_WON: System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); break; - case 3: + case DRAW: System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); break; default: From 2f0e491aefc6197c416533e314339d07c905065c Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 18:17:24 +0200 Subject: [PATCH 08/26] use streams --- .../java/org/example/game/TicTacToeGame.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 9cf3ae3..9b687a3 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -1,6 +1,7 @@ package org.example.game; import java.util.Scanner; +import java.util.stream.IntStream; public class TicTacToeGame { private static final char USER_MARK = 'X'; @@ -18,9 +19,7 @@ public TicTacToeGame() { } private void initializeNumberedBoard() { - for (int i = 0; i < BOARD_SIZE; i++) { - board[i] = (char) ('1' + i); - } + IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = (char) ('1' + i)); } public void play() { @@ -63,9 +62,7 @@ public void play() { } private void resetBoard() { - for (int i = 0; i < BOARD_SIZE; i++) { - board[i] = EMPTY_CELL; - } + IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = EMPTY_CELL); } public void printBoard() { @@ -133,11 +130,6 @@ private boolean checkWinner(char player) { } private boolean checkDraw() { - for (char cell : board) { - if (cell != USER_MARK && cell != COMPUTER_MARK) { - return false; - } - } - return true; + return IntStream.range(0, BOARD_SIZE).noneMatch(i -> board[i] == EMPTY_CELL); } } \ No newline at end of file From 4206be2a80824eff4297efdb26ba4cb2a849f15f Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 18:29:04 +0200 Subject: [PATCH 09/26] allow printing a board of different size --- .../java/org/example/game/TicTacToeGame.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 9b687a3..01c10f6 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -7,6 +7,7 @@ public class TicTacToeGame { private static final char USER_MARK = 'X'; private static final char COMPUTER_MARK = 'O'; private static final int BOARD_SIZE = 9; + private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); private static final char EMPTY_CELL = ' '; private final char[] board = new char[BOARD_SIZE]; @@ -66,11 +67,19 @@ private void resetBoard() { } public void printBoard() { - System.out.println("\n\n " + board[0] + " | " + board[1] + " | " + board[2] + " "); - System.out.println("-----------"); - System.out.println(" " + board[3] + " | " + board[4] + " | " + board[5] + " "); - System.out.println("-----------"); - System.out.println(" " + board[6] + " | " + board[7] + " | " + board[8] + " \n"); + System.out.println(); + for (int i = 0; i < BOARD_SIZE; i++) { + System.out.print(" " + board[i]); + if ((i + 1) % ROW_SIZE == 0) { + System.out.println(); + if (i < BOARD_SIZE - 1) { + System.out.println("--".repeat(ROW_SIZE * 2 - 1)); + } + } else { + System.out.print(" |"); + } + } + System.out.println(); } private void displayResult(Result result) { From 3feb92f7eb6ea2fb74f8b04ae6cd651f406c1105 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 18:34:57 +0200 Subject: [PATCH 10/26] refactor displayResult --- .../java/org/example/game/TicTacToeGame.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 01c10f6..2f5f888 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -83,20 +83,13 @@ public void printBoard() { } private void displayResult(Result result) { - switch (result) { - case USER_WON: - System.out.println("You won the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - case COMPUTER_WON: - System.out.println("You lost the game!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - case DRAW: - System.out.println("It's a draw!\nCreated by Shreyas Saha. Thanks for playing!"); - break; - default: - System.out.println("Error"); - break; - } + String message = switch (result) { + case USER_WON -> "You won the game!"; + case COMPUTER_WON -> "You lost the game!"; + case DRAW -> "It's a draw!"; + default -> "Unexpected result!"; + }; + System.out.println(message + "\nCreated by Shreyas Saha. Thanks for playing!"); } private void makeUserMove() { From b50ed8f2069f8590f01ddce915a6782890e3fee5 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 18:54:15 +0200 Subject: [PATCH 11/26] allow swapping the order of play between user and computer --- .../java/org/example/game/TicTacToeGame.java | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 2f5f888..b6c3db6 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -12,6 +12,7 @@ public class TicTacToeGame { private final char[] board = new char[BOARD_SIZE]; private final Scanner scanner = new Scanner(System.in); + private final char[] players = {USER_MARK, COMPUTER_MARK}; public TicTacToeGame() { initializeNumberedBoard(); @@ -24,24 +25,14 @@ private void initializeNumberedBoard() { } public void play() { - while (true) { - if (checkWinner(USER_MARK)) { - printBoard(); - displayResult(Result.USER_WON); - return; - } - - if (checkDraw()) { - printBoard(); - displayResult(Result.DRAW); - return; - } + int currentPlayerIndex = 0; - makeUserMove(); + while (true) { + makeMove(players[currentPlayerIndex]); - if (checkWinner(USER_MARK)) { + if (checkWinner(players[currentPlayerIndex])) { printBoard(); - displayResult(Result.USER_WON); + displayResult(players[currentPlayerIndex] == USER_MARK ? Result.USER_WON : Result.COMPUTER_WON); return; } @@ -51,14 +42,8 @@ public void play() { return; } - makeComputerMove(); - if (checkWinner(COMPUTER_MARK)) { - printBoard(); - displayResult(Result.COMPUTER_WON); - return; - } - printBoard(); + currentPlayerIndex = (currentPlayerIndex + 1) % players.length; } } @@ -92,6 +77,12 @@ private void displayResult(Result result) { System.out.println(message + "\nCreated by Shreyas Saha. Thanks for playing!"); } + private void makeMove(char player){ + if(player == USER_MARK){ + makeUserMove(); + } else makeComputerMove(); + } + private void makeUserMove() { byte move; do { From a0f0afc4722e5e4ec37bd2b9ba0f4deac8546581 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 19:43:00 +0200 Subject: [PATCH 12/26] make flexible board --- .../java/org/example/game/TicTacToeGame.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index b6c3db6..0d2cb6c 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -6,7 +6,7 @@ public class TicTacToeGame { private static final char USER_MARK = 'X'; private static final char COMPUTER_MARK = 'O'; - private static final int BOARD_SIZE = 9; + private static final int BOARD_SIZE = 16; private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); private static final char EMPTY_CELL = ' '; @@ -96,7 +96,7 @@ private byte getUserMove() { while (true) { System.out.print("Enter your move: "); input = scanner.nextByte(); - if (input >= 1 && input <= 9) { + if (input >= 1 && input <= BOARD_SIZE) { return input; } System.out.println("Invalid input. Enter again."); @@ -112,14 +112,27 @@ private void makeComputerMove() { } private boolean checkWinner(char player) { - return (board[0] == player && board[1] == player && board[2] == player) || - (board[3] == player && board[4] == player && board[5] == player) || - (board[6] == player && board[7] == player && board[8] == player) || - (board[0] == player && board[3] == player && board[6] == player) || - (board[1] == player && board[4] == player && board[7] == player) || - (board[2] == player && board[5] == player && board[8] == player) || - (board[0] == player && board[4] == player && board[8] == player) || - (board[2] == player && board[4] == player && board[6] == player); + for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { + if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> board[j] == player)) { + return true; + } + } + + for (int i = 0; i < ROW_SIZE; i++) { + int finalI = i; + if (IntStream.range(0, ROW_SIZE).allMatch(j -> board[finalI + j * ROW_SIZE] == player)) { + return true; + } + } + + if (IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)] == player)) { + return true; + } + if (IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)] == player)) { + return true; + } + + return false; } private boolean checkDraw() { From d4fbf2c56875731e279ba7f76da88ddcd00517e7 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 20:04:13 +0200 Subject: [PATCH 13/26] change board to String --- .../java/org/example/game/TicTacToeGame.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 0d2cb6c..087174f 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -8,9 +8,9 @@ public class TicTacToeGame { private static final char COMPUTER_MARK = 'O'; private static final int BOARD_SIZE = 16; private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); - private static final char EMPTY_CELL = ' '; + private static final String EMPTY_CELL = " "; - private final char[] board = new char[BOARD_SIZE]; + private final String[] board = new String[BOARD_SIZE]; private final Scanner scanner = new Scanner(System.in); private final char[] players = {USER_MARK, COMPUTER_MARK}; @@ -21,7 +21,7 @@ public TicTacToeGame() { } private void initializeNumberedBoard() { - IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = (char) ('1' + i)); + IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = String.valueOf(i + 1)); } public void play() { @@ -54,14 +54,14 @@ private void resetBoard() { public void printBoard() { System.out.println(); for (int i = 0; i < BOARD_SIZE; i++) { - System.out.print(" " + board[i]); + System.out.printf("%2s", board[i]); if ((i + 1) % ROW_SIZE == 0) { System.out.println(); if (i < BOARD_SIZE - 1) { - System.out.println("--".repeat(ROW_SIZE * 2 - 1)); + System.out.println("----".repeat(ROW_SIZE)); } } else { - System.out.print(" |"); + System.out.print(" | "); } } System.out.println(); @@ -77,8 +77,8 @@ private void displayResult(Result result) { System.out.println(message + "\nCreated by Shreyas Saha. Thanks for playing!"); } - private void makeMove(char player){ - if(player == USER_MARK){ + private void makeMove(char player) { + if (player == USER_MARK) { makeUserMove(); } else makeComputerMove(); } @@ -87,8 +87,8 @@ private void makeUserMove() { byte move; do { move = getUserMove(); - } while (board[move - 1] == USER_MARK || board[move - 1] == COMPUTER_MARK); - board[move - 1] = USER_MARK; + } while (board[move - 1].equals(String.valueOf(USER_MARK)) || board[move - 1].equals(String.valueOf(COMPUTER_MARK))); + board[move - 1] = String.valueOf(USER_MARK); } private byte getUserMove() { @@ -107,28 +107,28 @@ private void makeComputerMove() { byte move; do { move = (byte) ((Math.random() * BOARD_SIZE) + 1); - } while (board[move - 1] == USER_MARK || board[move - 1] == COMPUTER_MARK); - board[move - 1] = COMPUTER_MARK; + } while (board[move - 1].equals(String.valueOf(USER_MARK)) || board[move - 1].equals(String.valueOf(COMPUTER_MARK))); + board[move - 1] = String.valueOf(COMPUTER_MARK); } private boolean checkWinner(char player) { for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { - if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> board[j] == player)) { + if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> board[j].equals(String.valueOf(player)))) { return true; } } for (int i = 0; i < ROW_SIZE; i++) { int finalI = i; - if (IntStream.range(0, ROW_SIZE).allMatch(j -> board[finalI + j * ROW_SIZE] == player)) { + if (IntStream.range(0, ROW_SIZE).allMatch(j -> board[finalI + j * ROW_SIZE].equals(String.valueOf(player)))) { return true; } } - if (IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)] == player)) { + if (IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)].equals(String.valueOf(player)))) { return true; } - if (IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)] == player)) { + if (IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)].equals(String.valueOf(player)))) { return true; } @@ -136,6 +136,6 @@ private boolean checkWinner(char player) { } private boolean checkDraw() { - return IntStream.range(0, BOARD_SIZE).noneMatch(i -> board[i] == EMPTY_CELL); + return IntStream.range(0, BOARD_SIZE).noneMatch(i -> board[i].equals(EMPTY_CELL)); } } \ No newline at end of file From 0535fcd2a73a25a9803f42ff953576f57d0f5534 Mon Sep 17 00:00:00 2001 From: ruslana Date: Sun, 11 Aug 2024 20:50:58 +0200 Subject: [PATCH 14/26] extract win checks, add printMessageToUser --- .../java/org/example/game/TicTacToeGame.java | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 087174f..434e7d7 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -20,6 +20,18 @@ public TicTacToeGame() { resetBoard(); } + public static void printMessageToUser(String message, boolean newLine) { + if (newLine) { + System.out.println(message); //NOSONAR + } else { + System.out.print(message); //NOSONAR + } + } + + public static void printFormattedMessageToUser(String format, Object... args) { + System.out.printf(format, args); //NOSONAR + } + private void initializeNumberedBoard() { IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = String.valueOf(i + 1)); } @@ -52,19 +64,19 @@ private void resetBoard() { } public void printBoard() { - System.out.println(); + printMessageToUser("", true); for (int i = 0; i < BOARD_SIZE; i++) { - System.out.printf("%2s", board[i]); + printFormattedMessageToUser("%2s", board[i]); if ((i + 1) % ROW_SIZE == 0) { - System.out.println(); + printMessageToUser("", true); if (i < BOARD_SIZE - 1) { - System.out.println("----".repeat(ROW_SIZE)); + printMessageToUser("----".repeat(ROW_SIZE), true); } } else { - System.out.print(" | "); + printMessageToUser(" | ", false); } } - System.out.println(); + printMessageToUser("", true); } private void displayResult(Result result) { @@ -72,9 +84,8 @@ private void displayResult(Result result) { case USER_WON -> "You won the game!"; case COMPUTER_WON -> "You lost the game!"; case DRAW -> "It's a draw!"; - default -> "Unexpected result!"; }; - System.out.println(message + "\nCreated by Shreyas Saha. Thanks for playing!"); + printMessageToUser(message + "\nCreated by Shreyas Saha. Thanks for playing!", true); } private void makeMove(char player) { @@ -94,12 +105,12 @@ private void makeUserMove() { private byte getUserMove() { byte input; while (true) { - System.out.print("Enter your move: "); + printMessageToUser("Enter your move: ", true); input = scanner.nextByte(); if (input >= 1 && input <= BOARD_SIZE) { return input; } - System.out.println("Invalid input. Enter again."); + printMessageToUser("Invalid input. Enter again.", true); } } @@ -112,29 +123,33 @@ private void makeComputerMove() { } private boolean checkWinner(char player) { + return checkRows(player) || checkColumns(player) || checkDiagonals(player); + } + + private boolean checkRows(char player) { for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> board[j].equals(String.valueOf(player)))) { return true; } } + return false; + } + private boolean checkColumns(char player) { for (int i = 0; i < ROW_SIZE; i++) { int finalI = i; if (IntStream.range(0, ROW_SIZE).allMatch(j -> board[finalI + j * ROW_SIZE].equals(String.valueOf(player)))) { return true; } } - - if (IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)].equals(String.valueOf(player)))) { - return true; - } - if (IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)].equals(String.valueOf(player)))) { - return true; - } - return false; } + private boolean checkDiagonals(char player) { + return IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)].equals(String.valueOf(player))) || + IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)].equals(String.valueOf(player))); + } + private boolean checkDraw() { return IntStream.range(0, BOARD_SIZE).noneMatch(i -> board[i].equals(EMPTY_CELL)); } From 76c2d9ce875bfcd806319fc2198609a3560b75aa Mon Sep 17 00:00:00 2001 From: ruslana Date: Mon, 12 Aug 2024 13:14:14 +0200 Subject: [PATCH 15/26] add message utility class --- .../java/org/example/game/TicTacToeGame.java | 32 +++++++------------ src/main/java/org/example/util/Message.java | 20 ++++++++++++ 2 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/main/java/org/example/util/Message.java diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 434e7d7..e24b0eb 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -1,5 +1,7 @@ package org.example.game; +import org.example.util.Message; + import java.util.Scanner; import java.util.stream.IntStream; @@ -20,18 +22,6 @@ public TicTacToeGame() { resetBoard(); } - public static void printMessageToUser(String message, boolean newLine) { - if (newLine) { - System.out.println(message); //NOSONAR - } else { - System.out.print(message); //NOSONAR - } - } - - public static void printFormattedMessageToUser(String format, Object... args) { - System.out.printf(format, args); //NOSONAR - } - private void initializeNumberedBoard() { IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = String.valueOf(i + 1)); } @@ -64,19 +54,19 @@ private void resetBoard() { } public void printBoard() { - printMessageToUser("", true); + Message.printMessageToUser("", true); for (int i = 0; i < BOARD_SIZE; i++) { - printFormattedMessageToUser("%2s", board[i]); + Message.printFormattedMessageToUser("%2s", board[i]); if ((i + 1) % ROW_SIZE == 0) { - printMessageToUser("", true); + Message.printMessageToUser("", true); if (i < BOARD_SIZE - 1) { - printMessageToUser("----".repeat(ROW_SIZE), true); + Message.printMessageToUser("----".repeat(ROW_SIZE), true); } } else { - printMessageToUser(" | ", false); + Message.printMessageToUser(" | ", false); } } - printMessageToUser("", true); + Message.printMessageToUser("", true); } private void displayResult(Result result) { @@ -85,7 +75,7 @@ private void displayResult(Result result) { case COMPUTER_WON -> "You lost the game!"; case DRAW -> "It's a draw!"; }; - printMessageToUser(message + "\nCreated by Shreyas Saha. Thanks for playing!", true); + Message.printMessageToUser(message + "\nCreated by Shreyas Saha. Thanks for playing!", true); } private void makeMove(char player) { @@ -105,12 +95,12 @@ private void makeUserMove() { private byte getUserMove() { byte input; while (true) { - printMessageToUser("Enter your move: ", true); + Message.printMessageToUser("Enter your move: ", true); input = scanner.nextByte(); if (input >= 1 && input <= BOARD_SIZE) { return input; } - printMessageToUser("Invalid input. Enter again.", true); + Message.printMessageToUser("Invalid input. Enter again.", true); } } diff --git a/src/main/java/org/example/util/Message.java b/src/main/java/org/example/util/Message.java new file mode 100644 index 0000000..71592dc --- /dev/null +++ b/src/main/java/org/example/util/Message.java @@ -0,0 +1,20 @@ +package org.example.util; + +public class Message { + + private Message() { + throw new IllegalStateException("Utility class"); + } + + public static void printMessageToUser(String message, boolean newLine) { + if (newLine) { + System.out.println(message); //NOSONAR + } else { + System.out.print(message); //NOSONAR + } + } + + public static void printFormattedMessageToUser(String format, Object... args) { + System.out.printf(format, args); //NOSONAR + } +} From 1764d8b7aa3ee0792418d91ba17b55d4523ae86c Mon Sep 17 00:00:00 2001 From: ruslana Date: Mon, 12 Aug 2024 13:37:30 +0200 Subject: [PATCH 16/26] extracted Board class --- src/main/java/org/example/game/Board.java | 84 +++++++++++++++++ .../java/org/example/game/TicTacToeGame.java | 89 +++---------------- 2 files changed, 98 insertions(+), 75 deletions(-) create mode 100644 src/main/java/org/example/game/Board.java diff --git a/src/main/java/org/example/game/Board.java b/src/main/java/org/example/game/Board.java new file mode 100644 index 0000000..50b3b9f --- /dev/null +++ b/src/main/java/org/example/game/Board.java @@ -0,0 +1,84 @@ +package org.example.game; + +import org.example.util.Message; + +import java.util.stream.IntStream; + +public class Board { + private static final int BOARD_SIZE = 9; + private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); + private static final String EMPTY_CELL = " "; + private final String[] gameBoard = new String[BOARD_SIZE]; + + public Board() { + initializeNumberedBoard(); + } + + private void initializeNumberedBoard() { + IntStream.range(0, BOARD_SIZE).forEach(i -> gameBoard[i] = String.valueOf(i + 1)); + } + + public void resetBoard() { + IntStream.range(0, BOARD_SIZE).forEach(i -> gameBoard[i] = EMPTY_CELL); + } + + public void printBoard() { + Message.printMessageToUser("", true); + for (int i = 0; i < BOARD_SIZE; i++) { + Message.printFormattedMessageToUser("%2s", gameBoard[i]); + if ((i + 1) % ROW_SIZE == 0) { + Message.printMessageToUser("", true); + if (i < BOARD_SIZE - 1) { + Message.printMessageToUser("----".repeat(ROW_SIZE), true); + } + } else { + Message.printMessageToUser(" | ", false); + } + } + Message.printMessageToUser("", true); + } + + public boolean checkWinner(char player) { + return checkRows(player) || checkColumns(player) || checkDiagonals(player); + } + + private boolean checkRows(char player) { + for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { + if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> gameBoard[j].equals(String.valueOf(player)))) { + return true; + } + } + return false; + } + + private boolean checkColumns(char player) { + for (int i = 0; i < ROW_SIZE; i++) { + int finalI = i; + if (IntStream.range(0, ROW_SIZE).allMatch(j -> gameBoard[finalI + j * ROW_SIZE].equals(String.valueOf(player)))) { + return true; + } + } + return false; + } + + private boolean checkDiagonals(char player) { + return IntStream.range(0, ROW_SIZE).allMatch(i -> gameBoard[i * (ROW_SIZE + 1)].equals(String.valueOf(player))) || + IntStream.range(1, ROW_SIZE + 1).allMatch(i -> gameBoard[i * (ROW_SIZE - 1)].equals(String.valueOf(player))); + } + + public boolean checkDraw() { + return IntStream.range(0, BOARD_SIZE).noneMatch(i -> gameBoard[i].equals(EMPTY_CELL)); + } + + public boolean isCellEmpty(int index) { + return gameBoard[index].equals(EMPTY_CELL); + } + + public void updateCell(int index, char player) { + gameBoard[index] = String.valueOf(player); + } + + public int getBoardSize() { + return BOARD_SIZE; + } +} diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index e24b0eb..45b3eee 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -3,27 +3,18 @@ import org.example.util.Message; import java.util.Scanner; -import java.util.stream.IntStream; public class TicTacToeGame { private static final char USER_MARK = 'X'; private static final char COMPUTER_MARK = 'O'; - private static final int BOARD_SIZE = 16; - private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); - private static final String EMPTY_CELL = " "; - private final String[] board = new String[BOARD_SIZE]; + private final Board board = new Board(); private final Scanner scanner = new Scanner(System.in); private final char[] players = {USER_MARK, COMPUTER_MARK}; public TicTacToeGame() { - initializeNumberedBoard(); - printBoard(); - resetBoard(); - } - - private void initializeNumberedBoard() { - IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = String.valueOf(i + 1)); + board.printBoard(); + board.resetBoard(); } public void play() { @@ -32,43 +23,23 @@ public void play() { while (true) { makeMove(players[currentPlayerIndex]); - if (checkWinner(players[currentPlayerIndex])) { - printBoard(); + if (board.checkWinner(players[currentPlayerIndex])) { + board.printBoard(); displayResult(players[currentPlayerIndex] == USER_MARK ? Result.USER_WON : Result.COMPUTER_WON); return; } - if (checkDraw()) { - printBoard(); + if (board.checkDraw()) { + board.printBoard(); displayResult(Result.DRAW); return; } - printBoard(); + board.printBoard(); currentPlayerIndex = (currentPlayerIndex + 1) % players.length; } } - private void resetBoard() { - IntStream.range(0, BOARD_SIZE).forEach(i -> board[i] = EMPTY_CELL); - } - - public void printBoard() { - Message.printMessageToUser("", true); - for (int i = 0; i < BOARD_SIZE; i++) { - Message.printFormattedMessageToUser("%2s", board[i]); - if ((i + 1) % ROW_SIZE == 0) { - Message.printMessageToUser("", true); - if (i < BOARD_SIZE - 1) { - Message.printMessageToUser("----".repeat(ROW_SIZE), true); - } - } else { - Message.printMessageToUser(" | ", false); - } - } - Message.printMessageToUser("", true); - } - private void displayResult(Result result) { String message = switch (result) { case USER_WON -> "You won the game!"; @@ -88,8 +59,8 @@ private void makeUserMove() { byte move; do { move = getUserMove(); - } while (board[move - 1].equals(String.valueOf(USER_MARK)) || board[move - 1].equals(String.valueOf(COMPUTER_MARK))); - board[move - 1] = String.valueOf(USER_MARK); + } while (!board.isCellEmpty(move - 1)); + board.updateCell(move - 1, USER_MARK); } private byte getUserMove() { @@ -97,7 +68,7 @@ private byte getUserMove() { while (true) { Message.printMessageToUser("Enter your move: ", true); input = scanner.nextByte(); - if (input >= 1 && input <= BOARD_SIZE) { + if (input >= 1 && input <= board.getBoardSize()) { return input; } Message.printMessageToUser("Invalid input. Enter again.", true); @@ -107,40 +78,8 @@ private byte getUserMove() { private void makeComputerMove() { byte move; do { - move = (byte) ((Math.random() * BOARD_SIZE) + 1); - } while (board[move - 1].equals(String.valueOf(USER_MARK)) || board[move - 1].equals(String.valueOf(COMPUTER_MARK))); - board[move - 1] = String.valueOf(COMPUTER_MARK); - } - - private boolean checkWinner(char player) { - return checkRows(player) || checkColumns(player) || checkDiagonals(player); - } - - private boolean checkRows(char player) { - for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { - if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> board[j].equals(String.valueOf(player)))) { - return true; - } - } - return false; - } - - private boolean checkColumns(char player) { - for (int i = 0; i < ROW_SIZE; i++) { - int finalI = i; - if (IntStream.range(0, ROW_SIZE).allMatch(j -> board[finalI + j * ROW_SIZE].equals(String.valueOf(player)))) { - return true; - } - } - return false; - } - - private boolean checkDiagonals(char player) { - return IntStream.range(0, ROW_SIZE).allMatch(i -> board[i * (ROW_SIZE + 1)].equals(String.valueOf(player))) || - IntStream.range(1, ROW_SIZE + 1).allMatch(i -> board[i * (ROW_SIZE - 1)].equals(String.valueOf(player))); - } - - private boolean checkDraw() { - return IntStream.range(0, BOARD_SIZE).noneMatch(i -> board[i].equals(EMPTY_CELL)); + move = (byte) ((Math.random() * board.getBoardSize()) + 1); + } while (!board.isCellEmpty(move - 1)); + board.updateCell(move - 1, COMPUTER_MARK); } } \ No newline at end of file From 34304f390855e63f3d1040f0697f826508d22f29 Mon Sep 17 00:00:00 2001 From: ruslana Date: Mon, 12 Aug 2024 14:21:20 +0200 Subject: [PATCH 17/26] add Player enum --- src/main/java/org/example/game/Player.java | 20 +++++++++++++++++++ .../java/org/example/game/TicTacToeGame.java | 20 ++++++++----------- 2 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/example/game/Player.java diff --git a/src/main/java/org/example/game/Player.java b/src/main/java/org/example/game/Player.java new file mode 100644 index 0000000..83f27cf --- /dev/null +++ b/src/main/java/org/example/game/Player.java @@ -0,0 +1,20 @@ +package org.example.game; + +public enum Player { + USER('X'), + COMPUTER('O'); + + private final char mark; + + Player(char mark) { + this.mark = mark; + } + + public char getMark(){ + return mark; + } + + public Player next(){ + return this == USER ? COMPUTER : USER; + } +} diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 45b3eee..e00d902 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -5,12 +5,8 @@ import java.util.Scanner; public class TicTacToeGame { - private static final char USER_MARK = 'X'; - private static final char COMPUTER_MARK = 'O'; - private final Board board = new Board(); private final Scanner scanner = new Scanner(System.in); - private final char[] players = {USER_MARK, COMPUTER_MARK}; public TicTacToeGame() { board.printBoard(); @@ -18,14 +14,14 @@ public TicTacToeGame() { } public void play() { - int currentPlayerIndex = 0; + Player currentPlayer = Player.USER; while (true) { - makeMove(players[currentPlayerIndex]); + makeMove(currentPlayer.getMark()); - if (board.checkWinner(players[currentPlayerIndex])) { + if (board.checkWinner(currentPlayer.getMark())) { board.printBoard(); - displayResult(players[currentPlayerIndex] == USER_MARK ? Result.USER_WON : Result.COMPUTER_WON); + displayResult(currentPlayer == Player.USER ? Result.USER_WON : Result.COMPUTER_WON); return; } @@ -36,7 +32,7 @@ public void play() { } board.printBoard(); - currentPlayerIndex = (currentPlayerIndex + 1) % players.length; + currentPlayer = currentPlayer.next(); } } @@ -50,7 +46,7 @@ private void displayResult(Result result) { } private void makeMove(char player) { - if (player == USER_MARK) { + if (player == Player.USER.getMark()) { makeUserMove(); } else makeComputerMove(); } @@ -60,7 +56,7 @@ private void makeUserMove() { do { move = getUserMove(); } while (!board.isCellEmpty(move - 1)); - board.updateCell(move - 1, USER_MARK); + board.updateCell(move - 1, Player.USER.getMark()); } private byte getUserMove() { @@ -80,6 +76,6 @@ private void makeComputerMove() { do { move = (byte) ((Math.random() * board.getBoardSize()) + 1); } while (!board.isCellEmpty(move - 1)); - board.updateCell(move - 1, COMPUTER_MARK); + board.updateCell(move - 1, Player.COMPUTER.getMark()); } } \ No newline at end of file From 8b7703dc181ef63945943753259966bed0f42a53 Mon Sep 17 00:00:00 2001 From: ruslana Date: Mon, 12 Aug 2024 20:19:49 +0200 Subject: [PATCH 18/26] add readme --- README.md | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..357d8a0 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Tic Tac Toe Game + +This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. +While working on this task, the educational project was analyzed with a focus on clean code principles. The SonarLint plugin was used to identify issues related to clean code standards, and the code was refactored accordingly. + +## Refactoring description + +#### **1. Renamed Variables** + +- In original code variables such as `box`, `i`, `rand`, `box` were named ambiguously. +- **Refactored Code:** Variables were renamed to more descriptive names like `gameBoard`, `currentPlayer`, `move`, etc. +#### **2. Extracted Methods** + +- Original code for checking the game winner, handling user input, and the game board printing logic were all embedded within the `main` method. +- **Refactored Code:** The logic for handling different functionalities has been extracted into separate methods (`makeMove`, `makeUserMove`, `makeComputerMove`, `checkWinner`, `printBoard`, etc.). +#### **3. Extracted Classes** + +- In original code the `App` class handled all responsibilities, including user interaction, game logic, and board management. +- **Refactored Code:** The responsibilities were distributed among multiple classes: `TicTacToeGame` for game flow, `Board` for board management, `Player` for player-related logic, `Result` for game outcomes, and `Message` for user interactions and printing text in the console. +1. The `App` class serves as the entry point for the application. It creates an instance of the TicTacToeGame class and initiates the game by calling the play method. +2. `TicTacToeGame` Class - his class manages the game loop, controls the flow of the game, and handles the interaction between the players and the board. It checks for game-winning conditions and handles the display of results. +3. The `Board` class encapsulates the game board's state, including methods for resetting the board, printing it, checking for a winner or a draw, and updating cell values. +4. `Player` Enum - this enum represents the two players in the game, USER and COMPUTER, and contains their respective marks ('X' and 'O'). Player enum encapsulates the user and computer players along with their respective marks (USER_MARK and COMPUTER_MARK). The `next()` method in the `Player` enum to switch between the user and computer. +5. `Result` Enum - this enum defines the possible outcomes of the game: USER_WON, COMPUTER_WON, and DRAW. +6. `Message` Utility Class - this utility class is responsible for printing messages to the user, including formatted text. It centralises all user interaction-related logic. +#### **4. Moved Method** + +- **Refactored Code:** As the `Board` class is responsible for managing the game board, including initializing, updating, and checking the state of the board. This class will encapsulate all board-related operations. Methods related to the board itself, such as `initializeNumberedBoard`, `resetBoard`, `printBoard`, and the various methods to check the board state (`checkRows`, `checkColumns`, `checkDiagonals`, `checkDraw`, etc.), were moved to the `Board` class where they conceptually belong. +- The `TicTacToeGame` class focuses on the game flow, such as taking turns, making moves, and determining the game result. So all the methods responsible for this logic will be in class `TicTacToeGame`: `play`, `displayResult`, `makeMove`, `makeUserMove`, `getUserMove`, `makeComputerMove`. `TicTacToeGame` class will use the `Board` class to interact with the board.` +#### **5. Removed Duplication** + +- The original code for checking if a player has won was duplicated for both players. +- **Refactored Code:** This logic was generalised and encapsulated within the `Board` class’s methods like `checkWinner`, `checkRows`, `checkColumns`, and `checkDiagonals`. +#### **6. Replaced Conditional with Polymorphism** + +- The original code used conditionals to check the winner, switch players, and handle moves. +- **Refactored Code:** Polymorphism was introduced through the `Player` enum and `Result` enum to represent the players and game outcomes, reducing the need for conditionals. Replacing conditionals with polymorphism simplifies the code structure, making it more extensible and easier to modify when new cases arise. + +#### **7. Consolidate Methods** + +- **Refactored Code:** Similar operations were consolidated into methods like `makeMove` which delegates to `makeUserMove` or `makeComputerMove` based on the player. Consolidating methods reduces code duplication and centralises similar operations, leading to a more maintainable codebase. + +## Description of the project +As result of refactoring was made a simple console-based implementation of the classic Tic Tac Toe game in Java. It allows a user to play against the computer, with the game alternating turns between the user and the computer until there is a winner or the game ends in a draw. + +## Structure + +The project is organised into several packages and classes, each responsible for different aspects of the game: + +- **`org.example`** + - **`App`**: The main entry point of the application. This class initiates the game by creating an instance of `TicTacToeGame` and calling its `play()` method. + +- **`org.example.game`** + - **`TicTacToeGame`**: Manages the game flow, including player turns, checking for a winner or a draw, and handling user input. + - **`Board`**: Represents the game board. It manages the state of the board, printing it to the console, and checking for winning or drawing conditions. + - **`Player`**: An enum representing the two players (USER and COMPUTER) and their respective marks (X and O). + - **`Result`**: An enum representing the possible outcomes of the game (USER_WON, COMPUTER_WON, DRAW). + +- **`org.example.util`** + - **`Message`**: A utility class for handling console output, providing methods for printing messages to the user. +## Gameplay + +- The game starts with an empty 3x3 board. At the beginning of the game user will see the board with numbers of cells which they can use if it's empty. +``` +1 | 2 | 3 +---------- +4 | 5 | 6 +---------- +7 | 8 | 9 +``` +- The user is always assigned the 'X' mark, while the computer uses the 'O' mark. +- The user makes the first move by entering a number between 1 and 9 corresponding to the position on the board. +- The computer then makes its move by randomly selecting an available spot. +- The game continues until either the user or the computer wins, or the board is full (draw). +- After the game ends, the result is displayed, and the program terminates. +## Customisation + +- **Board Size**: The current implementation uses a fixed 3x3 board (9 cells). To modify this, you would need to adjust the `BOARD_SIZE` value and associated logic in the `Board` class: +- **Computer AI**: The current computer opponent uses a simple random move strategy. This can be enhanced to a smarter AI by implementing algorithms like Minimax. \ No newline at end of file From 18136712c86aa492b46d9713482e3d28567e5098 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 16:15:21 +0200 Subject: [PATCH 19/26] edit readme --- README.md | 90 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 357d8a0..e96cc8a 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,81 @@ # Tic Tac Toe Game -This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. -While working on this task, the educational project was analyzed with a focus on clean code principles. The SonarLint plugin was used to identify issues related to clean code standards, and the code was refactored accordingly. +This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. While working on this task, the educational project was analyzed with a focus on clean code principles. The SonarLint plugin was used to identify issues related to clean code standards, and the code was subsequently refactored. -## Refactoring description +## Refactoring Description #### **1. Renamed Variables** -- In original code variables such as `box`, `i`, `rand`, `box` were named ambiguously. -- **Refactored Code:** Variables were renamed to more descriptive names like `gameBoard`, `currentPlayer`, `move`, etc. +- In the original code, variables such as `box`, `i` and `rand` were ambiguously named. +- **Refactored Code:** Variables were renamed to more descriptive names like `gameBoard`, `currentPlayer`, and `move`. + #### **2. Extracted Methods** -- Original code for checking the game winner, handling user input, and the game board printing logic were all embedded within the `main` method. -- **Refactored Code:** The logic for handling different functionalities has been extracted into separate methods (`makeMove`, `makeUserMove`, `makeComputerMove`, `checkWinner`, `printBoard`, etc.). +- In the original code, logic for checking the game winner, handling user input, and printing the game board was embedded within the `main` method. +- **Refactored Code:** The logic was extracted into separate methods (`makeMove`, `makeUserMove`, `makeComputerMove`, `checkWinner`, `printBoard`, etc.) for better organisation. + #### **3. Extracted Classes** -- In original code the `App` class handled all responsibilities, including user interaction, game logic, and board management. -- **Refactored Code:** The responsibilities were distributed among multiple classes: `TicTacToeGame` for game flow, `Board` for board management, `Player` for player-related logic, `Result` for game outcomes, and `Message` for user interactions and printing text in the console. -1. The `App` class serves as the entry point for the application. It creates an instance of the TicTacToeGame class and initiates the game by calling the play method. -2. `TicTacToeGame` Class - his class manages the game loop, controls the flow of the game, and handles the interaction between the players and the board. It checks for game-winning conditions and handles the display of results. -3. The `Board` class encapsulates the game board's state, including methods for resetting the board, printing it, checking for a winner or a draw, and updating cell values. -4. `Player` Enum - this enum represents the two players in the game, USER and COMPUTER, and contains their respective marks ('X' and 'O'). Player enum encapsulates the user and computer players along with their respective marks (USER_MARK and COMPUTER_MARK). The `next()` method in the `Player` enum to switch between the user and computer. -5. `Result` Enum - this enum defines the possible outcomes of the game: USER_WON, COMPUTER_WON, and DRAW. -6. `Message` Utility Class - this utility class is responsible for printing messages to the user, including formatted text. It centralises all user interaction-related logic. -#### **4. Moved Method** - -- **Refactored Code:** As the `Board` class is responsible for managing the game board, including initializing, updating, and checking the state of the board. This class will encapsulate all board-related operations. Methods related to the board itself, such as `initializeNumberedBoard`, `resetBoard`, `printBoard`, and the various methods to check the board state (`checkRows`, `checkColumns`, `checkDiagonals`, `checkDraw`, etc.), were moved to the `Board` class where they conceptually belong. -- The `TicTacToeGame` class focuses on the game flow, such as taking turns, making moves, and determining the game result. So all the methods responsible for this logic will be in class `TicTacToeGame`: `play`, `displayResult`, `makeMove`, `makeUserMove`, `getUserMove`, `makeComputerMove`. `TicTacToeGame` class will use the `Board` class to interact with the board.` +- In the original code, the `App` class was responsible for all tasks, including user interaction, game logic, and board management. +- **Refactored Code:** Responsibilities were distributed among multiple classes: + + - `TicTacToeGame` manages the game flow. + - `Board` handles board management. + - `Player` manages player-related logic. + - `Result` deals with game outcomes. + - `Message` handles user interactions and console output. + + Specifics: + 1. The `App` class serves as the entry point, creating an instance of `TicTacToeGame` and starting the game by calling the `play` method. + 2. The `TicTacToeGame` class manages the game loop, controls the flow, and handles player and board interactions, including checking win conditions and displaying results. + 3. The `Board` class encapsulates the game board's state and includes methods for resetting the board, printing it, checking for a winner or draw, and updating cell values. + 4. The `Player` enum represents the two players, USER and COMPUTER, and contains their respective marks ('X' and 'O'). It also includes a `next()` method for switching between players. + 5. The `Result` enum defines possible game outcomes: USER_WON, COMPUTER_WON, and DRAW. + 6. The `Message` utility class centralises all user interaction logic, handling console output and formatted text. + +#### **4. Moved Methods** + +- **Refactored Code:** Methods related to board management (e.g., `initializeNumberedBoard`, `resetBoard`, `printBoard`, `checkRows`, `checkColumns`, `checkDiagonals`, `checkDraw`, etc.) were moved to the `Board` class, where they logically belong. This class now encapsulates all board-related operations. +- The `TicTacToeGame` class now focuses on the game flow, including taking turns, making moves, and determining the game outcome. It uses the `Board` class to interact with the board. + #### **5. Removed Duplication** -- The original code for checking if a player has won was duplicated for both players. -- **Refactored Code:** This logic was generalised and encapsulated within the `Board` class’s methods like `checkWinner`, `checkRows`, `checkColumns`, and `checkDiagonals`. -#### **6. Replaced Conditional with Polymorphism** +- The original code had duplicated logic for checking if a player had won for both players. +- **Refactored Code:** This logic was generalised and encapsulated within methods like `checkWinner`, `checkRows`, `checkColumns`, and `checkDiagonals` in the `Board` class. + +#### **6. Replaced Conditionals with Polymorphism** + +- The original code relied on conditionals to check the winner, switch players, and handle moves. +- **Refactored Code:** Polymorphism was introduced through the `Player` and `Result` enums to represent players and game outcomes, reducing the need for conditionals. This change simplifies the code structure, making it more extensible and easier to modify. -- The original code used conditionals to check the winner, switch players, and handle moves. -- **Refactored Code:** Polymorphism was introduced through the `Player` enum and `Result` enum to represent the players and game outcomes, reducing the need for conditionals. Replacing conditionals with polymorphism simplifies the code structure, making it more extensible and easier to modify when new cases arise. +#### **7. Consolidated Methods** -#### **7. Consolidate Methods** +- **Refactored Code:** Similar operations were consolidated into methods like `makeMove`, which delegates tasks to `makeUserMove` or `makeComputerMove` based on the player. This consolidation reduces code duplication and centralises similar operations, leading to a more maintainable codebase. -- **Refactored Code:** Similar operations were consolidated into methods like `makeMove` which delegates to `makeUserMove` or `makeComputerMove` based on the player. Consolidating methods reduces code duplication and centralises similar operations, leading to a more maintainable codebase. +## Project Description -## Description of the project -As result of refactoring was made a simple console-based implementation of the classic Tic Tac Toe game in Java. It allows a user to play against the computer, with the game alternating turns between the user and the computer until there is a winner or the game ends in a draw. +The result of the refactoring is a simple, console-based implementation of the classic Tic Tac Toe game in Java. It allows a user to play against the computer, with the game alternating turns between the user and the computer until there is a winner or the game ends in a draw. ## Structure The project is organised into several packages and classes, each responsible for different aspects of the game: - **`org.example`** - - **`App`**: The main entry point of the application. This class initiates the game by creating an instance of `TicTacToeGame` and calling its `play()` method. + - **`App`**: The main entry point of the application. This class initiates the game by creating an instance of `TicTacToeGame` and calling its `play()` method. - **`org.example.game`** - - **`TicTacToeGame`**: Manages the game flow, including player turns, checking for a winner or a draw, and handling user input. - - **`Board`**: Represents the game board. It manages the state of the board, printing it to the console, and checking for winning or drawing conditions. - - **`Player`**: An enum representing the two players (USER and COMPUTER) and their respective marks (X and O). - - **`Result`**: An enum representing the possible outcomes of the game (USER_WON, COMPUTER_WON, DRAW). + - **`TicTacToeGame`**: Manages the game flow, including player turns, checking for a winner or a draw, and handling user input. + - **`Board`**: Represents the game board, manages its state, prints it to the console, and checks for winning or drawing conditions. + - **`Player`**: An enum representing the two players (USER and COMPUTER) and their respective marks (X and O). + - **`Result`**: An enum representing possible game outcomes (USER_WON, COMPUTER_WON, DRAW). - **`org.example.util`** - - **`Message`**: A utility class for handling console output, providing methods for printing messages to the user. + + - **`Message`**: A utility class for handling console output, providing methods for printing messages to the user. + ## Gameplay -- The game starts with an empty 3x3 board. At the beginning of the game user will see the board with numbers of cells which they can use if it's empty. +- The game starts with an empty 3x3 board. At the beginning of the game, the board displays the numbers of cells that the user can select if they are empty. ``` 1 | 2 | 3 ---------- @@ -73,7 +88,8 @@ The project is organised into several packages and classes, each responsible for - The computer then makes its move by randomly selecting an available spot. - The game continues until either the user or the computer wins, or the board is full (draw). - After the game ends, the result is displayed, and the program terminates. + ## Customisation -- **Board Size**: The current implementation uses a fixed 3x3 board (9 cells). To modify this, you would need to adjust the `BOARD_SIZE` value and associated logic in the `Board` class: -- **Computer AI**: The current computer opponent uses a simple random move strategy. This can be enhanced to a smarter AI by implementing algorithms like Minimax. \ No newline at end of file +- **Board Size**: The current implementation uses a fixed 3x3 board (9 cells). To modify this, you would need to adjust the `BOARD_SIZE` value and associated logic in the `Board` class. +- **Computer AI**: The current computer opponent uses a simple random move strategy. This can be enhanced by implementing more advanced algorithms like Minimax. \ No newline at end of file From ff3b2bff050fa97404c39db2de244bd87890ba6e Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 17:20:47 +0200 Subject: [PATCH 20/26] board size passed in the constructor --- src/main/java/org/example/App.java | 2 +- src/main/java/org/example/game/Board.java | 39 ++++++++++--------- .../java/org/example/game/TicTacToeGame.java | 7 ++-- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 844168e..563f709 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -5,7 +5,7 @@ public class App { public static void main(String[] args) { - TicTacToeGame game = new TicTacToeGame(); + TicTacToeGame game = new TicTacToeGame(9); game.play(); } } \ No newline at end of file diff --git a/src/main/java/org/example/game/Board.java b/src/main/java/org/example/game/Board.java index 50b3b9f..3507b80 100644 --- a/src/main/java/org/example/game/Board.java +++ b/src/main/java/org/example/game/Board.java @@ -5,31 +5,34 @@ import java.util.stream.IntStream; public class Board { - private static final int BOARD_SIZE = 9; - private static final int ROW_SIZE = (int) Math.sqrt(BOARD_SIZE); private static final String EMPTY_CELL = " "; - private final String[] gameBoard = new String[BOARD_SIZE]; + private final int boardSize; + private final int rowSize; + private final String[] gameBoard; - public Board() { + public Board(int boardSize) { + this.boardSize = boardSize; + this.rowSize = (int) Math.sqrt(boardSize); + this.gameBoard = new String[boardSize]; initializeNumberedBoard(); } private void initializeNumberedBoard() { - IntStream.range(0, BOARD_SIZE).forEach(i -> gameBoard[i] = String.valueOf(i + 1)); + IntStream.range(0, boardSize).forEach(i -> gameBoard[i] = String.valueOf(i + 1)); } public void resetBoard() { - IntStream.range(0, BOARD_SIZE).forEach(i -> gameBoard[i] = EMPTY_CELL); + IntStream.range(0, boardSize).forEach(i -> gameBoard[i] = EMPTY_CELL); } public void printBoard() { Message.printMessageToUser("", true); - for (int i = 0; i < BOARD_SIZE; i++) { + for (int i = 0; i < boardSize; i++) { Message.printFormattedMessageToUser("%2s", gameBoard[i]); - if ((i + 1) % ROW_SIZE == 0) { + if ((i + 1) % rowSize == 0) { Message.printMessageToUser("", true); - if (i < BOARD_SIZE - 1) { - Message.printMessageToUser("----".repeat(ROW_SIZE), true); + if (i < boardSize - 1) { + Message.printMessageToUser("----".repeat(rowSize), true); } } else { Message.printMessageToUser(" | ", false); @@ -43,8 +46,8 @@ public boolean checkWinner(char player) { } private boolean checkRows(char player) { - for (int i = 0; i < BOARD_SIZE; i += ROW_SIZE) { - if (IntStream.range(i, i + ROW_SIZE).allMatch(j -> gameBoard[j].equals(String.valueOf(player)))) { + for (int i = 0; i < boardSize; i += rowSize) { + if (IntStream.range(i, i + rowSize).allMatch(j -> gameBoard[j].equals(String.valueOf(player)))) { return true; } } @@ -52,9 +55,9 @@ private boolean checkRows(char player) { } private boolean checkColumns(char player) { - for (int i = 0; i < ROW_SIZE; i++) { + for (int i = 0; i < rowSize; i++) { int finalI = i; - if (IntStream.range(0, ROW_SIZE).allMatch(j -> gameBoard[finalI + j * ROW_SIZE].equals(String.valueOf(player)))) { + if (IntStream.range(0, rowSize).allMatch(j -> gameBoard[finalI + j * rowSize].equals(String.valueOf(player)))) { return true; } } @@ -62,12 +65,12 @@ private boolean checkColumns(char player) { } private boolean checkDiagonals(char player) { - return IntStream.range(0, ROW_SIZE).allMatch(i -> gameBoard[i * (ROW_SIZE + 1)].equals(String.valueOf(player))) || - IntStream.range(1, ROW_SIZE + 1).allMatch(i -> gameBoard[i * (ROW_SIZE - 1)].equals(String.valueOf(player))); + return IntStream.range(0, rowSize).allMatch(i -> gameBoard[i * (rowSize + 1)].equals(String.valueOf(player))) || + IntStream.range(1, rowSize + 1).allMatch(i -> gameBoard[i * (rowSize - 1)].equals(String.valueOf(player))); } public boolean checkDraw() { - return IntStream.range(0, BOARD_SIZE).noneMatch(i -> gameBoard[i].equals(EMPTY_CELL)); + return IntStream.range(0, boardSize).noneMatch(i -> gameBoard[i].equals(EMPTY_CELL)); } public boolean isCellEmpty(int index) { @@ -79,6 +82,6 @@ public void updateCell(int index, char player) { } public int getBoardSize() { - return BOARD_SIZE; + return boardSize; } } diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index e00d902..deebaad 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -5,10 +5,11 @@ import java.util.Scanner; public class TicTacToeGame { - private final Board board = new Board(); + private final Board board; private final Scanner scanner = new Scanner(System.in); - public TicTacToeGame() { + public TicTacToeGame(int boardSize) { + this.board = new Board(boardSize); board.printBoard(); board.resetBoard(); } @@ -64,7 +65,7 @@ private byte getUserMove() { while (true) { Message.printMessageToUser("Enter your move: ", true); input = scanner.nextByte(); - if (input >= 1 && input <= board.getBoardSize()) { + if (input >= 1 && input < (board.getBoardSize() + 1)) { return input; } Message.printMessageToUser("Invalid input. Enter again.", true); From a38f1950ce79af2624d4a5cb55969ac2d32c1931 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 17:34:41 +0200 Subject: [PATCH 21/26] exception for invalid input --- .../java/org/example/game/TicTacToeGame.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index deebaad..c043268 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -61,14 +61,19 @@ private void makeUserMove() { } private byte getUserMove() { - byte input; while (true) { - Message.printMessageToUser("Enter your move: ", true); - input = scanner.nextByte(); - if (input >= 1 && input < (board.getBoardSize() + 1)) { - return input; + try { + Message.printMessageToUser("Enter your move: ", true); + String inputString = scanner.next(); + int input = Integer.parseInt(inputString); + if (input >= 1 && input < (board.getBoardSize() + 1)) { + return (byte) input; + } else { + Message.printMessageToUser("Invalid input. Enter again.", true); + } + } catch (NumberFormatException e) { + Message.printMessageToUser("Invalid input. Enter again.", true); } - Message.printMessageToUser("Invalid input. Enter again.", true); } } From c3548867fb39969b741af0b42241a8fe617df554 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 17:45:41 +0200 Subject: [PATCH 22/26] use random class --- src/main/java/org/example/game/TicTacToeGame.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index c043268..8b55af0 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -2,11 +2,13 @@ import org.example.util.Message; +import java.util.Random; import java.util.Scanner; public class TicTacToeGame { private final Board board; private final Scanner scanner = new Scanner(System.in); + private final Random random = new Random(); public TicTacToeGame(int boardSize) { this.board = new Board(boardSize); @@ -80,7 +82,7 @@ private byte getUserMove() { private void makeComputerMove() { byte move; do { - move = (byte) ((Math.random() * board.getBoardSize()) + 1); + move = (byte) (random.nextInt(board.getBoardSize()) + 1); } while (!board.isCellEmpty(move - 1)); board.updateCell(move - 1, Player.COMPUTER.getMark()); } From 27b6c92b6727f0057c019db417ed84ade15fd301 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 17:58:15 +0200 Subject: [PATCH 23/26] move result message to enum --- src/main/java/org/example/game/Result.java | 16 +++++++++++++--- .../java/org/example/game/TicTacToeGame.java | 7 +------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/example/game/Result.java b/src/main/java/org/example/game/Result.java index 8e37945..16ec2e2 100644 --- a/src/main/java/org/example/game/Result.java +++ b/src/main/java/org/example/game/Result.java @@ -1,7 +1,17 @@ package org.example.game; public enum Result { - USER_WON, - COMPUTER_WON, - DRAW + USER_WON("You won the game!"), + COMPUTER_WON("You lost the game!"), + DRAW("It's a draw!"); + + private final String message; + + Result(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } } diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 8b55af0..3b4128a 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -40,12 +40,7 @@ public void play() { } private void displayResult(Result result) { - String message = switch (result) { - case USER_WON -> "You won the game!"; - case COMPUTER_WON -> "You lost the game!"; - case DRAW -> "It's a draw!"; - }; - Message.printMessageToUser(message + "\nCreated by Shreyas Saha. Thanks for playing!", true); + Message.printMessageToUser(result.getMessage() + "\nCreated by Shreyas Saha. Thanks for playing!", true); } private void makeMove(char player) { From 513d2b029ae010f427fd5bdf0da4958fa818aa6f Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 18:06:36 +0200 Subject: [PATCH 24/26] close scanner --- src/main/java/org/example/App.java | 8 ++++++-- src/main/java/org/example/game/TicTacToeGame.java | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 563f709..8bc660a 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -2,10 +2,14 @@ import org.example.game.TicTacToeGame; +import java.util.Scanner; + public class App { public static void main(String[] args) { - TicTacToeGame game = new TicTacToeGame(9); - game.play(); + try (Scanner scanner = new Scanner(System.in)) { + TicTacToeGame game = new TicTacToeGame(9, scanner); + game.play(); + } } } \ No newline at end of file diff --git a/src/main/java/org/example/game/TicTacToeGame.java b/src/main/java/org/example/game/TicTacToeGame.java index 3b4128a..3efc1c8 100644 --- a/src/main/java/org/example/game/TicTacToeGame.java +++ b/src/main/java/org/example/game/TicTacToeGame.java @@ -7,11 +7,12 @@ public class TicTacToeGame { private final Board board; - private final Scanner scanner = new Scanner(System.in); + private final Scanner scanner; private final Random random = new Random(); - public TicTacToeGame(int boardSize) { + public TicTacToeGame(int boardSize, Scanner scanner) { this.board = new Board(boardSize); + this.scanner = scanner; board.printBoard(); board.resetBoard(); } From 143d460fd8e0901866f43c16c606754f9d5ac156 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 18:22:14 +0200 Subject: [PATCH 25/26] edit readme --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e96cc8a..71bcbb0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Tic Tac Toe Game -This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. While working on this task, the educational project was analyzed with a focus on clean code principles. The SonarLint plugin was used to identify issues related to clean code standards, and the code was subsequently refactored. +This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. While working on this task, the educational project (https://github.com/goitProjects/java-dev-homework-module-14-code-quality) was analyzed with a focus on clean code principles. The SonarLint plugin was used to identify issues related to clean code standards, and the code was subsequently refactored. ## Refactoring Description @@ -52,6 +52,14 @@ This is the solution for the GoIT Academy Module 2.2 [Code Quality] homework. Wh - **Refactored Code:** Similar operations were consolidated into methods like `makeMove`, which delegates tasks to `makeUserMove` or `makeComputerMove` based on the player. This consolidation reduces code duplication and centralises similar operations, leading to a more maintainable codebase. +#### **8. Scanner Resource Management** + +- **Refactored Code:** The `Scanner` instance used for user input was properly closed by utilizing a try-with-resources block in the `App` class. This ensures that the `Scanner` resource is automatically closed after its use, preventing potential resource leaks. + +#### **9. Exception Handling for Invalid Input** + +- **Refactored Code:** The `getUserMove` method in the `TicTacToeGame` class was refactored to handle exceptions related to invalid user input. Specifically, a `try-catch` block was added to catch `NumberFormatException` when the user enters non-numeric input. The user is prompted to enter a valid move until a correct input is provided. + ## Project Description The result of the refactoring is a simple, console-based implementation of the classic Tic Tac Toe game in Java. It allows a user to play against the computer, with the game alternating turns between the user and the computer until there is a winner or the game ends in a draw. From fafc0a80b7688a1b12a1c105448434e7e3557f58 Mon Sep 17 00:00:00 2001 From: ruslana Date: Tue, 13 Aug 2024 18:31:37 +0200 Subject: [PATCH 26/26] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71bcbb0..fb3167f 100644 --- a/README.md +++ b/README.md @@ -99,5 +99,5 @@ The project is organised into several packages and classes, each responsible for ## Customisation -- **Board Size**: The current implementation uses a fixed 3x3 board (9 cells). To modify this, you would need to adjust the `BOARD_SIZE` value and associated logic in the `Board` class. +- **Board Size**: The current implementation uses a fixed 3x3 board (9 cells). To change this, adjust the `boardSize` value passed to the `TicTacToeGame` constructor in the `App` class, along with the associated logic in the `Board` class. - **Computer AI**: The current computer opponent uses a simple random move strategy. This can be enhanced by implementing more advanced algorithms like Minimax. \ No newline at end of file