Klaverjassen

Klaverjassen Image source: CC 0 Public Domain

Summary

Klaverjassen is a trick-taking partnership card game. Cards are assigned point values depending on their rank. The Piquet deck is shuffled and 8 cards are dealt to each player. Play proceeds in rounds, with complex rules for how and when to follow suit and play trump. The winner of each trick leads the next trick. Points are awarded for individual cards, differentiated by trump and non-trump suits. Points are also calcuated based on if a trick contains sets of face cards, runs in sequence in the same suit, or marriages (King and Queen pairs). The team that captures cards with the most points wins.

Rules of Play

These rules are summarized from https://www.pagat.com/jass/klaverjassen.html.

Variants Used

Valet uses the Utrecht rule for determining trump, where the first player picks the trump suit after seeing their hand.

It also incorporates the Amsterdam rules for playing trumps (quoted from Pagat):

  1. If you cannot follow suit and the card which is winning the trick so far has been played by the opposing team then you must beat this card with a trump if you can. If they are winning with a trump, you must play a higher trump if you can. If you have no higher trump you must throw away a card of another (non-trump) suit. You are not allowed to undertrump (play a lower trump) unless your hand consists entirely of lower trumps;
  2. If you cannot follow suit and the card which is winning the trick so far is your partner’s, then you may play any card of any suit, except that if your partner’s winning card is a trump, you are not allowed to undertrump unless your hand consists entirely of lower trumps;
  3. If trumps are led then subsequent players must beat the highest trump so far played to the trick if they can, even if this highest trump was played by their partner. If they cannot beat the highest trump so far played, they must play a lower trump. If they have no trumps at all, they can play any card.

Klaverjassen represents the Jack-Nine subgenre of Ace-Ten trick-taking games. Other Jack-Nine games include Belote, Jass, and 304.

Information Flow

Card Movement Graph

The diagram above visualizes information flow in Klaverjassen using a single random rollout in CardStock. Visibility is attached to card locations, so all cards in a location share the same visibility; information is revealed or concealed as cards move between locations and ownership changes.

Public locations are shown in green, hidden locations in yellow, private locations in blue, and memory locations in light gray. Locations containing cards with distinct backs are indicated with bold borders. Rectangles denote ownership (players or table), and directed edges show card movement between locations and resulting visibility changes. Taken information is shown with red dotted edges (public to private/hidden), and shared information with blue dashed edges (private cards transferred to another player).

RECYCLE Code

The rules for Klaverjassen are coded in RECYCLE, a card game description language, to encourage standardized implementations across different systems.

You can also Download the code.

;; Klaverjassen
;; https://www.pagat.com/jass/klaverjassen.html

(game
 (setup
  (create players 4)
  (create teams (1, 3) (2, 4))
  ;; Create the deck source, 32 card piquet pack
  (create deck (game iloc STOCK) 
   (deck (RANK (SEVEN, EIGHT, NINE, TEN, J, Q, K, A))
         (COLOR (RED (SUIT (HEARTS, DIAMONDS)))
                (BLACK (SUIT (CLUBS, SPADES)))))))

 (do (
  (shuffle (game iloc STOCK))

  ;; give everyone 8 cards
  (all player 'P 
    (repeat 8 (move (top (game iloc STOCK))
                    (top ('P iloc HAND)))))
  (set (game str TRUMP) NONE)
  (set (game str LEAD) NONE)))

 ;; Forced choice of trump by first player: Utrecht (compulsory play)
 ;; it's just easier this way
 (choice (
  (any (CLUBS, HEARTS, SPADES, DIAMONDS) 'S
       (set (game str TRUMP) 'S))))

 ;; Now we can set up the points
 (do (
  (set (game points TRUMPPOINTS)
   (((RANK : J) (SUIT: (game str TRUMP)) 20) 
    ((RANK : NINE) (SUIT: (game str TRUMP)) 14) 
    ((RANK : A) (SUIT: (game str TRUMP)) 11) 
    ((RANK : TEN) (SUIT: (game str TRUMP)) 10) 
    ((RANK : K) (SUIT: (game str TRUMP)) 4) 
    ((RANK : Q) (SUIT: (game str TRUMP)) 3)))
  (set (game points POINTS)
   (((RANK : A) 11) ((RANK : TEN) 10) ((RANK : K) 4) 
    ((RANK : Q)  3)  ((RANK : J)   2)))
  (set (game points SEQUENCE)
   (((RANK : A)    14) ((RANK : K)    13) ((RANK : Q)   12) 
    ((RANK : J)    11) ((RANK : TEN)  10) ((RANK : NINE) 9) 
    ((RANK : EIGHT) 8) ((RANK : SEVEN) 7)))))

 ;; players play a round until their cards are gone    
 (stage player
  (end (all player 'P (== (size ('P iloc HAND)) 0)))
                
  ;; players play a hand once
  (stage player
   (end (all player 'P (> (size ('P vloc TRICK)) 0)))
            
   (choice (
    ;; if first player, play any card, remember it in the lead spot
    ((== (game str LEAD) NONE)                       
     (any ((current player) iloc HAND) 'C
      (do (
       (move 'C (top ((current player) vloc TRICK)))
       (set (game str LEAD) 
            (cardatt SUIT (top ((current player) vloc TRICK))))
       ;; solidfy card precedence so we know the current winner
       (set (game points PRECEDENCE)
        (((SUIT : (game str TRUMP)) 200) ((SUIT : (game str LEAD)) 100)
         ((RANK : J) (SUIT: (game str TRUMP)) 6) ;; added on to below
         ((RANK : NINE) (SUIT: (game str TRUMP)) 6) ;; added on to below
         ((RANK : A) 8) ((RANK : TEN) 7) ((RANK : K) 6) 
         ((RANK : Q) 5) ((RANK : J) 4) ((RANK : NINE) 3) 
         ((RANK : EIGHT) 2) ((RANK : SEVEN) 1)))))))

    ;; if following player can follow SUIT
    ;;  and suit is TRUMP
    ;;   and you have trump that is higher than the highest played trump?
    ;;    play it
    ((and (== (game str LEAD) (game str TRUMP))
          (> (size (filter ((current player) iloc HAND) 'L 
                    (> (score 'L using (game points PRECEDENCE)) 
                       (scoremax (union (all player 'P ('P vloc TRICK))) 
                        using (game points PRECEDENCE))))) 0))
     (any (filter ((current player) iloc HAND) 'L 
           (> (score 'L using (game points PRECEDENCE)) 
              (scoremax (union (all player 'P ('P vloc TRICK))) 
               using (game points PRECEDENCE)))) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player can follow SUIT
    ;;  and suit is TRUMP
    ;;   cannot beat highest card but still have trump?
    ;;    play lower trump
    ((and (== (game str LEAD) (game str TRUMP))
          (== (size (filter ((current player) iloc HAND) 'L 
                     (> (score 'L using (game points PRECEDENCE)) 
                        (scoremax (union (all player 'P ('P vloc TRICK))) 
                         using (game points PRECEDENCE))))) 0)
          (> (size (filter ((current player) iloc HAND) 'L 
                    (and (== (cardatt SUIT 'L) (game str LEAD))
                         (< (score 'L using (game points PRECEDENCE)) 
                            (scoremax (union (all player 'P ('P vloc TRICK))) 
                             using (game points PRECEDENCE)))))) 0))
     (any (filter ((current player) iloc HAND) 'L 
           (and (== (cardatt SUIT 'L) (game str LEAD))
                (< (score 'L using (game points PRECEDENCE)) 
                   (scoremax (union (all player 'P ('P vloc TRICK))) 
                    using (game points PRECEDENCE))))) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player and can follow SUIT
    ;;  and that suit is not TRUMP
    ;;   play any card that follows SUIT
    ((and (!= (game str LEAD) NONE)
          (!= (game str LEAD) (game str TRUMP))
          (> (size (filter ((current player) iloc HAND) 'L 
                    (== (cardatt SUIT 'L) (game str LEAD)))) 0))
     (any (filter ((current player) iloc HAND) 'L 
           (== (cardatt SUIT 'L) (game str LEAD))) 'C
      (move 'C (top ((current player) vloc TRICK)))))
                                                        
    ;; AMSTERDAM RULES
    ;; if following player and cannot follow SUIT
    ;;  winning card so far, opposing team?
    ;;   and you have trump that is higher?
    ;;    play it
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str LEAD)))) 0)
          (!= (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (> (size (filter ((current player) iloc HAND) 'L 
                    (and (== (cardatt SUIT 'L) (game str TRUMP))
                         (> (score 'L using (game points PRECEDENCE)) 
                            (scoremax (union (all player 'P ('P vloc TRICK))) 
                             using (game points PRECEDENCE)))))) 0))
     (any (filter ((current player) iloc HAND) 'L 
           (and (== (cardatt SUIT 'L) (game str TRUMP))
                (> (score 'L using (game points PRECEDENCE)) 
                   (scoremax (union (all player 'P ('P vloc TRICK))) 
                    using (game points PRECEDENCE))))) 'C
      (move 'C (top ((current player) vloc TRICK)))))
                    
    ;; if following player and cannot follow SUIT
    ;;  winning card so far, opposing team?               
    ;;   no trump higher?
    ;;    play non-trump card
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L)
                     (game str LEAD)))) 0)
          (!= (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (== (size (filter ((current player) iloc HAND) 'L 
                     (and (== (cardatt SUIT 'L) (game str TRUMP))
                          (> (score 'L using (game points PRECEDENCE)) 
                             (scoremax (union (all player 'P ('P vloc TRICK))) 
                              using (game points PRECEDENCE)))))) 0))
     (any (filter ((current player) iloc HAND) 'L 
           (!= (cardatt SUIT 'L) (game str TRUMP))) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player and cannot follow SUIT
    ;;  winning card so far, opposing team?               
    ;;   no trump higher?
    ;;    nothing but trump?
    ;;     play anything
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str LEAD)))) 0)
          (!= (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (== (size (filter ((current player) iloc HAND) 'L 
                     (and (== (cardatt SUIT 'L) (game str TRUMP))
                          (> (score 'L using (game points PRECEDENCE)) 
                             (scoremax (union (all player 'P ('P vloc TRICK))) 
                              using (game points PRECEDENCE)))))) 0)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (and (== (cardatt SUIT 'L) (game str TRUMP))
                          (< (score 'L using (game points PRECEDENCE)) 
                             (scoremax (union (all player 'P ('P vloc TRICK))) 
                              using (game points PRECEDENCE)))))) 
              (size ((current player) iloc HAND))))
     (any ((current player) iloc HAND) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player and cannot follow SUIT
    ;;  winning card so far, partner?
    ;;   winning card is trump?
    ;;    only trump in hand?
    ;;     play any card
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str LEAD)))) 0)
          (== (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (== (cardatt SUIT (max (union (all player 'P ('P vloc TRICK))) 
                             using (game points PRECEDENCE)))
              (game str TRUMP))
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str TRUMP)))) 
              (size ((current player) iloc HAND))))
     (any ((current player) iloc HAND) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player and cannot follow SUIT
    ;;  winning card so far, partner?
    ;;   winning card is trump?
    ;;    not just trump?
    ;;     play anything not trump
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str LEAD)))) 0)
          (== (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (== (cardatt SUIT (max (union (all player 'P ('P vloc TRICK))) 
                             using (game points PRECEDENCE)))
              (game str TRUMP))
          (> (size (filter ((current player) iloc HAND) 'L 
                    (!= (cardatt SUIT 'L) (game str TRUMP)))) 0))                          
                 
     (any (filter ((current player) iloc HAND) 'L 
           (!= (cardatt SUIT 'L) (game str TRUMP))) 'C
      (move 'C (top ((current player) vloc TRICK)))))

    ;; if following player and cannot follow SUIT
    ;;  winning card so far, partner?
    ;;   winning card is not trump?
    ;;    play any card
    ((and (!= (game str LEAD) NONE)
          (== (size (filter ((current player) iloc HAND) 'L 
                     (== (cardatt SUIT 'L) (game str LEAD)))) 0)
          (== (team (current player)) 
              (team (owner (max (union (all player 'P ('P vloc TRICK))) 
                            using (game points PRECEDENCE)))))
          (!= (cardatt SUIT (max (union (all player 'P ('P vloc TRICK))) 
                             using (game points PRECEDENCE))) (game str TRUMP)))                          
                 
     (any ((current player) iloc HAND) 'C
      (move 'C (top ((current player) vloc TRICK))))))))
        
  ;; after players play hand, wrap up trick
  (do (
   ;; determine who won the hand, set them first next time
   (set (game str LEAD) NONE)
   (cycle next (owner (max (union (all player 'P ('P vloc TRICK))) 
                       using (game points PRECEDENCE))))

   ;; extra 10 if last trick
   ((all player 'P (== (size ('P iloc HAND)) 0))
    (inc ((team (next player)) sto SCORE) 10))

   ;; move all the played cards, easier to score
   (all player 'P
    (move (top ('P vloc TRICK)) 
          (top (game vloc PLAYED))))
 
   ;; calculate roem bonus points
   ;;  Run of three cards of the same suit - 20 bonus points
   ;;  Run of four cards of the same suit - 50 bonus points
   (all (partition SUIT (game vloc PLAYED)) 'PC 
    (all (runs largest 3 'PC using (game points SEQUENCE)) 'PR
     (do (
      ((== (size 'PR) 3)
       (inc ((team (next player)) sto SCORE) 20))
      ((== (size 'PR) 4)
       (inc ((team (next player)) sto SCORE) 50))))))


   (all (partition RANK (game vloc PLAYED)) 'PC
    (do (
     ;;  Four kings, queens aces or tens -> 100 bonus points
     ((and (== (size 'PC) 4)
           (or (== (cardatt RANK (top 'PC)) K)
               (== (cardatt RANK (top 'PC)) Q)
               (== (cardatt RANK (top 'PC)) A)
               (== (cardatt RANK (top 'PC)) TEN)))
      (inc ((team (next player)) sto SCORE) 100))

     ;;  Four jacks - 200 bonus points
     ((and (== (size 'PC) 4)
           (== (cardatt RANK (top 'PC)) J))
      (inc ((team (next player)) sto SCORE) 200)))))

     ;;  King and queen of trumps (stuk) - 20 bonus points
     ((and (any (game vloc PLAYED) 'C 
            (and (== (cardatt RANK 'C) K)
                 (== (cardatt SUIT 'C) (game str TRUMP))))
           (any (game vloc PLAYED) 'C 
            (and (== (cardatt RANK 'C) Q)
                 (== (cardatt SUIT 'C) (game str TRUMP)))))
      (inc ((team (next player)) sto SCORE) 20))

     ;; discard all the played cards
     (repeat all
      (move (top (game vloc PLAYED)) 
            (top ((next player) vloc TRICKSWON)))))))

 ;; determine team score
 (stage team
  (end (all player 'P (== (size ('P vloc TRICKSWON)) 0)))
  (do (                               
   ;; team sums up player points
   (all (current team) 'TP
    (do (
     (inc ((current team) sto SCORE) 
          (sum (filter ('TP vloc TRICKSWON) 'FC 
                (!= (cardatt SUIT 'FC) (game str TRUMP))) 
           using (game points POINTS)))
     (inc ((current team) sto SCORE) 
          (sum (filter ('TP vloc TRICKSWON) 'FC 
                (== (cardatt SUIT 'FC) (game str TRUMP))) 
           using (game points TRUMPPOINTS)))
     (inc ((current team) sto TRICKS)
          (// (size ('TP vloc TRICKSWON)) 4))
     (repeat all
        (move (top ('TP vloc TRICKSWON))
              (top (game vloc DISCARD)))))))
     
   ;; add 100 points if one team won all the tricks
   ((== ((current team) sto TRICKS) 8)
    (inc ((current team) sto SCORE) 100)))))

   ;; determine if the bidding team won - not included because only one round

 (scoring max ((team (current player)) sto SCORE)))