wip: implement action tree and minimax for ai

This commit is contained in:
2021-06-07 03:06:33 +02:00
parent 81cbec5348
commit 7a5d9dca76
7 changed files with 274 additions and 36 deletions

View File

@ -1,6 +1,5 @@
package uulm.teamname.marvelous.gamelibrary.ai;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView;
@ -11,18 +10,148 @@ import java.util.HashMap;
class BoardAnalyzer {
private final Board origin;
private final EntityType player;
private final HashMap<Integer, Float> cache = new HashMap<>();
private final HashMap<Integer, Float> scoreCache = new HashMap<>();
private final HashMap<Integer, ArrayList<Action>> actionCache = new HashMap<>();
private Action bestAction = null;
public BoardAnalyzer(GameStateView state, EntityID turn) {
this.origin = Board.generate(state, turn);
this.player = turn.type;
}
public Action analyze(GameStateView state, IntVector2 position, EntityID turn) {
ArrayList<Action> actions = origin.generateActions(state);
public Action analyze(GameStateView state) {
Node tree = new Node(origin, new Action(ActionType.None));
//TODO: create minimax tree
int maxDepth = 2;
int depth = 0;
long startTime = System.nanoTime();
while(System.nanoTime() - startTime <= 1000 * 1000 * 1000) {
expandTree(tree, state);
depth++;
if(depth > maxDepth) {
break;
}
}
alphaBetaMax(tree, Float.MIN_VALUE, Float.MAX_VALUE, true);
if(bestAction != null) {
return bestAction;
}
return new Action(ActionType.None);
}
private Float alphaBetaMax(Node root, Float a, Float b, boolean main) {
if(!root.hasChildren()) {
return calculateScore(root.board);
}
Float w = a;
for(Node child: root.getChildren()) {
Float v;
if(child.board.turn.type == child.board.origin) {
v = alphaBetaMax(child, a, w, false);
}else {
v = alphaBetaMin(child, w, b);
}
if(v > w) {
w = v;
}
if(w >= b) {
if(main) {
bestAction = child.action;
}
return w;
}
if(main) {
bestAction = child.action;
}
}
return w;
}
private Float alphaBetaMin(Node root, Float a, Float b) {
if(!root.hasChildren()) {
return calculateScore(root.board);
}
Float w = b;
for(Node child: root.getChildren()) {
Float v;
if(child.board.turn.type == child.board.origin) {
v = alphaBetaMax(child, a, w, false);
}else {
v = alphaBetaMin(child, w, b);
}
if(v < w) {
w = v;
}
if(w <= a) {
return w;
}
}
return w;
}
private ArrayList<Node> getLeaves(Node root) {
return getLeaves(root, new ArrayList<>());
}
private ArrayList<Node> getLeaves(Node root, ArrayList<Node> accumulator) {
for(Node child: root.getChildren()) {
if(child.hasChildren()) {
getLeaves(child, accumulator);
}else {
accumulator.add(child);
}
}
return accumulator;
}
private void expandTree(Node root, GameStateView state) {
if(!root.hasChildren()) {
expandNode(root, state);
return;
}
for(Node child: root.getChildren()) {
if(child.board.ended) {
continue;
}
if(child.hasChildren()) {
expandTree(child, state);
}else {
expandNode(child, state);
}
}
}
private void expandNode(Node origin, GameStateView state) {
ArrayList<Action> actions = generateActions(origin.board, state);
for(Action action: actions) {
Board result = origin.board.applyAction(state, action);
scoreCache.put(result.hashCode(), result.calculateScore());
origin.addChild(result, action, result.calculateScore());
}
}
private ArrayList<Action> generateActions(Board board, GameStateView state) {
int hash = board.hashCode();
if(actionCache.containsKey(hash)) {
return actionCache.get(hash);
}else {
ArrayList<Action> result = board.generateActions(state);
actionCache.put(hash, result);
return result;
}
}
private Float calculateScore(Board board) {
int hash = board.hashCode();
if(scoreCache.containsKey(hash)) {
return scoreCache.get(hash);
}else {
Float result = board.calculateScore();
scoreCache.put(hash, result);
return result;
}
}
}