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.
@@ -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)
@@ -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::GameFactory.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
@@ -1,6 +1,7 @@
1
1
  require 'tic_tac_toe/board'
2
2
  require 'tic_tac_toe/rules'
3
3
  require 'tic_tac_toe/player_factory'
4
+ require 'tic_tac_toe/values'
4
5
 
5
6
  class SimpleStrategy
6
7
  attr_accessor :next_move
@@ -11,9 +12,12 @@ class SimpleStrategy
11
12
  end
12
13
 
13
14
  describe "Unbeatable computer", :slow_test => true do
15
+ attr_reader :computer
16
+
14
17
  before(:each) do
15
18
  @board = TicTacToe::Board.new
16
- @human_value = "X"
19
+ @human_value = TicTacToe::VALUES[0]
20
+ @computer = TicTacToe::PlayerFactory.new.create(:computer, TicTacToe::VALUES[1])
17
21
  end
18
22
 
19
23
  it "wins all game when computer goes first" do
@@ -25,10 +29,8 @@ describe "Unbeatable computer", :slow_test => true do
25
29
  play_game_for_each_available_move(@board)
26
30
  end
27
31
 
28
- def make_computer_move(clone_board)
29
- computer = TicTacToe::PlayerFactory.new.computer(clone_board)
30
- clone_board.mark(computer.move, computer.value)
31
- clone_board
32
+ def make_computer_move(board)
33
+ board.mark(computer.move(board), computer.value)
32
34
  end
33
35
 
34
36
  def rules(board)
@@ -50,7 +52,7 @@ describe "Unbeatable computer", :slow_test => true do
50
52
 
51
53
  def make_players_move(board, move_history, move)
52
54
  move_history << move
53
- board.mark(move, "X")
55
+ board.mark(move, @human_value)
54
56
  make_computer_move(board) if !rules(board).game_over?
55
57
  play_game_for_each_available_move(board, move_history)
56
58
  end
data/spec/mocks/game.rb CHANGED
@@ -5,9 +5,9 @@ class MockGame
5
5
  define_reader :board
6
6
  define_reader :current_player
7
7
 
8
- define(:make_move) {|*value| }
9
8
  define :winner
10
- define(:over?){true}
9
+ define(:game_over?){true}
10
+ define(:make_player_move){|*move|}
11
11
  end
12
12
 
13
13
  describe TicTacToe::Game do
@@ -4,7 +4,7 @@ class MockGameFactory
4
4
  Surrogate.endow(self)
5
5
  define(:initialize) {|player_factory|}
6
6
  define :types
7
- define(:create) {|type, board|}
7
+ define(:create) {|type|}
8
8
  end
9
9
 
10
10
  describe TicTacToe::GameFactory do
@@ -0,0 +1,16 @@
1
+ require 'surrogate/rspec'
2
+
3
+ class MockPlayer
4
+ Surrogate.endow(self)
5
+ define(:initialize) {|name = "Player", value = "X", strategy = "no"|}
6
+ define_reader :name
7
+ define_reader :value
8
+ define_reader :strategy
9
+ define(:move) {|board|}
10
+ end
11
+
12
+ describe TicTacToe::Player do
13
+ it "checks if mock is substitutable" do
14
+ MockPlayer.should be_substitutable_for(TicTacToe::Player)
15
+ end
16
+ end
@@ -1,10 +1,11 @@
1
1
  require 'surrogate/rspec'
2
+ require 'tic_tac_toe/values'
2
3
 
3
4
  class MockPlayerFactory
4
5
  Surrogate.endow(self)
5
6
 
6
- define(:human) {|name = "Sue", value = "X"|}
7
- define(:computer) {|board, value = "X", opponent="O"|}
7
+ define(:create){|player_type, value|}
8
+ define(:types){TicTacToe::PlayerFactory.new.types}
8
9
  end
9
10
 
10
11
  describe TicTacToe::PlayerFactory do
@@ -9,7 +9,7 @@ class MockDynamicStrategy
9
9
  @moves << move
10
10
  end
11
11
 
12
- def move
12
+ def move(*board)
13
13
  @moves.shift
14
14
  end
15
15
 
@@ -1,78 +1,59 @@
1
1
  require 'tic_tac_toe/spec_helper'
2
2
  require 'tic_tac_toe/game_factory'
3
3
  require 'tic_tac_toe/player_factory'
4
+ require 'tic_tac_toe/player'
4
5
  require 'mocks/player_factory'
5
6
 
6
7
  describe TicTacToe::GameFactory do
7
-
8
8
  before(:each) do
9
- @board = "board"
10
9
  @player_factory = MockPlayerFactory.new
11
10
  @game_factory = TicTacToe::GameFactory.new(@player_factory)
12
11
  end
13
12
 
14
- it "ensures MockPlayerFactory has the same interface as PlayerFactory" do
15
- MockPlayerFactory.should be_substitutable_for(TicTacToe::PlayerFactory)
16
- end
17
-
18
13
  it "returns 4 types of games" do
19
14
  @game_factory.types.size.should == 4
20
15
  end
21
16
 
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
17
+ context "creates game" do
18
+ before(:each) do
19
+ @human = TicTacToe::Player.new("Human", TicTacToe::VALUES[0], nil)
20
+ @computer = TicTacToe::Player.new("Computer", opponent_value(@human.value), nil)
21
+ end
35
22
 
36
- @player_factory.was asked_for :computer
37
- @player_factory.was asked_for :human
38
- end
23
+ def test_game_state_creation(game_type, players)
24
+ @player_factory.will_create players[0], players[1]
39
25
 
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
26
+ game = @game_factory.create(game_type)
27
+ game.current_player.should == players[0]
28
+ game.make_player_move(1)
29
+ game.current_player.should == players[1]
30
+ end
45
31
 
46
- game = @game_factory.create(2, @board)
47
- game.current_player.should == computer
32
+ it "returns user vs user game" do
33
+ @human2 = TicTacToe::Player.new("Human 2", opponent_value(@human.value), nil)
34
+ test_game_state_creation(1, [@human, @human2])
35
+ end
48
36
 
49
- @player_factory.was asked_for :computer
50
- @player_factory.was asked_for :human
51
- end
37
+ it "returns human vs computer game" do
38
+ test_game_state_creation(2, [@human, @computer])
39
+ end
52
40
 
53
- it "returns user vs user game" do
54
- human1 = "mary"
55
- human2 = "alice"
56
- @player_factory.will_have_human human1, human2
41
+ it "returns computer vs human game" do
42
+ test_game_state_creation(3, [@computer, @human])
43
+ end
57
44
 
58
- game = @game_factory.create(3, @board)
59
- game.current_player.should == human1
45
+ it "returns computer vs computer game" do
46
+ @computer2 = TicTacToe::Player.new("Computer 2", opponent_value(@computer.value), nil)
47
+ test_game_state_creation(4, [@computer, @computer2])
48
+ end
60
49
 
61
- @player_factory.was asked_for(:human).times(2)
50
+ it "raises an error when the game type doesn't exist" do
51
+ lambda{@game_factory.create(10)}.should raise_error(ArgumentError)
62
52
  end
63
53
 
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
54
  end
74
55
 
75
- it "raises an error when the game type doesn't exist" do
76
- lambda{@game_factory.create(5, @board)}.should raise_error(ArgumentError)
56
+ def opponent_value(value)
57
+ TicTacToe::Values.opponent(value)
77
58
  end
78
59
  end
@@ -1,113 +1,104 @@
1
1
  require 'tic_tac_toe/spec_helper'
2
- require 'tic_tac_toe/game'
3
2
  require 'tic_tac_toe/board'
3
+ require 'tic_tac_toe/game'
4
4
  require 'tic_tac_toe/player'
5
+ require 'tic_tac_toe/values'
5
6
  require 'mocks/strategy/dynamic'
6
7
 
7
8
  describe TicTacToe::Game do
9
+ attr_reader :game_state, :player1, :player2, :board
10
+
8
11
  before(:each) do
9
12
  @board = TicTacToe::Board.new
10
-
11
13
  @player1_strategy = MockDynamicStrategy.new
12
- @player1 = TicTacToe::Player.new("Todd", "X", @player1_strategy)
14
+ @player1 = TicTacToe::Player.new("player1", TicTacToe::VALUES[0], @player1_strategy)
15
+ @player2 = TicTacToe::Player.new("player1", TicTacToe::VALUES[1], nil)
16
+ @game_state = TicTacToe::Game.new([@player1, @player2], @board)
17
+ end
13
18
 
14
- @player2_strategy = MockDynamicStrategy.new
15
- @player2 = TicTacToe::Player.new("John", "O", @player2_strategy)
16
19
 
17
- @game = TicTacToe::Game.new(@board, @player1, @player2)
20
+ it "can read board" do
21
+ game_state.board.should == board
18
22
  end
19
23
 
20
- context "marking board" do
21
- it "mark the board with user input when caller doesn't pass in a move" do
24
+ describe "game over" do
25
+ it "is false when there is no mark" do
26
+ game_state.should_not be_game_over
27
+ end
28
+
29
+ it "should be over when there is a winner" do
30
+ mark_winning_board(player1.value)
31
+ game_state.should be_game_over
32
+ end
33
+ end
34
+
35
+ describe "winner" do
36
+ it "has no winner when there is no mark" do
37
+ game_state.winner.should be_nil
38
+ end
39
+
40
+ it "is player 1" do
41
+ mark_winning_board(player1.value)
42
+ game_state.winner.should == player1
43
+ end
44
+
45
+ it "is player 2" do
46
+ mark_winning_board(player2.value)
47
+ game_state.winner.should == player2
48
+ end
49
+ end
50
+
51
+ context "make player move" do
52
+ it "mark the board with player move when caller doesn't pass in a move" do
22
53
  @player1_strategy.add_move(1)
23
- @game.make_move
54
+ @game_state.make_player_move
24
55
  @board.unique_marked_values.should include(@player1.value)
25
56
  end
26
57
 
27
58
  it "does not mark the board if user doesn't return an input and caller didn't pass in a move" do
28
59
  @player1_strategy.add_move(nil)
29
- @game.make_move
60
+ @game_state.make_player_move
30
61
  @board.unique_marked_values.should_not include(@player1.value)
31
62
  end
32
63
 
33
64
  it "marks the board with the move passed in" do
34
65
  @player1_strategy.add_move(1)
35
66
  move = 2
36
- @game.make_move(move)
67
+ @game_state.make_player_move(move)
37
68
  @board.available_moves.should_not include(move)
38
69
  end
39
70
 
40
71
  it "marks the board with player move when move passed in is nil" do
41
72
  @player1_strategy.add_move(1)
42
- @game.make_move(nil)
73
+ @game_state.make_player_move(nil)
43
74
  @board.available_moves.should_not include(1)
44
75
  end
45
76
 
46
77
  it "marks the board with current player value when the move passed in" do
47
78
  move = 2
48
- @game.make_move(move)
79
+ @game_state.make_player_move(move)
49
80
  @board.unique_marked_values.should include(@player1.value)
50
81
  end
51
82
  end
52
83
 
53
- describe "return winner player based on the value return from rules" do
54
- it "is Todd when value is X" do
55
- mark_board([0, 1, 2], @player1.value)
56
- @game.winner.should == @player1
57
- end
58
-
59
- it "is John when it is O" do
60
- mark_board([0, 1, 2], @player2.value)
61
- @game.winner.should == @player2
62
- end
63
-
64
- it "is nil when no one wins" do
65
- @game.winner.should be_nil
66
- end
67
- end
68
-
69
84
  describe "changes player" do
70
85
  it "doesn't change player if player 1 doesn't return a move" do
71
- @game.current_player.should == @player1
72
- @player1_strategy.add_move(nil)
73
-
74
- @game.make_move
75
- @game.current_player.should == @player1
86
+ @game_state.current_player.should == @player1
87
+ @game_state.make_player_move
88
+ @game_state.current_player.should == @player1
76
89
  end
77
90
 
78
91
  it "changes to player 2 after player 1 moves" do
79
- @game.current_player.should == @player1
80
- @player1_strategy.add_move(1)
81
-
82
- @game.make_move
83
- @game.current_player.should == @player2
92
+ @game_state.current_player.should == @player1
93
+ @game_state.make_player_move(1)
94
+ @game_state.current_player.should == @player2
84
95
  end
85
96
  end
86
97
 
87
- describe "game over" do
88
- it "is over when there is a winner" do
89
- mark_board([0, 1, 2], @player2.value)
90
- @game.should be_over
91
- end
92
-
93
- it "is over when there is a tied" do
94
- mark_board((0...9).to_a, "i")
95
- @game.should be_over
96
- end
97
-
98
- it "is not over when there is no mark on the board" do
99
- @game.should_not be_over
100
- end
101
-
102
- it "is not over when there is no winner or a tied" do
103
- mark_board([1, 2], @player1.value)
104
- @game.should_not be_over
105
- end
106
- end
107
98
 
108
- def mark_board(moves, value)
109
- moves.each do |m|
110
- @board.mark(m, value)
99
+ def mark_winning_board(value)
100
+ [0, 4, 8].each do |move|
101
+ game_state.board.mark(move, value)
111
102
  end
112
103
  end
113
104
  end