Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Board Games with Ruby

Yonatan Bergman
September 19, 2014

Building Board Games with Ruby

Given @ GoGaruco 2014

Board games are seeing a huge resurgence right now, everywhere you look people are playing new games. The Internet and crowd funding has made it possible for almost anyone to design and develop a physical board game.

Join me while I share the story of how I used Ruby to design my first board game. How I honed the game mechanics and optimized all the different moving parts. You will leave this talk knowing the process of designing and developing a board game from scratch and how Ruby can help you iterate fast on new ideas using simple code, simulations and some statistics.

Yonatan Bergman

September 19, 2014
Tweet

More Decks by Yonatan Bergman

Other Decks in Programming

Transcript

  1. 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010

    2011 2012 2013 2000 540 547 614 759 850 956 943 974 987 1053 1100 1106 1118 1033 Games Published Per Year
  2. ! ! ! ! ! ! ! ! ! !

    ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ,''''', '''''' '''' :++++++ `++++++, '''' '''' ++++++++++++++ `++++++. '+' :++++++ ++++++ ++++ +++++++++: :+++++++++ ++++ ++++ ++++++++++++++ '+++++++++ +++++ :++++++ ++++++ ++++ +++++++++++. ,+++++++++++ ++++ ++++ ++++++++++++++ :+++++++++++ +++.'++ :++++++ `++++++ ++++ ;++++++++++++ ++++++++++++: ++++ ++++ ++++++++++++++ ++++++++++++. `+' ;+` :++++++` ;++++++ ++++ ++++++'++++++: ;+++++'+++++++ ++++ ++++ ++++++++++++++ '+++++'+++++++ '+ +' :++++++' +++++++ ++++ `++++ +++++ +++++ '++++ ++++ ++++ ++++++++++++++ ++++; +++++ ++ ++ :+++++++ +++++++ ++++ ;+++' `++++ ++++ ++++: ++++ ++++ ++++ ++++ ++++. ++ ++ :+++++++ +++++++ ++++ ++++. ++++ ++++ '+++' ++++ ++++ ++++ ++++ ++++: '+ ++ :+++++++ .+++++++ ++++ ++++. ++++ ++++ .++++ ++++ ++++ ++++ ++++ ;+++; .+. `+, :+++:+++: ++++++++ ++++ +++++ ++++ ++++ ++++ ++++ ++++ ++ ++ ,+++,++++ ++++++++ ++++ :+++++ +++++. ++++ ++++ ++++ +++++` ++ ,+' ,+++,++++ +++'++++ ++++ +++++++` +++++++; ++++ ++++ ++++ +++++++: ++.++ ,+++,++++ +++`++++ ++++ +++++++++' ,+++++++++. ++++ ++++ +++++++++++++` ;+++++++++` ++++ ,+++,.+++. :+++ ++++ ++++ `++++++++++' +++++++++++ ++++ ++++ +++++++++++++` +++++++++++ +++ ,+++, ++++ ++++ ++++ ++++ ;+++++++++++ +++++++++++` ++++ ++++ +++++++++++++` +++++++++++ ++++, ,+++, ++++ ++++ ++++ ++++ .++++++++++; +++++++++++ ++++ ++++ +++++++++++++` +++++++++++ +++ ++ ,+++, ++++ +++, ++++ ++++ .+++++++++ '++++++++; ++++ ++++ +++++++++++++` '++++++++, ;++ ;+; '. ,+++, ,+++``+++ ++++ ++++ '++++++, .+++++++ ++++ ++++ ++++ ,+++++++ ++ ++ +. ,+++, +++;'+++ ++++ ++++ '+++++ `+++++ ++++ ++++ ++++ .+++++ ;+. ,++ +` ,+++, ++++++++ ++++ ++++ ```` +++++ ```` ++++ ++++ ++++ ++++ ```` `++++ ++ ++ `+ ,+++, +++++++: ++++ ++++ ++++ ++++ ++++ ++++` ++++ ++++ ++++ ++++ ++++ ++ `++;+ ,+++, ;++++++ ++++ ++++ ++++ ++++ ++++` ++++ ++++ ++++ ++++ ++++ ++++ +' ++++ ,+++, ++++++ ++++ ++++ ++++ ++++ +++++ ++++ ++++ ++++ ++++ ++++' ++++ +' ++' ,+++, ++++++ ++++ ++++ +++++ ++++; ,++++ ++++ ++++ ++++ ++++ ;++++ .++++ ++ ++, ,+++, +++++' ++++ ++++ '+++++ +++++ +++++, .+++++ ++++ +++++++++++++` ++++++++++++++` +++++. ,+++++ ++ +++ ,+++, '++++` ++++ ++++ ++++++++++++++ ++++++++++++++. ++++ +++++++++++++` ++++++++++++++` ++++++++++++++ ;+, ++'+; ,+++, `++++ ++++ ++++ '++++++++++++. +++++++++++++ ++++ +++++++++++++` ++++++++++++++` +++++++++++++ ++ ;++ ++ ,+++, ++++ ++++ ++++ +++++++++++' .+++++++++++ ++++ +++++++++++++` ++++++++++++++` ,+++++++++++ ;++; `+++ ,+' ,+++, ++++ ++++ ++++ '++++++++: `+++++++++ ++++ +++++++++++++` ++++++++++++++` .+++++++++ '++++++ ++ ;++++: .++++'` .++++'` `;+'` ;+++: .++++` .+++' :+', ,+++++; ++++++ ++++ '+++++++: ++++++++++++` `++++++++ `++++++++ ++++: ++++; ++++, +++++++++++. ++++++++ ,++++++ ++++++ ++++ ++++++++++' +++++++++++++; ,++++++++++` :++++++++++ ++++: ++++; ++++, +++++++++++++ ++++++++++ ,++++++ ++++++ ++++ ++++++++++++: ++++++++++++++. `++++++++++++ `++++++++++++ ++++: ++++; ++++, ++++++++++++++ ++++++++++++ ,++++++ ,++++++ ++++ .+++++++++++++ +++++++++++++++ ++++++++++++++ +++++++++++++, ++++: ++++; ++++, ++++++++++++++` ;++++++++++++, ,++++++, +++++++ ++++ +++++++;++++++' +++++++++++++++` :++++++;'++++++` ;++++++;'++++++ ++++: ++++; ++++, +++++++++++++++ +++++. `'+++++ ,+++++++ +++++++ ++++ `+++++ +++++ ++++ ,++++' +++++. :+++++ +++++` +++++` ++++: ++++; ++++, ++++` +++++ ++++ :++++ ,+++++++ +++++++ ++++ +++++ `++++ ++++ +++++ `++++: '++++ .++++, +++++ ++++: ++++; ++++, ++++` ++++ `++++ ++++ ,+++++++ +++++++ ++++ ++++: ++++: ++++ :++++ +++++ ++++: +++++ ,++++ ++++: ++++; ++++, ++++` ++++. ,+++' ++++` ,+++++++` :+++++++ ++++ ++++ ,++++ ++++ .++++ ++++: +++++ ++++, ++++ ++++: ++++; ++++. ++++` '+++, ,++++ ''''. ,+++,+++' ++++++++ ++++ :++++ '''' ++++ .++++ ++++ `++++ ++++ '''' ++++: ++++; ++++. ++++` '+++: .++++` ,+++,++++ ++++++++ ++++ ++++' ++++ :+++' ++++ ++++ `++++ ++++: ++++; ++++. ++++` ++++, +++++' ,+++,++++ +++:++++ ++++ ++++, ++++ ++++` `++++ ++++ ,++++ ++++++++++++++++; ++++. ++++. ++++ ++++++++, ,+++,;+++ `+++ ++++ ++++ ++++` ++++ ;++++ ,++++ ++++ ;++++ ++++++++++++++++; ++++. ++++. :++++ :++++++++++ ,+++, +++: '+++ ++++ ++++ ++++ ++++++++++++++: :++++ ++++` '++++ ++++++++++++++++; ++++. ++++::::;++++++ +++++++++++. ,+++, ++++ ++++ ++++ ++++ ++++ +++++++++++++' ;++++ ++++` '+++' ++++++++++++++++; ++++. ++++++++++++++: +++++++++++` ,+++, ++++ +++' ++++ ++++ ++++ +++++++++++++` :++++ ++++` '++++ ++++++++++++++++; ++++. ++++++++++++++ ,++++++++++ ,+++, '+++ +++` ++++ ++++ ++++` ++++++++++++++ ,++++ ++++ :++++ ++++:```````++++; ++++. +++++++++++++ `++++++++, ,+++, `+++.,+++ ++++ ++++ ++++, ++++;;;;;++++++ `++++ ++++ ,++++ ++++: ++++; ++++. ++++++++++++ ;++++++ ,+++, ++++++++ ++++ ++++ ++++' ++++ ++++ :++++ ++++ ++++ ++++ ++++ ++++: ++++; ++++, ++++,....` +++++ ,+++, ++++++++ ++++ ++++ :++++ `++++ ++++ ++++` ++++ .++++ ++++ ++++ ++++: ++++; ++++, ++++` ++++ `++++ ,+++, +++++++. ++++ ++++ ++++ '++++ ++++ ++++, ++++; +++++ ++++: ++++ ++++: ++++; ++++, ++++` ++++ ++++ ,+++, ,++++++ ++++ ++++ ++++; ++++, ++++ ;+++; '++++ ++++, +++++ '++++ ++++: ++++; ++++, ++++` ++++ ++++ ,+++, ++++++ ++++ ++++ +++++ ,++++ ++++ ,+++' ++++' +++++ `++++; ++++; ++++: ++++; ++++, ++++` ++++' ++++ ,+++, ++++++ ++++ ++++ +++++ `+++++ ++++ `++++ +++++: '+++++ +++++: +++++ ++++: ++++; ++++, ++++` '++++` +++++ ,+++, +++++: ++++ ++++ ++++++++++++++, ++++ ++++ .++++++++++++++ ,++++++++++++++ ++++: ++++; ++++, ++++` ++++++.`:+++++` ,+++, :++++ ++++ ++++ +++++++++++++ ++++ ++++ ++++++++++++++ +++++++++++++ ++++: ++++; ++++, ++++` ++++++++++++++ ,+++, ++++ ++++ ++++ :+++++++++++ ++++ ++++ ++++++++++++ +++++++++++' ++++: ++++; ++++, ++++` ++++++++++++` ,+++, ++++ ++++ ++++ ;+++++++++. ++++ ++++ ++++++++++ ++++++++++ ++++: ++++; ++++, ++++` ++++++++++, ,+++, +++; ++++ ++++ `+++++++ ++++ ++++ +++++++' +++++++, ++++: ++++; ++++. ++++` ++++++++ .:. `,, `:, `,:`
  3. ! game = SimpleGame.new(starting_energy: 10, 
 winning_energy: 20) game.run >

    “ --- TURN 1 --- “ 
 > “ #Player 1 (10) defends “
 > “ #Player 2 (10) attacks #Player 3 (10) “ 
 > “ #Player 3 (9) charges (10) “
 > …
 > “ --- TURN 47 --- “
 > “ #Player 1 (13) defends “
 > “ #Player 3 (19) charges (20) “
 > 
 > “ Winner: ["#Player 3"] at turn number 47 "
  4. ! games = MultiGames.new(games: 1000) games.simulate! ! game.average_turns
 > 24.821

    game.victory_count_per_player
 > “ [["Player 1", 314], “ 
 > “ ["Player 2", 354], “
 > “ ["Player 3", 340]] “

  5. ! class ActionResolver def resolve
 case player.action
 when :attack then

    resolve_attack
 when :charge then resolve_charge
 when :block then resolve_block
 end
 end def resolve_charge
 Result.new(player: +1)
 end end
  6. ! class RoundResolver def resolve
 @players.each do |player|
 resolver =

    ActionResolver.new(player)
 result = resolver.resolve()
 player.energy += result.player
 player.target.energy += result.target
 end
 end end
  7. ! class ActionResolver def resolve
 case player.action
 when :attack then

    resolve_attack
 when :charge then resolve_charge
 when :block then resolve_block
 end
 end def resolve_charge
 Result.new(player: +1)
 end end
  8. ! class ActionResolver def resolve
 case player.action
 when :attack then

    resolve_attack
 when :charge then resolve_charge
 when :block then resolve_block
 end
 end def resolve_charge
 unless under_attack?
 Result.new(player: +1)
 end
 end
 end
  9. ! class ActionResolver class Result < Hashie::Dash
 property :player, default:

    0
 property :target, default: 0
 end . . . Result.new(player: +1, target: -1)
 
 end
  10. ! class Player::Random < Player::Abstract def choose_aim
 targets.sample
 end def

    choose_action
 actions.sample
 end def change
 false
 end end
  11. ! class Player::Simple < Player::Random def choose_action
 if low_energy?
 [:block,

    :charge].sample
 else
 actions.sample
 end
 end def change
 targeted? ? :block : false
 end end
  12. ! games = MultiGames.new(players: [
 Player::Random.new,
 Player::Random.new, 
 Player::Simple.new,
 ])

    games.simulate! game.victory_count_per_player
 > “ [["Random 1", 30.08], “ 
 > “ ["Random 2", 31.93], “
 > “ ["Simple 3", 38.13]] “

  13. 2 1 5 4.0913 LMS 90.37% VP 9.63% 2 2

    5 6.0226 LMS 73.47% VP 26.53% 2 3 5 6.1922 LMS 50.30% VP 49.70% 2 4 5 4.0649 VP 77.52% LMS 22.48% 2 1 6 4.7287 LMS 93.21% VP 6.79% 2 2 6 7.4563 LMS 82.80% VP 17.20% 2 3 6 8.8977 LMS 66.31% VP 33.69% 2 4 6 8.2597 VP 53.56% LMS 46.44% 2 5 6 5.0287 VP 79.29% LMS 20.71% 2 1 7 5.0806 LMS 95.63% VP 4.37% 2 2 7 8.6032 LMS 87.94% VP 12.06% 2 3 7 11.1296 LMS 76.95% VP 23.05% 2 4 7 11.9691 LMS 61.34% VP 38.66% 2 5 7 10.266 VP 58.27% LMS 41.73% 2 6 7 5.9262 VP 80.90% LMS 19.10% 2 1 8 5.406 LMS 97.02% VP 2.98% 2 2 8 9.4609 LMS 91.88% VP 8.12% 2 3 8 12.9289 LMS 83.70% VP 16.30% 2 4 8 14.8757 LMS 72.81% VP 27.19% 2 5 8 15.0612 LMS 56.91% VP 43.09% 2 6 8 12.5056 VP 60.03% LMS 39.97% NUMBER OF PLAYERS STARTING ENERGY ENERGY TO WIN —————————— AVERAGE TURNS
  14. 2 1 5 4.0913 LMS 90.37% VP 9.63% 2 2

    5 6.0226 LMS 73.47% VP 26.53% 2 3 5 6.1922 LMS 50.30% VP 49.70% 2 4 5 4.0649 VP 77.52% LMS 22.48% 2 1 6 4.7287 LMS 93.21% VP 6.79% 2 2 6 7.4563 LMS 82.80% VP 17.20% 2 3 6 8.8977 LMS 66.31% VP 33.69% 2 4 6 8.2597 VP 53.56% LMS 46.44% 2 5 6 5.0287 VP 79.29% LMS 20.71% 2 1 7 5.0806 LMS 95.63% VP 4.37% 2 2 7 8.6032 LMS 87.94% VP 12.06% 2 3 7 11.1296 LMS 76.95% VP 23.05% 2 4 7 11.9691 LMS 61.34% VP 38.66% 2 5 7 10.266 VP 58.27% LMS 41.73% 2 6 7 5.9262 VP 80.90% LMS 19.10% 2 1 8 5.406 LMS 97.02% VP 2.98% 2 2 8 9.4609 LMS 91.88% VP 8.12% 2 3 8 12.9289 LMS 83.70% VP 16.30% 2 4 8 14.8757 LMS 72.81% VP 27.19% 2 5 8 15.0612 LMS 56.91% VP 43.09% 2 6 8 12.5056 VP 60.03% LMS 39.97%
  15. 2 1 5 4.0913 LMS 90.37% VP 9.63% 2 2

    5 6.0226 LMS 73.47% VP 26.53% 2 3 5 6.1922 LMS 50.30% VP 49.70% 2 4 5 4.0649 VP 77.52% LMS 22.48% 2 1 6 4.7287 LMS 93.21% VP 6.79% 2 2 6 7.4563 LMS 82.80% VP 17.20% 2 3 6 8.8977 LMS 66.31% VP 33.69% 2 4 6 8.2597 VP 53.56% LMS 46.44% 2 5 6 5.0287 VP 79.29% LMS 20.71% 2 1 7 5.0806 LMS 95.63% VP 4.37% 2 2 7 8.6032 LMS 87.94% VP 12.06% 2 3 7 11.1296 LMS 76.95% VP 23.05% 2 4 7 11.9691 LMS 61.34% VP 38.66% 2 5 7 10.266 VP 58.27% LMS 41.73% 2 6 7 5.9262 VP 80.90% LMS 19.10% 2 1 8 5.406 LMS 97.02% VP 2.98% 2 2 8 9.4609 LMS 91.88% VP 8.12% 2 3 8 12.9289 LMS 83.70% VP 16.30% 2 4 8 14.8757 LMS 72.81% VP 27.19% 2 5 8 15.0612 LMS 56.91% VP 43.09% 2 6 8 12.5056 VP 60.03% LMS 39.97%
  16. 2 1 5 4.0913 LMS 90.37% VP 9.63% 2 2

    5 6.0226 LMS 73.47% VP 26.53% 2 3 5 6.1922 LMS 50.30% VP 49.70% 2 4 5 4.0649 VP 77.52% LMS 22.48% 2 1 6 4.7287 LMS 93.21% VP 6.79% 2 2 6 7.4563 LMS 82.80% VP 17.20% 2 3 6 8.8977 LMS 66.31% VP 33.69% 2 4 6 8.2597 VP 53.56% LMS 46.44% 2 5 6 5.0287 VP 79.29% LMS 20.71% 2 1 7 5.0806 LMS 95.63% VP 4.37% 2 2 7 8.6032 LMS 87.94% VP 12.06% 2 3 7 11.1296 LMS 76.95% VP 23.05% 2 4 7 11.9691 LMS 61.34% VP 38.66% 2 5 7 10.266 VP 58.27% LMS 41.73% 2 6 7 5.9262 VP 80.90% LMS 19.10% 2 1 8 5.406 LMS 97.02% VP 2.98% 2 2 8 9.4609 LMS 91.88% VP 8.12% 2 3 8 12.9289 LMS 83.70% VP 16.30% 2 4 8 14.8757 LMS 72.81% VP 27.19% 2 5 8 15.0612 LMS 56.91% VP 43.09% 2 6 8 12.5056 VP 60.03% LMS 39.97%
  17. 2 1 5 4.0913 LMS 90.37% VP 9.63% 2 2

    5 6.0226 LMS 73.47% VP 26.53% 2 3 5 6.1922 LMS 50.30% VP 49.70% 2 4 5 4.0649 VP 77.52% LMS 22.48% 2 1 6 4.7287 LMS 93.21% VP 6.79% 2 2 6 7.4563 LMS 82.80% VP 17.20% 2 3 6 8.8977 LMS 66.31% VP 33.69% 2 4 6 8.2597 VP 53.56% LMS 46.44% 2 5 6 5.0287 VP 79.29% LMS 20.71% 2 1 7 5.0806 LMS 95.63% VP 4.37% 2 2 7 8.6032 LMS 87.94% VP 12.06% 2 3 7 11.1296 LMS 76.95% VP 23.05% 2 4 7 11.9691 LMS 61.34% VP 38.66% 2 5 7 10.266 VP 58.27% LMS 41.73% 2 6 7 5.9262 VP 80.90% LMS 19.10% 2 1 8 5.406 LMS 97.02% VP 2.98% 2 2 8 9.4609 LMS 91.88% VP 8.12% 2 3 8 12.9289 LMS 83.70% VP 16.30% 2 4 8 14.8757 LMS 72.81% VP 27.19% 2 5 8 15.0612 LMS 56.91% VP 43.09% 2 6 8 12.5056 VP 60.03% LMS 39.97%
  18. ! games = MultiGames.new(players: [
 Player::Simple.new,
 Player::Simple.new, 
 Player::SingleMinded.new(action: :block),


    ]) games.simulate! game.victory_count_per_player
 > “ [["Simple 1", 44.95], “ 
 > “ ["Simple 2", 45.28], “
 > “ [“Single Minded block", 9.77]] “

  19. ! games = MultiGames.new(players: [
 Player::Simple.new,
 Player::Simple.new, 
 Player::SingleMinded.new(action: :block),


    ]) games.simulate! game.victory_count_per_player
 > “ [["Simple 1", 44.95], “ 
 > “ ["Simple 2", 45.28], “
 > “ [“Single Minded block", 9.77]] “
 yonbergman/missiles_and_microchips