We got introduced to “Camel Up” by some friends on an annual ski trip, and we were instantly hooked. It’s a multiplayer game where you roll dice to determine the movement of “camels” which can stack on top of each other, and you place bets on which camel will win and which will lose.

The crux of the game is that there are low probability and high probability outcomes, and you have to assess the expected payout of different bets. All sorts of reasoning fallacies come into play – betting on colors you like, the possibility effect, sunk cost fallacies, etc etc. In other words, it’s a perfect game for engineers. And of course, all your careful planning goes out the window with one unlucky roll! Camels became an annual tradition on this ski trip, and on this year’s trip, Marty and I lost terribly. After that, I decided to trade in my meat computer for a silicon computer, and wrote a solver for Camel up. And I have yet to lose a game since!

## Game Rules:

There are:

- 16 tiles on the board
- 5 colored camels that can win the race and that you can bet on
- 2 “crazy camels” which move in the opposite direction and exist to cause chaos
- The game proceeds player by player, and on your turn you can take one of several actions.
- The round is over when 5 of the 6 possible dice have been rolled
- Each die has two 1’s, two 2’s, and two 3’s
- Each player has the opportunity to place a +1 or -1 booster
- Every time a camel lands on your booster you get a point
- Every time a camel lands on a -1 booster, it goes back and under any other camels on the previous tile
- You can bet on which camel will win the round. There are only 4 bets available per color – 5 points, 3 points, 2 points, and 2 points.
- If the camel you bet on for, say, 3 points wins, you get 3 points. If it comes in second, you get 1 point. If it comes in third or worse, you lose a point.
- You can place a bet on overall winner and loser of the entire game.
- When there’s more than 4 players, you can choose to ally with another player, and you get the same payout as their single best bet.

## Optimal Strategy

Phew! That’s a lot of rules. What it comes down to is that every time it’s your turn you can do one of the following:

- Place a bet on a camel to win the round: Win 5, 3, 2, 1, or -1 points at the end of the round.
- Place your booster tile down: Win +1 point whenever a camel lands on your booster.
- Roll the dice: Always worth 1 point.
- Ally with someone: Worth the value of their best bet at the end of the round
- Bet on overall winner or loser. Worth 8, 5, 3, 2, 1, or -1 points at the end of the game.

During any given round there are: 6*5*4*3*2*(3^5) possible permutations of the dice roll. When a camel lands on a tile, it stacks on any other camel on the tile. It actually gets a little more complicated because the “grey” die has three black numbers (for the black crazy camel) and three white numbers (for the white crazy camel). Overall, there are 320,760 unique permutations of dice.

So based on the current state of the board, you have to evaluate the expected value of each one of these options. My Camel Up probability solver takes all of these possibilities into account (except for betting on overall winner and loser, more on that later!) and gives you back the expected value of each move, ranked from best to worst. It’s then on you, dear player, to consider the solver’s recommendations, and make your move!

## Option 1: Placing a bet

The solver first computes the probability of each camel coming in first or second, and the number of expected landings on each tile. It does this by simulating all 320k unique dice permutations, and tallying up the winner after each round. It then simply multiplies the probability of each color camel winning by the value of the highest available bet.

For example, consider the following situation:

Blue | Green | Red | Yellow | Purple | |
---|---|---|---|---|---|

P(1st place) | .4 | .3 | .2 | .1 | 0 |

P(2nd place) | .1 | .6 | .2 | .1 | 0 |

P(not 1st/2nd place) | .5 | .1 | .6 | .8 | 1 |

Available bet | 3 | 5 | 3 | 5 | 5 |

Expected value | `(.4*3)+.1+(.5*-1)` .8 | `(.3*5)+.6+(.1*-1)` 2 | `(.2*3)+.2+.6*-1` .2 | `(.1*5)+.1+(.8*-1)` -.2 | `(0*5)+0+(1*-1)` -1 |

The best bet is to choose green, which has an expected value of 2.

## Option 2: Boost

This one’s a little more interesting. The value in placing a booster lies in the expected number of times a camel will land on that booster during the round, and the *change* in expected value of your existing bets were you to place a booster. In other words, if you bet on red to win this round, you probably shouldn’t place your booster such that it jeopardizes red’s chance of winning.

Calculating the payout is easy – based on the average number of landings per tile, you can calculate the expected points for placing a booster on each tile.

Calculating the change in expected value of your existing bets is harder – you would need to recalculate the probability of winning for every possible booster placement. If there are n possible placements, and you can place +1 or -1, then the number of calculations you would have to run is: 2*n*320k. That’s a lot!

In practice, I chose the following heuristic (which works very well):

- Choose the tile with the most landings based on the current state of the board.
- Calculate the expected number of points if you were to place a +1 or a -1 boost on that tile location, call these values (x1, x2), x1 being the expected number of points if you placed a +1, and x2 if you placed -1.
- Calculate the change in value of your existing bets if you were to place a +1 or a -1 boost on that tile location. Take the sum of these changes in value, call these values (y1, y2), same mapping as above.
- Return max(x1+y1, x2+y2)

This calculation took a couple of tries to get right, and led to some surprising (but ultimately quite valuable!) booster placements. Note that you can always move your booster from a previous placement when it’s your turn.

## Option 3: Roll the dice

Ah, the everyman’s move. The comforting move that when all else fails, advances the game and you know you can rely on for a steady payout. I was surprised by how often this was actually the highest expected value move.

## Option 4: Ally

First, take a good hard look around you. Who’s still free to ally with? It’s mutual and binding, so choose wisely! You can make cold, hard, friendless calculations by calculating the expected value of the return of each of your potential ally’s existing bets. Don’t pick your favorite person, pick the ally with the maximum expected value. For example, using the probabilities above, consider the situation:

Me | Player 1 | Player 2 | Player3 | |
---|---|---|---|---|

Status | N/A | available | available | not available |

Bets | N/A | red:5 | blue: 5 | N/A |

Expected Value | N/A | `(.4*5)+.1+(.5*-1)` 1.6 | `(.2*5)+.2+.6*-1` .6 | N/A |

If you were to ally, the best ally is Player 1, who has an expected value of 1.6

## Option 5: Bet on overall winner and loser

Now this one is tricky. On average, a Camel Up game lasts 6 rounds. There are 320k possible dice permutations, and some number fewer unique outcomes per round. Assuming there are are on the order of 300k unique outcomes after every round, that means there are on average 300,000^6 calculations to figure out the probabilities of overall winner and loser. Because I wrote my solver in Python, and not a fast compiled language, 300k calculations takes around 2.5 seconds on my M2 Macbook. That means that it would take about 5.6e22 days to figure out the overall winner and loser! This just isn’t feasible without some careful refactoring and multithreading, so we’ll leave this as an exercise to the reader :).

## Development Notes

**Optimizations**

I added caching using the nice lil `@cache`

Python decorator on the probabilities calculation. There are two caches:

- Based on the state of the board and available dice, get the expected probabilities of the winners and the probability of landing on each tile. This is to avoid repeat calculations.
- To speed up the calculation of all possible outcomes, notice that the format of dice rolls looks something like: [roll red 1, roll blue 1, roll green1], [roll red 1, roll blue1, roll green 2], [roll red 1, roll blue 1, roll green 3], [roll red 1, roll blue 2, roll green 1], …. This means that you can reuse the “prefix” of each dice permutation (i.e. roll red 1, roll blue 1) for multiple subsequent outcomes. So, I cache these too, and this sped up my execution from ~10 seconds to ~2.5 seconds.

**Restoring game state**

Several times, midway through a game, something would crash, or I would fat finger in the wrong move, and poor Marty would have to wait while I carefully re-entered in all of the game state. So I added in a mechanism to save the game state saved after every move, and reloadable with an argument.

## Using Camel Up to vanquish your friends, family, and foes

I one day will make a ui for this (maybe, this was an unemployment project when I had infinite time), but until then, it’s a Python command line utility.

Here’s an example run:

```
juliewang@pochoclo camelup % python3 main.py --n-players 4 --id 0
Camel Up!!!
Enter your move: optimal
Calculating optimal move
100%|███████████████████████████████████████████████████████████████████████████████| 320760/320760 [00:02<00:00, 127359.71it/s]
100%|███████████████████████████████████████████████████████████████████████████████| 320760/320760 [00:02<00:00, 128037.32it/s]
100%|███████████████████████████████████████████████████████████████████████████████| 320760/320760 [00:02<00:00, 124446.59it/s]
2.91: Bet purple
1.54: Boost location 13 + (current_val: 0.00)
1.00: Roll dice
0.00: Ally Player 0
Enter your move: bet purple
Player 0 bet on purple with value 5
Enter Player 1 move: roll red 1
Player 1 rolled red 1
Enter Player 2 move: bet yellow
Player 2 bet on yellow with value 5
Enter Player 3 move: boost 2 -
Player 3 placed booster at 2 with value -
```

There are 4 players, and as player 0, I am the first to move. These numbers are all configurable. Here I entered `optimal`

to get the probabilities of the best moves. Then I `bet purple`

thanks to the recommendation. I then entered the move that Player 1 took, `roll red 1`

, meaning that they rolled a red 1. Player 2 `bet yellow`

, and Player 3 put a booster down on tile 2 in the -1 orientation. The round the continues until five of the six dice are rolled.

At any point you can type in `print`

to check the state of the board, and `optimal`

to get anyone’s optimal move. At the end of the round the game keeps track of how many points each player has as well.

I have been extensively testing this solver with my wonderful husband Marty, who has patiently played way too many games of Camel Up with me while I leech out all the joy by using a computer to make all my decisions. Jk Jk Jk. Future me with more time (ha!) will write a simulator with various agents employing different strategies (e.g. always roll, always bet camel in front, random) and show that the the solver suggestions on average result in more wins.

You can find my code here on Github, but you must use it for good and not evil.

Happy Cameling!