Class: XO::Engine
- Inherits:
-
Object
- Object
- XO::Engine
- Defined in:
- lib/xo/engine.rb
Overview
A state machine that encapsulates the game logic for Tic-tac-toe. The operation of the engine is completely determined by the properties:
-
#grid, and
The engine can be in one of the 3 following states (represented by a symbol):
-
:init
-
:playing
-
:game_over
The engine begins in the :init state. And, the following methods are used to advance a single game of Tic-tac-toe (and transition the engine between its states) by obeying the standard rules of Tic-tac-toe:
-
#start: [:init]
-
#stop: [:playing, :game_over]
-
#play: [:playing]
-
#continue_playing: [:game_over]
The array of symbols after each method lists the states in which the method is allowed to be called.
Defined Under Namespace
Classes: IllegalStateError
Instance Attribute Summary collapse
- #last_event ⇒ Hash readonly
- #state ⇒ :init, ... readonly
- #turn ⇒ Grid::X, ... readonly
Instance Method Summary collapse
-
#continue_playing(turn) ⇒ self
Similar to start but should only be used to play another round when a game has ended.
-
#grid ⇒ Grid
Get the grid that’s managed by the engine.
-
#initialize ⇒ Engine
constructor
Creates a new Engine with its state set to :init, turn set to :nobody, an empty grid and last_event set to { name: :new }.
- #next_turn ⇒ Grid::X, ...
-
#play(r, c) ⇒ self
Makes a move at the given position (r, c) which may transition the engine into the :game_over state or leave it in the :playing state.
-
#start(turn) ⇒ self
Transitions the engine from the :init state into the :playing state.
-
#stop ⇒ self
Transitions the engine from the :playing or :game_over state into the :game_over state.
Constructor Details
#initialize ⇒ Engine
Creates a new XO::Engine with its state set to :init, turn set to :nobody, an empty grid and last_event set to { name: :new }.
60 61 62 63 64 65 66 |
# File 'lib/xo/engine.rb', line 60 def initialize @grid = Grid.new reset set_event(:new) end |
Instance Attribute Details
#last_event ⇒ Hash (readonly)
56 57 58 |
# File 'lib/xo/engine.rb', line 56 def last_event @last_event end |
#state ⇒ :init, ... (readonly)
50 51 52 |
# File 'lib/xo/engine.rb', line 50 def state @state end |
Instance Method Details
#continue_playing(turn) ⇒ self
Similar to start but should only be used to play another round when a game has ended. It transitions the engine from the :game_over state into the :playing state.
Sets the last event to be:
{ name: :game_started, type: :continue_playing }
177 178 179 180 181 182 183 184 185 186 |
# File 'lib/xo/engine.rb', line 177 def (turn) check_turn(turn) case state when :game_over (turn) else raise IllegalStateError, "must be in the :game_over state but state = :#{state}" end end |
#grid ⇒ Grid
Get the grid that’s managed by the engine.
71 72 73 |
# File 'lib/xo/engine.rb', line 71 def grid @grid.dup end |
#next_turn ⇒ Grid::X, ...
79 80 81 |
# File 'lib/xo/engine.rb', line 79 def next_turn Grid.other_token(turn) end |
#play(r, c) ⇒ self
Makes a move at the given position (r, c) which may transition the engine into the :game_over state or leave it in the :playing state.
Sets the last event as follows:
-
If the position is out of bounds, then
{ name: :invalid_move, type: :out_of_bounds } -
If the position is occupied, then
{ name: :invalid_move, type: :occupied } -
If the move was allowed and didn’t result in ending the game, then
{ name: :next_turn, last_move: { turn: :a_token, r: :a_row, c: :a_column } } -
If the move was allowed and resulted in a win, then
{ name: :game_over, type: :winner, last_move: { turn: :a_token, r: :a_row, c: :a_column }, details: :the_details } -
If the move was allowed and resulted in a squashed game, then
{ name: :game_over, type: :squashed, last_move: { turn: :a_token, r: :a_row, c: :a_column } }
Legend:
-
:a_row is one of 1, 2 or 3
-
:a_column is one of 1, 2 or 3
-
:the_details is taken verbatim from the :details key of the returned hash of XO::Evaluator#analyze
157 158 159 160 161 162 163 164 |
# File 'lib/xo/engine.rb', line 157 def play(r, c) case state when :playing handle_play(r, c) else raise IllegalStateError, "must be in the :playing state but state = :#{state}" end end |
#start(turn) ⇒ self
Transitions the engine from the :init state into the :playing state.
Sets the last event to be:
{ name: :game_started }
93 94 95 96 97 98 99 100 101 102 |
# File 'lib/xo/engine.rb', line 93 def start(turn) check_turn(turn) case state when :init handle_start(turn) else raise IllegalStateError, "must be in the :init state but state = :#{state}" end end |
#stop ⇒ self
Transitions the engine from the :playing or :game_over state into the :game_over state.
Sets the last event to be:
{ name: :game_over }
112 113 114 115 116 117 118 119 |
# File 'lib/xo/engine.rb', line 112 def stop case state when :playing, :game_over handle_stop else raise IllegalStateError, "must be in the :playing or :game_over state but state = :#{state}" end end |