tic_tac_toe_nhu 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tic_tac_toe +2 -2
  3. data/coverage/.last_run.json +1 -1
  4. data/coverage/.resultset.json +51 -136
  5. data/lib/tic_tac_toe/game.rb +44 -23
  6. data/lib/tic_tac_toe/game_state.rb +35 -0
  7. data/lib/tic_tac_toe/game_state_factory.rb +56 -0
  8. data/lib/tic_tac_toe/player.rb +2 -2
  9. data/lib/tic_tac_toe/player_factory.rb +3 -3
  10. data/lib/tic_tac_toe/rules.rb +1 -1
  11. data/lib/tic_tac_toe/strategy/console_user.rb +1 -1
  12. data/lib/tic_tac_toe/strategy/minimax.rb +18 -16
  13. data/lib/tic_tac_toe/ui/console.rb +3 -3
  14. data/lib/tic_tac_toe/values.rb +10 -0
  15. data/spec/integration/tictactoe/unbeatable_computer_spec.rb +3 -3
  16. data/spec/mocks/game_state.rb +18 -0
  17. data/spec/mocks/game_state_factory.rb +14 -0
  18. data/spec/mocks/player.rb +16 -0
  19. data/spec/mocks/player_factory.rb +3 -2
  20. data/spec/mocks/strategy/dynamic.rb +1 -1
  21. data/spec/tic_tac_toe/game_spec.rb +66 -61
  22. data/spec/tic_tac_toe/game_state_factory_spec.rb +63 -0
  23. data/spec/tic_tac_toe/game_state_spec.rb +73 -0
  24. data/spec/tic_tac_toe/player_factory_spec.rb +5 -9
  25. data/spec/tic_tac_toe/player_spec.rb +17 -16
  26. data/spec/tic_tac_toe/strategy/console_user_spec.rb +1 -1
  27. data/spec/tic_tac_toe/strategy/minimax_spec.rb +65 -64
  28. data/spec/tic_tac_toe/ui/console_spec.rb +1 -1
  29. data/spec/tic_tac_toe/values_spec.rb +17 -0
  30. data/tic_tac_toe_nhu.gemspec +1 -1
  31. metadata +10 -8
  32. data/lib/tic_tac_toe/game_factory.rb +0 -51
  33. data/lib/tic_tac_toe/main.rb +0 -54
  34. data/spec/integration/tictactoe/tic_tac_toe.rb +0 -4
  35. data/spec/mocks/game_factory.rb +0 -14
  36. data/spec/tic_tac_toe/game_factory_spec.rb +0 -78
  37. data/spec/tic_tac_toe/main_spec.rb +0 -95
  38. data/spec/tic_tac_toe/player_factory_mock.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ce848a6a1b98e49fe1ed21d164699803a9b4833
4
- data.tar.gz: 59ec4617389b657d7cfb61c494af0aacf3efeefe
3
+ metadata.gz: 8f1770dfd1f3fd4f446e2d8ccb4d31e023ef4986
4
+ data.tar.gz: d3d62e4b846be464fcf0343e7d3decc94c6afeca
5
5
  SHA512:
6
- metadata.gz: e7a2aeda27b4a5aec06a1103bb11a3d09a013266fcd4feabb27f8a3b10a734e6e6c0492a9196fd1dfe6d4ac8508e79c191ffbfe8d4a9954217fd09e3d2f834bd
7
- data.tar.gz: 874c77c1609da0cc49cf0d46cc12b056f46ae89d5a4400d88962e60d2daece1d12f70f5b079ab9cea5c73d48835b13cd6d3e0e3ef2668b1eb937ac26793444a5
6
+ metadata.gz: ad5258407ba0594a5988b1bbdb9a25ba0341da84746d61444ef0e906d913e98775ff3f7c20092e675d96ee86ba9b98fc938ce149c1d26ee2443959ef4532d667
7
+ data.tar.gz: 90f0131d85e62ef241ca81aafb2e65d50288e80b2f7765c6124a7b7c4cee7e6a0eabfdd50f509433c7786b3e3a0b123fe8d339071cead8e343dfc8606810e519
data/bin/tic_tac_toe CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  $LOAD_PATH << File.expand_path('../../lib', __FILE__)
3
- require 'tic_tac_toe/main'
3
+ require 'tic_tac_toe/game'
4
4
 
5
- game = TicTacToe::Main.new
5
+ game = TicTacToe::Game.new
6
6
  game.start
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "result": {
3
- "covered_percent": 100.0
3
+ "covered_percent": 91.14
4
4
  }
5
5
  }
@@ -1,234 +1,149 @@
1
1
  {
2
2
  "RSpec": {
3
3
  "coverage": {
4
- "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/game_factory.rb": [
4
+ "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/rules.rb": [
5
5
  1,
6
6
  1,
7
7
  1,
8
- null,
9
- 1,
10
- 1,
11
- 1,
12
- 13,
8
+ 30,
13
9
  null,
14
10
  null,
15
11
  1,
16
- 6,
12
+ 8,
13
+ 3,
14
+ 3,
17
15
  null,
18
16
  null,
19
17
  1,
20
- 6,
18
+ 4,
21
19
  null,
22
- 2,
23
20
  null,
24
21
  1,
22
+ 77,
25
23
  null,
26
- 1,
27
24
  null,
28
25
  1,
29
- null,
30
26
  1,
27
+ 47,
28
+ 734,
31
29
  null,
32
30
  null,
33
31
  null,
34
32
  1,
35
- 1,
36
- 1,
37
- null,
38
- null,
39
- 1,
40
- 2,
41
- null,
42
- null,
43
- 1,
44
- 1,
45
- null,
46
- null,
47
- 1,
48
- 1,
49
- null,
50
- null,
51
- 1,
52
- 5,
33
+ 47,
53
34
  null,
54
35
  null,
55
36
  null
56
37
  ],
57
- "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/game.rb": [
58
- 1,
59
- null,
38
+ "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/board.rb": [
60
39
  1,
61
40
  1,
62
- 1,
63
- null,
64
- 1,
65
- 16,
66
- 16,
67
- 16,
68
- 16,
69
41
  null,
70
42
  null,
71
43
  1,
72
- 4,
73
- 4,
74
- 2,
75
- 2,
76
- null,
77
- null,
78
- null,
79
44
  1,
80
- 4,
81
- null,
82
45
  null,
83
46
  1,
84
- 3,
47
+ 30,
48
+ 30,
85
49
  null,
86
50
  null,
87
51
  1,
88
- 1,
89
- 2,
52
+ 30,
53
+ 30,
54
+ 30,
90
55
  null,
91
56
  null,
92
57
  1,
93
- 3,
94
- 2,
58
+ 107,
59
+ 107,
60
+ 107,
95
61
  null,
62
+ 0,
96
63
  null,
97
64
  null,
98
- null
99
- ],
100
- "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/main.rb": [
101
- 1,
102
- 1,
103
- 1,
104
65
  null,
105
66
  1,
106
- 1,
107
- 1,
67
+ 0,
108
68
  null,
109
- 1,
110
- 12,
111
- 12,
112
- 12,
113
69
  null,
114
- null,
115
- 1,
116
- 12,
117
- 12,
118
- 12,
119
- 12,
120
- 12,
121
- null,
122
- null,
123
- 1,
124
- 1,
125
- 13,
126
- 13,
127
- 13,
128
70
  1,
129
- 1,
130
- null,
71
+ 5,
131
72
  null,
132
73
  null,
133
74
  1,
134
- 7,
135
- 7,
136
- 7,
137
- 7,
138
- 7,
139
- 1,
140
- 1,
141
- null,
75
+ 0,
142
76
  null,
143
77
  null,
144
78
  1,
145
- 12,
146
- 12,
147
- 11,
79
+ 47,
80
+ 141,
148
81
  null,
149
- 1,
150
- null,
151
- null,
152
- null,
153
- null,
154
- null
155
- ],
156
- "/Users/nhunguyen/Documents/Ruby/tictactoe/lib/tic_tac_toe/ui/console.rb": [
157
- 1,
158
82
  null,
159
- 1,
160
- 1,
161
83
  null,
162
84
  1,
163
- 14,
164
- 14,
85
+ 47,
86
+ 47,
165
87
  null,
88
+ 1269,
89
+ 141,
166
90
  null,
167
- 1,
168
- 1,
91
+ 47,
169
92
  null,
170
93
  null,
171
94
  1,
172
- 4,
95
+ 47,
96
+ 47,
97
+ 47,
173
98
  null,
174
99
  null,
175
100
  1,
176
101
  5,
177
102
  5,
178
- 5,
179
- 5,
180
- 4,
181
- null,
182
- 1,
103
+ 45,
183
104
  null,
105
+ 5,
184
106
  null,
185
107
  null,
186
108
  1,
187
- 1,
188
- null,
109
+ 0,
110
+ 0,
111
+ 0,
189
112
  null,
190
- 1,
191
- 1,
113
+ 0,
192
114
  null,
193
115
  null,
194
116
  1,
195
117
  1,
118
+ 152,
119
+ 152,
196
120
  null,
197
- null,
198
- 1,
199
- 1,
121
+ 134,
200
122
  null,
201
123
  null,
202
124
  1,
203
- 1,
204
- 4,
205
- 4,
206
- 21,
207
- 21,
208
- null,
209
- 4,
125
+ 152,
126
+ 152,
210
127
  null,
211
128
  null,
212
129
  1,
213
- 5,
214
- 5,
215
- 20,
216
- null,
217
- 5,
130
+ 152,
218
131
  null,
219
132
  null,
220
133
  1,
221
- 20,
134
+ 47,
135
+ 423,
136
+ null,
137
+ 47,
222
138
  null,
223
139
  null,
224
140
  1,
225
- 40,
226
- 20,
141
+ 470,
227
142
  null,
228
143
  null,
229
144
  null
230
145
  ]
231
146
  },
232
- "timestamp": 1374084056
147
+ "timestamp": 1375217786
233
148
  }
234
149
  }
@@ -1,41 +1,62 @@
1
- require 'tic_tac_toe/rules'
1
+ require 'tic_tac_toe/ui/console'
2
+ require 'tic_tac_toe/game_state_factory'
3
+ require 'tic_tac_toe/board'
2
4
 
3
5
  module TicTacToe
4
6
  class Game
5
- attr_reader :current_player, :board
6
7
 
7
- def initialize(board, player1, player2)
8
- @board = board
9
- @current_player = @player1 = player1
10
- @player2 = player2
11
- @rules = TicTacToe::Rules.new(@board)
8
+ def initialize(ui = TicTacToe::Console.new, game_state_factory = TicTacToe::GameStateFactory.new)
9
+ @ui = ui
10
+ @game_state_factory = game_state_factory
12
11
  end
13
12
 
14
- def make_move
15
- player_move = @current_player.move
16
- if player_move
17
- @board.mark(player_move, @current_player.value)
18
- change_player
19
- end
13
+ def start
14
+ ui.display_welcome_message
15
+ @game_state = create_game_state
16
+ play until game_state.game_over?
17
+ ui.display_board(board)
18
+ display_result
20
19
  end
21
20
 
22
- def over?
23
- @rules.game_over?
21
+ private
22
+ attr_reader :ui, :game_state, :game_state_factory
23
+
24
+ def create_game_state
25
+ game_type = ui.game_type
26
+ begin
27
+ game_state_factory.create(game_type)
28
+ rescue ArgumentError
29
+ create_game_state
30
+ end
24
31
  end
25
32
 
26
- def winner
27
- player(@rules.winner)
33
+ def play
34
+ ui.display_board(board)
35
+ player = game_state.current_player
36
+ ui.display_player_turn(player)
37
+ make_move(player)
38
+ game_state.change_player
28
39
  end
29
40
 
30
- private
31
- def change_player
32
- @current_player = (@current_player == @player1) ? @player2 : @player1
41
+ def make_move(player)
42
+ begin
43
+ player.move(board)
44
+ rescue MoveNotAvailableError
45
+ make_move(player)
46
+ end
33
47
  end
34
48
 
35
- def player(mark)
36
- return @player1 if mark == @player1.value
37
- return @player2 if mark == @player2.value
49
+ def display_result
50
+ winner = game_state.winner
51
+ if winner
52
+ ui.display_winner(winner)
53
+ else
54
+ ui.display_tied_game
55
+ end
38
56
  end
39
57
 
58
+ def board
59
+ game_state.board
60
+ end
40
61
  end
41
62
  end
@@ -0,0 +1,35 @@
1
+ require 'tic_tac_toe/board'
2
+ require 'tic_tac_toe/rules'
3
+
4
+ module TicTacToe
5
+ class GameState
6
+ attr_reader :board, :current_player
7
+
8
+ def initialize(players, board = TicTacToe::Board.new)
9
+ @board = board
10
+ @players = players
11
+ @current_player = players[0]
12
+ end
13
+
14
+ def change_player
15
+ @current_player = (@players[0] == @current_player)? @players[1] : @players[0]
16
+ end
17
+
18
+ def game_over?
19
+ rules.game_over?
20
+ end
21
+
22
+ def winner
23
+ player(rules.winner)
24
+ end
25
+
26
+ private
27
+ def rules
28
+ TicTacToe::Rules.new(board)
29
+ end
30
+
31
+ def player(value)
32
+ @players.detect {|p| p.value == value}
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,56 @@
1
+ require 'tic_tac_toe/game_state'
2
+ require 'tic_tac_toe/board'
3
+ require 'tic_tac_toe/player_factory'
4
+ require 'tic_tac_toe/values'
5
+
6
+ module TicTacToe
7
+ class GameStateFactory
8
+ def initialize(player_factory = TicTacToe::PlayerFactory.new)
9
+ @player_factory = player_factory
10
+ end
11
+
12
+ def types
13
+ [[:human, :computer], [:computer, :human], [:human, :human], [:computer, :computer]]
14
+ end
15
+
16
+ def create(game_type)
17
+ case game_type
18
+ when 1
19
+ human_computer_game
20
+ when 2
21
+ computer_human_game
22
+ when 3
23
+ human_human_game
24
+ when 4
25
+ computer_computer_game
26
+ else
27
+ raise ArgumentError, "Type #{game_type} does not exist. Please select a number corresponding to the game type."
28
+ end
29
+ end
30
+
31
+ private
32
+ def computer_human_game
33
+ create_game(@player_factory.computer, @player_factory.human)
34
+ end
35
+
36
+ def human_computer_game
37
+ create_game(@player_factory.human, @player_factory.computer)
38
+ end
39
+
40
+ def human_human_game
41
+ player1 = @player_factory.human
42
+ player2 = @player_factory.human("Friend", TicTacToe::Values.opponent(player1.value))
43
+ create_game(player1, player2)
44
+ end
45
+
46
+ def computer_computer_game
47
+ computer1 = @player_factory.computer(TicTacToe::VALUES[0])
48
+ computer2 = @player_factory.computer(TicTacToe::Values.opponent(computer1.value))
49
+ create_game(computer1, computer2)
50
+ end
51
+
52
+ def create_game(player1, player2)
53
+ TicTacToe::GameState.new([player1, player2])
54
+ end
55
+ end
56
+ end
@@ -8,8 +8,8 @@ module TicTacToe
8
8
  @strategy = strategy
9
9
  end
10
10
 
11
- def move
12
- @strategy.move
11
+ def move(board)
12
+ board.mark(strategy.move(board), value)
13
13
  end
14
14
  end
15
15
  end
@@ -5,12 +5,12 @@ require 'tic_tac_toe/player'
5
5
  module TicTacToe
6
6
  class PlayerFactory
7
7
 
8
- def human(name = "User", value = "X")
8
+ def human(name = "User", value = TicTacToe::VALUES[0])
9
9
  TicTacToe::Player.new(name, value, TicTacToe::Strategy::ConsoleUser.new)
10
10
  end
11
11
 
12
- def computer(board, value = "O", opponent_value = "X")
13
- ai = TicTacToe::Strategy::Minimax.new(board, value, opponent_value)
12
+ def computer(value = TicTacToe::VALUES[1])
13
+ ai = TicTacToe::Strategy::Minimax.new(value)
14
14
  TicTacToe::Player.new("Computer", value, ai)
15
15
  end
16
16
  end
@@ -29,4 +29,4 @@ module TicTacToe
29
29
  @board.rows + @board.columns + @board.diagonals
30
30
  end
31
31
  end
32
- end
32
+ end
@@ -7,7 +7,7 @@ module TicTacToe
7
7
  @output = output
8
8
  end
9
9
 
10
- def move
10
+ def move(*board)
11
11
  move = nil
12
12
 
13
13
  until move =~ NUMBER
@@ -1,33 +1,35 @@
1
1
  require 'tic_tac_toe/rules'
2
+ require 'tic_tac_toe/values'
2
3
 
3
4
  module TicTacToe
4
5
  module Strategy
5
6
  class Minimax
6
- def initialize(board, player, opponent)
7
- @board = board
8
- @player = player
9
- @opponent = opponent
7
+ WINNING_SCORE = 1
8
+ LOSING_SCORE = -1
9
+ TIE = 0
10
+ MIDDLE_SQUARE = 4
11
+
12
+ def initialize(player_value)
13
+ @player_value = player_value
14
+ @opponent_value = TicTacToe::Values.opponent(@player_value)
10
15
  end
11
16
 
12
- def move
13
- return first_move if first_move?
14
- move = minimax(@player, @board)
17
+ def move(board)
18
+ return first_move(board) if first_move?(board)
19
+ move = minimax(player_value, board)
15
20
  move.move
16
21
  end
17
22
 
18
23
  private
19
- WINNING_SCORE = 1
20
- LOSING_SCORE = -1
21
- TIE = 0
22
- MIDDLE_SQUARE = 4
24
+ attr_reader :player_value, :opponent_value
23
25
 
24
- def first_move?
25
- moves_count = @board.number_of_moves
26
+ def first_move?(board)
27
+ moves_count = board.number_of_moves
26
28
  moves_count == 0 || moves_count == 1
27
29
  end
28
30
 
29
- def first_move
30
- return MIDDLE_SQUARE if @board.available_moves.include?(MIDDLE_SQUARE)
31
+ def first_move(board)
32
+ return MIDDLE_SQUARE if board.available_moves.include?(MIDDLE_SQUARE)
31
33
  0
32
34
  end
33
35
 
@@ -63,7 +65,7 @@ module TicTacToe
63
65
  end
64
66
 
65
67
  def opponent(player)
66
- (player == @player) ? @opponent : @player
68
+ (player == player_value) ? opponent_value : player_value
67
69
  end
68
70
 
69
71
  def best_move(moves)
@@ -1,4 +1,4 @@
1
- require 'tic_tac_toe/game_factory'
1
+ require 'tic_tac_toe/game_state_factory'
2
2
 
3
3
  module TicTacToe
4
4
  class Console
@@ -28,7 +28,7 @@ module TicTacToe
28
28
  end
29
29
 
30
30
  def display_winner(winner)
31
- @output.puts("#{winner.name}(#{winner.value}) win!")
31
+ @output.puts("#{winner.name}(#{winner.value}) wins!")
32
32
  end
33
33
 
34
34
  def display_tied_game
@@ -55,7 +55,7 @@ module TicTacToe
55
55
 
56
56
  def game_type_list
57
57
  result = ""
58
- TicTacToe::GameFactory.new.types.each_with_index do |value, index|
58
+ TicTacToe::GameStateFactory.new.types.each_with_index.map do |value, index|
59
59
  result << "#{index + 1} - #{game_type_values(value)}\n"
60
60
  end
61
61
  result
@@ -0,0 +1,10 @@
1
+ module TicTacToe
2
+ VALUES = ["X", "O"]
3
+
4
+ class Values
5
+ def self.opponent(value)
6
+ return VALUES[1] if value == VALUES[0]
7
+ VALUES[0]
8
+ end
9
+ end
10
+ end
@@ -26,8 +26,8 @@ describe "Unbeatable computer", :slow_test => true do
26
26
  end
27
27
 
28
28
  def make_computer_move(clone_board)
29
- computer = TicTacToe::PlayerFactory.new.computer(clone_board)
30
- clone_board.mark(computer.move, computer.value)
29
+ computer = TicTacToe::PlayerFactory.new.computer
30
+ computer.move(clone_board)
31
31
  clone_board
32
32
  end
33
33
 
@@ -50,7 +50,7 @@ describe "Unbeatable computer", :slow_test => true do
50
50
 
51
51
  def make_players_move(board, move_history, move)
52
52
  move_history << move
53
- board.mark(move, "X")
53
+ board.mark(move, @human_value)
54
54
  make_computer_move(board) if !rules(board).game_over?
55
55
  play_game_for_each_available_move(board, move_history)
56
56
  end
@@ -0,0 +1,18 @@
1
+ require 'surrogate/rspec'
2
+ require 'tic_tac_toe/game_state'
3
+
4
+ class MockGameState
5
+ Surrogate.endow(self)
6
+ define_reader :board
7
+ define_reader :current_player
8
+
9
+ define :winner
10
+ define :change_player
11
+ define(:game_over?){true}
12
+ end
13
+
14
+ describe TicTacToe::GameState do
15
+ it "checks MockGameState" do
16
+ MockGameState.should be_substitutable_for(TicTacToe::GameState)
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'surrogate/rspec'
2
+
3
+ class MockGameStateFactory
4
+ Surrogate.endow(self)
5
+ define(:initialize) {|player_factory|}
6
+ define :types
7
+ define(:create) {|type|}
8
+ end
9
+
10
+ describe TicTacToe::GameStateFactory do
11
+ it "checks game factory" do
12
+ MockGameStateFactory.should be_substitutable_for(TicTacToe::GameStateFactory)
13
+ end
14
+ end