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
This commit is contained in:
parent
76e2c35457
commit
2f83bec231
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GameOfBones {
|
||||||
|
public static List<Integer> getPossibleStates(int noOfBonesInHeap) {
|
||||||
|
List<Integer> listOfPossibleHeaps = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= 3; i++) {
|
||||||
|
int newHeapCount = noOfBonesInHeap - i;
|
||||||
|
if (newHeapCount >= 0) {
|
||||||
|
listOfPossibleHeaps.add(newHeapCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listOfPossibleHeaps;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Integer> 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<Node> 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<Node> children) {
|
||||||
|
if (isMaxPlayer) {
|
||||||
|
return Collections.max(children, Comparator.comparing(c -> c.getScore()));
|
||||||
|
} else {
|
||||||
|
return Collections.min(children, Comparator.comparing(c -> c.getScore()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Node> 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<Node> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildren(List<Node> children) {
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Node newNode) {
|
||||||
|
children.add(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<State> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue