Blog Image
SHEDDING

LAMA: Coding

Our sixth game for analysis is a new shedding game, L.A.M.A by Reiner Knizia and AMIGO Games. LAMA was just nominated for the 2019 Spiel Des Jahres, one of the premier board game awards in Germany! You can find the official English rules over at BoardGameGeek.

Rules Overview

In L.A.M.A, players must quickly shed their hand to the discard pile, but can also bow out and hope they will earn less points than other players. This is definitely a matching game, and twists the basic stops mechanic of requiring card play in ascending order into a cyclic loop. Here’s the introduction from the rulebook:

The Llama commands it: Get rid of all your negative points! Play your cards or you will earn negative points. If you can’t play a card, you’ll have to decide: Do you quit now, or do you draw a card which hopefully you can play later? Negative points come in the form of tokens, and if you get rid of all your cards, you can return a token—which may be worth 1 or 10 points. The player with the fewest points at the end wins the game.

RECYCLE Coding

To illustrate how we encode the rules computationally, we will walk through in detail the RECYCLE code we wrote for LAMA.

First, we set up the game by initializing the number of players. This will be from 2 to 6 in our actual simulations. We put each player on its own team, since there are no teams in LAMA Finally, we create the deck of cards, eight each of the seven card types, ONE, TWO, THREE, FOUR, FIVE, SIX, and LLAMA.

(game
 (setup
  (create players 4)
  (create teams (0) (1) (2) (3))
  
  (repeat 8 (create deck (game vloc DISCARD) 
     (deck (NUMBER (ONE, TWO, THREE, FOUR, FIVE, SIX, LLAMA))))))

Next, we assign a numerical value to the cards using a PointMap. This will be used later to determine if they are being correctly played in ascending order, as well as for scoring purposes.

 (do    
     (   
      (put points 'VALUE
           (
            ((NUMBER (ONE)) 1)
            ((NUMBER (TWO)) 2)
            ((NUMBER (THREE)) 3)
            ((NUMBER (FOUR)) 4)
            ((NUMBER (FIVE)) 5)
            ((NUMBER (SIX)) 6)
            ((NUMBER (LLAMA)) 7)))))

The game will continue in rounds until one player has 40 or more points.

 (stage player 
        (end 
         (any player 'P
              (>= ('P sto SCORE) 40))) 

Before each round, the cards are moved from the visible DISCARD location to the hidden STOCK location and then shuffled. Each player is then dealt 6 cards into their HAND. The top card from the stock is turned face-up on the DISCARD pile to start.

  (do    
     (            
      (repeat all
              (move (top (game vloc DISCARD))
                    (top (game iloc STOCK))))
      (shuffle (game iloc STOCK))
      (all player 'P
           (repeat 6
                   (move (top (game iloc STOCK))
                         (top ('P iloc HAND)))))
      (move (top (game iloc STOCK))
            (top (game vloc DISCARD)))))

To keep track of a player’s cards, we will have two locations, a HAND location and a QUIT location. Both locations will be invisible to other players.

There are two ways to end a round. Either one player has played their last card, or everyone has decided to quit the round. For the first option, this will be determined when there are no cards in both their HAND and QUIT locations. In the second case, all players have cards in their QUIT location.

 (stage player
        (end (or (any player 'P
                      (and (== (size ('P iloc HAND)) 0)
                           (== (size ('P iloc QUIT)) 0)))
                 (all player 'P
                      (!= (size ('P iloc QUIT)) 0))))

Now for the choices. Before we get there, though, we need to account for the situation where players already have cards in their QUIT location. If this is the case, then the only thing they can do is pass.

(choice
 (
  ((> (size ((current player) iloc QUIT)) 0)
   (turn pass))

A player has three options for their turn: play a card, draw a card, or quit. First, a player can move a card from their HAND to the DISCARD pile if the card has the same 'VALUE as the card on top of the DISCARD pile. Also, the card can be played if the value is one more than the top of the DISCARD pile. Finally, the special llama cards can be wrapped around, since they are higher than a 6, and lower than a 1.

  (do
      (
       (any ((current player) iloc HAND) 'C
            ((or (== (score (top (game vloc DISCARD)) using 'VALUE) 
                    (score 'C using 'VALUE))
                (== (score (top (game vloc DISCARD)) using 'VALUE) 
                    (- (score 'C using 'VALUE) 1))
                (== (score 'C using 'VALUE) 
                    (- (score (top (game vloc DISCARD)) using 'VALUE) 6)))
             
             (move 'C 
                   (top (game vloc DISCARD)))))))

Second, to draw a card from the STOCK into their HAND, three conditions must be met. There must be cards in the STOCK to draw, the player must not have any cards in their QUIT location, and there must be at least one other player who has no cards in their QUIT location.

  ((and (!= (size (game iloc STOCK)) 0)  
        (== (size ((current player) iloc QUIT)) 0)  
        (any (other player) 'OP  
             (== (size ('OP iloc QUIT)) 0)))
                         
   (move (top (game iloc STOCK))
         (top ((current player) iloc HAND))))

Finally, a player can decide to quit the round. This is accomplished by moving all of their cards from their HAND location to their QUIT location.

  (repeat all
          (move (top ((current player) iloc HAND))
                (top ((current player) iloc QUIT)))))))

Once the round is over, it is time to determine player’s scores for the round. First, if a player ended the round by playing the last card from their HAND to the DISCARD pile, then they can decrease their score. If they have 10 or more points, they can decrease their score by 10. Otherwise, they can decrease their score by 1.

 (do 
     (
      (all player 'P
           (do
               (
                ((and (== (size ('P iloc HAND)) 0)
                      (== (size ('P iloc QUIT)) 0)
                      (> ('P sto SCORE) 0))
                 (do
                     (
                      (dec ('P sto SCORE) 1)
                      ((>= ('P sto SCORE) 9)
                       (dec ('P sto SCORE) 9)))))

For all other players, we first put the cards remaining in their HAND location into their QUIT location. Since the actual location no longer matters, this will make the remaining score calculations simpler.

(repeat all
        (move (top ('P iloc HAND))
              (top ('P iloc QUIT))))

Now, we add up the points for a player. We only want to count each card value once, so we iterate over the possible values, calling each one 'S. Then we make a filter of the player’s cards based on this value. When the size of this filter is greater than 0, we add that value to the player’s SCORE. While llama cards act like a 7 (and a 0) above for play, they are worth 10 points, thus we must add an additional 3 points to their score.

(all (range 1..8) 'S
     (do 
         (
          ((> (size (filter ('P iloc QUIT) 'H (== (score 'H using 'VALUE) 'S))) 0)
           (do
               (
                (inc ('P sto SCORE) 'S)
                ((== 'S 7)
                 (inc ('P sto SCORE) 3))))))))

To end the round, all of the cards are moved to the DISCARD pile, read for the next round.

                (repeat all
                        (move (top ('P iloc QUIT))
                              (top (game vloc DISCARD))))))))))

At the end of the game, we determine the winner by whoever has the least points.

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

Up Next

So, it looks like in LAMA the individual rounds will have some tactical play and a press-your-luck feel, but the overall scoring system, with its opportunity for large point swings when returning black tokens, should bring in some strategy and ability for players to catch up. We’ll look at both of these levels in our simulation analysis in the next post, and see if we can get a good feel for what makes the game tick. Thanks for reading, stay tuned for more!


comments powered by Disqus