diff --git a/AI/src/expert_iteration/ExpertIteration.java b/AI/src/expert_iteration/ExpertIteration.java index 2afc906..631cead 100644 --- a/AI/src/expert_iteration/ExpertIteration.java +++ b/AI/src/expert_iteration/ExpertIteration.java @@ -461,7 +461,7 @@ else if (!outDir.exists()) report ); - if (bestAgent.agent().equals("AlphaBeta")) + if (bestAgent.agent().equals("AlphaBeta") || bestAgent.agent().equals("Alpha-Beta")) { ai = new AlphaBetaSearch(bestAgentsDataDir + "/BestHeuristics.txt"); } diff --git a/AI/src/utils/BaseAI.java b/AI/src/utils/BaseAI.java new file mode 100644 index 0000000..260338e --- /dev/null +++ b/AI/src/utils/BaseAI.java @@ -0,0 +1,66 @@ +package utils; + +import game.Game; +import util.AI; +import util.Context; +import util.Move; + +/** + * AI player doing nothing. + * + * @author Eric.Piette + */ +public class BaseAI extends AI +{ + + //------------------------------------------------------------------------- + + /** Our player index */ + protected int player = -1; + + /** The last move we returned */ + protected Move lastReturnedMove = null; + + //------------------------------------------------------------------------- + + /** + * Constructor + */ + public BaseAI() + { + friendlyName = "Base"; + } + + //------------------------------------------------------------------------- + + @Override + public Move selectAction + ( + final Game game, + final Context context, + final double maxSeconds, + final int maxIterations, + final int maxDepth + ) + { + return null; + } + + /** + * @return The last move we returned + */ + public Move lastReturnedMove() + { + return lastReturnedMove; + } + + @Override + public void initAI(final Game game, final int playerID) + { + this.player = playerID; + lastReturnedMove = null; + } + + //------------------------------------------------------------------------- + +} diff --git a/AI/src/utils/LudiiGameWrapper.java b/AI/src/utils/LudiiGameWrapper.java index 7b5898d..fde31cc 100644 --- a/AI/src/utils/LudiiGameWrapper.java +++ b/AI/src/utils/LudiiGameWrapper.java @@ -20,6 +20,9 @@ import topology.TopologyElement; import util.GameLoader; import util.Move; +import util.action.Action; +import util.action.others.ActionPropose; +import util.action.others.ActionVote; import utils.data_structures.ludeme_trees.LudemeTreeUtils; /** @@ -129,6 +132,12 @@ public boolean equals(final Object obj) /** Maximum absolute distance we consider between from and to positions for move tensors */ protected final int moveTensorDistClip; + /** Channel index for the first proposition channel in move-tensor-representation */ + protected int FIRST_PROPOSITION_CHANNEL_IDX; + + /** Channel index for the first vote channel in move-tensor-representation */ + protected int FIRST_VOTE_CHANNEL_IDX; + /** Channel index for Pass move in move-tensor-representation */ protected int MOVE_PASS_CHANNEL_IDX; @@ -333,7 +342,39 @@ public String[] stateTensorChannelNames() */ public int[] moveToTensor(final Move move) { - if (move.isPass()) + if (move.isPropose()) + { + int offset = 0; + + for (final Action a : move.actions()) + { + if (a instanceof ActionPropose && a.isDecision()) + { + final ActionPropose action = (ActionPropose) a; + offset = action.propositionInt(); + break; + } + } + + return new int[] {FIRST_PROPOSITION_CHANNEL_IDX + offset, 0, 0}; + } + else if (move.isVote()) + { + int offset = 0; + + for (final Action a : move.actions()) + { + if (a instanceof ActionVote && a.isDecision()) + { + final ActionVote action = (ActionVote) a; + offset = action.voteInt(); + break; + } + } + + return new int[] {FIRST_VOTE_CHANNEL_IDX + offset, 0, 0}; + } + else if (move.isPass()) { return new int[] {MOVE_PASS_CHANNEL_IDX, 0, 0}; } @@ -707,7 +748,20 @@ else if (maxNonBoardContIdx > maxBoardIndexX && maxBoardIndexX > maxBoardIndexY) assert (channelNames.size() == stateTensorNumChannels); stateTensorChannelNames = channelNames.toArray(new String[stateTensorNumChannels]); - MOVE_PASS_CHANNEL_IDX = computeMovePassChannelIdx(); + final int firstAuxilChannelIdx = computeFirstAuxilChannelIdx(); + + if (game.usesVote()) + { + FIRST_PROPOSITION_CHANNEL_IDX = firstAuxilChannelIdx; + FIRST_VOTE_CHANNEL_IDX = FIRST_PROPOSITION_CHANNEL_IDX + game.numVoteStrings(); + + MOVE_PASS_CHANNEL_IDX = FIRST_VOTE_CHANNEL_IDX + game.numVoteStrings(); + } + else + { + MOVE_PASS_CHANNEL_IDX = firstAuxilChannelIdx; + } + MOVE_SWAP_CHANNEL_IDX = MOVE_PASS_CHANNEL_IDX + 1; ALL_ONES_CHANNEL_FLAT = new float[tensorDimX * tensorDimY]; @@ -729,9 +783,9 @@ else if (maxNonBoardContIdx > maxBoardIndexX && maxBoardIndexX > maxBoardIndexY) //------------------------------------------------------------------------- /** - * @return Channel index for Pass move in move-tensor-representation + * @return First channel index for auxiliary in move-tensor-representation */ - private int computeMovePassChannelIdx() + private int computeFirstAuxilChannelIdx() { // legal values for diff x = {-clip, ..., -2, -1, 0, 1, 2, ..., +clip} final int numValsDiffX = 2 * moveTensorDistClip + 1; diff --git a/AI/src/utils/LudiiStateWrapper.java b/AI/src/utils/LudiiStateWrapper.java index c250cc2..e185a53 100644 --- a/AI/src/utils/LudiiStateWrapper.java +++ b/AI/src/utils/LudiiStateWrapper.java @@ -389,7 +389,6 @@ public void undoLastMove() */ public float[] toTensorFlat() { - // TODO some channels are always the same, precompute and reuse those // TODO we also want to support edges and faces for some games final Container[] containers = game.game.equipment().containers(); diff --git a/AI/src/utils/RandomAI.java b/AI/src/utils/RandomAI.java index b08d0b5..c5492d3 100644 --- a/AI/src/utils/RandomAI.java +++ b/AI/src/utils/RandomAI.java @@ -47,10 +47,10 @@ public RandomAI() ) { FastArrayList legalMoves = game.moves(context).moves(); - + if (!game.isAlternatingMoveGame()) legalMoves = AIUtils.extractMovesForMover(legalMoves, player); - + final int r = ThreadLocalRandom.current().nextInt(legalMoves.size()); final Move move = legalMoves.get(r); lastReturnedMove = move; diff --git a/README.md b/README.md index a47a1a3..cd83e06 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Alternatively, the following email address may be used: `ludii(dot)games(at)gmai ## Changelog +- 27 November, 2020: Updated AI code for Ludii v1.1.4. - 18 November, 2020: Updated AI code for Ludii v1.1.1. - 28 October, 2020: Updated AI code for Ludii v1.0.9. - 17 October, 2020: Updated AI code for Ludii v1.0.8.