# HG changeset patch # User Steve Losh <steve@stevelosh.com> # Date 1458769008 0 # Node ID 641956c663988a9bee9e5ec8c4299673920e20f3 # Parent 621ae61499abf53513620bb6d1a6a028b975efa7 cl-ggp: Update site. diff -r 621ae61499ab -r 641956c66398 cl-ggp/changelog/index.html --- a/cl-ggp/changelog/index.html Wed Mar 23 16:24:01 2016 +0000 +++ b/cl-ggp/changelog/index.html Wed Mar 23 21:36:48 2016 +0000 @@ -13,7 +13,14 @@ <div class="wrap"> <header><h1><a href="..">cl-ggp</a></h1></header> <div class="markdown"> -<h1 id="changelog"><a href="">Changelog</a></h1></div> +<h1 id="changelog"><a href="">Changelog</a></h1><p>Here's the list of changes in each released version.</p> +<div class="toc"> +<ul> +<li><a href="#v001">v0.0.1</a></li> +</ul></div> +<h2 id="v001">v0.0.1</h2> +<p>Initial alpha version. Things are going to break a lot. Don't use this.</p> + </div> <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ diff -r 621ae61499ab -r 641956c66398 cl-ggp/installation/index.html --- a/cl-ggp/installation/index.html Wed Mar 23 16:24:01 2016 +0000 +++ b/cl-ggp/installation/index.html Wed Mar 23 21:36:48 2016 +0000 @@ -13,7 +13,10 @@ <div class="wrap"> <header><h1><a href="..">cl-ggp</a></h1></header> <div class="markdown"> -<h1 id="installation"><a href="">Installation</a></h1></div> +<h1 id="installation"><a href="">Installation</a></h1><p><code>cl-ggp</code> is compatible with Quicklisp, but not <em>in</em> Quicklisp (yet?). You can +clone the repository into your <a href="https://www.quicklisp.org/beta/faq.html#local-project">Quicklisp local-projects</a> directory for +now.</p> + </div> <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ diff -r 621ae61499ab -r 641956c66398 cl-ggp/overview/index.html --- a/cl-ggp/overview/index.html Wed Mar 23 16:24:01 2016 +0000 +++ b/cl-ggp/overview/index.html Wed Mar 23 21:36:48 2016 +0000 @@ -15,6 +15,8 @@ <div class="markdown"> <h1 id="overview"><a href="">Overview</a></h1><p><code>cl-ggp</code> handles the GGP protocol for you. Players are implemented as CLOS objects.</p> +<p>This document assumes you know what General Game Playing is, what GDL is, and +how the GGP community/competitions/etc work.</p> <div class="toc"> <ul> <li><a href="#basics">Basics</a></li> @@ -25,24 +27,26 @@ <li><a href="#player-stop-game">player-stop-game</a></li> </ul> </li> +<li><a href="#timeouts">Timeouts</a></li> +<li><a href="#symbols">Symbols</a></li> <li><a href="#example-player">Example Player</a></li> </ul></div> <h2 id="basics">Basics</h2> <p>You can create your own player by extending the <code>ggp-player</code> class, creating an object, and calling <code>start-player</code> on it to fire it up:</p> -<div class="codehilite"><pre>(defclass simple-player (ggp:ggp-player) - ()) +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defclass</span> <span class="nv">simple-player</span> <span class="p">(</span><span class="nv">ggp:ggp-player</span><span class="p">)</span> + <span class="p">())</span> -(defvar *player* (make-instance 'simple-player - :name "SimplePlayer" - :port 4000)) +<span class="p">(</span><span class="nb">defvar</span> <span class="vg">*player*</span> <span class="p">(</span><span class="nb">make-instance</span> <span class="ss">'simple-player</span> + <span class="ss">:name</span> <span class="s">"SimplePlayer"</span> + <span class="ss">:port</span> <span class="mi">4000</span><span class="p">))</span> -(ggp:start-player *player*) +<span class="p">(</span><span class="nv">ggp:start-player</span> <span class="vg">*player*</span><span class="p">)</span> </pre></div> -<p><code>ggp-player</code> takes <code>:name</code> and <code>:port</code> initargs. It has a few other internal -slots you shouldn't mess with.</p> +<p><code>ggp-player</code> takes <code>:name</code> and <code>:port</code> initargs, which do what you think they +do. It has a few other internal slots you shouldn't mess with.</p> <p>You can kill a player with <code>kill-player</code>.</p> <h2 id="functionality">Functionality</h2> <p><code>cl-ggp</code> defines four generic methods that are called on players at various @@ -51,8 +55,8 @@ <p>At a minimum you <strong>must</strong> implement <code>player-select-move</code>. The others are optional and will default to doing nothing.</p> <h3 id="player-start-game">player-start-game</h3> -<div class="codehilite"><pre>(defmethod player-start-game ((player YOUR-PLAYER) rules role start-clock play-clock) - ...) +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defmethod</span> <span class="nv">player-start-game</span> <span class="p">((</span><span class="nv">player</span> <span class="nv">YOUR-PLAYER</span><span class="p">)</span> <span class="nv">rules</span> <span class="nv">role</span> <span class="nv">timeout</span><span class="p">)</span> + <span class="o">...</span><span class="p">)</span> </pre></div> @@ -60,34 +64,132 @@ <p><code>rules</code> is the GDL rules of the game, parsed into Lisp lists/symbols. You'll probably want to feed this into a logic library.</p> <p><code>role</code> is a symbol representing which role you've been assigned.</p> -<p><code>start-clock</code> is </p> -<p><code>play-clock</code> is </p> +<p><code>timeout</code> is the timestamp that the response to the server is due by, in +internal-real time units (more on this later).</p> <h3 id="player-update-game">player-update-game</h3> -<div class="codehilite"><pre>(defmethod player-update-game ((player YOUR-PLAYER) moves) - ...) +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defmethod</span> <span class="nv">player-update-game</span> <span class="p">((</span><span class="nv">player</span> <span class="nv">YOUR-PLAYER</span><span class="p">)</span> <span class="nv">moves</span><span class="p">)</span> + <span class="o">...</span><span class="p">)</span> </pre></div> -<p>This is called once per turn, to update the game state with the moves each -player selected.</p> -<p><code>moves</code> is a list of the moves made by all players.</p> +<p>This is called once per turn, to allow you to update the game state with the +moves each player selected.</p> +<p><code>moves</code> will be an association list of <code>(role . move)</code> conses representing the +moves made by each player last turn.</p> <h3 id="player-select-move">player-select-move</h3> -<div class="codehilite"><pre>(defmethod player-select-move ((player YOUR-PLAYER)) - ...) +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defmethod</span> <span class="nv">player-select-move</span> <span class="p">((</span><span class="nv">player</span> <span class="nv">YOUR-PLAYER</span><span class="p">)</span> <span class="nv">timeout</span><span class="p">)</span> + <span class="o">...</span><span class="p">)</span> </pre></div> -<p>This is called once per turn. It should return the move your player wants to +<p>This is called once per turn. It needs to return the move your player wants to do. All players <strong>must</strong> implement this function.</p> +<p><code>timeout</code> is the timestamp that the response to the server is due by, in +internal-real time units (more on this later).</p> <h3 id="player-stop-game">player-stop-game</h3> -<div class="codehilite"><pre>(defmethod player-stop-game ((player YOUR-PLAYER)) - ...) +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defmethod</span> <span class="nv">player-stop-game</span> <span class="p">((</span><span class="nv">player</span> <span class="nv">YOUR-PLAYER</span><span class="p">))</span> + <span class="o">...</span><span class="p">)</span> </pre></div> <p>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.</p> +<h2 id="timeouts">Timeouts</h2> +<p>The GGP protocol specifies time limits for players.</p> +<p>When the initial game description is sent, players have a limited amount of time +for "metagaming" where they might process the rules, build alternate +representations (e.g. a propnet), start searching the game's DAG, etc.</p> +<p>Once the initial "metagaming" phase is over, the players must each choose a move +in every round, and there is a time limit on how long it takes them to respond.</p> +<p><code>cl-ggp</code> mostly handles the annoying work of calculating the time your methods +have available for work, but there are a few caveats.</p> +<p>First: the <code>timestamp</code> arguments your methods get are timestamps of +internal-real time. If you're not familiar with how interal time works in +Common Lisp, you should fix that. Read up on <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_get_in.htm#get-internal-real-time">get-internal-real-time</a> and +<a href="http://www.lispworks.com/documentation/HyperSpec/Body/v_intern.htm#internal-time-units-per-second">internal-time-units-per-second</a>.</p> +<p>So you need to finish responding to the request by the internal-real timestamp +given. This brings us to the second caveat: "finishing responding" includes +returning back up the call stack and sending the HTTP response back to the game +server. It's probably wise to bake a bit of breathing room into your player and +not use <em>all</em> the given time in <code>timeout</code>, but <code>cl-ggp</code> doesn't try to decide +how much time to reserve. You should decide that based on things like:</p> +<ul> +<li>Your ping to the GGP server.</li> +<li>How likely it is for your Lisp process to get descheduled by your OS, and how + long it might take to start running again.</li> +<li>Worst-case duration of a GC pause right before sending the response.</li> +<li>How brave you're feeling today.</li> +</ul> +<p>In a nutshell: when <code>(get-internal-real-time)</code> returns the number given to you +in <code>timeout</code>, your message better have already reached the server.</p> +<h2 id="symbols">Symbols</h2> +<p>The other tricky part about <code>cl-ggp</code> is how it handles symbols.</p> +<p>Game descriptions are written in GDL, a fragment of which might look like this:</p> +<div class="codehilite"><pre>(role x) +(role o) +(init (control x)) + +(<= (legal ?role (mark ?row ?col ?role)) + (control ?role) + (is-blank ?row ?col)) +</pre></div> + + +<p>This is obviously pretty close to Lisp — it's just a bunch of lists of +symbols — so reading it in is almost trivial. The main question is which +package the symbols get interned into.</p> +<p><code>cl-ggp</code> interns all GDL symbols into a separate package called <code>GGP-RULES</code> to +prevent polluting other packages. It also clears this package between matches +(except for a few special symbols that survive the clearing) to prevent building +up mountains of garbage symbols from building up over time, especially when GDL +scrambing is enabled on the server.</p> +<p>This means that when your player's methods get symbols in their input (i.e. in +the <code>rules</code>, <code>role</code>, and <code>moves</code> arguments) those symbols will be interned in +<code>GGP-RULES</code>. When your player returns a move to make from <code>player-select-move</code>, +any symbols inside it must be interned in <code>GGP-RULES</code> for things to work +correctly.</p> +<p>This is kind of shitty, and the author is aware of that. Suggestions for less +shitty alternatives that still feel vaguely lispy are welcome.</p> <h2 id="example-player">Example Player</h2> +<p><code>cl-ggp</code> 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.</p> +<p>First we'll define the player class and implement the required +<code>player-select-move</code> method for it:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defclass</span> <span class="nv">simple-player</span> <span class="p">(</span><span class="nv">ggp:ggp-player</span><span class="p">)</span> + <span class="p">())</span> + +<span class="p">(</span><span class="nb">defmethod</span> <span class="nv">ggp:player-select-move</span> <span class="p">((</span><span class="nv">player</span> <span class="nv">simple-player</span><span class="p">)</span> <span class="nv">timeout</span><span class="p">)</span> + <span class="ss">'ggp-rules::wait</span><span class="p">)</span> +</pre></div> + + +<p>Our player doesn't store any state of its own, so it doesn't need any extra +slots. Notice how <code>player-select-move</code> returns a symbol from the <code>GGP-RULES</code> +package as discussed above.</p> +<p>The move our stupid player always returns is <code>WAIT</code>. 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.</p> +<p>Now we can actually create a player:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nb">defvar</span> <span class="vg">*player*</span> + <span class="p">(</span><span class="nb">make-instance</span> <span class="ss">'simple-player</span> + <span class="ss">:name</span> <span class="s">"SimplePlayer"</span> + <span class="ss">:port</span> <span class="mi">5000</span><span class="p">))</span> +</pre></div> + + +<p>And fire it up:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nv">ggp:start-player</span> <span class="vg">*player*</span><span class="p">)</span> +</pre></div> + + +<p>Now we can play a few games with it. We'll probably lose every time unless +we're playing an unscrambled game of <a href="https://bitbucket.org/snippets/sjl/erRjL">Don't Press the Button</a>.</p> +<p>Once we're done we can kill it to free up the port:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nv">ggp:kill-player</span> <span class="vg">*player*</span><span class="p">)</span> +</pre></div> </div> <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p> <script> diff -r 621ae61499ab -r 641956c66398 cl-ggp/reference/index.html --- a/cl-ggp/reference/index.html Wed Mar 23 16:24:01 2016 +0000 +++ b/cl-ggp/reference/index.html Wed Mar 23 21:36:48 2016 +0000 @@ -24,6 +24,10 @@ <li><a href="#ggp-player-class">GGP-PLAYER (class)</a><ul> <li><a href="#slot-name">Slot NAME</a></li> <li><a href="#slot-port">Slot PORT</a></li> +<li><a href="#slot-match-roles">Slot MATCH-ROLES</a></li> +<li><a href="#slot-start-clock">Slot START-CLOCK</a></li> +<li><a href="#slot-play-clock">Slot PLAY-CLOCK</a></li> +<li><a href="#slot-message-start">Slot MESSAGE-START</a></li> <li><a href="#slot-current-match">Slot CURRENT-MATCH</a></li> <li><a href="#slot-server">Slot SERVER</a></li> </ul> @@ -36,8 +40,10 @@ <li><a href="#start-player-function">START-PLAYER (function)</a></li> </ul> </li> +<li><a href="#package-ggp-rules">Package GGP-RULES</a></li> </ul></div> <h2 id="package-ggp">Package GGP</h2> +<p>The main GGP package.</p> <h3 id="ggp-player-class">GGP-PLAYER (class)</h3> <p>The base class for a GGP player. Custom players should extend this.</p> <h4 id="slot-name">Slot NAME</h4> @@ -54,6 +60,31 @@ <li>Reader: <code>PLAYER-PORT</code></li> </ul> <p>The port the HTTP server should listen on.</p> +<h4 id="slot-match-roles">Slot MATCH-ROLES</h4> +<ul> +<li>Allocation: INSTANCE</li> +<li>Type: <code>(OR NULL LIST)</code></li> +<li>Reader: <code>PLAYER-MATCH-ROLES</code></li> +</ul> +<p>A list of the roles for the current match. Feel free to read and use this if you like. <strong>Do not modify this.</strong></p> +<h4 id="slot-start-clock">Slot START-CLOCK</h4> +<ul> +<li>Allocation: INSTANCE</li> +<li>Type: <code>(OR NULL (INTEGER 1))</code></li> +</ul> +<p>The start clock for the current game. <strong>Do not touch this.</strong> Use the <code>timeout</code> value passed to your methods instead.</p> +<h4 id="slot-play-clock">Slot PLAY-CLOCK</h4> +<ul> +<li>Allocation: INSTANCE</li> +<li>Type: <code>(OR NULL (INTEGER 1))</code></li> +</ul> +<p>The play clock for the current game. <strong>Do not touch this.</strong> Use the <code>timeout</code> value passed to your methods instead.</p> +<h4 id="slot-message-start">Slot MESSAGE-START</h4> +<ul> +<li>Allocation: INSTANCE</li> +<li>Type: <code>(OR NULL (INTEGER 0))</code></li> +</ul> +<p>The (internal-real) timestamp of when the current GGP message was received. <strong>Do not touch this.</strong> Use the <code>timeout</code> value passed to your methods instead.</p> <h4 id="slot-current-match">Slot CURRENT-MATCH</h4> <ul> <li>Allocation: INSTANCE</li> @@ -73,22 +104,30 @@ <p>This will <strong>not</strong> be done gently. No cleanup will be performed if the player is in the middle of a game. Be careful.</p> <h3 id="player-select-move-generic-function">PLAYER-SELECT-MOVE (generic function)</h3> -<div class="codehilite"><pre>(PLAYER-SELECT-MOVE PLAYER) +<div class="codehilite"><pre>(PLAYER-SELECT-MOVE PLAYER TIMEOUT) </pre></div> <p>Called when it's time for the player to select a move to play.</p> <p>Must return a list/symbol of the GDL move to play. Note that any symbols in - the move should be ones that are interned in the <code>GGP</code> package. The author is - aware that this sucks and welcomes suggestions on how to make it less awful.</p> + the move should be ones that are interned in the <code>GGP-RULES</code> package. The + author is aware that this sucks and welcomes suggestions on how to make it + less awful.</p> +<p><code>timeout</code> is the timestamp that the response to the server is due by, in + internal-real time units. Basically: when <code>(get-internal-real-time)</code> returns + this number, your message better have reached the server.</p> <h3 id="player-start-game-generic-function">PLAYER-START-GAME (generic function)</h3> -<div class="codehilite"><pre>(PLAYER-START-GAME PLAYER RULES ROLE START-CLOCK PLAY-CLOCK) +<div class="codehilite"><pre>(PLAYER-START-GAME PLAYER RULES ROLE TIMEOUT) </pre></div> <p>Called when the game is started.</p> <p><code>rules</code> is a list of lists/symbols representing the GDL description of the - game. Note that all symbols are interned in the <code>GGP</code> package.</p> + game. Note that all symbols are interned in the <code>GGP-RULES</code> package.</p> +<p><code>role</code> is a symbol representing the role of the player in this game.</p> +<p><code>timeout</code> is the timestamp that the response to the server is due by, in + internal-real time units. Basically: when <code>(get-internal-real-time)</code> returns + this number, your message better have reached the server.</p> <h3 id="player-stop-game-generic-function">PLAYER-STOP-GAME (generic function)</h3> <div class="codehilite"><pre>(PLAYER-STOP-GAME PLAYER) </pre></div> @@ -102,14 +141,23 @@ </pre></div> -<p>Called after all player have made their moves.</p> -<p><code>moves</code> will be a list of moves made by the players.</p> +<p>Called after all players have made their moves.</p> +<p><code>moves</code> will be a list of <code>(role . move)</code> conses representing moves made by + each player last turn.</p> <h3 id="start-player-function">START-PLAYER (function)</h3> <div class="codehilite"><pre>(START-PLAYER PLAYER) </pre></div> <p>Start the HTTP server for the given player.</p> +<h2 id="package-ggp-rules">Package GGP-RULES</h2> +<p>Symbol storage package.</p> +<p>The GGP-RULES package is used to hold all the symbols in the GDL game + descriptions, as well as some special symbols in the GGP protocol. It is + cleared between game runs to avoid a buildup of garbage symbols (especially + when GDL scrambling is turned on), though certain special symbols are allowed + to survive the clearing.</p> +<p>This is ugly. I'm sorry. I'm open to suggestions on better ways to do this.</p> </div> <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p> <script>