## cards.janet
## Card to Text
(defn to-text [{:suit suit :rank rank}]
(case suit
"joker" (case rank
2 "Big Joker"
1 "Little Joker")
(let [suits {"diamonds" "♦" "spades" "♠" "clubs" "♣" "hearts" "♥"}
ranks {1 "Ace"
2 "2"
3 "3"
4 "4"
5 "5"
6 "6"
7 "7"
8 "8"
9 "9"
10 "10"
11 "J"
12 "Q"
13 "K"}]
(string (suits suit) (ranks rank)))))
## Of Suit or Off
(defn- of-suit
"Return any cards in stack that match the led suit."
[led-suit stack]
(let [without-jokers |(= led-suit ($0 :suit))]
(filter without-jokers stack)))
(defn- of-suit-or-jokers
"Return any cards in stack that match the led suit, or are jokers."
[led-suit stack]
(let [with-jokers |(or (= led-suit ($0 :suit))
(= "joker" ($0 :suit)))]
(filter with-jokers stack)))
(defn of-suit-or-off
"Return any cards in stack that match the led suit, otherwise return all cards."
[led-suit current-bid stack]
(let [on-suit (if (= led-suit (current-bid :suit))
# If the led suit is trumps, then look for all cards
# of that suit or jokers.
(of-suit-or-jokers led-suit stack)
# Otherwise, look just for cards of that suit.
(of-suit led-suit stack))]
(if (> (length on-suit) 0) on-suit stack)))
## Enable Card Comparison
(defn- uptown [a b] (match [a b]
# `1` is the rank of the Ace.
@[1 1] 0
@[1 _] 1
@[_ 1] -1
@[x y] (compare x y)))
(defn- downtown [a b] (match [a b]
# `1` is the rank of the Ace.
@[1 1] 0
@[1 _] 1
@[_ 1] -1
@[x y] (- (compare x y))))
(defn- uptown-card [trumps]
@{:compare (fn [self other]
(match [(self :suit) (other :suit)]
@["joker" "joker"] (compare (self :rank) (other :rank))
@["joker" _] 1
@[_ "joker"] -1
@[trumps trumps] (uptown (self :rank) (other :rank))
@[trumps _] 1
@[_ trumps] -1
@[_ _] (uptown (self :rank) (other :rank))))})
(defn- downtown-card [trumps]
@{:compare (fn [self other]
(match [(self :suit) (other :suit)]
@["joker" "joker"] (compare (self :rank) (other :rank))
@["joker" _] 1
@[_ "joker"] -1
@[trumps trumps] (downtown (self :rank) (other :rank))
@[trumps _] 1
@[_ trumps] -1
@[_ _] (downtown (self :rank) (other :rank))) )})
(def- notrumps-card
@{:compare (fn [self other]
(match [(self :suit) (other :suit)]
@["joker" "joker"] (compare (self :rank) (other :rank))
@["joker" _] -1
@[_ "joker"] 1
@[_ _] (uptown (self :rank) (other :rank))))})
(def- notrumps-downtown-card
@{:compare (fn [self other]
(match [(self :suit) (other :suit)]
@["joker" "joker"] (compare (self :rank) (other :rank))
@["joker" _] -1
@[_ "joker"] 1
@[_ _] (downtown (self :rank) (other :rank))))})
(defn- make-compare-enable-fn [current-bid]
(let [proto (match [(current-bid :suit) (current-bid :direction)]
@["notrumps" "up"] notrumps-card
@["notrumps" "down"] notrumps-downtown-card
@[trumps "up"] (uptown-card trumps)
@[trumps "down"] (downtown-card trumps))]
(fn [card]
(table/setproto card proto))))
## High Card
(defn- of-suit-or-trumps
"Return any cards in stack that match the led suit, or are trumps."
[led-suit trumps stack]
(let [with-jokers-and-trumps
|(or (= led-suit ($0 :suit))
(= trumps ($0 :suit))
(= "joker" ($0 :suit)))]
(filter with-jokers-and-trumps stack)))
(defn high-card
"Trick resolution for all Bid Whist games."
[stack led-suit bid]
(let [f (make-compare-enable-fn bid)]
(->> stack
# Get a version of the current-trick where
# each card has a `:compare` method that's
# aware of the current bid.
(map f)
(of-suit-or-trumps led-suit (bid :suit))
# Get the highest card according to each
# one's `:compare`.
(extreme compare>))))