From 9dc7ff30d23e2ca00e0522d03342a33995f85eec Mon Sep 17 00:00:00 2001 From: Sufyan Date: Sun, 23 Feb 2025 15:24:30 +0530 Subject: [PATCH 1/3] SUF-9 Engineer1: DRY Principle : Code Refactoring --- api/RuleEngine.java | 90 +++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/api/RuleEngine.java b/api/RuleEngine.java index f5757c2..b7594ae 100644 --- a/api/RuleEngine.java +++ b/api/RuleEngine.java @@ -4,69 +4,40 @@ import game.Board; import game.GameState; +import java.util.function.BiFunction; + public class RuleEngine { public GameState getState(Board board){ if(board instanceof TicTacToeBoard board1) { - boolean rowComplete = true; - for (int i = 0; i < 3; i++) { - String firstCharacter = board1.getSymbol(i,0); - rowComplete = firstCharacter!=null; - if(firstCharacter!=null){ - for (int j = 1; j < 3; j++) { - if (!firstCharacter.equals(board1.getSymbol(i,j))) { - rowComplete = false; - break; - } - } - } - if (rowComplete) { - return new GameState(true, firstCharacter); - } - } - boolean colComplete = true; - for (int i = 0; i < 3; i++) { - String firstCharacter = board1.getSymbol(0,i); - colComplete = firstCharacter!=null; - if(firstCharacter!=null){ - for (int j = 1; j < 3; j++) { - if (!firstCharacter.equals(board1.getSymbol(j,i))) { - colComplete = false; - break; - } - } - } - if (colComplete) { - return new GameState(true, firstCharacter); - } - } + BiFunction getCell = board1::getSymbol; + BiFunction getCol = (i,j)->board1.getSymbol(j,i); - String firstCharacter = board1.getSymbol(0,0); - boolean diagonalComplete = firstCharacter!=null; - if(firstCharacter!=null){ - for (int i = 1; i < 3; i++) { - if (!firstCharacter.equals(board1.getSymbol(i,i))) { - diagonalComplete = false; - break; - } + GameState rowWin = isVictorious(getCell); + if(rowWin!=null) return rowWin; + + GameState colWin = isVictorious(getCol); + if(colWin!=null) return colWin; + + boolean diagonalComplete = true; + for (int i = 0; i < 3; i++) { + if (getCell.apply(i,i)== null || !getCell.apply(0,0).equals(getCell.apply(i,i))) { + diagonalComplete = false; + break; } } - if (diagonalComplete) { - return new GameState(true, firstCharacter); + return new GameState(true, getCell.apply(0,0)); } - firstCharacter = board1.getSymbol(0,2); - boolean reverseDiagonalComplete = firstCharacter!=null; - if(firstCharacter!=null){ - for (int i = 1; i < 3; i++) { - if (!firstCharacter.equals(board1.getSymbol(i ,3 - i - 1))) { - reverseDiagonalComplete = false; - break; - } + + boolean reverseDiagonalComplete =true; + for (int i = 0; i < 3; i++) { + if (getCell.apply(i,3 - i - 1)== null || !getCell.apply(0,2).equals(getCell.apply(i ,3 - i - 1))) { + reverseDiagonalComplete = false; + break; } } - if (reverseDiagonalComplete) { - return new GameState(true, firstCharacter); + return new GameState(true, getCell.apply(0,2)); } int count=0; @@ -87,4 +58,19 @@ public GameState getState(Board board){ return new GameState(true, "-"); } } + public GameState isVictorious(BiFunction next){ + for (int i = 0; i < 3; i++) { + boolean possibleStreak = true; + for (int j = 0; j < 3; j++) { + if (next.apply(i,j)==null || !next.apply(i,0).equals(next.apply(i,j))) { + possibleStreak = false; + break; + } + } + if (possibleStreak) { + return new GameState(true, next.apply(i,0)); + } + } + return null; + } } From bd93988cfd8a09b7892dd45438d51ed68b95195b Mon Sep 17 00:00:00 2001 From: Sufyan Date: Sun, 23 Feb 2025 15:53:42 +0530 Subject: [PATCH 2/3] SUF-9 Engineer1: DRY Principle : Code Refactoring : Diagonals : Part 2 --- README.md | 6 +++++- api/RuleEngine.java | 51 ++++++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 09e3a9c..f264a54 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,8 @@ While doing Step 6, I learned these things: 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 +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. + +Step 8: Using Prototype Design Pattern: It is used when we wanted to deep or shallow clone the object. It is beneficial when new Object creation is expensive + +Step 9: We used Lambda functions to adhere to the DRY principle. diff --git a/api/RuleEngine.java b/api/RuleEngine.java index b7594ae..da95d24 100644 --- a/api/RuleEngine.java +++ b/api/RuleEngine.java @@ -5,40 +5,28 @@ import game.GameState; import java.util.function.BiFunction; +import java.util.function.Function; public class RuleEngine { public GameState getState(Board board){ if(board instanceof TicTacToeBoard board1) { - BiFunction getCell = board1::getSymbol; + + BiFunction getRow = board1::getSymbol; BiFunction getCol = (i,j)->board1.getSymbol(j,i); + Function getDiagonal = (i)->board1.getSymbol(i,i); + Function getRevDiagonal = (i)->board1.getSymbol(i,3-i-1); - GameState rowWin = isVictorious(getCell); + GameState rowWin = findStreak(getRow); if(rowWin!=null) return rowWin; - GameState colWin = isVictorious(getCol); + GameState colWin = findStreak(getCol); if(colWin!=null) return colWin; - boolean diagonalComplete = true; - for (int i = 0; i < 3; i++) { - if (getCell.apply(i,i)== null || !getCell.apply(0,0).equals(getCell.apply(i,i))) { - diagonalComplete = false; - break; - } - } - if (diagonalComplete) { - return new GameState(true, getCell.apply(0,0)); - } + GameState diagonalWin = findDiagonalStreak(getDiagonal); + if(diagonalWin!=null) return diagonalWin; - boolean reverseDiagonalComplete =true; - for (int i = 0; i < 3; i++) { - if (getCell.apply(i,3 - i - 1)== null || !getCell.apply(0,2).equals(getCell.apply(i ,3 - i - 1))) { - reverseDiagonalComplete = false; - break; - } - } - if (reverseDiagonalComplete) { - return new GameState(true, getCell.apply(0,2)); - } + GameState reverseDiagonalWin = findDiagonalStreak(getRevDiagonal); + if(reverseDiagonalWin!=null) return reverseDiagonalWin; int count=0; for (int i = 0; i < 3; i++) { @@ -58,7 +46,8 @@ public GameState getState(Board board){ return new GameState(true, "-"); } } - public GameState isVictorious(BiFunction next){ + + public GameState findStreak(BiFunction next){ for (int i = 0; i < 3; i++) { boolean possibleStreak = true; for (int j = 0; j < 3; j++) { @@ -73,4 +62,18 @@ public GameState isVictorious(BiFunction next){ } return null; } + + public GameState findDiagonalStreak(Function next){ + boolean possibleStreak = true; + for (int i = 0; i < 3; i++) { + if (next.apply(i)== null || !next.apply(0).equals(next.apply(i))) { + possibleStreak = false; + break; + } + } + if (possibleStreak) { + return new GameState(true, next.apply(0)); + } + return null; + } } From f8cae41518b04048151247526b4dce4b8f50dbe8 Mon Sep 17 00:00:00 2001 From: Sufyan Date: Sun, 23 Feb 2025 16:03:32 +0530 Subject: [PATCH 3/3] SUF-9 Engineer1: DRY Principle : Code Refactoring : Part 3 --- api/RuleEngine.java | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/api/RuleEngine.java b/api/RuleEngine.java index da95d24..3f171a7 100644 --- a/api/RuleEngine.java +++ b/api/RuleEngine.java @@ -16,17 +16,17 @@ public GameState getState(Board board){ Function getDiagonal = (i)->board1.getSymbol(i,i); Function getRevDiagonal = (i)->board1.getSymbol(i,3-i-1); - GameState rowWin = findStreak(getRow); - if(rowWin!=null) return rowWin; + GameState rowWin = outerTraversal(getRow); + if(rowWin.isOver()) return rowWin; - GameState colWin = findStreak(getCol); - if(colWin!=null) return colWin; + GameState colWin = outerTraversal(getCol); + if(colWin.isOver()) return colWin; - GameState diagonalWin = findDiagonalStreak(getDiagonal); - if(diagonalWin!=null) return diagonalWin; + GameState diagonalWin = traverse(getDiagonal); + if(diagonalWin.isOver()) return diagonalWin; - GameState reverseDiagonalWin = findDiagonalStreak(getRevDiagonal); - if(reverseDiagonalWin!=null) return reverseDiagonalWin; + GameState reverseDiagonalWin = traverse(getRevDiagonal); + if(reverseDiagonalWin.isOver()) return reverseDiagonalWin; int count=0; for (int i = 0; i < 3; i++) { @@ -47,23 +47,22 @@ public GameState getState(Board board){ } } - public GameState findStreak(BiFunction next){ + public GameState outerTraversal(BiFunction next){ + GameState result = new GameState(false, "-"); for (int i = 0; i < 3; i++) { - boolean possibleStreak = true; - for (int j = 0; j < 3; j++) { - if (next.apply(i,j)==null || !next.apply(i,0).equals(next.apply(i,j))) { - possibleStreak = false; - break; - } - } - if (possibleStreak) { - return new GameState(true, next.apply(i,0)); + int finalI = i; + Function nextValue = (j) -> next.apply(finalI,j); + GameState traversal = traverse(nextValue); + if(traversal.isOver()) { + result = traversal; + break; } } - return null; + return result; } - public GameState findDiagonalStreak(Function next){ + public GameState traverse(Function next){ + GameState result = new GameState(false, "-"); boolean possibleStreak = true; for (int i = 0; i < 3; i++) { if (next.apply(i)== null || !next.apply(0).equals(next.apply(i))) { @@ -72,8 +71,8 @@ public GameState findDiagonalStreak(Function next){ } } if (possibleStreak) { - return new GameState(true, next.apply(0)); + result = new GameState(true, next.apply(0)); } - return null; + return result; } }