Skip to content

Instantly share code, notes, and snippets.

(defn- start-game-loop
"Manage transitions between player moves, ai moves, and generic game events"
[]
(let [play-events (chan 1 (filter #(not @store/ai-player?)))
game-events (chan 1)]
(go
(while true
(let [play-chan (if @store/ai-player?
(ai-computation @store/game)
play-events)]
(defn- handle-game-event!
[msg]
(store/swap-game!
(case msg
:new-game (fn [_] (game/new-game))
:restart game/restart-game
:undo game/undo-player-move)))
(defonce game-loop (start-game-loop))
(defn send-play-event! [e] (put! (game-loop :play-events) e))
(defn send-game-event! [e] (put! (game-loop :game-events) e))
(defn minimax-step
"One stage of the minimax algorithm:
* Apply the maximizing or mininizing step to all transitions of the turn
* Evaluate the lower level using the on-transition function"
[ai turn open-recur
& {:keys [max-fn min-fn]
:or {max-fn max, min-fn min}}]
(apply
(if (maximizing? ai turn) max-fn min-fn)
(map
(defn- eval-next-score-of
"Perform a last minimax step on the next scores following the turn
* Allows to see one level deeper for simple scoring strategies
* While being fast (the transition does not need to be followed)"
[ai {:keys [scores] :as turn} players]
(minimax/minimax-step ai turn
(fn [_ transition]
(let [scores (scores/update-scores scores transition)]
(apply + (map #(get scores %) players)))
)))
(defn optimize-own-score-ai
"Create an AI strategy to optmize its own score: good late game play"
[player]
(reify minimax/AIStrategy
(eval-turn [this turn] (eval-next-score-of this turn [player]))
(maximizing? [_ turn] (= (:player turn) player))
))
(defn optmize-ai-scores-ai
"Create an AI strategy to optmize the AI scores: good cheat when the player wins"
(defn optimize-own-choices-ai
"Create an AI strategy to optmize the AI choices: avoid being trapped with no moves left"
[player]
(reify minimax/AIStrategy
(eval-turn [_ turn]
(count (get (transition/all-transitions (:board turn)) player)))
(maximizing? [_ turn] (= (:player turn) player))
))
(defn- game-tree-search
"The top level of the minimax algorithm
* Trigger sub-game-trees minimax evaluations
* Remember the transition that led to the max"
[ai turn depth]
(first
(ai-algo/minimax-step-by
second ai turn
(fn [coord transition]
(let [new-turn (turn/next-turn turn transition)]
(defn- high-level-ai
"High level AI: choose the right evaluation function to play"
[{:keys [player scores] :as turn}]
(cond
(human-player-winning? scores) (strategies/optmize-ai-scores-ai)
(in-late-game? scores) (strategies/optimize-own-score-ai player)
(limited-move-options? turn) (strategies/optimize-own-choices-ai player)
:else (strategies/optimize-own-score-ai player)
))
(defn find-best-move
"Find the best available move for the current player"
[game]
(let [turn (game/current-turn game)
ai (high-level-ai turn)]
(game-tree-search ai turn 1)))