4e100d7ed0b0

Prep for initial release
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Fri, 10 Feb 2017 16:27:29 +0000
parents e33f59e97ecb
children ff7d66a41217
branches/tags (none)
files cl-ggp.asd cl-ggp.reasoner.asd docs/02-overview.markdown docs/04-reference-reasoner.markdown docs/05-changelog.markdown docs/api.lisp package.lisp

Changes

--- a/cl-ggp.asd	Sun Jan 29 22:29:18 2017 +0000
+++ b/cl-ggp.asd	Fri Feb 10 16:27:29 2017 +0000
@@ -1,18 +1,17 @@
 (asdf:defsystem :cl-ggp
-  :name "ggp"
   :description "A framework for writing General Game Playing clients."
 
   :author "Steve Losh <steve@stevelosh.com>"
   :maintainer "Steve Losh <steve@stevelosh.com>"
 
   :license "MIT/X11"
-  :version "0.0.1"
+  :version "1.0.0"
 
-  :depends-on (#:clack
-               #:flexi-streams
-               #:optima
-               #:fare-quasiquote-optima
-               #:fare-quasiquote-readtable)
+  :depends-on (:clack
+               :flexi-streams
+               :optima
+               :fare-quasiquote-optima
+               :fare-quasiquote-readtable)
 
   :serial t
   :components ((:file "package")
--- a/cl-ggp.reasoner.asd	Sun Jan 29 22:29:18 2017 +0000
+++ b/cl-ggp.reasoner.asd	Fri Feb 10 16:27:29 2017 +0000
@@ -1,13 +1,11 @@
 (asdf:defsystem :cl-ggp.reasoner
-  :name "ggp.reasoner"
-
   :description "A reasoner to use as a starting point for General Game Playing clients."
 
   :author "Steve Losh <steve@stevelosh.com>"
   :maintainer "Steve Losh <steve@stevelosh.com>"
 
   :license "MIT/X11"
-  :version "0.0.1"
+  :version "1.0.0"
 
   :depends-on (:temperance
                :cl-ggp)
--- a/docs/02-overview.markdown	Sun Jan 29 22:29:18 2017 +0000
+++ b/docs/02-overview.markdown	Fri Feb 10 16:27:29 2017 +0000
@@ -1,16 +1,21 @@
 Overview
 ========
 
-`cl-ggp` handles the GGP protocol for you.  Players are implemented as CLOS
-objects.
+This document assumes you know what [General Game Playing][] is, what [GDL][]
+is, and how the GGP community/competitions/etc work.
 
-This document assumes you know what General Game Playing is, what GDL is, and
-how the GGP community/competitions/etc work.
+[General Game Playing]: http://ggp.org/
+[GDL]: https://en.wikipedia.org/wiki/Game_Description_Language
 
 [TOC]
 
-Basics
-------
+GGP Protocol
+------------
+
+The `cl-ggp` system handles the GGP network protocol and game flow for you.
+Players are implemented as CLOS objects.
+
+### Basics
 
 You can create your own player by extending the `ggp-player` class, creating an
 object, and calling `start-player` on it to fire it up:
@@ -30,8 +35,7 @@
 
 You can kill a player with `kill-player`.
 
-Functionality
--------------
+### Game Functionality
 
 `cl-ggp` defines four generic methods that are called on players at various
 points in each game.  You can provide method definitions for some or all of
@@ -40,10 +44,10 @@
 At a minimum you **must** implement `player-select-move`.  The others are
 optional and will default to doing nothing.
 
-### player-start-game
+#### player-start-game
 
     :::lisp
-    (defmethod player-start-game ((player YOUR-PLAYER) rules role timeout)
+    (defmethod ggp:player-start-game ((player YOUR-PLAYER) rules role timeout)
       ...)
 
 This is called when a new game starts.
@@ -56,10 +60,10 @@
 `timeout` is the timestamp that the response to the server is due by, in
 internal-real time units (more on this later).
 
-### player-update-game
+#### player-update-game
 
     :::lisp
-    (defmethod player-update-game ((player YOUR-PLAYER) moves)
+    (defmethod ggp:player-update-game ((player YOUR-PLAYER) moves)
       ...)
 
 This is called once per turn, to allow you to update the game state with the
@@ -68,10 +72,10 @@
 `moves` will be an association list of `(role . move)` conses representing the
 moves made by each player last turn.
 
-### player-select-move
+#### player-select-move
 
     :::lisp
-    (defmethod player-select-move ((player YOUR-PLAYER) timeout)
+    (defmethod ggp:player-select-move ((player YOUR-PLAYER) timeout)
       ...)
 
 This is called once per turn.  It needs to return the move your player wants to
@@ -80,17 +84,16 @@
 `timeout` is the timestamp that the response to the server is due by, in
 internal-real time units (more on this later).
 
-### player-stop-game
+#### player-stop-game
 
     :::lisp
-    (defmethod player-stop-game ((player YOUR-PLAYER))
+    (defmethod ggp:player-stop-game ((player YOUR-PLAYER))
       ...)
 
 This is called when the game is stopped.  You can use it for things like tearing
 down any extra data structures you've made, suggesting a GC to your Lisp, etc.
 
-Timeouts
---------
+### Timeouts
 
 The GGP protocol specifies time limits for players.
 
@@ -128,8 +131,7 @@
 [get-internal-real-time]: http://www.lispworks.com/documentation/HyperSpec/Body/f_get_in.htm#get-internal-real-time
 [internal-time-units-per-second]: http://www.lispworks.com/documentation/HyperSpec/Body/v_intern.htm#internal-time-units-per-second
 
-Symbols
--------
+### Symbols
 
 The other tricky part about `cl-ggp` is how it handles symbols.
 
@@ -162,52 +164,106 @@
 This is kind of shitty, and the author is aware of that.  Suggestions for less
 shitty alternatives that still feel vaguely lispy are welcome.
 
+Reasoning
+---------
+
+The `cl-ggp.reasoner` system contains a Prolog-based GDL reasoner based on the
+[Temperance][] logic programming library.
+
+[Temperance]: https://sjl.bitbucket.io/temperance/
+
+It's useful as a starting point if you just want to get a bot up and running
+quickly.  If you want more speed or control over the reasoning process you'll
+probably want to replace it with your own implementation.
+
+You can make a reasoner for a set of GDL rules (e.g. the rules given to you by
+`player-start-game`) with `(make-reasoner rules)`.
+
+Once you've got a reasoner you can ask it for the initial state of the game with
+`(initial-state reasoner)`.  This will give you back a state object -- it's
+currently just a list, but this may change in the future, so you should just
+treat it as an opaque object.
+
+Once you've got a state and a set of moves you can compute the next state with
+`(next-state reasoner current-state moves)`.
+
+States can be queried for their terminality, goal values, and legal moves with
+`terminalp`, `goal-values-for`, and `legal-moves-for` respectively.
+
+See the [Reasoner API Reference][reasoner] for more details.
+
+[reasoner]: ../reference-reasoner/
+
 Example Player
 --------------
 
-`cl-ggp` is pretty bare bones, so it's tough to show an example player on its
-own without bringing in a logic library.  But we can at least sketch out
-a stupid little player that just returns the same move all the time, regardless
-of whether it's valid or not, just to show the end-to-end process of creating
-a player.
+Let's create a small example player that uses the reasoner to play any GGP game
+legally.  We'll start by creating a class for the player, with three slots to
+keep track of the data we need to play a game:
+
+    :::lisp
+    (defclass random-player (ggp:ggp-player)
+      ((role          :accessor p-role)
+       (current-state :accessor p-current-state)
+       (reasoner      :accessor p-reasoner)))
 
-First we'll define the player class and implement the required
-`player-select-move` method for it:
+Now we can implement `player-start-game`.  We'll store the role we've been
+assigned, and create a reasoner so we can compute our legal moves later.  We'll
+ignore the deadline because we're not doing any extensive processing:
+
+    :::lisp
+    (defmethod ggp:player-start-game
+        ((player random-player) rules role deadline)
+      (declare (ignore deadline))
+      (setf (p-role player) role
+            (p-reasoner player) (ggp.reasoner:make-reasoner rules)))
+
+Next we'll implement `player-update-game`, which will compute the current state
+of the game and store it in the `current-state` slot:
 
     :::lisp
-    (defclass simple-player (ggp:ggp-player)
-      ())
-
-    (defmethod ggp:player-select-move ((player simple-player) timeout)
-      'ggp-rules::wait)
+    (defmethod ggp:player-update-game
+        ((player random-player) moves)
+      (setf (p-current-state player)
+            (if (null moves)
+              (ggp.reasoner:initial-state (p-reasoner player))
+              (ggp.reasoner:next-state (p-reasoner player)
+                                       (p-current-state player)
+                                       moves))))
 
-Our player doesn't store any state of its own, so it doesn't need any extra
-slots.  Notice how `player-select-move` returns a symbol from the `GGP-RULES`
-package as discussed above.
+If `moves` is null we ask the reasoner for the initial state, otherwise we
+compute the next state from the current one and the moves that were made.
 
-The move our stupid player always returns is `WAIT`.  If the game supports that
-move we'll make it every time, otherwise the game server will reject it as
-invalid and just choose a random move for us.
-
-Now we can actually create a player:
+Now we can implement `player-select-move`.  We'll just ask the reasoner for all
+our legal moves and choose one at random.  If we wanted to make a smarter
+player, this is where we would search the game tree to find a good move:
 
     :::lisp
-    (defvar *player*
-      (make-instance 'simple-player
-                     :name "SimplePlayer"
-                     :port 5000))
+    (defmethod ggp:player-select-move
+        ((player random-player) deadline)
+      (declare (ignore deadline))
+      (let ((moves (ggp.reasoner:legal-moves-for
+                     (p-reasoner player)
+                     (p-current-state player)
+                     (p-role player))))
+        (nth (random (length moves)) moves)))
 
-And fire it up:
+Finally we can implement `player-stop-game`.  We'll just clear out the player's
+slots so the data can be garbage collected:
 
     :::lisp
-    (ggp:start-player *player*)
+    (defmethod ggp:player-stop-game
+        ((player random-player))
+      (setf (p-current-state player) nil
+            (p-reasoner player) nil
+            (p-role player) nil))
 
-Now we can play a few games with it.  We'll probably lose every time unless
-we're playing an unscrambled game of [Don't Press the Button][dptb].
-
-Once we're done we can kill it to free up the port:
+Now we can make an instance of our player and start it up!
 
     :::lisp
-    (ggp:kill-player *player*)
+    (defvar *random-player*
+      (make-instance 'random-player
+                     :name "RandomPlayer"
+                     :port 4000))
 
-[dptb]: https://bitbucket.org/snippets/sjl/erRjL
+    (ggp:start-player *random-player*)
--- a/docs/04-reference-reasoner.markdown	Sun Jan 29 22:29:18 2017 +0000
+++ b/docs/04-reference-reasoner.markdown	Fri Feb 10 16:27:29 2017 +0000
@@ -1,6 +1,6 @@
 # Reasoner API Reference
 
-cl-ggp includes a simple Prolog-based reasoner you can use as a starting point when writing general game players in the `cl-ggp.reasoner` system.
+cl-ggp includes a simple Prolog-based reasoner you can use as a starting point when writing general game players in the `ggp.reasoner` system.
 
   [TOC]
 
--- a/docs/05-changelog.markdown	Sun Jan 29 22:29:18 2017 +0000
+++ b/docs/05-changelog.markdown	Fri Feb 10 16:27:29 2017 +0000
@@ -5,8 +5,8 @@
 
 [TOC]
 
-Pending
--------
+v1.0.0
+------
 
 * `start-player` now takes `:server` and `:use-thread` options which it passes
   along to Clack.
--- a/docs/api.lisp	Sun Jan 29 22:29:18 2017 +0000
+++ b/docs/api.lisp	Fri Feb 10 16:27:29 2017 +0000
@@ -19,7 +19,7 @@
   :cl-ggp.reasoner
   #p"docs/04-reference-reasoner.markdown"
   (list "GGP.REASONER")
-  "cl-ggp includes a simple Prolog-based reasoner you can use as a starting point when writing general game players in the `cl-ggp.reasoner` system.
+  "cl-ggp includes a simple Prolog-based reasoner you can use as a starting point when writing general game players in the `ggp.reasoner` system.
 
   "
   :title "Reasoner API Reference")
--- a/package.lisp	Sun Jan 29 22:29:18 2017 +0000
+++ b/package.lisp	Fri Feb 10 16:27:29 2017 +0000
@@ -16,8 +16,7 @@
     :start-player
     :kill-player
 
-    :read-gdl-from-file
-    )
+    :read-gdl-from-file)
   (:documentation "The main GGP package."))
 
 (defpackage :ggp-rules