Skip to content

Instantly share code, notes, and snippets.

@larswaechter
Last active August 6, 2020 15:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larswaechter/595f5f63df3de5579e8dbe47b69ede4e to your computer and use it in GitHub Desktop.
Save larswaechter/595f5f63df3de5579e8dbe47b69ede4e to your computer and use it in GitHub Desktop.
Generic Interface for Minimax-Algorithm in Kotlin
/**
* 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