Reputation: 217
I'm writing an AI for a game similar to chess. The board is 10x10 with 15 pieces for each side which all have chess similar moves.
Everything in the game is organized in objects. Tile[][] tiles; 10x10, every Tile has a piece-pointer to a piece or null.
I've implemented a MinMax algorithm with no pruning so far. Possible moves each round are on average twice as many as in a chess game.
The algorithm works but it's very slow. On average it can check 40000 moves/pr sec. So with a depth of 4 my game uses around 4-5 seconds to calculate all possible moves. I will implement pruning later, but could need some feedback on my implementation first.
Question: Do i have to convert to char-arrays/bit-board or similar to speed up the calculation or am i doing something horribly wrong?
Implementation: (sudo) To avoid lots of double for-loops i keep track of myPieces and opponentPieces in Lists of Tiles. Board evaluation is also done once and then only updates and only updated by adding and subtracting the value of a move. In my implementation of the minMax algorithm i use a GameState class that holds the current gameState.
GameState {
Tile[][] board
List<Tile> myPieces;
List<Tile> otherPieces;
int[][] evaluatedBoard
int evaluatedValue
Piece moveFrom, moveTo //used to save pointer when applying move
int moveFromValue, moveToValue //used to save evaluationValue when applying move
void applyMove(Tile from, Tile to)
void undoMove(Tile from, Tile to)
GameState createCopy()
}
ApplyMove only updates the evaluatedValue and does not go through the whole array.myPieces and otherPieces are also updated by apply/undo.
MinMax:
maxMove(GameState state, int depth) {
for(Tile from : state.getMyPieces().clone()) { //list will be changed
for(Tile to : from.getPiece().getPossibleMoves()) {
if(depth == 0)
//find best move and so forth
else {
state.applyMove(from, to);
minMove(state.createCopy(), depth - 1) //similar to maxMove except it uses otherPieces
state.undoMove(from, to)
}
}
}
//return best move
}
Edit: Added information about applyMove and Profiler
Profiler: instructions
applyMove() 3200ms 11 430 000
undoMove() 1800ms 11 430 000
evaluateTile() 1400ms 22 400 000
minMove() 1700ms 315 493
applyMove and undoMove only store/reverse pointers to the old value off from and to and replace them with the new ones from the move. Then calls evaluateTile which returns a number from 1-10 depending on a enum Type in piece.
Upvotes: 1
Views: 1723
Reputation: 5295
Your choice of representation is going to cost you a great deal - every move you consider requires you to copy a lot of state. The way I see it, you have two choices:
(1) make your state representation very small (in chess you can do this in 64 x 4 bits, or 4 Int64s in .NET) so copying it is very cheap; or
(2) make your state representation immutable with deltas, so creating an updated state is cheap.
I'd try option (1) first and see how you go.
Here's some links you may find useful: http://en.wikipedia.org/wiki/Board_representation_(chess) http://www.cis.uab.edu/hyatt/boardrep.html
Upvotes: 1