~subsetpark/whist

d8954c485c92c4401d35d0626c314a2c3124c3e1 — Zach Smith 6 months ago 7781d35
Work around @include bug for now
4 files changed, 115 insertions(+), 119 deletions(-)

M bids.janet
M lit/bids.lit
M lit/events.lit
M whist.lit
M bids.janet => bids.janet +1 -0
@@ 45,6 45,7 @@
    (row 1)
    (error (string "Not found: " (string/format "%q" bid) " in " (string/format "%q" source)))))
## Available Bids
(defn- no-trumps? [bid] (= (bid :suit) "no_trumps"))

(defn available-bids
  [&opt high-bid]

M lit/bids.lit => lit/bids.lit +0 -113
@@ 1,113 0,0 @@
@s Appendix: Bids

We can hardcode the list of possible bids and define a few functions
to expose them. They are written in the `select` format (ie, in the
format used to define the choices for a `select` prompt). It's a
simple format, a list of pairs, where the first element is the value
that will be sent to the rules engine if that choice is selected, and
the second element is the string to display when exposing the prompt
to the player.

This kind of general format gives us a maximum of flexibility as
game-developers; in this case, the value of a selection is an object
containing `count` and `direction` attributes, but this value will be
passed transparently back to us from the server, so we can make it
whatever we like (as long as it can be serialized and deserialized
from JSON!).

--- All Bids
(def-
  bids
  [[{:count 3 :direction "up"} "3 Uptown"]
   [{:count 3 :direction "down"} "3 Downtown"]
   [{:count 3 :suit "no_trumps"} "3 No-Trumps"]
   [{:count 4 :direction "up"} "4 Uptown"]
   [{:count 4 :direction "down"} "4 Downtown"]
   [{:count 4 :suit "no_trumps"} "4 No-Trumps"]
   [{:count 5 :direction "up"} "5 Uptown"]
   [{:count 5 :direction "down"} "5 Downtown"]
   [{:count 5 :suit "no_trumps"} "5 No-Trumps"]
   [{:count 6 :direction "up"} "6 Uptown"]
   [{:count 6 :direction "down"} "6 Downtown"]
   [{:count 6 :suit "no_trumps"} "6 No-Trumps"]
   [{:count 7 :direction "up"} "7 Uptown"]
   [{:count 7 :direction "down"} "7 Downtown"]
   [{:count 7 :suit "no_trumps"} "7 No-Trumps"]])
---

@s Determining the Second Bid

For each of the two types of bids in the auction--suited bids and
no-trumps bids---there's a second call that the declarer makes when
they have won the auction. For suited bids, the declarer calls the
suit. For no-trumps bids, the declarer calls the direction.

--- Second Bids
(def- direction [[{:direction "up"} "Uptown"]
		[{:direction "down"} "Downtown"]])

(def- suit [[{:suit "hearts"} "Hearts"]
	   [{:suit "spades"} "Spades"]
	   [{:suit "diamonds"} "Diamonds"]
	   [{:suit "clubs"} "Clubs"]])

(def second-bids (array/concat @[] direction suit))

(defn- no-trumps? [bid] (= (bid :suit) "no_trumps"))

(defn second-bid
  [bid]
  (if (no-trumps? bid) direction suit))
---

@s Determining Available Bids

The bulk of the game logic that we need to implement in the bids
module is determining what bids are available for the next bidder
given what the current high bid in the auction is.

The logic is slightly complex, because *Uptown and Downtown bids are
equivalent* in terms of the auction. That means that if the current
high bid is a suited bid, the next available bid is the no-trumps bid
of the same number. If the high bid is a no-trumps bid, the next
available bid is the Uptown bid of the next number.

Finally, if three out of four players pass, then the fourth player
*has* to bid; so in that case, the `Pass` option isn't available to them.

---Available Bids
(defn- no-trumps? [bid] (= (bid :suit) "no_trumps"))

(defn available-bids
  [&opt high-bid]
  (case high-bid
    nil (array ;bids ["pass" "Pass"])
    (if-let [minimum-bid (if (no-trumps? high-bid)
		 	   {:count (inc (high-bid :count)) :direction "up"} 
			   {:count (high-bid :count) :suit "no_trumps"})
	     minimum-bid-ind (find-row-index minimum-bid bids)]
      (array/push (array/slice bids minimum-bid-ind) ["pass" "Pass"])
      @[["pass" "Pass"]])))

(defn force-bid
  "The bidder can't pass; they can only select an actual bid."
  []
  (array ;bids))
---

@s 

--- bids.janet
@{All Bids}
@{Second Bids}
(defn- find-row-index [bid source] (find-index |(= ($0 0) bid) source))

(defn to-text
  [bid &opt source]
  (default source bids)
  (if-let [ind (find-row-index (freeze bid) source)
	   row (source ind)]
    (row 1)
    (error (string "Not found: " (string/format "%q" bid) " in " (string/format "%q" source)))))
@{Available Bids}
---
\ No newline at end of file

M lit/events.lit => lit/events.lit +114 -0
@@ 1,3 1,117 @@
@s Appendix: Bids

We can hardcode the list of possible bids and define a few functions
to expose them. They are written in the `select` format (ie, in the
format used to define the choices for a `select` prompt). It's a
simple format, a list of pairs, where the first element is the value
that will be sent to the rules engine if that choice is selected, and
the second element is the string to display when exposing the prompt
to the player.

This kind of general format gives us a maximum of flexibility as
game-developers; in this case, the value of a selection is an object
containing `count` and `direction` attributes, but this value will be
passed transparently back to us from the server, so we can make it
whatever we like (as long as it can be serialized and deserialized
from JSON!).

--- All Bids
(def-
  bids
  [[{:count 3 :direction "up"} "3 Uptown"]
   [{:count 3 :direction "down"} "3 Downtown"]
   [{:count 3 :suit "no_trumps"} "3 No-Trumps"]
   [{:count 4 :direction "up"} "4 Uptown"]
   [{:count 4 :direction "down"} "4 Downtown"]
   [{:count 4 :suit "no_trumps"} "4 No-Trumps"]
   [{:count 5 :direction "up"} "5 Uptown"]
   [{:count 5 :direction "down"} "5 Downtown"]
   [{:count 5 :suit "no_trumps"} "5 No-Trumps"]
   [{:count 6 :direction "up"} "6 Uptown"]
   [{:count 6 :direction "down"} "6 Downtown"]
   [{:count 6 :suit "no_trumps"} "6 No-Trumps"]
   [{:count 7 :direction "up"} "7 Uptown"]
   [{:count 7 :direction "down"} "7 Downtown"]
   [{:count 7 :suit "no_trumps"} "7 No-Trumps"]])
---

@s Determining the Second Bid

For each of the two types of bids in the auction--suited bids and
no-trumps bids---there's a second call that the declarer makes when
they have won the auction. For suited bids, the declarer calls the
suit. For no-trumps bids, the declarer calls the direction.

--- Second Bids
(def- direction [[{:direction "up"} "Uptown"]
		[{:direction "down"} "Downtown"]])

(def- suit [[{:suit "hearts"} "Hearts"]
	   [{:suit "spades"} "Spades"]
	   [{:suit "diamonds"} "Diamonds"]
	   [{:suit "clubs"} "Clubs"]])

(def second-bids (array/concat @[] direction suit))

(defn- no-trumps? [bid] (= (bid :suit) "no_trumps"))

(defn second-bid
  [bid]
  (if (no-trumps? bid) direction suit))
---

@s Determining Available Bids

The bulk of the game logic that we need to implement in the bids
module is determining what bids are available for the next bidder
given what the current high bid in the auction is.

The logic is slightly complex, because *Uptown and Downtown bids are
equivalent* in terms of the auction. That means that if the current
high bid is a suited bid, the next available bid is the no-trumps bid
of the same number. If the high bid is a no-trumps bid, the next
available bid is the Uptown bid of the next number.

Finally, if three out of four players pass, then the fourth player
*has* to bid; so in that case, the `Pass` option isn't available to them.

--- Available Bids
(defn- no-trumps? [bid] (= (bid :suit) "no_trumps"))

(defn available-bids
  [&opt high-bid]
  (case high-bid
    nil (array ;bids ["pass" "Pass"])
    (if-let [minimum-bid (if (no-trumps? high-bid)
		 	   {:count (inc (high-bid :count)) :direction "up"} 
			   {:count (high-bid :count) :suit "no_trumps"})
	     minimum-bid-ind (find-row-index minimum-bid bids)]
      (array/push (array/slice bids minimum-bid-ind) ["pass" "Pass"])
      @[["pass" "Pass"]])))

(defn force-bid
  "The bidder can't pass; they can only select an actual bid."
  []
  (array ;bids))
---

@s 

--- bids.janet
@{All Bids}
@{Second Bids}
(defn- find-row-index [bid source] (find-index |(= ($0 0) bid) source))

(defn to-text
  [bid &opt source]
  (default source bids)
  (if-let [ind (find-row-index (freeze bid) source)
	   row (source ind)]
    (row 1)
    (error (string "Not found: " (string/format "%q" bid) " in " (string/format "%q" source)))))
@{Available Bids}
---

@s Appendix: All Events

--- events.janet

M whist.lit => whist.lit +0 -6
@@ 2,10 2,6 @@
@code_type janet .janet
@comment_type ## %s

<link href="https://fonts.googleapis.com/css2?family=Old+Standard+TT:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">


@add_css whist.css
@colorscheme atelier.css



@@ 1027,6 1023,4 @@ we can wrap up all the components into a single module.
@{Main Play Function}
---

@include lit/bids.lit

@include lit/events.lit
\ No newline at end of file