From 86034dad1871e3c58913ff663b66a5c7466c6fda Mon Sep 17 00:00:00 2001 From: Yuan Mai <4registration@gmail.com> Date: Sat, 28 Jul 2012 17:39:52 +0800 Subject: [PATCH] alpha beta pruning --- .gitignore | 10 ++++++ README.md | 15 ++++++++ project.clj | 6 ++++ src/yuan/game/alpha_beta_pruning.clj | 54 ++++++++++++++++++++++++++++ src/yuan/game/game_tree.clj | 16 +++++++++ test/yuan.game/core_test.clj | 7 ++++ 6 files changed, 108 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 project.clj create mode 100644 src/yuan/game/alpha_beta_pruning.clj create mode 100644 src/yuan/game/game_tree.clj create mode 100644 test/yuan.game/core_test.clj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee508f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/target +/lib +/classes +/checkouts +pom.xml +*.jar +*.class +.lein-deps-sum +.lein-failures +.lein-plugins diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3558bd --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# yuan.game + +I'm an app. Or maybe I'm a library? I haven't decided yet. + +The choice is up to you! + +## Usage + +FIXME + +## License + +Copyright © 2012 FIXME + +Distributed under the Eclipse Public License, the same as Clojure. diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..eda4ca2 --- /dev/null +++ b/project.clj @@ -0,0 +1,6 @@ +(defproject yuan/game "0.1.0-SNAPSHOT" + :description "Alpha beta pruning" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.3.0"] + [org.clojure/tools.trace "0.7.3"]]) diff --git a/src/yuan/game/alpha_beta_pruning.clj b/src/yuan/game/alpha_beta_pruning.clj new file mode 100644 index 0000000..8e6eec6 --- /dev/null +++ b/src/yuan/game/alpha_beta_pruning.clj @@ -0,0 +1,54 @@ +(ns yuan.game.alpha-beta-pruning + (:use [clojure.tools.trace] + [yuan.game.game-tree])) + +(def ^:dynamic *current-player*) +(def ^:dynamic *turn-fn*) +(def ^:dynamic *eval-fn*) + +(declare ab-rate-position) + +(defn ab-get-rating-max [{:keys [moves board] :as tree} upper-limit lower-limit] + (letfn [(f [[move & more] lower-limit] + (if-let [x (and move + (ab-rate-position move upper-limit lower-limit))] + (conj (if (>= x upper-limit) + () + (f more (max x lower-limit))) + x)))] + (f moves lower-limit))) + +(defn ab-get-rating-min [{:keys [moves board] :as tree} upper-limit lower-limit] + (letfn [(f [[move & more] upper-limit] + (if-let [x (and move + (ab-rate-position move upper-limit lower-limit))] + (conj (if (<= x lower-limit) + () + (f more (min x upper-limit))) + x)))] + (f moves upper-limit))) + +(defn ab-rate-position [{:keys [moves board] :as tree} upper-limit lower-limit] + (if (seq moves) + (if (= (*turn-fn* board) *current-player*) + (apply max (ab-get-rating-max tree + upper-limit + lower-limit)) + (apply min (ab-get-rating-min tree + upper-limit + lower-limit))) + (*eval-fn* *current-player* board))) + +(defn make-agent + [turn-fn eval-fn] + (fn [{:keys [moves board] :as tree}] + {:pre [(seq moves)]} + (binding [*turn-fn* turn-fn + *eval-fn* eval-fn + *current-player* (trace :turn (turn-fn board))] + (let [ratings (trace :ratings + (ab-get-rating-max (limit-tree-depth tree) + 1 + -1)) + best (apply max ratings)] + (:board (nth moves (.indexOf ratings best))))))) diff --git a/src/yuan/game/game_tree.clj b/src/yuan/game/game_tree.clj new file mode 100644 index 0000000..16e883d --- /dev/null +++ b/src/yuan/game/game_tree.clj @@ -0,0 +1,16 @@ +(ns yuan.game.game-tree) + +(def ^:dynamic *ai-level* 5) + +(defn game-tree + [make-move board] + {:board board + :moves (map (partial game-tree make-move) + (make-move board))}) + +(defn limit-tree-depth [tree & {:keys [depth] :or {depth *ai-level*}}] + (update-in tree + [:moves] + (if (pos? depth) + (partial map (fn [tree] (limit-tree-depth tree :depth (dec depth)))) + (constantly ())))) diff --git a/test/yuan.game/core_test.clj b/test/yuan.game/core_test.clj new file mode 100644 index 0000000..8fc1838 --- /dev/null +++ b/test/yuan.game/core_test.clj @@ -0,0 +1,7 @@ +(ns yuan.game.core-test + (:use clojure.test + yuan.game.core)) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1)))) \ No newline at end of file