From b0d582f09d212b93729ad3194deb10ee2ebe075a Mon Sep 17 00:00:00 2001 From: Sufyan Date: Sat, 22 Feb 2025 20:24:22 +0530 Subject: [PATCH] SUF-7 Engineer1: Adhering to Open Close Principle + Making Tests Reliable --- README.md | 20 ++++++++++++ api/AIEngine.java | 67 ++++++++++++++++++++++++++++++++++---- api/GameEngine.java | 2 +- api/RuleEngine.java | 18 +++++----- boards/TicTacToeBoard.java | 32 ++++++++++++++++-- game/Board.java | 3 +- game/Player.java | 3 ++ test/GamePlayTest.java | 49 +++++++++++++++------------- 8 files changed, 152 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index d48ba2c..09e3a9c 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,23 @@ Game Engine => Responsibility => Any changes in the board, we come here (Like di Rule Engine => Responsibility => Make sure that rules are followed in the game AI Player => Responsibility => Suggestion Moves + +Step 5: I got into the problem of testing the code again and again using manual testing. There-fore decided to write the unit test cases to validate my changes. + +While doing Step 5, I learned these things: +1. Unit Test cases are important for quick validation of logic, in fact I found 2 bugs while doing unit testing +2. Unit Test should be readable and extensible so that we don't need to rewrite our tests again + +Step 6: Open CLose Principle : Our system should be open to extension but close to modification + +What we are doing in this: We want to have optimised suggestMove function but +instead of changing in the Main file, we are extending the suggestMove function to incorporate smart and simple move + +While doing Step 6, I learned these things: + +1. We should not write such code which forces us to make changes in the callers or main functions which are already tested. +2. While modifying anything , we create a wrapper over already tested code instead of modifying it. +3. Smartly think of handling concurrent request without causing deadlock (Ex here is if we wanted to undo the suggested move , then high possibility is that parallel request can create deadlocks) + + +Step 7: Making tests extensible, we now don't rely on AiEngine to suggest move, as our main focus is to test the moves and rules. \ No newline at end of file diff --git a/api/AIEngine.java b/api/AIEngine.java index 5330450..3490ec4 100644 --- a/api/AIEngine.java +++ b/api/AIEngine.java @@ -5,19 +5,74 @@ public class AIEngine { - public Move suggestMove(Player player, Board board){ - if(board instanceof TicTacToeBoard board1){ - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - if (board1.getCell(i,j)==null){ - return new Move(player,new Cell(i,j)); + public Move getBasicMove(Player player, TicTacToeBoard board){ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board.getSymbol(i,j)==null){ + return new Move(player,new Cell(i,j)); + } + } + } + return null; + } + + public Move getSmartMove(Player player, TicTacToeBoard board){ + RuleEngine ruleEngine = new RuleEngine(); + // Victorious Move = We found a move where Computer Wins + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board.getSymbol(i,j)==null){ + Move move = new Move(player, new Cell(i,j)); + Board boardCopy = board.copy(); + boardCopy.move(move); + if(ruleEngine.getState(boardCopy).isOver()){ + return move; } } } + } + + // Defensive Move = We found a move where Player Wins and we replace it with computer move + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board.getSymbol(i,j)==null){ + Move move = new Move(player.flip(), new Cell(i,j)); + + Board boardCopy = board.copy(); + boardCopy.move(move); + if(ruleEngine.getState(boardCopy).isOver()){ + return new Move(player, new Cell(i,j)); + } + } + } + } + return getBasicMove(player, board); + } + + public Move suggestMove(Player player, Board board){ + if(board instanceof TicTacToeBoard board1){ + Move suggestedMove = null; + if(hasLessMove(board1,2)){ + suggestedMove = getBasicMove(player,board1); + } else { + suggestedMove = getSmartMove(player,board1); + } + if(suggestedMove != null) return suggestedMove; throw new IllegalStateException(); } else{ throw new IllegalArgumentException(); } } + public boolean hasLessMove(TicTacToeBoard board, int threshHold) { + int count = 0; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board.getSymbol(i, j) != null) { + count++; + } + } + } + return count