nick_tac_toe 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/bin/nick_tac_toe ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'nick_tac_toe'
6
+
7
+ players = [Human.new("x"), Minimax.new("o")]
8
+ players.shuffle!
9
+ game = Game.new(players.first, players.last)
10
+
11
+ # the main game loop!
12
+ until game.over?
13
+ game.board.print
14
+ game.take_next_turn
15
+ end
16
+
17
+ # displays the winner
18
+ game.board.print
19
+ if game.won? == true
20
+ puts "We have a winner! o__o"
21
+ else
22
+ puts "It is a draw. u__u"
23
+ end
@@ -0,0 +1,140 @@
1
+ class Board
2
+
3
+ attr_accessor :player_one, :player_two, :cells
4
+
5
+ def initialize(player_one, player_two)
6
+ @cells = [empty_spot]*9
7
+ @player_one = player_one
8
+ @player_two = player_two
9
+ end
10
+
11
+ def print
12
+ list = []
13
+ cells.each_with_index do |marker, index|
14
+ if marker == empty_spot
15
+ list << index+1
16
+ else
17
+ list << marker
18
+ end
19
+ end
20
+ list.map! do |m|
21
+ case m
22
+ when 'x'
23
+ "\e[1;36mx\e[0m"
24
+ when 'o'
25
+ "\e[1;33mo\e[0m"
26
+ else
27
+ "\e[1;30m#{m}\e[0m"
28
+ end
29
+ end
30
+ puts "\n#{list[0]} | #{list[1]} | #{list[2]}\n----------\n#{list[3]} | #{list[4]} | #{list[5]}\n----------\n#{list[6]} | #{list[7]} | #{list[8]}\n "
31
+ end
32
+
33
+ def get_cell position
34
+ @cells[position]
35
+ end
36
+
37
+ def set_cell position, team
38
+ @cells[position] = team
39
+ end
40
+
41
+ def rows
42
+ [@cells[0..2], @cells[3..5], @cells[6..8]]
43
+ end
44
+
45
+ def columns
46
+ [
47
+ [@cells[0], @cells[3], @cells[6]],
48
+ [@cells[1], @cells[4], @cells[7]],
49
+ [@cells[2], @cells[5], @cells[8]]
50
+ ]
51
+ end
52
+
53
+ def diagonals
54
+ [
55
+ [@cells[0], @cells[4], @cells[8]],
56
+ [@cells[2], @cells[4], @cells[6]],
57
+ ]
58
+ end
59
+
60
+ def valid_move?(position)
61
+ if [0,1,2,3,4,5,6,7,8].include?(position.to_i-1)
62
+ if get_cell(position-1) == empty_spot
63
+ return true
64
+ end
65
+ else
66
+ return false
67
+ end
68
+ end
69
+
70
+ def blocking_group_for(team)
71
+ 3.times do |i| # rows
72
+ return [3*i, 3*i + 1, 3*i + 2] if rows[i].sort == [empty_spot, opponent_for(team), opponent_for(team)]
73
+ end
74
+
75
+ 3.times do |i| # columns
76
+ return [i, i + 3, i + 6] if columns[i].sort == [empty_spot, opponent_for(team), opponent_for(team)]
77
+ end
78
+
79
+ return [0, 4, 8] if diagonals[0].sort == [empty_spot, opponent_for(team), opponent_for(team)]
80
+ return [2, 4, 6] if diagonals[1].sort == [empty_spot, opponent_for(team), opponent_for(team)]
81
+
82
+ return nil
83
+ end
84
+
85
+ def empty_spot_in_group(group)
86
+ empty_spots = group.select do |position|
87
+ get_cell(position) == empty_spot
88
+ end
89
+ return empty_spots.first
90
+ end
91
+
92
+ def winning_group_for(team)
93
+ blocking_group_for(opponent_for(team))
94
+ end
95
+
96
+ def center
97
+ 4
98
+ end
99
+
100
+ def empty_at_center?
101
+ get_cell(4) == empty_spot
102
+ end
103
+
104
+ def first_empty_cell
105
+ @cells.each_with_index do |cell, index|
106
+ return index if cell == empty_spot
107
+ end
108
+ end
109
+
110
+ def opponent_for(team)
111
+ if team == @player_one
112
+ return @player_two
113
+ else
114
+ return @player_one
115
+ end
116
+ end
117
+
118
+ def remaining_moves
119
+ count = []
120
+ 9.times do |index|
121
+ count << index if @cells[index] == empty_spot
122
+ end
123
+ return count
124
+ end
125
+
126
+ def copy
127
+ new_board = self.dup
128
+ new_board.cells = @cells.dup
129
+ # new_board.player_one = @player_one.dup
130
+ # new_board.player_one = @player_two.dup
131
+ return new_board
132
+ end
133
+
134
+ private ################################
135
+
136
+ def empty_spot
137
+ ""
138
+ end
139
+
140
+ end
@@ -0,0 +1,61 @@
1
+ require 'nick_tac_toe/board'
2
+
3
+ class Game
4
+ attr_reader :board, :current_player, :player_one, :player_two
5
+
6
+ def initialize(player_one, player_two)
7
+ @board = Board.new(player_one.team, player_two.team)
8
+ @player_one = player_one
9
+ @player_two = player_two
10
+ @current_player = player_one
11
+ end
12
+
13
+ def take_next_turn
14
+ make_move(current_player.move_for(board))
15
+ end
16
+
17
+ def make_move(position)
18
+ @board.set_cell(position, current_player.team)
19
+ toggle_current_player
20
+ end
21
+
22
+ def over?
23
+ return true if draw? || won?
24
+ end
25
+
26
+ def draw?
27
+ return false if won?
28
+ return board.cells.all? {|cell| cell == @player_one.team || cell == @player_two.team }
29
+ end
30
+
31
+ def won?
32
+ return true if board.rows.any? { |row| winner_in_group(row) }
33
+ return true if board.columns.any? { |column| winner_in_group(column) }
34
+ return true if board.diagonals.any? { |diagonal| winner_in_group(diagonal) }
35
+ end
36
+
37
+ def winner_in_group(group)
38
+ group == [@player_two.team]*3 || group == [@player_one.team]*3
39
+ end
40
+
41
+ def player_won?(player)
42
+ return true if board.rows.any? { |row| row == [player.team]*3 }
43
+ return true if board.columns.any? { |column| column == [player.team]*3 }
44
+ return true if board.diagonals.any? { |diagonal| diagonal == [player.team]*3 }
45
+ end
46
+
47
+ def winner
48
+ return player_one if player_won?(player_one)
49
+ return player_two if player_won?(player_two)
50
+ end
51
+
52
+ private
53
+
54
+ def toggle_current_player
55
+ if @current_player == @player_one
56
+ @current_player = @player_two
57
+ else
58
+ @current_player = @player_one
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,12 @@
1
+ class Human < Player
2
+ def move_for(board)
3
+ puts "Where will you move? Pick an empty space, 1-9."
4
+ move_choice = gets.chomp.to_i
5
+ while !board.valid_move?(move_choice)
6
+ puts "That is not a valid move!"
7
+ move_choice = gets.chomp.to_i
8
+ end
9
+ return move_choice - 1
10
+ end
11
+ end
12
+
@@ -0,0 +1,58 @@
1
+ class Minimax < Player
2
+ def move_for(board)
3
+ get_max_move(board).position
4
+ end
5
+
6
+
7
+ def get_max_move(board)
8
+ current_max_move = Move.new(0, -1)
9
+ board.remaining_moves.each do |remaining_move|
10
+ new_board = board.copy
11
+ new_board.set_cell(remaining_move, team)
12
+ return Move.new(remaining_move, 1) if player_won?(team, new_board)
13
+ return Move.new(remaining_move, 0) if new_board.cells.all? { |cell| cell == new_board.player_one || cell == new_board.player_two }
14
+ return Move.new(remaining_move, -1) if player_won?(new_board.opponent_for(team), new_board)
15
+ new_rating = get_min_move(new_board).rating
16
+ if new_rating > current_max_move.rating
17
+ current_max_move = Move.new(remaining_move, new_rating)
18
+ end
19
+ end
20
+ return current_max_move
21
+ end
22
+
23
+ def get_min_move(board)
24
+ current_min_move = Move.new(0, 1)
25
+ board.remaining_moves.each do |remaining_move|
26
+ new_board = board.copy
27
+ new_board.set_cell(remaining_move, new_board.opponent_for(team));
28
+ return Move.new(current_min_move.position, 1) if player_won?(team, new_board)
29
+ return Move.new(current_min_move.position, 0) if new_board.cells.all? { |cell| cell == new_board.player_one || cell == new_board.player_two }
30
+ return Move.new(current_min_move.position, -1) if player_won?(new_board.opponent_for(team), new_board)
31
+ new_rating = get_max_move(new_board).rating
32
+ if new_rating < current_min_move.rating
33
+ current_min_move = Move.new(remaining_move, new_rating)
34
+ end
35
+ end
36
+ return current_min_move
37
+ end
38
+
39
+ def player_won?(team, board)
40
+ return true if board.rows.any? { |row| row == [team]*3 }
41
+ return true if board.columns.any? { |column| column == [team]*3 }
42
+ return true if board.diagonals.any? { |diagonal| diagonal == [team]*3 }
43
+ end
44
+
45
+ def computer?
46
+ true
47
+ end
48
+
49
+ private #################
50
+
51
+ def winning_group(board)
52
+ board.winning_group_for(@team)
53
+ end
54
+
55
+ def blocking_group(board)
56
+ board.blocking_group_for(@team)
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ class Move
2
+ attr_accessor :position, :rating
3
+ def initialize(position, rating)
4
+ @position = position
5
+ @rating = rating
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ class Player
2
+ attr_reader :team
3
+
4
+ def initialize(team)
5
+ @team = team
6
+ end
7
+
8
+ def computer?
9
+ false
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ def get_player_team
2
+ puts "Choose a team (x or o)"
3
+ input = gets.chomp
4
+ unless input == "x" || input == "X" || input == "o" || input == "O"
5
+ puts "That is neither x nor o!"
6
+ get_player_team
7
+ else
8
+ input = "x" if input == "X"
9
+ input = "o" if input == "O"
10
+ return input
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'nick_tac_toe/board'
2
+ require 'nick_tac_toe/game'
3
+ require 'nick_tac_toe/player'
4
+ require 'nick_tac_toe/human'
5
+ require 'nick_tac_toe/minimax'
6
+ require 'nick_tac_toe/move'
7
+ require 'nick_tac_toe/setup'
@@ -0,0 +1,255 @@
1
+ require "nick_tac_toe"
2
+
3
+ describe Board do
4
+ it "has a team for the computer player" do
5
+ board = Board.new("o", "x")
6
+ board.player_one.should == "o"
7
+ end
8
+
9
+ it "has a team for the opponent" do
10
+ board = Board.new("o", "x")
11
+ board.player_two.should == "x"
12
+ end
13
+
14
+ it "gets a cell" do
15
+ board = Board.new("o", "x")
16
+ board.get_cell(0).should == ""
17
+ end
18
+
19
+ it "sets a cell" do
20
+ board = Board.new("o", "x")
21
+ board.set_cell(0, "x")
22
+ board.get_cell(0).should == "x"
23
+ end
24
+
25
+ it "returns the board as rows" do
26
+ board = Board.new("o", "x")
27
+ board.set_cell(0, "x")
28
+ board.set_cell(3, "x")
29
+ board.set_cell(6, "x")
30
+ board.rows.should == [["x", "", ""],["x", "", ""],["x", "", ""]]
31
+ end
32
+
33
+ it "returns the board as coulmns" do
34
+ board = Board.new("o", "x")
35
+ board.set_cell(0, "x")
36
+ board.set_cell(3, "x")
37
+ board.set_cell(6, "x")
38
+ board.columns.should == [["x", "x", "x"],["", "", ""],["", "", ""]]
39
+ end
40
+
41
+ it "returns the board as diagonals" do
42
+ board = Board.new("o", "x")
43
+ board.set_cell(0, "x")
44
+ board.set_cell(3, "x")
45
+ board.set_cell(6, "x")
46
+ board.diagonals.should == [["x", "", ""],["", "", "x"]]
47
+ end
48
+
49
+ it "is valid for an in range move on an empty board" do
50
+ board = Board.new("o", "x")
51
+ board.valid_move?(3).should be_true
52
+ end
53
+
54
+ it "is not valid for a move out of range on an empty board" do
55
+ board = Board.new("o", "x")
56
+ board.valid_move?(-1).should be_false
57
+ end
58
+
59
+ it "is not valid for a move to a space that is not empty" do
60
+ board = Board.new("o", "x")
61
+ board.set_cell(4, "x")
62
+ board.valid_move?(5).should be_false
63
+ end
64
+
65
+ it "is not valid for a string to be a move" do
66
+ board = Board.new("o", "x")
67
+ board.valid_move?("s").should be_false
68
+ end
69
+
70
+ context "blocking_group_for" do
71
+ before(:each) do
72
+ @board = Board.new("o", "x")
73
+ end
74
+
75
+ it "finds the first row as a blocking group" do
76
+ @board.set_cell(0, "x")
77
+ @board.set_cell(1, "x")
78
+ @board.blocking_group_for("o").should == [0,1,2]
79
+ end
80
+
81
+ it "returns nothing if there is nowhere to block" do
82
+ @board.blocking_group_for("o").should == nil
83
+ end
84
+
85
+ it "returns the second row" do
86
+ @board.set_cell(3, "x")
87
+ @board.set_cell(4, "x")
88
+ @board.blocking_group_for("o").should == [3,4,5]
89
+ end
90
+
91
+ it "returns the second row if you can block in the center" do
92
+ @board.set_cell(3, "x")
93
+ @board.set_cell(5, "x")
94
+ @board.blocking_group_for("o").should == [3,4,5]
95
+ end
96
+
97
+ it "returns a blocking row for the other team" do
98
+ @board.set_cell(3, "o")
99
+ @board.set_cell(5, "o")
100
+ @board.blocking_group_for("x").should == [3,4,5]
101
+ end
102
+
103
+ it "does not return a row that is full" do
104
+ @board.set_cell(3, "o")
105
+ @board.set_cell(4, "x")
106
+ @board.set_cell(5, "o")
107
+ @board.blocking_group_for("x").should == nil
108
+ end
109
+
110
+ it "finds the first column as a blocking group" do
111
+ @board.set_cell(0, "x")
112
+ @board.set_cell(3, "x")
113
+ @board.blocking_group_for("o").should == [0,3,6]
114
+ end
115
+
116
+ it "finds the second column as a blocking group" do
117
+ @board.set_cell(1, "x")
118
+ @board.set_cell(4, "x")
119
+ @board.blocking_group_for("o").should == [1,4,7]
120
+ end
121
+
122
+ it "finds the third column as a blocking group" do
123
+ @board.set_cell(2, "x")
124
+ @board.set_cell(5, "x")
125
+ @board.blocking_group_for("o").should == [2,5,8]
126
+ end
127
+
128
+ it "finds the first diagonal as a blocking group" do
129
+ @board.set_cell(0, "x")
130
+ @board.set_cell(4, "x")
131
+ @board.blocking_group_for("o").should == [0,4,8]
132
+ end
133
+
134
+ it "finds the other diagonal as a blocking group" do
135
+ @board.set_cell(2, "x")
136
+ @board.set_cell(4, "x")
137
+ @board.blocking_group_for("o").should == [2,4,6]
138
+ end
139
+ end
140
+
141
+ context "empty_spot_in_group" do
142
+ before(:each) do
143
+ @board = Board.new("o", "x")
144
+ end
145
+
146
+ it "returns the first spot" do
147
+ @board.set_cell(1, "x")
148
+ @board.set_cell(2, "o")
149
+ @board.empty_spot_in_group([0,1,2]).should == 0
150
+ end
151
+
152
+ it "returns the first spot" do
153
+ @board.set_cell(0, "x")
154
+ @board.set_cell(2, "o")
155
+ @board.empty_spot_in_group([0,1,2]).should == 1
156
+ end
157
+
158
+ it "returns the third spot" do
159
+ @board.set_cell(2, "x")
160
+ @board.set_cell(4, "o")
161
+ @board.empty_spot_in_group([2,4,6]).should == 6
162
+ end
163
+
164
+ it "returns nil if the row is full" do
165
+ @board.set_cell(0, "x")
166
+ @board.set_cell(1, "x")
167
+ @board.set_cell(2, "o")
168
+ @board.empty_spot_in_group([0,1,2]).should == nil
169
+ end
170
+ end
171
+
172
+ context "winning_group_for" do
173
+ before(:each) do
174
+ @board = Board.new("o", "x")
175
+ end
176
+
177
+ it "finds the first row as a winning group" do
178
+ @board.set_cell(0, "x")
179
+ @board.set_cell(1, "x")
180
+ @board.winning_group_for("x").should == [0,1,2]
181
+ end
182
+
183
+ it "finds the first column as a winning group" do
184
+ @board.set_cell(0, "x")
185
+ @board.set_cell(3, "x")
186
+ @board.winning_group_for("x").should == [0,3,6]
187
+ end
188
+ end
189
+
190
+ context "remaining_moves" do
191
+ before(:each) do
192
+ @board = Board.new("o", "x")
193
+ end
194
+ it "calculates the number of remaining moves when there is one spot left" do
195
+ @board.set_cell(0, "x")
196
+ @board.set_cell(1, "x")
197
+ @board.set_cell(2, "o")
198
+ @board.set_cell(3, "x")
199
+ @board.set_cell(4, "x")
200
+ @board.set_cell(5, "o")
201
+ @board.set_cell(6, "x")
202
+ @board.set_cell(7, "x")
203
+ @board.remaining_moves.should == [8]
204
+ end
205
+
206
+ it "calculates the number of remaining moves when there are two spots left" do
207
+ @board.set_cell(0, "x")
208
+ @board.set_cell(1, "x")
209
+ @board.set_cell(2, "o")
210
+ @board.set_cell(3, "x")
211
+ @board.set_cell(4, "x")
212
+ @board.set_cell(5, "o")
213
+ @board.set_cell(6, "x")
214
+ @board.remaining_moves.should == [7,8]
215
+ end
216
+
217
+ it "calculates the number of remaining moves when there are no spots left" do
218
+ @board.set_cell(0, "x")
219
+ @board.set_cell(1, "x")
220
+ @board.set_cell(2, "o")
221
+ @board.set_cell(3, "x")
222
+ @board.set_cell(4, "x")
223
+ @board.set_cell(5, "o")
224
+ @board.set_cell(6, "x")
225
+ @board.set_cell(7, "x")
226
+ @board.set_cell(8, "x")
227
+ @board.remaining_moves.should == []
228
+ end
229
+ end
230
+
231
+ context "dup" do
232
+ before(:each) do
233
+ @board = Board.new("o", "x")
234
+ end
235
+ it "returns a copy of the board" do
236
+ @board.set_cell(1,"x")
237
+ new_board = @board.copy
238
+ new_board.get_cell(1).should == "x"
239
+ end
240
+
241
+ it "does not affect the original board when changing the new board" do
242
+ @board.set_cell(1,"x")
243
+ new_board = @board.copy
244
+ new_board.set_cell(0, "o")
245
+ @board.get_cell(0).should_not == "o"
246
+ end
247
+
248
+ it "dups players!" do
249
+ new_board = @board.copy
250
+ new_board.player_one = "f"
251
+ @board.player_one.should_not == "f"
252
+ end
253
+
254
+ end
255
+ end
data/spec/game_spec.rb ADDED
@@ -0,0 +1,181 @@
1
+ require "nick_tac_toe"
2
+
3
+ describe "Game" do
4
+
5
+ before(:each) do
6
+ @player_one = Human.new("x")
7
+ @player_two = Human.new("o")
8
+ @game = Game.new(@player_one, @player_two)
9
+ end
10
+
11
+ context "Game over" do
12
+ it "starts with player one as the current player" do
13
+ @game.current_player.should == @player_one
14
+ end
15
+
16
+ it "makes a move on the board" do
17
+ @game.make_move(1)
18
+ @game.board.get_cell(1).should == @player_one.team
19
+ end
20
+
21
+ it "makes a move using player twos team" do
22
+ @game.make_move(1)
23
+ @game.make_move(2)
24
+ @game.board.get_cell(2).should == @player_two.team
25
+ end
26
+
27
+ it "switches to player two's turn" do
28
+ @game.make_move(2)
29
+ @game.current_player.should == @player_two
30
+ end
31
+
32
+ it "switches back to player one" do
33
+ @game.make_move(2)
34
+ @game.make_move(2)
35
+ @game.current_player.should == @player_one
36
+ end
37
+
38
+ it "does not call the game a draw if the board is not full" do
39
+ @game.board.set_cell(0, "x")
40
+ @game.draw?.should be_false
41
+ end
42
+
43
+ it "is not a draw if the game is full with a winner" do
44
+ @game.board.set_cell(0, "o")
45
+ @game.board.set_cell(1, "o")
46
+ @game.board.set_cell(2, "o")
47
+ @game.board.set_cell(3, "o")
48
+ @game.board.set_cell(4, "x")
49
+ @game.board.set_cell(5, "x")
50
+ @game.board.set_cell(6, "o")
51
+ @game.board.set_cell(7, "x")
52
+ @game.board.set_cell(8, "x")
53
+ @game.draw?.should be_false
54
+ end
55
+
56
+ it "has no winner for new game" do
57
+ @game.winner.should be_nil
58
+ end
59
+
60
+ it "has player one as the winner" do
61
+ @game.board.set_cell(0, "x")
62
+ @game.board.set_cell(3, "o")
63
+ @game.board.set_cell(1, "x")
64
+ @game.board.set_cell(4, "o")
65
+ @game.board.set_cell(2, "x")
66
+ @game.winner.should == @game.player_one
67
+ end
68
+
69
+ it "has player two as the winner" do
70
+ @game.board.set_cell(0, "x")
71
+ @game.board.set_cell(3, "o")
72
+ @game.board.set_cell(1, "x")
73
+ @game.board.set_cell(4, "o")
74
+ @game.board.set_cell(8, "x")
75
+ @game.board.set_cell(5, "o")
76
+ @game.winner.should == @game.player_two
77
+ end
78
+
79
+ it "has no winner for draw game" do
80
+ @game.board.set_cell(0, "x")
81
+ @game.board.set_cell(1, "o")
82
+ @game.board.set_cell(2, "x")
83
+ @game.board.set_cell(3, "x")
84
+ @game.board.set_cell(4, "o")
85
+ @game.board.set_cell(5, "o")
86
+ @game.board.set_cell(6, "o")
87
+ @game.board.set_cell(7, "x")
88
+ @game.board.set_cell(8, "x")
89
+ @game.winner.should be_nil
90
+ end
91
+ # new tests end
92
+
93
+
94
+ it "knows that the game a draw if the board is full" do
95
+ # x o x
96
+ # x o o
97
+ # o x x
98
+ @game.board.set_cell(0, "x")
99
+ @game.board.set_cell(1, "o")
100
+ @game.board.set_cell(2, "x")
101
+ @game.board.set_cell(3, "x")
102
+ @game.board.set_cell(4, "o")
103
+ @game.board.set_cell(5, "o")
104
+ @game.board.set_cell(6, "o")
105
+ @game.board.set_cell(7, "x")
106
+ @game.board.set_cell(8, "x")
107
+ @game.draw?.should be_true
108
+ end
109
+
110
+ it "knows that three horizonal marks is a win" do
111
+ @game.board.set_cell(0, "o")
112
+ @game.board.set_cell(1, "o")
113
+ @game.board.set_cell(2, "o")
114
+ @game.won?.should be_true
115
+ end
116
+
117
+ it "knows that three vertical marks is a win" do
118
+ @game.board.set_cell(0, "o")
119
+ @game.board.set_cell(3, "o")
120
+ @game.board.set_cell(6, "o")
121
+ @game.won?.should be_true
122
+ end
123
+
124
+ it "knows that three diagonal marks is a win" do
125
+ @game.board.set_cell(0, "o")
126
+ @game.board.set_cell(4, "o")
127
+ @game.board.set_cell(8, "o")
128
+ @game.won?.should be_true
129
+ end
130
+
131
+ it "knows if there is not a winner in a group" do
132
+ @game.winner_in_group(["x","x","o"]).should be_false
133
+ end
134
+
135
+ it "knows if there is a winner in a group" do
136
+ @game.winner_in_group(["x","x","x"]).should be_true
137
+ end
138
+
139
+ it "knows if a specific player won in a row" do
140
+ @game.board.set_cell(0, @player_one.team)
141
+ @game.board.set_cell(1, @player_one.team)
142
+ @game.board.set_cell(2, @player_one.team)
143
+ @game.player_won?(@player_one).should be_true
144
+ end
145
+
146
+ it "returns false if the other player won in a row" do
147
+ @game.board.set_cell(0, @player_one.team)
148
+ @game.board.set_cell(1, @player_one.team)
149
+ @game.board.set_cell(2, @player_one.team)
150
+ @game.player_won?(@player_two).should be_false
151
+ end
152
+
153
+ it "knows if a specific player won in a column" do
154
+ @game.board.set_cell(0, @player_one.team)
155
+ @game.board.set_cell(3, @player_one.team)
156
+ @game.board.set_cell(6, @player_one.team)
157
+ @game.player_won?(@player_one).should be_true
158
+ end
159
+
160
+ it "returns false if the other player won in a column" do
161
+ @game.board.set_cell(0, @player_one.team)
162
+ @game.board.set_cell(3, @player_one.team)
163
+ @game.board.set_cell(6, @player_one.team)
164
+ @game.player_won?(@player_two).should be_false
165
+ end
166
+
167
+ it "knows if a specific player won in a diagonal" do
168
+ @game.board.set_cell(0, @player_one.team)
169
+ @game.board.set_cell(4, @player_one.team)
170
+ @game.board.set_cell(8, @player_one.team)
171
+ @game.player_won?(@player_one).should be_true
172
+ end
173
+
174
+ it "returns false if the other player won in a diagonal" do
175
+ @game.board.set_cell(0, @player_one.team)
176
+ @game.board.set_cell(4, @player_one.team)
177
+ @game.board.set_cell(8, @player_one.team)
178
+ @game.player_won?(@player_two).should be_false
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,76 @@
1
+ require "nick_tac_toe"
2
+
3
+ describe "Minimax" do
4
+ before(:each) do
5
+ @board = Board.new("o", "x")
6
+ @minimax = Minimax.new("o")
7
+ end
8
+
9
+ it "blocks the opponent" do
10
+ # o - -
11
+ # x x -
12
+ # - - -
13
+ @board.set_cell(0, "o")
14
+ @board.set_cell(3, "x")
15
+ @board.set_cell(4, "x")
16
+ @minimax.move_for(@board).should == 5
17
+ end
18
+
19
+ xit "should win" do
20
+ @board.set_cell(0, "o")
21
+ @board.set_cell(1, "o")
22
+ @minimax.move_for(@board).should == 2
23
+ end
24
+
25
+ it "places a mark in the last available spot on the board" do
26
+ # 0 1 2 x x o
27
+ # 3 4 5 o o x
28
+ # 6 7 8 x o x
29
+ @board.set_cell(0, "x")
30
+ @board.set_cell(1, "x")
31
+ @board.set_cell(2, "o")
32
+ @board.set_cell(3, "o")
33
+ @board.set_cell(4, "o")
34
+ @board.set_cell(5, "x")
35
+ @board.set_cell(6, "x")
36
+ @board.set_cell(8, "x")
37
+ @minimax.move_for(@board).should == 7
38
+ end
39
+
40
+ xit "places in the top left corner if it goes first" do
41
+ @minimax.move_for(@board).should == 0
42
+ end
43
+
44
+ it "avoids this trap I made!" do
45
+ # x 1 2
46
+ # 3 o 5
47
+ # 6 x 8
48
+ @board.set_cell(0, "x")
49
+ @board.set_cell(4, "o")
50
+ @board.set_cell(7, "x")
51
+ @minimax.move_for(@board).should == 3
52
+ end
53
+
54
+ it "avoids the classic trap!" do
55
+ @board.set_cell(0, "x")
56
+ @minimax.move_for(@board).should == 4
57
+ end
58
+
59
+ it "should avoid stage 2 of the classic trap!" do
60
+ @board.set_cell(0, "x")
61
+ @board.set_cell(4, "o")
62
+ @board.set_cell(8, "x")
63
+ @minimax.move_for(@board).should_not == 2
64
+ end
65
+
66
+ it "should avoid stage 2 of the classic trap part 2!" do
67
+ @board.set_cell(0, "x")
68
+ @board.set_cell(4, "o")
69
+ @board.set_cell(8, "x")
70
+ @minimax.move_for(@board).should_not == 6
71
+ end
72
+
73
+ it "is a computer" do
74
+ Minimax.new("o").should be_computer
75
+ end
76
+ end
data/spec/move_spec.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "nick_tac_toe"
2
+
3
+ describe Move do
4
+ it "has a position" do
5
+ move = Move.new("position", nil)
6
+ move.position.should == "position"
7
+ end
8
+
9
+ it "has a rating" do
10
+ move = Move.new(nil, "rating")
11
+ move.rating.should == "rating"
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require "nick_tac_toe"
2
+
3
+ describe "Player" do
4
+ it "has a team" do
5
+ Player.new("team").team.should == "team"
6
+ end
7
+
8
+ it "is not a computer by default" do
9
+ Player.new(nil).should_not be_computer
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nick_tac_toe
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.4
6
+ platform: ruby
7
+ authors:
8
+ - Nick Meccia
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-10-25 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Your mum.
18
+ email: nickmeccia@gmail.com
19
+ executables:
20
+ - nick_tac_toe
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - bin/nick_tac_toe
27
+ - spec/board_spec.rb
28
+ - spec/game_spec.rb
29
+ - spec/minimax_spec.rb
30
+ - spec/move_spec.rb
31
+ - spec/player_spec.rb
32
+ - lib/nick_tac_toe/board.rb
33
+ - lib/nick_tac_toe/game.rb
34
+ - lib/nick_tac_toe/human.rb
35
+ - lib/nick_tac_toe/minimax.rb
36
+ - lib/nick_tac_toe/move.rb
37
+ - lib/nick_tac_toe/player.rb
38
+ - lib/nick_tac_toe/setup.rb
39
+ - lib/nick_tac_toe.rb
40
+ has_rdoc: true
41
+ homepage:
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.6.2
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Yes, sir.
68
+ test_files: []
69
+