From 2f83bec2317351b8987f478922de74f92f539fb5 Mon Sep 17 00:00:00 2001 From: Parth Karia Date: Wed, 5 Jul 2017 13:24:46 +0530 Subject: [PATCH] BAEL-1004 (#2206) * Dependency Injection examples Dependency Injection examples for evaluation article * Junit test cases added for dependency injection Junit test cases added for dependency injection * ClassNotFoundException vs NoClassDefFoundError Example to reproduce ClassNotFoundException & NoClassDefFoundError * JUnit test cases for ClassNotFoundException & NoClassDefFoundError test cases to reproduce ClassNotFoundException & NoClassDefFoundError * Deleting exampls for evaluation article * BAEL-831 Examples for ClassNotFoundException & NoClassDefFoundError * BAEL-831 Removed wrapper class * Removing evaluation article example * BAEL-875 - Hill Climbing Algorithm BAEL-875 - Implementation for Hill Climbing Algorithm * BAEL-875 removed unused imports * BAEL-984 Monte Carlo tree search BAEL-984 Implementation for tic tac toe using Monte Carlo tree search * BAEL-984 test cases for MCTS BAEL-984 test cases for Monte Carlo tree search implementation * BAEL-1004 Implementation of Minimax algorithm --- .../algorithms/hillclimbing/HillClimbing.java | 2 +- .../algorithms/hillclimbing/State.java | 2 +- .../algorithms/minimax/GameOfBones.java | 17 ++++ .../baeldung/algorithms/minimax/MiniMax.java | 68 ++++++++++++++ .../com/baeldung/algorithms/minimax/Node.java | 54 +++++++++++ .../com/baeldung/algorithms/minimax/Tree.java | 20 ++++ .../algorithms/HillClimbingAlgorithmTest.java | 2 +- .../src/test/java/algorithms/MCTSTest.java | 92 +++++++++++++++++++ .../java/algorithms/minimax/MinimaxTest.java | 36 ++++++++ .../NoClassDefFoundErrorExample.java | 2 +- .../ClassNotFoundExceptionTest.java | 2 +- .../NoClassDefFoundErrorTest.java | 2 +- 12 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/minimax/GameOfBones.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/minimax/MiniMax.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/minimax/Node.java create mode 100644 algorithms/src/main/java/com/baeldung/algorithms/minimax/Tree.java create mode 100644 algorithms/src/test/java/algorithms/MCTSTest.java create mode 100644 algorithms/src/test/java/algorithms/minimax/MinimaxTest.java diff --git a/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/HillClimbing.java b/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/HillClimbing.java index d278418a87..77089636c8 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/HillClimbing.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/HillClimbing.java @@ -186,4 +186,4 @@ public class HillClimbing { return stackHeuristics; } -} +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/State.java b/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/State.java index ad22aa27e7..9180b33b5b 100644 --- a/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/State.java +++ b/algorithms/src/main/java/com/baeldung/algorithms/hillclimbing/State.java @@ -40,4 +40,4 @@ public class State { public void setHeuristics(int heuristics) { this.heuristics = heuristics; } -} +} \ No newline at end of file diff --git a/algorithms/src/main/java/com/baeldung/algorithms/minimax/GameOfBones.java b/algorithms/src/main/java/com/baeldung/algorithms/minimax/GameOfBones.java new file mode 100644 index 0000000000..2949faede4 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/minimax/GameOfBones.java @@ -0,0 +1,17 @@ +package com.baeldung.algorithms.minimax; + +import java.util.ArrayList; +import java.util.List; + +public class GameOfBones { + public static List getPossibleStates(int noOfBonesInHeap) { + List listOfPossibleHeaps = new ArrayList<>(); + for (int i = 1; i <= 3; i++) { + int newHeapCount = noOfBonesInHeap - i; + if (newHeapCount >= 0) { + listOfPossibleHeaps.add(newHeapCount); + } + } + return listOfPossibleHeaps; + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/minimax/MiniMax.java b/algorithms/src/main/java/com/baeldung/algorithms/minimax/MiniMax.java new file mode 100644 index 0000000000..9857a44db6 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/minimax/MiniMax.java @@ -0,0 +1,68 @@ +package com.baeldung.algorithms.minimax; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class MiniMax { + private Tree tree; + + public Tree getTree() { + return tree; + } + + public void setTree(Tree tree) { + this.tree = tree; + } + + public void constructTree(int noOfBones) { + tree = new Tree(); + Node root = new Node(noOfBones, true); + tree.setRoot(root); + constructTree(root); + } + + private void constructTree(Node node) { + List listofPossibleHeaps = GameOfBones.getPossibleStates(node.getNoOfBones()); + boolean isMaxPlayer = !node.isMaxPlayer(); + listofPossibleHeaps.forEach(n -> { + Node newNode = new Node(n, isMaxPlayer); + node.addChild(newNode); + if (newNode.getNoOfBones() > 0) { + constructTree(newNode); + } + }); + } + + public boolean checkWin() { + Node root = tree.getRoot(); + checkWin(root); + return root.getScore() == 1 ? true : false; + } + + private void checkWin(Node node) { + List children = node.getChildren(); + boolean isMaxPlayer = node.isMaxPlayer(); + children.forEach(child -> { + if (child.getNoOfBones() == 0) { + if (isMaxPlayer) { + child.setScore(1); + } else { + child.setScore(-1); + } + } else { + checkWin(child); + } + }); + Node bestChild = findBestChild(isMaxPlayer, children); + node.setScore(bestChild.getScore()); + } + + private Node findBestChild(boolean isMaxPlayer, List children) { + if (isMaxPlayer) { + return Collections.max(children, Comparator.comparing(c -> c.getScore())); + } else { + return Collections.min(children, Comparator.comparing(c -> c.getScore())); + } + } +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/minimax/Node.java b/algorithms/src/main/java/com/baeldung/algorithms/minimax/Node.java new file mode 100644 index 0000000000..4768935dcb --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/minimax/Node.java @@ -0,0 +1,54 @@ +package com.baeldung.algorithms.minimax; + +import java.util.ArrayList; +import java.util.List; + +public class Node { + private int noOfBones; + private boolean isMaxPlayer; + private int score; + private List children; + + public Node(int noOfBones, boolean isMaxPlayer) { + this.noOfBones = noOfBones; + this.isMaxPlayer = isMaxPlayer; + children = new ArrayList<>(); + } + + public int getNoOfBones() { + return noOfBones; + } + + public void setNoOfBones(int noOfBones) { + this.noOfBones = noOfBones; + } + + public boolean isMaxPlayer() { + return isMaxPlayer; + } + + public void setMaxPlayer(boolean maxPlayer) { + isMaxPlayer = maxPlayer; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Node newNode) { + children.add(newNode); + } + +} diff --git a/algorithms/src/main/java/com/baeldung/algorithms/minimax/Tree.java b/algorithms/src/main/java/com/baeldung/algorithms/minimax/Tree.java new file mode 100644 index 0000000000..edc16ee962 --- /dev/null +++ b/algorithms/src/main/java/com/baeldung/algorithms/minimax/Tree.java @@ -0,0 +1,20 @@ +package com.baeldung.algorithms.minimax; + +public class Tree { + private Node root; + + public Tree() { + } + + public Tree(Node root) { + this.root = root; + } + + public Node getRoot() { + return root; + } + + public void setRoot(Node root) { + this.root = root; + } +} diff --git a/algorithms/src/test/java/algorithms/HillClimbingAlgorithmTest.java b/algorithms/src/test/java/algorithms/HillClimbingAlgorithmTest.java index 6e5055da6e..666adbb180 100644 --- a/algorithms/src/test/java/algorithms/HillClimbingAlgorithmTest.java +++ b/algorithms/src/test/java/algorithms/HillClimbingAlgorithmTest.java @@ -55,4 +55,4 @@ public class HillClimbingAlgorithmTest { State nextState = hillClimbing.findNextState(currentState, goalStack); assertTrue(nextState.getHeuristics() > currentState.getHeuristics()); } -} +} \ No newline at end of file diff --git a/algorithms/src/test/java/algorithms/MCTSTest.java b/algorithms/src/test/java/algorithms/MCTSTest.java new file mode 100644 index 0000000000..f969c26311 --- /dev/null +++ b/algorithms/src/test/java/algorithms/MCTSTest.java @@ -0,0 +1,92 @@ +package algorithms; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.algorithms.mcts.montecarlo.MonteCarloTreeSearch; +import com.baeldung.algorithms.mcts.montecarlo.State; +import com.baeldung.algorithms.mcts.montecarlo.UCT; +import com.baeldung.algorithms.mcts.tictactoe.Board; +import com.baeldung.algorithms.mcts.tictactoe.Position; +import com.baeldung.algorithms.mcts.tree.Tree; + +public class MCTSTest { + Tree gameTree; + MonteCarloTreeSearch mcts; + + @Before + public void initGameTree() { + gameTree = new Tree(); + mcts = new MonteCarloTreeSearch(); + } + + @Test + public void givenStats_whenGetUCTForNode_thenUCTMatchesWithManualData() { + double uctValue = 15.79; + assertEquals(UCT.uctValue(600, 300, 20), uctValue, 0.01); + } + + @Test + public void giveninitBoardState_whenGetAllPossibleStates_thenNonEmptyList() { + State initState = gameTree.getRoot().getState(); + List possibleStates = initState.getAllPossibleStates(); + assertTrue(possibleStates.size() > 0); + } + + @Test + public void givenEmptyBoard_whenPerformMove_thenLessAvailablePossitions() { + Board board = new Board(); + int initAvailablePositions = board.getEmptyPositions().size(); + board.performMove(Board.P1, new Position(1, 1)); + int availablePositions = board.getEmptyPositions().size(); + assertTrue(initAvailablePositions > availablePositions); + } + + @Test + public void givenEmptyBoard_whenSimulateInterAIPlay_thenGameDraw() { + Board board = new Board(); + + int player = Board.P1; + int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE; + for (int i = 0; i < totalMoves; i++) { + board = mcts.findNextMove(board, player); + if (board.checkStatus() != -1) { + break; + } + player = 3 - player; + } + int winStatus = board.checkStatus(); + assertEquals(winStatus, Board.DRAW); + } + + @Test + public void givenEmptyBoard_whenLevel1VsLevel3_thenLevel3WinsOrDraw() { + Board board = new Board(); + MonteCarloTreeSearch mcts1 = new MonteCarloTreeSearch(); + mcts1.setLevel(1); + MonteCarloTreeSearch mcts3 = new MonteCarloTreeSearch(); + mcts3.setLevel(3); + + int player = Board.P1; + int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE; + for (int i = 0; i < totalMoves; i++) { + if (player == Board.P1) + board = mcts3.findNextMove(board, player); + else + board = mcts1.findNextMove(board, player); + + if (board.checkStatus() != -1) { + break; + } + player = 3 - player; + } + int winStatus = board.checkStatus(); + assertTrue(winStatus == Board.DRAW || winStatus == Board.P1); + } + +} diff --git a/algorithms/src/test/java/algorithms/minimax/MinimaxTest.java b/algorithms/src/test/java/algorithms/minimax/MinimaxTest.java new file mode 100644 index 0000000000..b29c16386c --- /dev/null +++ b/algorithms/src/test/java/algorithms/minimax/MinimaxTest.java @@ -0,0 +1,36 @@ +package algorithms.minimax; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; +import com.baeldung.algorithms.minimax.MiniMax; +import com.baeldung.algorithms.minimax.Tree; + +public class MinimaxTest { + private Tree gameTree; + private MiniMax miniMax; + + @Before + public void initMiniMaxUtility() { + miniMax = new MiniMax(); + } + + @Test + public void givenMiniMax_whenConstructTree_thenNotNullTree() { + assertNull(gameTree); + miniMax.constructTree(6); + gameTree = miniMax.getTree(); + assertNotNull(gameTree); + } + + @Test + public void givenMiniMax_whenCheckWin_thenComputeOptimal() { + miniMax.constructTree(6); + boolean result = miniMax.checkWin(); + assertTrue(result); + miniMax.constructTree(8); + result = miniMax.checkWin(); + assertFalse(result); + } +} diff --git a/core-java/src/main/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorExample.java b/core-java/src/main/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorExample.java index 2c852b5e82..0d02391a73 100644 --- a/core-java/src/main/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorExample.java +++ b/core-java/src/main/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorExample.java @@ -11,4 +11,4 @@ public class NoClassDefFoundErrorExample { test = new ClassWithInitErrors(); return test; } -} +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/classnotfoundexception/ClassNotFoundExceptionTest.java b/core-java/src/test/java/com/baeldung/classnotfoundexception/ClassNotFoundExceptionTest.java index a6104e635b..8714d084ab 100644 --- a/core-java/src/test/java/com/baeldung/classnotfoundexception/ClassNotFoundExceptionTest.java +++ b/core-java/src/test/java/com/baeldung/classnotfoundexception/ClassNotFoundExceptionTest.java @@ -8,4 +8,4 @@ public class ClassNotFoundExceptionTest { public void givenNoDriversInClassPath_whenLoadDrivers_thenClassNotFoundException() throws ClassNotFoundException { Class.forName("oracle.jdbc.driver.OracleDriver"); } -} +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorTest.java b/core-java/src/test/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorTest.java index bb446dc385..aa11aaa788 100644 --- a/core-java/src/test/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorTest.java +++ b/core-java/src/test/java/com/baeldung/noclassdeffounderror/NoClassDefFoundErrorTest.java @@ -9,4 +9,4 @@ public class NoClassDefFoundErrorTest { NoClassDefFoundErrorExample sample = new NoClassDefFoundErrorExample(); sample.getClassWithInitErrors(); } -} +} \ No newline at end of file