Camel Up Cup 2K18
Camel Up is a board game I was playing with some buddies. While arguing about what move should have been made someone remarked "someone should write a program to play this game". That sparked the idea that became Camel Up Cup 2K18 - the premier bot creation tournament of the year. The idea is simple, make a digital bot that is able to play the game Camel Up better than all competitors.
The game was chosen for several reasons: it has a relatively small pool of options to select from (roughly 20 choices per turn, easily narrowed down to usually about 3-4), games are short and there is an element of luck (makes it so even the "bad" bots can win). After conveniently bringing the game to a few game nights and gauging interest I made the game moderator, a few basic example bots and sent out invitations.
The bots would be written in a language that interfaces with Python 3.x. They would take in the gamestate (camel positions, current bets, player money values, etc) and return their move. Bot creators had access to the game's code so could point out bugs and test their bots locally.
The event itself
On the day of the event plenty of people showed up... but not very many bots - there were two serious bots and two joke bots, along with a bot that was supposed to be a joke but was accidentally a jpeg image (don't ask me how). We agreed to a best of four with a rotating player order (ABCD, BCDA, CDAB, BCDA) - figuring out a tiebreak if we ran into it.
Initially a bug that had the game in a test "move-from-the-bottom-always" mode resulted in "trashbot", one of the joke bots, absolutely crush the other bots.
The bug was correct and we found the two competative bots TimeCamel [made by Libby and Jack] and Sir_Humpfree_Bogart [made by myself] well ahead of trashbot [made by Charles] and randobot [made by Sean]. TimeCamel took the first game in which it was first up but Sir_Humpfree_Bogart took the next three games to pull home the win.
How the bots work
Both bots work in a similar fashon. First, they look at all the possible configurations that the camels could end up at the end of the round. Then they determine the expected value of betting on a camel winning the round using
EV_roundwin = (chance_1st)*(payout) + (chance_2nd)*(payout) - (chance_3rd_or_worse)*(cost)
Then they move camels randomly until a camel wins. After doing this a few thousand times you can estimate the chance each camel will win and lose. Again, we get the expected value of these using
EV_gamewin = (chance_1st)*(payout) - (chance_2nd_or_worse)*(cost)
The only other options are moving a camel (which always yields one coin, so its expected value is one) and placing a trap. Both teams felt placing a trap was a weak enough option to ignore it entirely. With this information the bots chose the option with the highest expected value.
Addendum: Since our tournament viewed a second place finish the same as a last place finish it makes sense if you are behind to take chances to get into first place. SBH used distance_from_first_place and nearness_to_end to determine how risky the bot should be, where if you are far from first and close to the end then riskiness would be high and if you are in first or far from the end of the game the riskiness would be low. With a low riskiness the bot would decide on an action with a high expected value option and a high riskiness yielding the option with a high upshot. The exact equation was
Functional_EV = EV + (upshot-EV) * riskiness
where upshot is the highest payout you can get from a decision and riskiness ranges from 0 to 1.
With such a small sample size it was natural to extend the tests to be more rigorous.
Test 1: TimeCamel vs SHB
This test was for more trials under a more controlled environement. I alternated between a game with [Randobot, SHB, Randobot, TimeCamel] and [Randobot, TimeCamel, Randobot, SHB], with TimeCamel and SHB switching places as there is a pretty large advantage to going first.
SHB had about a 10 point lead over many trials. It should be noted that they took approximately the same amount of time per turn.
Test 2: The disorder of Trashbot
This test was to determine the effects of Trashbot. This bot was made the day of the tournament - it always bets for a camel in the lead to win the round. However this is usually one of the best options, so while it may not have won it greatly diminished the choices the following player could make. I alternated between a game with [Trashbot, SHB, Randobot, TimeCamel] and [Trashbot, TimeCamel, Randobot, SHB], with TimeCamel and SHB alternating being right behind Trashbot.
As you can see the taking of round bets was a huge gamechanger. From what I could see, the taking of round winners forced the next bot to move camels which gives modest return and, more importantly, plenty of information for the next "smart" bot. Still, just the taking away of that option made the other bot get a 50 point upper hand and even led Trashbot to winning ~10% of games. Amazing for four lines of code.
Test 3: Effects of Riskiness
If you look at how each of our bots are made they implement a very similar structure and method to finding the correct move. The main feature that mine had that his did not was a concept called "Riskiness". For this test I hardcoded my riskiness to be 0. I then again alternated between [Randobot, SHB, Randobot, TimeCamel] and [Randobot, TimeCamel, Randobot, SHB].
It seems like riskiness didn't have much an effect as I'd guessed. How I gain an edge then is a mystery to me... maybe my game_winner_bets better take into effect the case in which opponents have bet for which camels? Maybe my higher tolerance for betting helped? 10 percentage points is quite a lot... Maybe we'll learn more in the future!