Golf Six

Summary

In Golf, the deck is shuffled, and then 6 cards are dealt face-down to each player in a 3x2 grid. One card is turned face-up to begin the discard pile. On a player’s turn, they can either draw the top card from the discard pile, or the top of the remaining deck. The player can then decide to replace one of their cards, either face-down or face-up, with the drawn card, keeping the new card face-up. Play continues until one player has all face-up cards in their grid. Duplicate card ranks in columns are removed, and then players score for their remaining cards. The player with the lowest score wins.

Rules of Play

These rules are summarized from https://www.pagat.com/draw/golf.html.

Golf is a draw-and-discard game. Other games in this genre include Sam Sip and Knock Poker, and is the basis for commercial games like Skyjo and Rat a TatCat.

Information Flow

Card Movement Graph

The diagram above visualizes information flow in Golf Six 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).

Branching Factor

Branching Factor Graph

The figure above illustrates the branching factor (y-axis) for each decision point (x-axis) of Golf Six, summarized over 100 random games. We define a decision point as a specific moment at which a player must choose from a set of possible actions. Following this definition, a player can be part of multiple distinct decision points during one of their turns, resulting in successive actions.

Decision Spread

Decision Spread Graph

The figure above illustrates the normalized spread (y-axis) for each decision point (x-axis) of Golf Six, summarized over 100 games with all MCTS players. At each decision point, a player evaluates the potential moves in terms of their potential final score. The spread is the difference between the best estimated move and the worst estimated move. This value is then divided by the highest spread found during the game to calculate the normalized spread.

Lead History

Lead History Graph

The figure above illustrates the normalized average lead history (y-axis) for each decision point (x-axis) of Golf Six, summarized over 100 games with all MCTS players. At each decision point, a player evaluates the potential moves in terms of their potential final score. For the best estimated move, they record the estimated score for all players, assigning them normalized rank estimates, where 1 is first place, and 0 is last place. Across 100 games, these histories are grouped according to final game rank, and then averaged.

RECYCLE Code

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

You can also Download the code.

;; Golf
;; https://www.pagat.com/draw/golf.html

(game
 (declare (range 1..6) 'PLOCS)
 (setup
  (create players 4)
  (create deck (game iloc STOCK) 
   (deck (RANK (A, TWO, THREE, FOUR, FIVE, SIX, 
                SEVEN, EIGHT, NINE, TEN, J, Q, K))
         (COLOR (RED (SUIT (HEARTS, DIAMONDS)))
                (BLACK (SUIT (CLUBS, SPADES)))))))
 (do (
  ;; set scoring points for the cards
  (set (game points POSITIVE)
   (((RANK : Q)    10) ((RANK : J)    10) ((RANK : TEN)  10)
    ((RANK : NINE)  9) ((RANK : EIGHT) 8) ((RANK : SEVEN) 7)
    ((RANK : SIX)   6) ((RANK : FIVE)  5) ((RANK : FOUR)  4)
    ((RANK : THREE) 3) ((RANK : A)     1)))
  (set (game points NEGATIVE)
   (((RANK : TWO) 2)))

  ;; Shuffle the deck, give each player one card in each of their locations
  (shuffle (game iloc STOCK))    
  (all player 'P
   (all 'PLOCS 'L
    (move (top (game iloc STOCK)) 
          (top ('P hloc LOC 'L)))))

  ;; Start the discard pile
  (move (top (game iloc STOCK))
        (top (game vloc DISCARD)))))
 
 ;; each player turns over two spots
 (stage player
  (end (all player 'P (== (size (union (all 'PLOCS 'L ('P hloc LOC 'L)))) 4)))
  (choice (
   (any 'PLOCS 'L
    ((!= (size ((current player) hloc LOC 'L)) 0) 
     (move (top ((current player) hloc LOC 'L))   
           (top ((current player) vloc LOC 'L)))))))

  (choice (
   (any 'PLOCS 'L
    ((!= (size ((current player) hloc LOC 'L)) 0) 
     (move (top ((current player) hloc LOC 'L))   
           (top ((current player) vloc LOC 'L))))))))

 ;; play the game until one player has all faceup cards
 (stage player
  (end (any player 'P (== (size (union (all 'PLOCS 'L ('P hloc LOC 'L)))) 0)))

  ;; Draw a card
  (choice (
   ((> (size (game iloc STOCK)) 0)
    (move (top (game iloc STOCK)) 
          (top((current player) iloc HAND))))

   ((> (size (game vloc DISCARD)) 0)
    (do (
     (move (top (game vloc DISCARD)) 
           (top((current player) iloc HAND)))
     (set ((current player) str DRAWFROM) DISCARD))))))
        
  ;; Play a card
  (choice (
   ((!= ((current player) str DRAWFROM) DISCARD)
    (move (top ((current player) iloc HAND)) 
     (top (game vloc DISCARD))))
          
   (any 'PLOCS 'L
    (do (
     ((== (size ((current player) hloc LOC 'L)) 0) 
      (move (top ((current player) vloc LOC 'L))   
            (top (game vloc DISCARD))))
     ((!= (size ((current player) hloc LOC 'L)) 0) 
      (move (top ((current player) hloc LOC 'L))   
            (top (game vloc DISCARD))))
     (move (top ((current player) iloc HAND)) 
           (top ((current player) vloc LOC 'L))))))))
        
  ;; Forget if you drew from the discard pile
  (do (
   (set ((current player) str DRAWFROM) NONE))))

 ;; Calculate your scores
 (stage player
  (end (all player 'P (== ('P str FINISHED) TRUE)))

  (do (
   ;; turn all cards face up
   (all 'PLOCS 'L 
    (repeat all (move (top ((current player) hloc LOC 'L))
                      (top ((current player) vloc LOC 'L)))))

   ;; throw away column matches
   (all (range 1..3) 'L
    ((== (cardatt RANK (top ((current player) vloc LOC 'L)))
         (cardatt RANK (top ((current player) vloc LOC (+ 3 'L)))))
     (do (
      (move (top ((current player) vloc LOC 'L))
            (top (game vloc DISCARD)))
      (move (top ((current player) vloc LOC (+ 3 'L)))
            (top (game vloc DISCARD)))))))

   ;; then add up cards for pos, subtract neg
   (set ((current player) sto SCORE) 
        (sum (union (all 'PLOCS 'L ((current player) vloc LOC 'L))) 
             using (game points POSITIVE)))
   (dec ((current player) sto SCORE) 
        (sum (union (all 'PLOCS 'L ((current player) vloc LOC 'L)))
             using (game points NEGATIVE)))

   ;; end the scoring turn
   (set ((current player) str FINISHED) TRUE))))

 (scoring min ((current player) sto SCORE)))