Solving Ricochet Robots

Solving Ricochet Robots

Ricochet Robots is a puzzle board game for any number of players. While being a very fun game to play with some fascinating properties, it is also interesting to think about writing a program to play the game.

Let’s discuss a computerized player for Ricochet Robots that finds the optimal solution to any board in a reasonable amount of time. Along the way, we’ll learn about graph search techniques, data representation, algorithms, heuristics, pruning, and optimization.

27204e228cc521c6cafed3c92b95184c?s=128

Randy Coulman

March 09, 2015
Tweet

Transcript

  1. www .codingzeal.com @codingzeal Randy Coulman Solving Ricochet Robots Senior Software

    Engineer @randycoulman
  2. Why Graphs, Trees & Algorithms? Hierarchical structures: Organization charts

  3. Why Graphs, Trees & Algorithms? Relationship Maps: Social Networks like

    LinkedIn & Facebook
  4. Why Graphs, Trees & Algorithms? Trip Planning

  5. Why Graphs, Trees & Algorithms? Network Routing

  6. What is Ricochet Robots?

  7. Moves to get to

  8. 1 1. Blue Right Moves to get to

  9. 1 2 1. Blue Right 2. Blue Down Moves to

    get to
  10. 1 2 3 1. Blue Right 2. Blue Down 3.

    Blue Left Moves to get to
  11. 1 2 3 4 1. Blue Right 2. Blue Down

    3. Blue Left 4. Green Right Moves to get to
  12. 1 2 3 4 5 1. Blue Right 2. Blue

    Down 3. Blue Left 4. Green Right 5. Green Down Moves to get to
  13. 1 2 3 4 5 6 1. Blue Right 2.

    Blue Down 3. Blue Left 4. Green Right 5. Green Down 6. Green Left Moves to get to
  14. 1 2 3 4 5 6 7 1. Blue Right

    2. Blue Down 3. Blue Left 4. Green Right 5. Green Down 6. Green Left 7. Green Down Moves to get to
  15. Characterizing the Problem Possible Board States (size of state space):

    252 * 251 * 250 * 249 * 248 = 976,484,376,000
  16. Characterizing the Problem Branching Factor: 9 - 20 possible moves

    from each state
  17. Representing the Board • Board (Static: 16 x 16)

  18. Representing the Board • Board (Static: 16 x 16) •

    Walls & Targets (changes each game)
  19. Representing the Board • Board (Static: 16 x 16) •

    Walls & Targets (changes each game) • Goal (changes each turn)
  20. Representing the Board • Board (Static: 16 x 16) •

    Walls & Targets (changes each game) • Goal (changes each turn) • Robot Positions (changes each move)
  21. Representing Robot Movement 1 2 1 2 1

  22. Trees

  23. Search Algorithms See Jamis Buck’s book Basil & Fabian: Three

    Ways Through
  24. Depth-First Search

  25. Depth-First Search 1

  26. Depth-First Search 1 2

  27. Depth-First Search 1 2 3

  28. Depth-First Search 1 2 3 4

  29. Depth-First Search 1 2 3 4 5

  30. Depth-First Search 1 2 3 4 5 6

  31. Depth-First Search 1 2 3 4 5 6 7

  32. Depth-First Search 1 2 3 4 5 6 7 8

  33. Depth-First Search 1 2 3 4 5 6 7 8

    9
  34. Depth-First Search 1 2 3 4 5 6 7 8

    9 10
  35. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11
  36. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12
  37. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13
  38. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14
  39. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14 15
  40. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14 15 16
  41. Depth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14 15 16
  42. Depth-First Search def solve solve_recursively(Path.initial(state)) candidates.min_by(&:length) || Outcome.no_solution(state) end def

    solve_recursively(path) return candidates << path.to_outcome if path.solved? path.allowable_successors.each do |successor| solve_recursively(successor) end end
  43. Depth-First Search: Cycles

  44. Depth-First Search: Cycles

  45. Depth-First Search: Cycles

  46. Depth-First Search: Cycles

  47. Depth-First Search: Cycles

  48. Depth-First Search: Cycles

  49. Depth-First Search: Cycles

  50. Depth-First Search: Cycles

  51. Depth-First Search: Cycles

  52. Depth-First Search: Cycles 3 1 1 2 3 2 4

  53. Depth-First Search: Cycles

  54. Depth-First Search: Cycles

  55. Depth-First Search: Cycles

  56. Depth-First Search: Cycles

  57. 1 Depth-First Search: Cycles

  58. 1 2 Depth-First Search: Cycles

  59. 1 2 3 Depth-First Search: Cycles

  60. 1 2 3 4 Depth-First Search: Cycles

  61. 1 2 3 4 5 Depth-First Search: Cycles

  62. Breadth-First Search

  63. Breadth-First Search 1

  64. Breadth-First Search 1 2

  65. Breadth-First Search 1 2 3

  66. Breadth-First Search 1 2 3 4

  67. Breadth-First Search 1 2 3 4 5

  68. Breadth-First Search 1 2 3 4 5 6

  69. Breadth-First Search 1 2 3 4 5 6 7

  70. Breadth-First Search 1 2 3 4 5 6 7 8

  71. Breadth-First Search 1 2 3 4 5 6 7 8

    9
  72. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10
  73. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11
  74. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12
  75. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13
  76. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14
  77. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14 15
  78. Breadth-First Search 1 2 3 4 5 6 7 8

    9 10 11 12 13 14 15 16
  79. Breadth-First Search 1 2 5 3 7 8 4 9

    10 11 6 12 13 14 15 16
  80. Breadth-First Search def solve paths = [Path.initial(initial_state)] until paths.empty? path

    = paths.shift return path.to_outcome if path.solved? paths += path.allowable_successors end Outcome.no_solution(initial_state) end
  81. Breadth-First Search Global visited list works now

  82. Optimization Do Less Things: Reduce the size of the state

    space by pruning branches from the graph
  83. Optimization Do Things Faster: Less work per state

  84. Optimization Heuristics: Rules of Thumb Less certain; may work in

    some circumstances, but not others
  85. Heuristic: Move Active Robot First States Considered 0 350000 700000

    1050000 1400000 Original Algorithm Active First
  86. Do Less Things: Check for Solutions at Generation Time def

    solve paths = [Path.initial(initial_state)] until paths.empty? path = paths.shift return path.to_outcome if path.solved? paths += path.allowable_successors end Outcome.no_solution(initial_state) end
  87. Do Less Things: Check for Solutions at Generation Time 1

    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  88. Do Less Things: Check for Solutions at Generation Time def

    solve paths = [Path.initial(initial_state)] until paths.empty? path = paths.shift successors = path.allowable_successors solution = successors.find(&:solved?) return solution.to_outcome if solution paths += successors end Outcome.no_solution(initial_state) end
  89. Do Less Things: Check for Solutions at Generation Time 1

    2 3 4 5 6 X X X X X X X X X 16
  90. Do Less Things: Check for Solutions at Generation Time 0

    800,000 1,600,000 2,400,000 3,200,000 Original Algorithm Check at Generation Total States Considered
  91. Do Things Faster: Precompute stopping cells

  92. Do Things Faster: Precompute stopping cells States / second 0

    675 1350 2025 2700 Original Algorithm Pre-compute Stops
  93. Do Less Things / Do Things Faster: Treat non-active robots

    as equivalent
  94. Do Less Things / Do Things Faster: Treat non-active robots

    as equivalent 0 1,000,000 2,000,000 3,000,000 4,000,000 Original Algorithm Check at Generation Robot Equivalence Total States Considered
  95. Do Less Things / Do Things Faster: Treat non-active robots

    as equivalent States / second 0 750 1500 2250 3000 Original Algorithm Pre-compute Stops Robot Equiv.
  96. Do Things Faster: Sorted Array vs Set States / second

    0 800 1600 2400 3200 Original Algorithm Pre-compute Stops Robot Equiv. Arrays not Sets
  97. Do Things Faster: Less Object Creation States / second 0

    1250 2500 3750 5000 Original Algorithm Pre-compute Stops Robot Equiv. Arrays not Sets Less Objects
  98. Do Things Faster: Use Object Identity Instead of Deep Equality

    States / second 0 1750 3500 5250 7000 Original Algorithm Pre-compute Stops Robot Equiv. Arrays not Sets Less Objects Object Identity
  99. Final Results Solving time (seconds) 0 750 1500 2250 3000

    Original Active First Check at Gen. Pre-compute Robot Equiv. Arrays not Sets Less Objects Robot Identity
  100. Demo

  101. Future Ideas: Better Algorithms A* Algorithm

  102. A* Algorithm Next state to expand is one with minimum

    distance so far + estimated distance to goal
  103. A* Algorithm 0

  104. A* Algorithm 1 1 1 1 1 1 1 1

    1 1 1 1 1 1 1 1 1 1 1 0
  105. A* Algorithm 1 1 1 1 1 1 1 1

    1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0
  106. A* Algorithm 1 1 1 1 1 1 1 1

    1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0
  107. A* Algorithm 1 1 1 1 1 1 1 1

    1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 0
  108. A* Algorithm 1 1 1 1 1 1 1 1

    1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 0
  109. Future Ideas: Better Algorithms Collision Detection

  110. Future Ideas: Better Algorithms Work Backwards

  111. Work Backwards

  112. Future Ideas: Better Heuristics Move most-recently moved robots first (MRU)

  113. Future Ideas: Better Heuristics Some combination of active robot and

    MRU robots
  114. Future Ideas: Better Heuristics Sort movement directions intelligently

  115. Future Ideas: More Optimizations Pre-compute per-robot stopping positions

  116. Future Ideas: More Optimizations Use less objects and more primitive

    types
  117. Future Ideas: More Optimizations Parallelism

  118. Acknowledgements • Trever Yarrish of Zeal for the awesome graphics

    and visualizations • The Zealots of Zeal for ideas, feedback, and pairing on the solver • Michael Fogleman for some optimization ideas • Trevor Lalish-Menagh for introducing me to the game
  119. Questions ? http://randycoulman.com http://speakerdeck.com/randycoulman http://speakerrate.com/randycoulman Code is on Github https://github.com/CodingZeal/robots

  120. www .codingzeal.com @codingzeal Thank You! Randy Coulman @randycoulman