tic_tac_toe_nhu 0.0.11 → 0.1.0
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.
- checksums.yaml +4 -4
- data/bin/tic_tac_toe +2 -2
- data/coverage/.resultset.json +52 -65
- data/lib/tic_tac_toe/board.rb +1 -1
- data/lib/tic_tac_toe/game.rb +21 -19
- data/lib/tic_tac_toe/game_factory.rb +11 -33
- data/lib/tic_tac_toe/player.rb +2 -2
- data/lib/tic_tac_toe/player_factory.rb +19 -4
- data/lib/tic_tac_toe/rules.rb +1 -1
- data/lib/tic_tac_toe/runner.rb +27 -20
- data/lib/tic_tac_toe/strategy/console_user.rb +1 -1
- data/lib/tic_tac_toe/strategy/minimax.rb +18 -16
- data/lib/tic_tac_toe/ui/console.rb +2 -2
- data/lib/tic_tac_toe/values.rb +10 -0
- data/spec/{integration → acceptance}/tictactoe/unbeatable_computer_spec.rb +8 -6
- data/spec/mocks/game.rb +2 -2
- data/spec/mocks/game_factory.rb +1 -1
- data/spec/mocks/player.rb +16 -0
- data/spec/mocks/player_factory.rb +3 -2
- data/spec/mocks/strategy/dynamic.rb +1 -1
- data/spec/tic_tac_toe/game_factory_spec.rb +31 -50
- data/spec/tic_tac_toe/game_spec.rb +53 -62
- data/spec/tic_tac_toe/player_factory_spec.rb +21 -28
- data/spec/tic_tac_toe/player_spec.rb +12 -17
- data/spec/tic_tac_toe/runner_spec.rb +49 -56
- data/spec/tic_tac_toe/strategy/console_user_spec.rb +1 -1
- data/spec/tic_tac_toe/strategy/minimax_spec.rb +65 -64
- data/spec/tic_tac_toe/ui/console_spec.rb +2 -2
- data/spec/tic_tac_toe/values_spec.rb +17 -0
- data/tic_tac_toe_nhu.gemspec +2 -1
- metadata +7 -4
- data/spec/tic_tac_toe/player_factory_mock.rb +0 -10
@@ -2,58 +2,51 @@ require 'tic_tac_toe/spec_helper'
|
|
2
2
|
require 'tic_tac_toe/player_factory'
|
3
3
|
require 'tic_tac_toe/strategy/minimax'
|
4
4
|
require 'tic_tac_toe/strategy/console_user'
|
5
|
+
require 'tic_tac_toe/values'
|
5
6
|
|
6
7
|
describe TicTacToe::PlayerFactory do
|
7
|
-
|
8
8
|
before(:each) do
|
9
9
|
@factory = TicTacToe::PlayerFactory.new
|
10
|
+
@value = TicTacToe::VALUES[0]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "checks player types" do
|
14
|
+
[:human, :computer] == @factory.types
|
10
15
|
end
|
11
16
|
|
12
|
-
context "
|
13
|
-
|
14
|
-
|
15
|
-
value = "bill"
|
16
|
-
player = @factory.human(name, value)
|
17
|
-
player.name.should == name
|
18
|
-
player.value.should == value
|
17
|
+
context "human player" do
|
18
|
+
before(:each) do
|
19
|
+
@human = @factory.create(:human, @value)
|
19
20
|
end
|
20
21
|
|
21
22
|
it "strategy is console user" do
|
22
|
-
@
|
23
|
+
@human.strategy.should be_kind_of(TicTacToe::Strategy::ConsoleUser)
|
23
24
|
end
|
24
25
|
|
25
|
-
it "checks
|
26
|
-
@
|
26
|
+
it "checks name" do
|
27
|
+
@human.name.should == "User"
|
27
28
|
end
|
28
29
|
|
29
|
-
it "checks
|
30
|
-
@
|
30
|
+
it "checks value" do
|
31
|
+
@human.value.should == @value
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
context "computer player" do
|
35
36
|
before(:each) do
|
36
|
-
@
|
37
|
+
@computer = @factory.create(:computer, @value)
|
37
38
|
end
|
38
39
|
|
39
|
-
it "
|
40
|
-
|
41
|
-
opponent_value = "Bill"
|
42
|
-
player = @factory.computer(@board, computer_value, opponent_value)
|
43
|
-
player.value.should == computer_value
|
40
|
+
it "checks strategy" do
|
41
|
+
@computer.strategy.should be_kind_of(TicTacToe::Strategy::Minimax)
|
44
42
|
end
|
45
43
|
|
46
|
-
it "checks
|
47
|
-
@
|
44
|
+
it "checks name" do
|
45
|
+
@computer.name.should == "Computer"
|
48
46
|
end
|
49
47
|
|
50
|
-
it "checks
|
51
|
-
@
|
52
|
-
end
|
53
|
-
|
54
|
-
it "checks default value" do
|
55
|
-
@factory.computer(@board).value.should == "O"
|
48
|
+
it "checks value" do
|
49
|
+
@computer.value.should == @value
|
56
50
|
end
|
57
51
|
end
|
58
|
-
|
59
52
|
end
|
@@ -1,32 +1,27 @@
|
|
1
1
|
require 'tic_tac_toe/spec_helper'
|
2
2
|
require 'tic_tac_toe/player'
|
3
|
+
require 'tic_tac_toe/board'
|
4
|
+
require 'tic_tac_toe/values'
|
3
5
|
require 'mocks/strategy/dynamic'
|
4
6
|
|
5
7
|
describe TicTacToe::Player do
|
8
|
+
before(:each) do
|
9
|
+
@move = 4
|
10
|
+
@value = TicTacToe::VALUES[0]
|
11
|
+
@name = "blu"
|
12
|
+
@strategy = MockDynamicStrategy.new([@move])
|
13
|
+
@player = TicTacToe::Player.new(@name, @value, @strategy)
|
14
|
+
end
|
6
15
|
|
7
16
|
it "gets name" do
|
8
|
-
name
|
9
|
-
player = TicTacToe::Player.new(name, nil, nil)
|
10
|
-
player.name.should == name
|
17
|
+
@player.name.should == @name
|
11
18
|
end
|
12
19
|
|
13
20
|
it "gets player value" do
|
14
|
-
value
|
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 = MockDynamicStrategy.new([4])
|
22
|
-
player = TicTacToe::Player.new(nil, nil, strategy)
|
23
|
-
player.move.should == move
|
21
|
+
@player.value.should == @value
|
24
22
|
end
|
25
23
|
|
26
24
|
it "gets strategy" do
|
27
|
-
strategy
|
28
|
-
player = TicTacToe::Player.new(nil, nil, strategy)
|
29
|
-
player.strategy.should == strategy
|
30
|
-
|
25
|
+
@player.strategy.should == @strategy
|
31
26
|
end
|
32
27
|
end
|
@@ -1,95 +1,88 @@
|
|
1
1
|
require 'tic_tac_toe/spec_helper'
|
2
2
|
require 'tic_tac_toe/runner'
|
3
|
-
|
4
|
-
require 'tic_tac_toe/game'
|
5
|
-
require 'tic_tac_toe/rules'
|
6
|
-
require 'tic_tac_toe/board'
|
7
|
-
require 'mocks/game'
|
3
|
+
|
8
4
|
require 'mocks/game_factory'
|
5
|
+
require 'mocks/game'
|
9
6
|
require 'mocks/ui/console'
|
10
|
-
require 'mocks/
|
11
|
-
require 'mocks/rules'
|
7
|
+
require 'mocks/player'
|
12
8
|
|
13
9
|
describe TicTacToe::Runner do
|
10
|
+
attr_reader :game, :ui, :game_state, :game_state_factory
|
11
|
+
|
14
12
|
before(:each) do
|
13
|
+
@game_state = MockGame.factory
|
14
|
+
@game_state_factory = MockGameFactory.factory create: game_state
|
15
15
|
@ui = MockConsole.factory
|
16
|
-
@
|
17
|
-
@game = MockGame.factory current_player: @todd, winner: @todd
|
18
|
-
@game_factory = MockGameFactory.factory create: @game
|
19
|
-
@controller = TicTacToe::Runner.new(@ui, @game_factory)
|
16
|
+
@game = TicTacToe::Runner.new(@ui, @game_state_factory)
|
20
17
|
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
19
|
+
it "displays a welcome message" do
|
20
|
+
game.start
|
21
|
+
ui.was told_to(:display_welcome_message)
|
22
|
+
end
|
27
23
|
|
28
|
-
|
29
|
-
|
24
|
+
describe "create game state" do
|
25
|
+
it "asks user for a game type" do
|
26
|
+
game.start
|
30
27
|
@ui.was asked_for(:game_type)
|
31
28
|
end
|
32
29
|
|
33
|
-
it "
|
34
|
-
|
35
|
-
|
30
|
+
it "asks game state factory to create a game with input game type" do
|
31
|
+
game_type = 4
|
32
|
+
ui.will_have_game_type 4
|
33
|
+
game.start
|
34
|
+
@game_state_factory.was told_to(:create).with(game_type)
|
36
35
|
end
|
37
36
|
|
38
37
|
it "asks ui for game type again if the input is incorrect" do
|
39
|
-
|
40
|
-
|
38
|
+
game_state_factory.will_create ArgumentError.new, game_state
|
39
|
+
game.start
|
40
|
+
ui.was asked_for(:game_type).times(2)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
describe "play game" do
|
45
|
-
|
46
|
-
@game.will_over? false, true
|
47
|
-
end
|
45
|
+
attr_reader :player
|
48
46
|
|
49
|
-
|
50
|
-
@
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
it "displays whose turn it is" do
|
55
|
-
@controller.start
|
56
|
-
@ui.was told_to(:display_player_turn).with(@todd)
|
47
|
+
before(:each) do
|
48
|
+
@player = MockPlayer.new
|
49
|
+
game_state.will_game_over? false, true
|
50
|
+
game_state.will_have_current_player @player
|
57
51
|
end
|
58
52
|
|
59
|
-
it "
|
60
|
-
|
61
|
-
|
62
|
-
@game.was told_to(:make_move)
|
53
|
+
it "displays the board" do
|
54
|
+
game.start
|
55
|
+
ui.was told_to(:display_board)
|
63
56
|
end
|
64
57
|
|
65
|
-
it "
|
66
|
-
|
67
|
-
|
68
|
-
@game.was told_to(:make_move).times(3)
|
58
|
+
it "tells ui to display player turn" do
|
59
|
+
game.start
|
60
|
+
ui.was told_to(:display_player_turn).with(player)
|
69
61
|
end
|
70
62
|
|
71
|
-
it "
|
72
|
-
|
73
|
-
|
74
|
-
|
63
|
+
it "tells player to move again if there is an error" do
|
64
|
+
game_state.will_make_player_move TicTacToe::MoveNotAvailableError.new, nil
|
65
|
+
game.start
|
66
|
+
game_state.was told_to(:make_player_move).times(2)
|
75
67
|
end
|
76
68
|
end
|
77
69
|
|
78
70
|
describe "end game" do
|
79
|
-
it "
|
80
|
-
|
81
|
-
|
71
|
+
it "display a board at the end of the game" do
|
72
|
+
game.start
|
73
|
+
ui.was told_to(:display_board)
|
82
74
|
end
|
83
75
|
|
84
|
-
it "
|
85
|
-
|
86
|
-
|
76
|
+
it "asks ui to display a winner if there is a winner" do
|
77
|
+
winner = "Winner"
|
78
|
+
game_state.will_have_winner winner
|
79
|
+
game.start
|
80
|
+
ui.was told_to(:display_winner).with(winner)
|
87
81
|
end
|
88
82
|
|
89
|
-
it "
|
90
|
-
|
91
|
-
|
92
|
-
@ui.was told_to(:display_tied_game)
|
83
|
+
it "asks ui to display a draw" do
|
84
|
+
game.start
|
85
|
+
ui.was told_to(:display_tied_game)
|
93
86
|
end
|
94
87
|
end
|
95
88
|
end
|
@@ -13,7 +13,7 @@ describe TicTacToe::Strategy::ConsoleUser do
|
|
13
13
|
it "prompts the user for a move" do
|
14
14
|
@output.should_receive(:puts).with("Please enter a square number that is not marked: ")
|
15
15
|
@input.should_receive(:gets).and_return("3")
|
16
|
-
@strategy.move
|
16
|
+
@strategy.move(@board)
|
17
17
|
end
|
18
18
|
|
19
19
|
it "returns a number when user enters a number" do
|
@@ -1,156 +1,157 @@
|
|
1
1
|
require 'tic_tac_toe/spec_helper'
|
2
2
|
require 'tic_tac_toe/board'
|
3
3
|
require 'tic_tac_toe/strategy/minimax'
|
4
|
+
require 'tic_tac_toe/values'
|
4
5
|
|
5
6
|
describe TicTacToe::Strategy::Minimax, :slow_test => true do
|
6
7
|
|
7
8
|
before(:each) do
|
8
9
|
@board = TicTacToe::Board.new(3)
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@strategy = TicTacToe::Strategy::Minimax.new(@
|
10
|
+
@computer_value = TicTacToe::VALUES[1]
|
11
|
+
@opponent_value = TicTacToe::Values.opponent(@computer_value)
|
12
|
+
@strategy = TicTacToe::Strategy::Minimax.new(@computer_value)
|
12
13
|
end
|
13
14
|
|
14
15
|
context "move" do
|
15
16
|
context "when there is only one position left" do
|
16
17
|
it "returns move when it's a winning move" do
|
17
|
-
mark((1...@board.size**2), @
|
18
|
-
@strategy.move.should == 0
|
18
|
+
mark((1...@board.size**2), @computer_value)
|
19
|
+
@strategy.move(@board).should == 0
|
19
20
|
end
|
20
21
|
|
21
22
|
it "returns the move when it's a tied" do
|
22
|
-
mark([0, 2, 5, 6], @
|
23
|
-
mark([1, 3, 4, 8], @
|
24
|
-
@strategy.move.should == 7
|
23
|
+
mark([0, 2, 5, 6], @computer_value)
|
24
|
+
mark([1, 3, 4, 8], @opponent_value)
|
25
|
+
@strategy.move(@board).should == 7
|
25
26
|
end
|
26
27
|
|
27
28
|
it "returns the move when opponent wins" do
|
28
|
-
mark((0...@board.size**2 - 1).to_a, @
|
29
|
-
@strategy.move.should == 8
|
29
|
+
mark((0...@board.size**2 - 1).to_a, @opponent_value)
|
30
|
+
@strategy.move(@board).should == 8
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
context "when there are two positions left" do
|
34
35
|
it "returns losing scores when the two moves are losing moves" do
|
35
|
-
mark((2...@board.size**2).to_a, @
|
36
|
-
@strategy.move.should == 0
|
36
|
+
mark((2...@board.size**2).to_a, @opponent_value)
|
37
|
+
@strategy.move(@board).should == 0
|
37
38
|
end
|
38
39
|
|
39
40
|
it "returns the first win move when the two moves are winning moves" do
|
40
|
-
mark((2...@board.size**2).to_a, @
|
41
|
-
@strategy.move.should == 0
|
41
|
+
mark((2...@board.size**2).to_a, @computer_value)
|
42
|
+
@strategy.move(@board).should == 0
|
42
43
|
end
|
43
44
|
|
44
45
|
it "returns the first tie move when the two moves are tie moves" do
|
45
|
-
mark([0, 2, 7], @
|
46
|
-
mark([1, 4, 6, 8], @
|
47
|
-
@strategy.move.should == 3
|
46
|
+
mark([0, 2, 7], @computer_value)
|
47
|
+
mark([1, 4, 6, 8], @opponent_value)
|
48
|
+
@strategy.move(@board).should == 3
|
48
49
|
end
|
49
50
|
|
50
51
|
it "returns win move when there are winning and losing moves" do
|
51
|
-
mark([0, 1, 3, 7], @
|
52
|
-
mark([2, 4, 5], @
|
53
|
-
@strategy.move.should == 6
|
52
|
+
mark([0, 1, 3, 7], @computer_value)
|
53
|
+
mark([2, 4, 5], @opponent_value)
|
54
|
+
@strategy.move(@board).should == 6
|
54
55
|
end
|
55
56
|
|
56
57
|
it "returns winning move when there are winning and tie moves" do
|
57
|
-
mark([0, 2, 3, 4], @
|
58
|
-
mark([1, 5, 8], @
|
59
|
-
@strategy.move.should == 6
|
58
|
+
mark([0, 2, 3, 4], @computer_value)
|
59
|
+
mark([1, 5, 8], @opponent_value)
|
60
|
+
@strategy.move(@board).should == 6
|
60
61
|
end
|
61
62
|
|
62
63
|
it "returns tied move when there are losing and tie moves" do
|
63
|
-
mark([2, 3, 4], @
|
64
|
-
mark([0, 5, 6, 7], @
|
65
|
-
@strategy.move.should == 8
|
64
|
+
mark([2, 3, 4], @computer_value)
|
65
|
+
mark([0, 5, 6, 7], @opponent_value)
|
66
|
+
@strategy.move(@board).should == 8
|
66
67
|
end
|
67
68
|
|
68
69
|
context "winning move appear later in the board" do
|
69
70
|
it "returns win move when winning move is later than losing move" do
|
70
|
-
mark([1, 2, 5, 7], @
|
71
|
-
mark([0, 3, 4], @
|
72
|
-
@strategy.move.should == 8
|
71
|
+
mark([1, 2, 5, 7], @computer_value)
|
72
|
+
mark([0, 3, 4], @opponent_value)
|
73
|
+
@strategy.move(@board).should == 8
|
73
74
|
end
|
74
75
|
|
75
76
|
it "returns winning move when winning move is later than tie move" do
|
76
|
-
mark([0, 2, 4, 5], @
|
77
|
-
mark([1, 3, 6], @
|
78
|
-
@strategy.move.should == 8
|
77
|
+
mark([0, 2, 4, 5], @computer_value)
|
78
|
+
mark([1, 3, 6], @opponent_value)
|
79
|
+
@strategy.move(@board).should == 8
|
79
80
|
end
|
80
81
|
|
81
82
|
it "returns tied move when there are losing and tie moves" do
|
82
|
-
mark([2, 3, 4], @
|
83
|
-
mark([0, 5, 6, 7], @
|
84
|
-
@strategy.move.should == 8
|
83
|
+
mark([2, 3, 4], @computer_value)
|
84
|
+
mark([0, 5, 6, 7], @opponent_value)
|
85
|
+
@strategy.move(@board).should == 8
|
85
86
|
end
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
89
90
|
context "when there are three moves left" do
|
90
91
|
it "chooses the winning move when there is one available" do
|
91
|
-
mark([1, 4], @
|
92
|
-
mark([0, 3, 2, 5], @
|
93
|
-
@strategy.move.should == 7
|
92
|
+
mark([1, 4], @computer_value)
|
93
|
+
mark([0, 3, 2, 5], @opponent_value)
|
94
|
+
@strategy.move(@board).should == 7
|
94
95
|
end
|
95
96
|
|
96
97
|
it "chooses a tie move when there are only losing and tie moves" do
|
97
|
-
mark([1, 4], @
|
98
|
-
mark([0, 2, 5, 7], @
|
99
|
-
@strategy.move.should == 8
|
98
|
+
mark([1, 4], @computer_value)
|
99
|
+
mark([0, 2, 5, 7], @opponent_value)
|
100
|
+
@strategy.move(@board).should == 8
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
103
104
|
context "when there are 4 moves left" do
|
104
105
|
it "chooses a winning move when one is available" do
|
105
|
-
mark([0, 4], @
|
106
|
-
mark([1, 2, 6], @
|
107
|
-
@strategy.move.should == 8
|
106
|
+
mark([0, 4], @computer_value)
|
107
|
+
mark([1, 2, 6], @opponent_value)
|
108
|
+
@strategy.move(@board).should == 8
|
108
109
|
end
|
109
110
|
|
110
111
|
it "chooses a move that would create 2 winning moves" do
|
111
|
-
mark([0, 6], @
|
112
|
-
mark([1, 3, 5], @
|
113
|
-
@strategy.move.should == 4
|
112
|
+
mark([0, 6], @computer_value)
|
113
|
+
mark([1, 3, 5], @opponent_value)
|
114
|
+
@strategy.move(@board).should == 4
|
114
115
|
end
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
118
119
|
it "chooses the winning move when available" do
|
119
|
-
mark([1, 4], @
|
120
|
-
mark([0], @
|
121
|
-
@strategy.move.should == 7
|
120
|
+
mark([1, 4], @computer_value)
|
121
|
+
mark([0], @opponent_value)
|
122
|
+
@strategy.move(@board).should == 7
|
122
123
|
end
|
123
124
|
|
124
125
|
it "chooses the winning move when available" do
|
125
|
-
mark([0, 4], @
|
126
|
-
mark([1, 2], @
|
127
|
-
@strategy.move.should == 8
|
126
|
+
mark([0, 4], @computer_value)
|
127
|
+
mark([1, 2], @opponent_value)
|
128
|
+
@strategy.move(@board).should == 8
|
128
129
|
end
|
129
130
|
|
130
131
|
it "chooses a blocking move when there is no winning move" do
|
131
|
-
mark([2], @
|
132
|
-
mark([0, 4], @
|
133
|
-
@strategy.move.should == 8
|
132
|
+
mark([2], @computer_value)
|
133
|
+
mark([0, 4], @opponent_value)
|
134
|
+
@strategy.move(@board).should == 8
|
134
135
|
end
|
135
136
|
|
136
137
|
it "chooses a blocking move when there is no winning move" do
|
137
|
-
mark([2], @
|
138
|
-
mark([1, 4], @
|
139
|
-
@strategy.move.should == 7
|
138
|
+
mark([2], @computer_value)
|
139
|
+
mark([1, 4], @opponent_value)
|
140
|
+
@strategy.move(@board).should == 7
|
140
141
|
end
|
141
142
|
|
142
143
|
it "chooses the middle square when computer is the second player" do
|
143
|
-
mark([0], @
|
144
|
-
@strategy.move.should == 4
|
144
|
+
mark([0], @opponent_value)
|
145
|
+
@strategy.move(@board).should == 4
|
145
146
|
end
|
146
147
|
|
147
148
|
it "chooses the first corner of the board when the middle square is taken as a second player" do
|
148
|
-
mark([4], @
|
149
|
-
@strategy.move.should == 0
|
149
|
+
mark([4], @opponent_value)
|
150
|
+
@strategy.move(@board).should == 0
|
150
151
|
end
|
151
152
|
|
152
153
|
it "chooses the middle square on the first move" do
|
153
|
-
@strategy.move.should == 4
|
154
|
+
@strategy.move(@board).should == 4
|
154
155
|
end
|
155
156
|
|
156
157
|
def move_node(move, score)
|