Generic Interface for Minimax-Algorithm in Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Interface for implementing Minimax algorithm in two-player zero-sum games | |
* <T> => Move data type | |
*/ | |
interface Minimax<T> { | |
// 1 or -1 | |
var currentPlayer: Int | |
/** | |
* Evaluate game state for current player. | |
* For player +1, a higher value is better. (maximizer) | |
* For player -1, a lower value is better. (minimizer) | |
* | |
* @return Positive or negative integer | |
*/ | |
fun evaluate(): Int | |
/** | |
* Get list of all possible moves | |
* | |
* @return List of possible moves | |
*/ | |
fun getPossibleMoves(): MutableList<T> | |
/** | |
* Check if a player has won or no further moves are possible | |
* @return If game has ended | |
*/ | |
fun isGameOver(): Boolean | |
/** | |
* Switch current player | |
* 1 -> -1 | |
* -1 -> 1 | |
*/ | |
fun switchCurrentPlayer() { | |
assert(this.currentPlayer == 1 || this.currentPlayer == -1) | |
this.currentPlayer = this.currentPlayer * -1 | |
} | |
/** | |
* Do move and switch current player | |
*/ | |
fun doMove(move: T) | |
/** | |
* Undo move and switch current player | |
*/ | |
fun undoMove(move: T) | |
/** | |
* Pick random move from possible moves list | |
* | |
* @return Random move | |
*/ | |
fun getRandomMove(): T = this.getPossibleMoves().random() | |
/** | |
* Pick best possible move for current player | |
* | |
* @return Pair of (Move, Score) | |
*/ | |
fun getBestMove(): Pair<T?, Float> = when(this.currentPlayer) { | |
1 -> minimax() | |
else -> minimax(maximize = false) | |
} | |
/** | |
* Simulate game. Should end up in a draw | |
*/ | |
fun simulate() { | |
do { | |
this.doMove(this.getBestMove().first ?: this.getRandomMove()) | |
println(this) | |
} while (!this.isGameOver()) | |
} | |
/** | |
* Minimax algorithm that finds best move | |
* | |
* @param depth [Int] Tree depth | |
* @param maximize [Boolean] Maximize for current player | |
* @return Pair of (Move, Score) | |
*/ | |
private fun minimax(depth: Int = this.getPossibleMoves().size, maximize: Boolean = true): Pair<T?, Float> { | |
if (depth == 0 || this.isGameOver()) return Pair<T?, Float>(null, this.evaluate().toFloat()) | |
var minOrMax: Pair<T?, Float> = Pair(null, if(maximize) Float.NEGATIVE_INFINITY else Float.POSITIVE_INFINITY) | |
// Iterate all possible moves | |
for (move in this.getPossibleMoves()) { | |
this.doMove(move) | |
// Call children | |
val child = this.minimax(depth - 1, !maximize) | |
val score: Pair<T?, Float> = Pair(move, child.second) | |
this.undoMove(move) | |
// Check for maximum or minimum | |
if ((maximize && score.second > minOrMax.second) || (!maximize && score.second < minOrMax.second)) | |
minOrMax = score | |
} | |
return minOrMax | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment