tic_tac_toe_nhu 0.0.2

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +39 -0
  6. data/README.md +19 -0
  7. data/Rakefile +15 -0
  8. data/bin/tic_tac_toe +6 -0
  9. data/coverage/.last_run.json +5 -0
  10. data/coverage/.resultset.json +502 -0
  11. data/features/step_definitions/tictactoe_steps.rb +6 -0
  12. data/features/support/env.rb +2 -0
  13. data/features/tictactoe_start_game.feature +11 -0
  14. data/features/tictactoe_submit_move.feature +9 -0
  15. data/lib/tic_tac_toe/board.rb +99 -0
  16. data/lib/tic_tac_toe/game.rb +41 -0
  17. data/lib/tic_tac_toe/game_factory.rb +55 -0
  18. data/lib/tic_tac_toe/main.rb +47 -0
  19. data/lib/tic_tac_toe/player.rb +15 -0
  20. data/lib/tic_tac_toe/player_factory.rb +17 -0
  21. data/lib/tic_tac_toe/rules.rb +32 -0
  22. data/lib/tic_tac_toe/strategy/console_user.rb +26 -0
  23. data/lib/tic_tac_toe/strategy/minimax.rb +83 -0
  24. data/lib/tic_tac_toe/ui/console.rb +60 -0
  25. data/spec/integration/tictactoe/tic_tac_toe.rb +7 -0
  26. data/spec/mocks/game.rb +11 -0
  27. data/spec/mocks/game_factory.rb +8 -0
  28. data/spec/mocks/player_factory.rb +8 -0
  29. data/spec/mocks/rules.rb +9 -0
  30. data/spec/mocks/strategy/dynamic.rb +19 -0
  31. data/spec/mocks/ui/console.rb +15 -0
  32. data/spec/tic_tac_toe/board_spec.rb +186 -0
  33. data/spec/tic_tac_toe/game_factory_spec.rb +78 -0
  34. data/spec/tic_tac_toe/game_spec.rb +94 -0
  35. data/spec/tic_tac_toe/main_spec.rb +104 -0
  36. data/spec/tic_tac_toe/player_factory_mock.rb +10 -0
  37. data/spec/tic_tac_toe/player_factory_spec.rb +59 -0
  38. data/spec/tic_tac_toe/player_spec.rb +32 -0
  39. data/spec/tic_tac_toe/rules_spec.rb +185 -0
  40. data/spec/tic_tac_toe/spec_helper.rb +9 -0
  41. data/spec/tic_tac_toe/strategy/console_user_spec.rb +34 -0
  42. data/spec/tic_tac_toe/strategy/minimax_spec.rb +163 -0
  43. data/spec/tic_tac_toe/strategy/mock.rb +19 -0
  44. data/spec/tic_tac_toe/ui/console_spec.rb +93 -0
  45. data/tic_tac_toe_nhu.gemspec +11 -0
  46. metadata +86 -0
@@ -0,0 +1,186 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/board'
3
+
4
+ describe TicTacToe::Board do
5
+ before(:each) do
6
+ @player = "X"
7
+ @size = 3
8
+ @board = TicTacToe::Board.new(@size)
9
+ end
10
+
11
+ it "clears all the markings for the board" do
12
+ @board.mark(0, @player)
13
+ @board.reset
14
+ @board.squares.should_not include @player
15
+ end
16
+
17
+ context "marking the board" do
18
+ it "marks the board" do
19
+ @board.mark(0, @player)
20
+ @board.available_moves.should_not include 0
21
+ end
22
+
23
+ context "raises an exception" do
24
+ it "when move is less than the first square" do
25
+ lambda {@board.mark(-1, @player)}.should raise_error TicTacToe::MoveNotAvailableError
26
+ end
27
+
28
+ it "when move is higher than the last square" do
29
+ lambda {@board.mark(9, @player)}.should raise_error TicTacToe::MoveNotAvailableError
30
+ end
31
+
32
+ it "when move is already mark" do
33
+ @board.mark(0, @player)
34
+ lambda {@board.mark(0, @player)}.should raise_error TicTacToe::MoveNotAvailableError
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ context "number of moves made" do
41
+ it "is 0 when there is no move" do
42
+ @board.number_of_moves.should == 0
43
+ end
44
+
45
+ it "is 1 after user makes a move" do
46
+ @board.mark(0, @player)
47
+ @board.number_of_moves.should == 1
48
+ end
49
+
50
+ it "is 9 when user marks all the squares" do
51
+ mark_all_squares
52
+ @board.number_of_moves.should == @size**2
53
+ end
54
+ end
55
+
56
+ it "clears the value" do
57
+ @board.mark(0, @player)
58
+ @board.clear(0)
59
+ @board.available_moves.should include 0
60
+ end
61
+
62
+ context "filled?" do
63
+ it "is true when all the squares are marked" do
64
+ mark_all_squares
65
+ @board.should be_filled
66
+ end
67
+
68
+ it "is false when the board is empty" do
69
+ @board.should_not be_filled
70
+ end
71
+
72
+ it "is false when all but one squares are marked" do
73
+ mark_all_squares
74
+ @board.clear(0)
75
+ @board.should_not be_filled
76
+ end
77
+ end
78
+
79
+ context "empty?" do
80
+
81
+
82
+ end
83
+
84
+ it "board @size is the @size set" do
85
+ @board.size.should == @size
86
+ end
87
+
88
+ describe "rows" do
89
+ it "number of board rows is the same as the board @size" do
90
+ rows = @board.rows
91
+ rows.size.should == @size
92
+ end
93
+
94
+ it "the first element in the rows is the first row" do
95
+ @board.mark(0, @player)
96
+ rows = @board.rows
97
+ rows[0].should == [@player, nil, nil]
98
+ end
99
+
100
+ it "returns the rows with the middle element as the middle rows" do
101
+ @board.mark(3, @player)
102
+ rows = @board.rows
103
+ rows[@size/2].should == [@player, nil, nil]
104
+ end
105
+
106
+ it "returns the rows with the last elements as the last rows" do
107
+ @board.mark(6, @player)
108
+ rows = @board.rows
109
+ rows[@size - 1].should == [@player, nil, nil]
110
+ end
111
+
112
+ it "returns an empty row when the squares in the row is not mark" do
113
+ rows = @board.rows
114
+ rows[0].should == default_square_sets[0]
115
+ end
116
+ end
117
+
118
+ describe "columns" do
119
+ it "returns the columns of the boards" do
120
+ cols = default_square_sets(3)
121
+ @board.columns.should == cols
122
+ end
123
+
124
+ it "number of columns is the @size of the board" do
125
+ @board.columns.size.should == @size
126
+ end
127
+ end
128
+
129
+ describe "diagonals" do
130
+ it "returns the diagonal squares" do
131
+ squares = default_square_sets(2)
132
+ @board.diagonals.should == squares
133
+ end
134
+
135
+ it "returns the diagonal squares with the marked value" do
136
+ @board.mark(4, @player)
137
+ squares = [[nil, @player, nil], [nil, @player, nil]]
138
+ @board.diagonals.should == squares
139
+ end
140
+ end
141
+
142
+ describe "available squares" do
143
+ it "returns all squares when none is marked" do
144
+ @board.available_moves.should == (0...@size**2).to_a
145
+ end
146
+
147
+ it "doesn't return the marked square" do
148
+ move = 2
149
+ @board.mark(move, @player)
150
+ @board.available_moves.should_not include move
151
+ end
152
+
153
+ it "is empty when all the squares are marked" do
154
+ mark_all_squares
155
+ @board.available_moves.should == []
156
+ end
157
+ end
158
+
159
+ describe "unique marks" do
160
+ it "returns user marks when user marks the board" do
161
+ @board.mark(4, @player)
162
+ @board.unique_marked_values.should == [@player]
163
+ end
164
+
165
+ it "returns two marks when two user marks the board" do
166
+ @board.mark(4, "X")
167
+ @board.mark(0, "O")
168
+ @board.unique_marked_values.should == ["X", "O"]
169
+ end
170
+
171
+ it "returns one mark even though user marks the board twice" do
172
+ @board.mark(4, @player)
173
+ @board.mark(0, @player)
174
+ @board.unique_marked_values.should == [@player]
175
+ end
176
+
177
+ end
178
+
179
+ def mark_all_squares
180
+ (0...@size**2).each {|position| @board.mark(position, @player)}
181
+ end
182
+
183
+ def default_square_sets(size = 1)
184
+ Array.new(size, Array.new(3))
185
+ end
186
+ end
@@ -0,0 +1,78 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/game_factory'
3
+ require 'tic_tac_toe/player_factory'
4
+ require 'mocks/player_factory'
5
+
6
+ describe TicTacToe::GameFactory do
7
+
8
+ before(:each) do
9
+ @board = "board"
10
+ @player_factory = MockPlayerFactory.new
11
+ @game_factory = TicTacToe::GameFactory.new(@player_factory)
12
+ end
13
+
14
+ it "ensures MockPlayerFactory has the same interface as PlayerFactory" do
15
+ MockPlayerFactory.should be_substitutable_for(TicTacToe::PlayerFactory)
16
+ end
17
+
18
+ it "returns 4 types of games" do
19
+ @game_factory.types.size.should == 4
20
+ end
21
+
22
+ it "sets the board in the game" do
23
+ game = @game_factory.create(1, @board)
24
+ game.board.should == @board
25
+ end
26
+
27
+ it "returns human vs computer game" do
28
+ human = "human"
29
+ computer = "computer"
30
+ @player_factory.will_have_human human
31
+ @player_factory.will_have_computer computer
32
+
33
+ game = @game_factory.create(1, @board)
34
+ game.current_player.should == human
35
+
36
+ @player_factory.was asked_for :computer
37
+ @player_factory.was asked_for :human
38
+ end
39
+
40
+ it "returns computer vs human game" do
41
+ human = "human"
42
+ computer = "computer"
43
+ @player_factory.will_have_human human
44
+ @player_factory.will_have_computer computer
45
+
46
+ game = @game_factory.create(2, @board)
47
+ game.current_player.should == computer
48
+
49
+ @player_factory.was asked_for :computer
50
+ @player_factory.was asked_for :human
51
+ end
52
+
53
+ it "returns user vs user game" do
54
+ human1 = "mary"
55
+ human2 = "alice"
56
+ @player_factory.will_have_human human1, human2
57
+
58
+ game = @game_factory.create(3, @board)
59
+ game.current_player.should == human1
60
+
61
+ @player_factory.was asked_for(:human).times(2)
62
+ end
63
+
64
+ it "returns computer vs computer game" do
65
+ computer1 = "computer1"
66
+ computer2 = "computer2"
67
+ @player_factory.will_have_computer computer2, computer1
68
+
69
+ game = @game_factory.create(4, @board)
70
+ game.current_player.should == computer2
71
+
72
+ @player_factory.was asked_for(:computer).times(2)
73
+ end
74
+
75
+ it "raises an error when the game type doesn't exist" do
76
+ lambda{@game_factory.create(5, @board)}.should raise_error(ArgumentError)
77
+ end
78
+ end
@@ -0,0 +1,94 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/game'
3
+ require 'tic_tac_toe/board'
4
+ require 'tic_tac_toe/player'
5
+ require 'mocks/strategy/dynamic'
6
+
7
+ describe TicTacToe::Game do
8
+ before(:each) do
9
+ @board = TicTacToe::Board.new
10
+
11
+ @todd_strategy = MockDynamicStrategy.new
12
+ @todd = TicTacToe::Player.new("Todd", "X", @todd_strategy)
13
+
14
+ @john_strategy = MockDynamicStrategy.new
15
+ @john = TicTacToe::Player.new("John", "O", @john_strategy)
16
+
17
+ @game = TicTacToe::Game.new(@board, @todd, @john)
18
+ end
19
+
20
+ context "marking board" do
21
+ it "mark the board with user input" do
22
+ @todd_strategy.add_move(1)
23
+ @game.make_move
24
+ @board.unique_marked_values.should include(@todd.value)
25
+ end
26
+
27
+ it "does not mark the board if user doesn't return an input" do
28
+ @todd_strategy.add_move(nil)
29
+ @game.make_move
30
+ @board.unique_marked_values.should_not include(@todd.value)
31
+ end
32
+ end
33
+
34
+ describe "return winner player based on the value return from rules" do
35
+ it "is Todd when value is X" do
36
+ mark_board([0, 1, 2], @todd.value)
37
+ @game.winner.should == @todd
38
+ end
39
+
40
+ it "is John when it is O" do
41
+ mark_board([0, 1, 2], @john.value)
42
+ @game.winner.should == @john
43
+ end
44
+
45
+ it "is nil when no one wins" do
46
+ @game.winner.should be_nil
47
+ end
48
+ end
49
+
50
+ describe "changes player" do
51
+ it "doesn't change player if player 1 doesn't return a move" do
52
+ @game.current_player.should == @todd
53
+ @todd_strategy.add_move(nil)
54
+
55
+ @game.make_move
56
+ @game.current_player.should == @todd
57
+ end
58
+
59
+ it "changes to player 2 after player 1 moves" do
60
+ @game.current_player.should == @todd
61
+ @todd_strategy.add_move(1)
62
+
63
+ @game.make_move
64
+ @game.current_player.should == @john
65
+ end
66
+ end
67
+
68
+ describe "game over" do
69
+ it "is over when there is a winner" do
70
+ mark_board([0, 1, 2], @john.value)
71
+ @game.should be_over
72
+ end
73
+
74
+ it "is over when there is a tied" do
75
+ mark_board((0...9).to_a, "i")
76
+ @game.should be_over
77
+ end
78
+
79
+ it "is not over when there is no mark on the board" do
80
+ @game.should_not be_over
81
+ end
82
+
83
+ it "is not over when there is no winner or a tied" do
84
+ mark_board([1, 2], @todd.value)
85
+ @game.should_not be_over
86
+ end
87
+ end
88
+
89
+ def mark_board(moves, value)
90
+ moves.each do |m|
91
+ @board.mark(m, value)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,104 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/main'
3
+ require 'tic_tac_toe/player'
4
+ require 'tic_tac_toe/game'
5
+ require 'tic_tac_toe/rules'
6
+ require 'tic_tac_toe/board'
7
+ require 'mocks/game'
8
+ require 'mocks/game_factory'
9
+ require 'mocks/ui/console'
10
+ require 'mocks/strategy/dynamic'
11
+ require 'mocks/rules'
12
+
13
+ describe TicTacToe::Main do
14
+ before(:each) do
15
+ @ui = MockConsole.factory
16
+ @todd = TicTacToe::Player.new("Todd", "X", MockDynamicStrategy.new([1,2,3]))
17
+ @game = MockGame.factory current_player: @todd, winner: @todd
18
+ @game_factory = MockGameFactory.factory create: @game
19
+ @controller = TicTacToe::Main.new(@ui, @game_factory)
20
+ end
21
+
22
+ context "ensure mock class has the same interface" do
23
+ it "checks MockGame" do
24
+ MockGame.should be_substitutable_for(TicTacToe::Game)
25
+ end
26
+
27
+ it "checks console" do
28
+ MockConsole.should be_substitutable_for(TicTacToe::Console)
29
+ end
30
+
31
+ it "checks game factory" do
32
+ MockGameFactory.should be_substitutable_for(TicTacToe::GameFactory)
33
+ end
34
+ end
35
+
36
+ describe "starts game" do
37
+ it "displays welcome message" do
38
+ @controller.start
39
+ @ui.was told_to(:display_welcome_message)
40
+ end
41
+
42
+ it "asked ui to get game type" do
43
+ @controller.start
44
+ @ui.was asked_for(:game_type)
45
+ end
46
+
47
+ it "creates a game based on user input" do
48
+ @controller.start
49
+ @game_factory.was told_to(:create)
50
+ end
51
+ end
52
+
53
+ describe "play game" do
54
+ before(:each) do
55
+ @game.will_over? false, true
56
+ end
57
+
58
+ it "display board" do
59
+ @controller.start
60
+ @ui.was told_to(:display_board)
61
+ end
62
+
63
+ it "displays whose turn it is" do
64
+ @controller.start
65
+ @ui.was told_to(:display_player_turn).with(@todd)
66
+ end
67
+
68
+ it "tells game to make a move" do
69
+ @game.will_over? false, true
70
+ @controller.start
71
+ @game.was told_to(:make_move)
72
+ end
73
+
74
+ it "asks for game move until game is over" do
75
+ @game.will_over? false, false, false, true
76
+ @controller.start
77
+ @game.was told_to(:make_move).times(3)
78
+ end
79
+
80
+ it "displays message when square is not available" do
81
+ @game.will_make_move TicTacToe::MoveNotAvailableError.new
82
+ @controller.start
83
+ @ui.was told_to(:display_square_not_available)
84
+ end
85
+ end
86
+
87
+ describe "end game" do
88
+ it "displays the board when the game is over" do
89
+ @controller.start
90
+ @ui.was told_to(:display_board)
91
+ end
92
+
93
+ it "notifies user when player wins" do
94
+ @controller.start
95
+ @ui.was told_to(:display_winner).with(@todd)
96
+ end
97
+
98
+ it "notifies user when it's a tied game" do
99
+ @game.will_have_winner nil
100
+ @controller.start
101
+ @ui.was told_to(:display_tied_game)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,10 @@
1
+ class PlayerFactoryMock
2
+
3
+ def human(name = "User", value = "X")
4
+ "Human"
5
+ end
6
+
7
+ def computer(board, value = "O", opponent_value = "X")
8
+ "Computer"
9
+ end
10
+ end
@@ -0,0 +1,59 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/player_factory'
3
+ require 'tic_tac_toe/strategy/minimax'
4
+ require 'tic_tac_toe/strategy/console_user'
5
+
6
+ describe TicTacToe::PlayerFactory do
7
+
8
+ before(:each) do
9
+ @factory = TicTacToe::PlayerFactory.new
10
+ end
11
+
12
+ context "console player" do
13
+ it "returns a console player with passed in values" do
14
+ name = "Billy"
15
+ value = "bill"
16
+ player = @factory.human(name, value)
17
+ player.name.should == name
18
+ player.value.should == value
19
+ end
20
+
21
+ it "strategy is console user" do
22
+ @factory.human.strategy.should be_kind_of(TicTacToe::Strategy::ConsoleUser)
23
+ end
24
+
25
+ it "checks default name" do
26
+ @factory.human.name.should == "User"
27
+ end
28
+
29
+ it "checks default value" do
30
+ @factory.human.value.should == "X"
31
+ end
32
+ end
33
+
34
+ context "computer player" do
35
+ before(:each) do
36
+ @board = "fake board"
37
+ end
38
+
39
+ it "returns a computer player with given value" do
40
+ computer_value = "Danny"
41
+ opponent_value = "Bill"
42
+ player = @factory.computer(@board, computer_value, opponent_value)
43
+ player.value.should == computer_value
44
+ end
45
+
46
+ it "checks default strategy" do
47
+ @factory.computer(@board).strategy.should be_kind_of(TicTacToe::Strategy::Minimax)
48
+ end
49
+
50
+ it "checks default name" do
51
+ @factory.computer(@board).name.should == "Computer"
52
+ end
53
+
54
+ it "checks default value" do
55
+ @factory.computer(@board).value.should == "O"
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,32 @@
1
+ require 'tic_tac_toe/spec_helper'
2
+ require 'tic_tac_toe/player'
3
+ require 'tic_tac_toe/strategy/mock'
4
+
5
+ describe TicTacToe::Player do
6
+
7
+ it "gets name" do
8
+ name = "bruce"
9
+ player = TicTacToe::Player.new(name, nil, nil)
10
+ player.name.should == name
11
+ end
12
+
13
+ it "gets player value" do
14
+ value = "X"
15
+ player = TicTacToe::Player.new(nil, value, nil)
16
+ player.value.should == value
17
+ end
18
+
19
+ it "gets player's move" do
20
+ move = 4
21
+ strategy = MockStrategy.new([4])
22
+ player = TicTacToe::Player.new(nil, nil, strategy)
23
+ player.move.should == move
24
+ end
25
+
26
+ it "gets strategy" do
27
+ strategy = MockStrategy.new([4])
28
+ player = TicTacToe::Player.new(nil, nil, strategy)
29
+ player.strategy.should == strategy
30
+
31
+ end
32
+ end