@@ 0,0 1,24 @@
+@s Appendix: All Events
+
+--- events.janet
+(import players)
+
+@{Events: Pick 1}
+@{Events: Draw}
+@{Events: Add Decoration}
+
+(defn clear-decoration [player name] {:event "clear_decoration" :name name :player player})
+
+(defn end-game
+ [players team1 score1 team2 score2]
+ (let [players1 (players/of-team players (string team1))
+ players2 (players/of-team players (string team2))]
+ {:event "end_game" :scores (zipcoll (array/concat players1 players2)
+ [score1 score1 score2 score2])}))
+
+(defn prompt-play [player &opt from] {:event "prompt_play" :player player :to "trick" :count 1 :from from})
+
+(defn prompt-discard [player count] {:event "prompt_discard" :player player :count count})
+
+(defn add-info [id label] {:event "add_info" :id id :label label})
+---<
\ No newline at end of file
@@ 0,0 1,238 @@
+@title Bid Whist: An Implementation for Tamerlane
+@code_type janet .janet
+@comment_type # %s
+@compiler JANET_PATH=janet_modules jpm build
+
+@s Introduction
+
+This program implements a **rules engine** for Tamerlane, the Card
+Game Server. It provides a working version of the game Bid Whist, as
+well as an example implementation of a realistically complex rules
+engine.
+
+--- whist.janet
+(defn next
+ ```
+ Rules engine for Bid Whist.
+ ```
+ [{:state state
+ :players players
+ :action action}]
+ @{Return Next State})
+---
+
+The most important function that we need to implement when building a
+rules engine is `next`. Ultimately, we'll expose an API endpoint that
+accepts a POST request and passes the body to `next`. The input to our
+function describes the state of the entire session, and the return
+value of this function will be to describe *what happens next.*
+
+The three components of the input are the `state`, the `players`, and
+an `action`.
+
+Every *next state* describes a change after some single action.
+
+@s State
+
+The `state` consists of the state of the game. This includes things
+like the cards on the table and the scores for the players, but it
+also will include arbitrary metadata that you read and write.
+
+The `players` are a list of the players, their metadata, and their
+current hands.
+
+@s
+
+The most basic and important attribute of the game state is the
+`phase`. You can populate this however you like, though it must be
+present.
+
+Here we'll match on the game phase to call one of the five main game
+modules. Each one has the same return signature and implements the
+`next` function.
+
+--- whist.janet :=
+(import game/deal)
+(import game/bid)
+(import game/discard)
+(import game/beginplay)
+(import game/play)
+
+(defn next
+ ```
+ Rules engine for Bid Whist.
+ ```
+ [{:state state
+ :players players
+ :action action}]
+ (match (state :phase)
+ "deal" (deal/deal-phase state players)
+ "bid" (bid/bid-phase state players action)
+ "discard" (discard/discard-phase state players action)
+ "begin_play" (beginplay/begin-play-phase state players action)
+ "play" (play/play-phase state players action)))
+---
+
+We'll start with the deal. We see that we only need the game state and
+the players, as it's the first thing that happens in the game.
+
+@s The Deal
+
+There's very little logic in the deal, so it's a good place to
+start. We simply pull out the first player and then return.
+
+--- Main Deal Function
+(defn deal-phase
+ ```
+ Each player starts with 12 cards.
+
+ The first player is prompted to begin bidding.
+ ```
+ [state players]
+ @{Get The First Player}
+ @{Return Value}
+---
+
+@s
+
+`players` is a list of objects representing each player. `(player :id)`
+is a string that's guaranteed to be unique among the players. Here
+we'll simply grab the first player's id.
+
+--- Get The First Player
+(let [bidder ((in players 0) :id)]
+---
+
+@s Return: the New State and a List of Events
+
+Finally we see the return value of our functions.
+
+We simply return a tuple of two elements. The first is the new game
+state; the second is an array of **events** to be emitted.
+
+--- Return Value
+[
+ @{New State}
+ @{Deal Events}
+]))
+---
+
+@s
+
+First the state.
+
+We begin by merging some values into the state we were passed in. In
+general this is good practice, because whatever we return will become
+the new state. So if we clobber something useful, it be lost.
+
+The new state we're merging in has two attributes: `phase`, which
+we've already covered, and `meta`. In fact, `meta` has no semantics to
+the Tamerlane system. We'll be using it for our own purposes. The game
+won't display it; it will simply include it in the next game state it
+sends over. Thus, it's an easy way for us to keep track of any game
+variables we like.
+
+In this case, we'll need to keep track of two variables: `high_bid`,
+which is empty for now; and `not_passed`, which is a set containing
+all the players. We'll use them both in the bidding phase.
+
+--- New State
+# State: Deal -> Bid
+(merge state {:phase "bid"
+ :meta (new-meta players)})
+---
+
+--- Initialize Metadata
+(defn- new-meta [players] {:high_bid {}
+ :not_passed (zipcoll (map |($0 :id) players) [true true true true])})
+---
+
+@s
+
+Next are the events.
+
+--- Deal Events
+(array/concat
+ (all-draw players)
+ (events/add-decoration bidder "bid_action" "bidding")
+ (events/pick1 "bid" bidder (bids/available-bids)))
+---
+
+@s Draw
+
+Each element in the events array is, fittingly, a call to the `events`
+module. There are three types of event. First, the `draw` event:
+
+--- Each Player Draws
+(defn- all-draw [players] (map |(events/draw ($0 :id) 12) players))
+---
+
+--- Events: Draw
+(defn draw [player count] {:event "draw" :player player :count count})
+---
+
+`all-draw` emits one event for each player. We end up with four
+structs corresponding to the four players.
+
+As we will see, every event has an `event` attribute, which identifies
+a specific side effect understood by the Tamerlane server. Every type
+of event has its own set of attributes. Together they constitute the Tamerlane API.
+
+The `draw` event tells the server to draw a certain number of cards
+into the specified player's hand.
+
+Notice that the hands are not part of the state, and we haven't seen
+the deck anywhere either; these are controlled by the server. When the
+server processes one of these events, it will remove the first 12
+cards from the deck and append them to the hand of the specified
+player.
+
+@s Add Decoration
+
+The next type of event is `add-decoration`.
+
+--- Events: Add Decoration
+(defn add-decoration [player name value] {:event "add_decoration" :name name :player player :value value})
+---
+
+**decoration** is a generic way of displaying information associated
+with a player. In this case we set the `bid_action` decoration for
+the first player to `bidding`. The name, `bid_action`, is for our use
+only; the server will display `bidding` next to the icon for that
+player.
+
+@s Pick 1
+
+Finally, we include a **prompt**.
+
+--- Events: Pick 1
+(defn pick1 [name player choices] {:event "prompt_select" :name name :player player :count 1 :from choices})
+---
+
+Most state changes (except for the first, as we've seen!) are in
+response to **actions**. And all actions are in response to
+prompts. This is the first kind of prompt we've seen: a **select
+prompt**, where the player is presented with a simple list of choices
+and they have to choose one.
+
+When they do that, the game server will make a `next` call to our
+rules server, and the value of their selection will be the `action`.
+
+In this case, the choices are a list of possible bids. Thus the bid phase begins.
+
+@s
+
+We've now performed everything we need to complete the deal: we've set
+up our state, populated the players' hands, set a player decoration,
+and prompted for the first player action.
+
+--- game/deal.janet
+(import events)
+(import bids)
+
+@{Each Player Draws}
+@{Initialize Metadata}
+@{Main Deal Function}
+---
+
+@include lit/events.lit<
\ No newline at end of file