tic_tac_toe_nhu 0.0.11 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|