A framework for having bots play your board game

This blogpost was moved here from https://creatingboardgames.wordpress.com/2021/12/18/a-framework-for-having-bots-playing-your-board-game/.

You might have seen a blogpost or two of mine, where I talk about using a computer to simulate a board game in order to get a data for a lot of games. Thanks to some feedback from Facebook, the code I wrote for my own game project is now turning into a framework that could be used by other board game developers who are interested in coding.

The ideas are these:

Use Google spreadsheet to describe volumes of input data: cards, player stats, properties on the board, and the like. The framework gives you nice functions to get the data into formats that could be used in the simulation (such as buildObjectArrayFromRows(‘sheet1’, ‘A1:D201’)).

The high-order simulation stuff is managed by the framework, and the code specific for your game is kept in a module. On a very high level, the framework does this to simulate a game:

  1. Find which module to use.
  2. Call buildInitialData() in the module. This function does two things. The first is populating the global variable with static stuff that are needed in the game simulations. This could be information about the board, setting up default values for how do use dice, or other things that do not change within or between games. The second thing is to build the initialGameState, which could be setting up decks, describing initial data for players (called agents in the framework) and the like. Both these tasks will most likely read a lot of data from the spreadsheet.
  3. The framework then prepares for the first game iteration, by copying the initial game state and processing it – data about agents, cards/decks and tracks/spaces are fed into pre-made classes that simplifies a lot of work later on. The framework also sets up some variables used to keep track of things – both within a game iteration (such as the number of rounds) and between rounds (such as data that should be used for statistics).
  4. The framework allows the module to do any extra processing before the first iteration starts, by calling preIteration(gameState) in the module. Examples of where this is useful is to remove three cards from the deck in No Thanks!, or drawing cards and placing initial disease cubes in Pandemic.
  5. Then the game iteration starts. The framework calls gameOver(gameState) in the module, to check if the game is still on. If we’re still game, the framework ticks up one round and calls playRound(gameState) in the module. This is where the magic happens – agents do stuff in ways described by the game rules and implemented by the coding game designer. In games where players take turns, the code will most likely be broken down into turns inside the round, but I’ve left that for developers to implement. In any case, there are a lot of tools provided by the framework that speeds up the programming. For example:
    • The Agent class has room for swappable strategies, provided by the module. By calling agent.consultStrategy(‘considerReroll’, gameState), for example, the agent’s strategy is looked up, any method with name considerReroll is called with the game state (and agent) as argument, and its result is returned. Apply to whatever strategy functions you need to write.
    • The Agent class also has methods for recording when stats are changed, so you can pick up how many times hit points have been lost, or how often the player’s money increased.
    • The Deck and Card classes provide methods for shuffling decks, drawing/discaring/returning cards, placing cards in a display, and more. One particularly useful aspect is the resolver property that can be set on cards. If present, card.resolve(arguments) can be called to take care of cards when handling needs special-case code.
    • The DiceRoll class provides some neat shortcuts, also for customized dice.
    • The Track class can be used for moving pawns on tracks, or emulating simple game boards. The pawns can be player pawns or general pawns keeping track of global game elements. Apart from moving pawns and getting information about which space each pawn is on, there are also options for the same type of resolvers as the cards have. If spaces need special code, it can be handled by setting a resolver.
  6. When the round is done, gameOver(gameState) is called again and the game moves on if it is not over. Goto 5.
  7. When the game is over, the framework calls buildStatistics(gameState) to allow selecting which parts of the game state should be kept for later statistics: scores, number of rounds, whether any player achieved something particular, and what not.
  8. Then the framework starts a new iteration, unless all the set number of iterations have passed. Goto 3.
  9. Once all iterations have been played, the framework processes the stored data and presents statistics. All stored data are presented with average, at which percentile the data goes above zero, and the values at percentiles selected by the developer. This means that you for example can get average and distribution of number of cards players have left when the game ends. Or the ratio income/round. Or see that players in a cooperative game win in 73 percent of the simulated games. The data is both visible in the log in the coding environment (slower) and in the spreadsheet (when the simulate function is called from the spreadsheet, which is faster).
  10. To make developing and inspection easier, the framework has logging functionality. If you set log messages you can follow what’s happening in each game, and you can turn on/off log messages per category to focus on whatever interesting at the moment. The logging also has a switch for time stamps, which may be useful if you find yourself having slow scripts.

All of this is in place right now, but I want to write an example or two and also create a few videos showing how the framework can be used before releasing version 1.0 beta. Anyone’s interested can go over to GitHub and check out the project page: https://github.com/Itangalo/Board-game-scripting-tools

A few additional thoughts:

  • I consider moving to Python, instead of running things in Google spreadsheet. This would allow heavier simulations, since scripts in Google spreadsheet time out after 30 seconds. Also, I’m not very fond of JavaScript as language. However, I so far feel that it is worth keeping the threshold as low as possible and Google spreadsheets is much quicker to get started with if you’re new to programming. Thoughts are welcome.
  • I think that the first example I provide in the framework will be No Thanks, and I’m looking for a second example. This should ideally be a simple worker placement game, to show how cards can be used to emulate worker placement. If you have any suggestion for a suitable game, please let me know.
  • I’m looking for a good name for the framework/toolbox. Have any idea?
Recommend0 recommendationsPublished in Design Theory