ruby_ttt 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ai.rb CHANGED
@@ -1,70 +1,72 @@
1
- POS_INF = 999
2
- NEG_INF = -999
3
- WIN = 1
4
- LOSE = -1
5
- TIE = 0
1
+ module RubyTictactoe
6
2
 
7
- class AI
8
- attr_reader :current_player
9
- def initialize(player)
10
- @current_player = player
11
- end
3
+ class AI
4
+ POS_INF = 999
5
+ NEG_INF = -999
6
+ WIN = 1
7
+ LOSE = -1
8
+ TIE = 0
9
+ attr_reader :current_player
10
+ def initialize(player)
11
+ @current_player = player
12
+ end
12
13
 
13
- def computer_move(board, player)
14
- test_board = board.dup
15
- test_board.all_cells = board.all_cells.dup
16
- get_best_move(test_board, player)
17
- end
14
+ def computer_move(board, player)
15
+ test_board = board.dup
16
+ test_board.all_cells = board.all_cells.dup
17
+ get_best_move(test_board, player)
18
+ end
18
19
 
19
- def get_best_move(board, player)
20
- ranked_moves = rank_possible_moves(board, player)
21
- move = ranked_moves.max_by {|cell, score| score}
22
- move.first
23
- end
20
+ def get_best_move(board, player)
21
+ ranked_moves = rank_possible_moves(board, player)
22
+ move = ranked_moves.max_by {|cell, score| score}
23
+ move.first
24
+ end
24
25
 
25
- private
26
+ private
26
27
 
27
- def rank_possible_moves(board, player)
28
- possible_moves = board.open_cells
29
- possible_moves.each_key do |cell|
30
- possible_moves[cell] = get_move_score(board, player, cell)
28
+ def rank_possible_moves(board, player)
29
+ possible_moves = board.open_cells
30
+ possible_moves.each_key do |cell|
31
+ possible_moves[cell] = get_move_score(board, player, cell)
32
+ end
31
33
  end
32
- end
33
34
 
34
- def get_move_score(board, player, cell)
35
- player.add_marker(board, cell)
36
- best_score = apply_minimax(board, player, cell, depth=0, NEG_INF, POS_INF)
37
- board.remove_marker(cell)
38
- best_score
39
- end
35
+ def get_move_score(board, player, cell)
36
+ player.add_marker(board, cell)
37
+ best_score = apply_minimax(board, player, cell, depth=0, NEG_INF, POS_INF)
38
+ board.remove_marker(cell)
39
+ best_score
40
+ end
40
41
 
41
- def get_score(board, player)
42
- return WIN if board.winner?(player.marker) && (player == current_player)
43
- return LOSE if board.winner?(player.marker)
44
- TIE
45
- end
42
+ def get_score(board, player)
43
+ return WIN if board.winner?(player.marker) && (player == current_player)
44
+ return LOSE if board.winner?(player.marker)
45
+ TIE
46
+ end
46
47
 
47
- def apply_minimax(board, player, cell, depth, alpha, beta)
48
- return get_score(board, player) if board.game_over?
49
- if (player == current_player)
50
- maximizing_player = MaximizingPlayer.new(player)
51
- alphabeta(board, maximizing_player, depth, alpha, beta)
52
- else
53
- minimizing_player = MinimizingPlayer.new(player)
54
- alphabeta(board, minimizing_player, depth, alpha, beta)
48
+ def apply_minimax(board, player, cell, depth, alpha, beta)
49
+ return get_score(board, player) if board.game_over?
50
+ if (player == current_player)
51
+ maximizing_player = MaximizingPlayer.new(player)
52
+ alphabeta(board, maximizing_player, depth, alpha, beta)
53
+ else
54
+ minimizing_player = MinimizingPlayer.new(player)
55
+ alphabeta(board, minimizing_player, depth, alpha, beta)
56
+ end
55
57
  end
56
- end
57
58
 
58
- def alphabeta(board, player, depth, alpha, beta)
59
- board.open_cells.each_key do |cell|
60
- player.opponent.add_marker(board, cell)
61
- score = (apply_minimax(board, player.opponent, cell, depth += 1, alpha, beta) / depth.to_f)
62
- alpha = player.get_alpha(alpha, score)
63
- beta = player.get_beta(beta, score)
64
- board.remove_marker(cell)
65
- break if alpha >= beta
59
+ def alphabeta(board, player, depth, alpha, beta)
60
+ board.open_cells.each_key do |cell|
61
+ player.opponent.add_marker(board, cell)
62
+ score = (apply_minimax(board, player.opponent, cell, depth += 1, alpha, beta) / depth.to_f)
63
+ alpha = player.get_alpha(alpha, score)
64
+ beta = player.get_beta(beta, score)
65
+ board.remove_marker(cell)
66
+ break if alpha >= beta
67
+ end
68
+ player.return_best_score(alpha, beta)
66
69
  end
67
- player.return_best_score(alpha, beta)
68
70
  end
69
71
 
70
72
  end
@@ -0,0 +1,39 @@
1
+ module RubyTictactoe
2
+
3
+ class AlphaBetaPlayer
4
+ attr_accessor :marker, :opponent
5
+ def initialize(player)
6
+ @marker = player.marker
7
+ @opponent = player.opponent
8
+ end
9
+
10
+ def get_alpha(alpha, score)
11
+ alpha
12
+ end
13
+
14
+ def get_beta(beta, score)
15
+ beta
16
+ end
17
+ end
18
+
19
+ class MinimizingPlayer < AlphaBetaPlayer
20
+ def get_alpha(alpha, score)
21
+ score > alpha ? score : alpha
22
+ end
23
+
24
+ def return_best_score(alpha, beta)
25
+ alpha
26
+ end
27
+ end
28
+
29
+ class MaximizingPlayer < AlphaBetaPlayer
30
+ def get_beta(beta, score)
31
+ score < beta ? score : beta
32
+ end
33
+
34
+ def return_best_score(alpha, beta)
35
+ beta
36
+ end
37
+ end
38
+
39
+ end
data/lib/board.rb CHANGED
@@ -1,220 +1,150 @@
1
- MARKER_X = 'X'
2
- MARKER_O = 'O'
3
-
4
- class Board
5
- attr_accessor :all_cells, :num_of_rows, :winning_lines
6
- def initialize(num_of_rows)
7
- @num_of_rows = num_of_rows
8
- @all_cells = create_board_hash
9
- @winning_lines = get_winning_lines
10
- end
1
+ require 'tictactoe_constants'
2
+
3
+ module RubyTictactoe
11
4
 
12
- def create_board_hash
13
- new_board = Hash.new
14
- alpha = 'A'
15
- numeric = 1
16
- num_of_rows.times do
5
+ class Board
6
+ include TictactoeConstants
7
+ attr_accessor :all_cells, :num_of_rows, :winning_lines
8
+ def initialize(num_of_rows)
9
+ @num_of_rows = num_of_rows
10
+ @all_cells = create_board_hash
11
+ @winning_lines = get_winning_lines
12
+ end
13
+
14
+ def create_board_hash
15
+ new_board = Hash.new
16
+ alpha = 'A'
17
+ numeric = 1
17
18
  num_of_rows.times do
18
- cellID = numeric.to_s + alpha
19
- numeric += 1
20
- new_board[cellID] = nil
19
+ num_of_rows.times do
20
+ cellID = numeric.to_s + alpha
21
+ numeric += 1
22
+ new_board[cellID] = nil
23
+ end
24
+ alpha = alpha.next
25
+ numeric = 1
21
26
  end
22
- alpha = alpha.next
23
- numeric = 1
27
+ new_board
24
28
  end
25
- new_board
26
- end
27
29
 
28
- def get_winning_lines
29
- lines = []
30
- all_rows.each { |row| lines << row }
31
- all_cols.each { |col| lines << col }
32
- diagonals.each { |diagonal| lines << diagonal }
33
- lines
34
- end
35
-
36
- def all_rows
37
- rows = []
38
- cellIDs = all_cells.keys
39
- beg = 0
40
- ending = num_of_rows - 1
41
- until rows.length == num_of_rows
42
- rows << cellIDs[beg..ending]
43
- beg += num_of_rows
44
- ending += num_of_rows
45
- end
46
- rows
47
- end
48
-
49
- def add_test_marker(marker, cell)
50
- all_cells[cell] = marker
51
- end
52
-
53
- def available_cell?(cell)
54
- valid_cell?(cell) && all_cells[cell].nil?
55
- end
56
-
57
- def valid_cell?(cell)
58
- all_cells.has_key?(cell)
59
- end
60
-
61
- def remove_marker(cell)
62
- all_cells[cell] = nil
63
- end
64
-
65
- def moves_remaining?
66
- all_cells.has_value?(nil)
67
- end
68
-
69
- def winner?(marker)
70
- board_markers = all_cells.select { |cell, value| value == marker }.keys
71
- winning_lines.each do |line|
72
- return true if (line & board_markers).length == num_of_rows
30
+ def get_winning_lines
31
+ lines = []
32
+ all_rows.each { |row| lines << row }
33
+ all_cols.each { |col| lines << col }
34
+ diagonals.each { |diagonal| lines << diagonal }
35
+ lines
73
36
  end
74
- false
75
- end
76
-
77
- def game_over?
78
- !moves_remaining? || winner?(MARKER_X) || winner?(MARKER_O)
79
- end
80
37
 
81
- def open_cells
82
- all_cells.select { |k,v| v.nil? }
83
- end
84
-
85
- def empty?
86
- open_cells.length == (num_of_rows * num_of_rows)
87
- end
88
-
89
- def random_cell
90
- cells = open_cells.keys
91
- cells_count = cells.length - 1
92
- cells[rand(cells_count)]
93
- end
94
-
95
- private
96
- def all_cols
97
- cols = []
98
- index = 0
99
- num_of_rows.times do
100
- cols << get_column(index)
101
- index += 1
38
+ def all_rows
39
+ rows = []
40
+ cellIDs = all_cells.keys
41
+ beg = 0
42
+ ending = num_of_rows - 1
43
+ until rows.length == num_of_rows
44
+ rows << cellIDs[beg..ending]
45
+ beg += num_of_rows
46
+ ending += num_of_rows
47
+ end
48
+ rows
102
49
  end
103
- cols
104
- end
105
50
 
106
- def get_column(index)
107
- column = []
108
- cellIDs = all_cells.keys
109
- num_of_rows.times do
110
- column << cellIDs[index]
111
- index += num_of_rows
51
+ def add_test_marker(marker, cell)
52
+ all_cells[cell] = marker
112
53
  end
113
- column
114
- end
115
-
116
- def diagonals
117
- diagonals = []
118
- diagonals << diagonal_one
119
- diagonals << diagonal_two
120
- end
121
54
 
122
- def diagonal_one
123
- diagonal = []
124
- alpha = 'A'
125
- numeric = 1
126
- num_of_rows.times do
127
- diagonal << numeric.to_s + alpha
128
- alpha = alpha.next
129
- numeric += 1
55
+ def available_cell?(cell)
56
+ valid_cell?(cell) && all_cells[cell].nil?
130
57
  end
131
- diagonal
132
- end
133
58
 
134
- def diagonal_two
135
- diagonal = []
136
- alpha = 'A'
137
- numeric = num_of_rows
138
- num_of_rows.times do
139
- diagonal << numeric.to_s + alpha
140
- alpha = alpha.next
141
- numeric -= 1
59
+ def valid_cell?(cell)
60
+ all_cells.has_key?(cell)
142
61
  end
143
- diagonal
144
- end
145
62
 
146
- end
63
+ def remove_marker(cell)
64
+ all_cells[cell] = nil
65
+ end
147
66
 
148
- class CLIBoard < Board
149
- attr_accessor :all_cells, :num_of_rows, :winning_lines, :io
150
- def initialize(num_of_rows)
151
- super
152
- @io = Kernel
153
- end
67
+ def moves_remaining?
68
+ all_cells.has_value?(nil)
69
+ end
154
70
 
155
- def print_board_numbers
156
- num = 1
157
- io.print " "
158
- num_of_rows.times do
159
- io.print "--#{num}-- "
160
- num += 1
71
+ def winner?(marker)
72
+ board_markers = all_cells.select { |cell, value| value == marker }.keys
73
+ winning_lines.each do |line|
74
+ return true if (line & board_markers).length == num_of_rows
75
+ end
76
+ false
161
77
  end
162
- io.print "\n"
163
- end
164
78
 
165
- def print_divider
166
- io.print " "
167
- num_of_rows.times { io.print "------" }
168
- io.print "\n"
169
- end
79
+ def game_over?
80
+ !moves_remaining? || winner?(RubyTictactoe::TictactoeConstants::MARKER_X) || winner?(RubyTictactoe::TictactoeConstants::MARKER_O)
81
+ end
170
82
 
171
- def print_board_rows
172
- alpha = 'A'
173
- all_rows.each do |row|
174
- show_row(alpha, row)
175
- alpha = alpha.next
83
+ def open_cells
84
+ all_cells.select { |k,v| v.nil? }
176
85
  end
177
- end
178
86
 
179
- def show_row(letter, cells)
180
- io.print "#{letter}"
181
- cells.each { |cell| io.print " | " + show_marker(cell) }
182
- io.print " | #{letter}\n"
183
- print_divider
184
- end
87
+ def empty?
88
+ open_cells.length == (num_of_rows * num_of_rows)
89
+ end
185
90
 
186
- def show_marker(cell)
187
- all_cells[cell].nil? ? ' ' : all_cells[cell]
188
- end
91
+ def random_cell
92
+ cells = open_cells.keys
93
+ cells_count = cells.length - 1
94
+ cells[rand(cells_count)]
95
+ end
189
96
 
190
- def display_board
191
- print_board_numbers
192
- print_board_rows
193
- print_board_numbers
194
- end
97
+ private
98
+ def all_cols
99
+ cols = []
100
+ index = 0
101
+ num_of_rows.times do
102
+ cols << get_column(index)
103
+ index += 1
104
+ end
105
+ cols
106
+ end
195
107
 
196
- end
108
+ def get_column(index)
109
+ column = []
110
+ cellIDs = all_cells.keys
111
+ num_of_rows.times do
112
+ column << cellIDs[index]
113
+ index += num_of_rows
114
+ end
115
+ column
116
+ end
197
117
 
198
- class WebBoard < Board
118
+ def diagonals
119
+ diagonals = []
120
+ diagonals << diagonal_one
121
+ diagonals << diagonal_two
122
+ end
199
123
 
200
- def print_active_board
201
- board_string = ''
202
- all_rows.each do |row|
203
- board_string += "<div class='row'>"
204
- row.each { |cell| board_string += "<button name='move' value='#{cell}'> #{all_cells[cell]} <span class='cell'>.</span></button>" }
205
- board_string += "</div>"
124
+ def diagonal_one
125
+ diagonal = []
126
+ alpha = 'A'
127
+ numeric = 1
128
+ num_of_rows.times do
129
+ diagonal << numeric.to_s + alpha
130
+ alpha = alpha.next
131
+ numeric += 1
132
+ end
133
+ diagonal
206
134
  end
207
- board_string
208
- end
209
135
 
210
- def print_inactive_board
211
- board_string = ''
212
- all_rows.each do |row|
213
- board_string += "<div class='row'>"
214
- row.each { |cell| board_string += "<button> #{all_cells[cell]} <span class='cell'>.</span></button>" }
215
- board_string += "</div>"
136
+ def diagonal_two
137
+ diagonal = []
138
+ alpha = 'A'
139
+ numeric = num_of_rows
140
+ num_of_rows.times do
141
+ diagonal << numeric.to_s + alpha
142
+ alpha = alpha.next
143
+ numeric -= 1
144
+ end
145
+ diagonal
216
146
  end
217
- board_string
147
+
218
148
  end
219
149
 
220
150
  end
data/lib/game.rb ADDED
@@ -0,0 +1,49 @@
1
+ module RubyTictactoe
2
+
3
+ class Game
4
+ include TictactoeConstants
5
+ attr_accessor :board, :player_one, :player_two, :ui, :player_first_move
6
+ def initialize(settings)
7
+ @board = settings[:board]
8
+ @player_one = settings[:player_one]
9
+ @player_two = settings[:player_two]
10
+ @player_first_move = settings[:player_first_move]
11
+ @ui = UI.new
12
+ end
13
+
14
+ def advance_game
15
+ game_status_check(current_player.opponent.marker)
16
+ ui.next_move_message(current_player.marker) unless board.game_over?
17
+ end
18
+
19
+ def game_status_check(marker)
20
+ if board.winner?(marker)
21
+ ui.winning_game_message(marker)
22
+ elsif !board.moves_remaining?
23
+ ui.tie_game_message
24
+ end
25
+ end
26
+
27
+ def verify_move(cell)
28
+ return false if !board.available_cell?(cell)
29
+ current_player.add_marker(board, cell)
30
+ true
31
+ end
32
+
33
+ def current_player
34
+ if total_markers(MARKER_X) > total_markers(MARKER_O)
35
+ player_two
36
+ elsif total_markers(MARKER_O) > total_markers(MARKER_X)
37
+ player_one
38
+ else
39
+ player_first_move
40
+ end
41
+ end
42
+
43
+ def total_markers(marker)
44
+ board.all_cells.select { |cell, value| value == marker }.count
45
+ end
46
+
47
+ end
48
+
49
+ end
data/lib/game_setup.rb CHANGED
@@ -1,116 +1,17 @@
1
- COMPUTER_PLAYER = 'computer'
2
- HUMAN_PLAYER = 'human'
3
- AI_PLAYER = 'hard computer'
4
- HARD_LEVEL = 'hard'
5
- EASY_LEVEL = 'easy'
1
+ require 'tictactoe_constants'
6
2
 
7
- class GameSetup
8
- attr_accessor :ui
9
- def initialize
10
- @ui = UI.new
11
- end
12
-
13
- def get_settings
14
- {}
15
- end
16
- end
17
-
18
- class CLIGameSetup < GameSetup
19
- attr_accessor :ui
20
- def initialize
21
- @ui = CLIUI.new
22
- end
3
+ module RubyTictactoe
23
4
 
24
- def get_settings
25
- settings = super
26
- begin
27
- players = set_up_players
28
- settings[:board] = get_board
29
- settings[:player_one] = players.player_one
30
- settings[:player_two] = players.player_two
31
- settings[:player_first_move] = players.player_goes_first
32
- settings
33
- rescue Interrupt
34
- ui.early_exit_message
35
- exit
5
+ class GameSetup
6
+ include TictactoeConstants
7
+ attr_accessor :ui
8
+ def initialize
9
+ @ui = UI.new
36
10
  end
37
- end
38
-
39
- def set_up_players
40
- player_one_type = get_player_type(MARKER_X)
41
- player_two_type = get_player_type(MARKER_O)
42
- PlayerFactory.new(player_one_type, player_two_type)
43
- end
44
-
45
- def get_player_type(marker)
46
- type = ui.request_player_type(marker)
47
- validated_type = valid_type?(type) ? get_difficulty_level(type) : invalid_type(type, marker)
48
- ui.type_assigned_message(validated_type, marker)
49
- validated_type
50
- end
51
-
52
- def get_difficulty_level(type)
53
- return type if type == HUMAN_PLAYER
54
- level = ui.request_difficulty_level
55
- valid_level?(level) ? ui.level_assigned_message(level) : invalid_level(level)
56
- player_type_by_level(level)
57
- end
58
-
59
- def valid_type?(type)
60
- (type == HUMAN_PLAYER) || (type == COMPUTER_PLAYER)
61
- end
62
-
63
- def invalid_type(type, marker)
64
- ui.invalid_input_message(type)
65
- get_player_type(marker)
66
- end
67
-
68
- def valid_level?(level)
69
- (level == HARD_LEVEL) || (level == EASY_LEVEL)
70
- end
71
11
 
72
- def invalid_level(level)
73
- ui.invalid_input_message(level)
74
- get_difficulty_level(COMPUTER_PLAYER)
75
- end
76
-
77
- def player_type_by_level(level)
78
- level == HARD_LEVEL ? AI_PLAYER : COMPUTER_PLAYER
79
- end
80
-
81
- def get_board
82
- rows = ui.request_board_size
83
- valid_board_size?(rows) ? ui.board_assigned_message(rows) : invalid_board_size(rows)
84
- CLIBoard.new(rows.to_i)
85
- end
86
-
87
- def valid_board_size?(input)
88
- rows = input.to_i
89
- rows.is_a?(Integer) && (rows > 2 && rows < 6)
90
- end
91
-
92
- def invalid_board_size(rows)
93
- ui.invalid_input_message(rows)
94
- get_board
95
- end
96
-
97
- end
98
-
99
- class WebGameSetup < GameSetup
100
-
101
- def set_up_players(player_one_type, player_two_type)
102
- PlayerFactory.new(player_one_type, player_two_type)
103
- end
104
-
105
- def get_first_move_player(player_one_type, player_two_type)
106
- players = PlayerFactory.new(player_one_type, player_two_type)
107
- players.player_goes_first
108
- end
109
-
110
- def get_board(board_size, current_board)
111
- board = WebBoard.new(board_size)
112
- board.all_cells = current_board
113
- board
12
+ def get_settings
13
+ {}
14
+ end
114
15
  end
115
16
 
116
17
  end
data/lib/player.rb CHANGED
@@ -1,73 +1,36 @@
1
- class Player
2
- attr_accessor :marker, :opponent
3
- def initialize(marker)
4
- @marker = marker
5
- @opponent = nil
6
- end
7
-
8
- def add_marker(board, cell)
9
- board.all_cells[cell] = self.marker
10
- end
11
-
12
- def get_alpha(alpha, score)
13
- alpha
14
- end
15
-
16
- def get_beta(beta, score)
17
- beta
18
- end
19
- end
20
-
21
- class AIPlayer < Player
22
-
23
- def make_move(board)
24
- ai = AI.new(self)
25
- cell = ai.computer_move(board, self)
26
- add_marker(board, cell)
27
- end
28
-
29
- end
1
+ module RubyTictactoe
30
2
 
31
- class ComputerPlayer < Player
3
+ class Player
4
+ attr_accessor :marker, :opponent
5
+ def initialize(marker)
6
+ @marker = marker
7
+ @opponent = nil
8
+ end
32
9
 
33
- def make_move(board)
34
- cell = board.random_cell
35
- add_marker(board, cell)
10
+ def add_marker(board, cell)
11
+ board.all_cells[cell] = self.marker
12
+ end
36
13
  end
37
14
 
38
- end
15
+ class AIPlayer < Player
39
16
 
40
- class HumanPlayer < Player; end
17
+ def make_move(board)
18
+ ai = AI.new(self)
19
+ cell = ai.computer_move(board, self)
20
+ add_marker(board, cell)
21
+ end
41
22
 
42
- class MinimizingPlayer < Player
43
- attr_accessor :marker, :opponent
44
- def initialize(player)
45
- @marker = player.marker
46
- @opponent = player.opponent
47
23
  end
48
24
 
49
- def get_alpha(alpha, score)
50
- score > alpha ? score : alpha
51
- end
25
+ class ComputerPlayer < Player
52
26
 
53
- def return_best_score(alpha, beta)
54
- alpha
55
- end
56
- end
27
+ def make_move(board)
28
+ cell = board.random_cell
29
+ add_marker(board, cell)
30
+ end
57
31
 
58
- class MaximizingPlayer < Player
59
- attr_accessor :marker, :opponent
60
- def initialize(player)
61
- @marker = player.marker
62
- @opponent = player.opponent
63
32
  end
64
33
 
65
- def get_beta(beta, score)
66
- score < beta ? score : beta
67
- end
34
+ class HumanPlayer < Player; end
68
35
 
69
- def return_best_score(alpha, beta)
70
- beta
71
- end
72
36
  end
73
-
@@ -1,31 +1,37 @@
1
- class PlayerFactory
2
- attr_accessor :player_one, :player_two
3
- def initialize(type_one, type_two)
4
- @player_one = create_player(type_one, MARKER_X)
5
- @player_two = create_player(type_two, MARKER_O)
6
- set_opponents
7
- end
1
+ require 'tictactoe_constants'
2
+
3
+ module RubyTictactoe
8
4
 
9
- def create_player(type, marker)
10
- case type
11
- when COMPUTER_PLAYER
12
- ComputerPlayer.new(marker)
13
- when HUMAN_PLAYER
14
- HumanPlayer.new(marker)
15
- when AI_PLAYER
16
- AIPlayer.new(marker)
17
- else
18
- raise "Invalid player type"
5
+ class PlayerFactory
6
+ include TictactoeConstants
7
+ attr_accessor :player_one, :player_two
8
+ def initialize(type_one, type_two)
9
+ @player_one = create_player(type_one, MARKER_X)
10
+ @player_two = create_player(type_two, MARKER_O)
11
+ set_opponents
19
12
  end
20
- end
21
13
 
22
- def player_goes_first
23
- rand(0..1) == 1 ? player_one : player_two
24
- end
14
+ def create_player(type, marker)
15
+ case type
16
+ when COMPUTER_PLAYER
17
+ ComputerPlayer.new(marker)
18
+ when HUMAN_PLAYER
19
+ HumanPlayer.new(marker)
20
+ when AI_PLAYER
21
+ AIPlayer.new(marker)
22
+ else
23
+ raise "Invalid player type"
24
+ end
25
+ end
26
+
27
+ def player_goes_first
28
+ rand(0..1) == 1 ? player_one : player_two
29
+ end
25
30
 
26
- def set_opponents
27
- player_one.opponent = player_two
28
- player_two.opponent = player_one
31
+ def set_opponents
32
+ player_one.opponent = player_two
33
+ player_two.opponent = player_one
34
+ end
29
35
  end
30
36
 
31
37
  end
@@ -0,0 +1,11 @@
1
+ module RubyTictactoe
2
+ module TictactoeConstants
3
+ MARKER_X = 'X'
4
+ MARKER_O = 'O'
5
+ COMPUTER_PLAYER = 'computer'
6
+ HUMAN_PLAYER = 'human'
7
+ AI_PLAYER = 'hard computer'
8
+ HARD_LEVEL = 'hard'
9
+ EASY_LEVEL = 'easy'
10
+ end
11
+ end
data/lib/ui.rb CHANGED
@@ -1,112 +1,27 @@
1
- class UI
2
- attr_accessor :io
3
- def initialize
4
- @io = Kernel
5
- end
6
-
7
- def first_move_message(marker)
8
- "Player '#{marker}' goes first."
9
- end
10
-
11
- def next_move_message(marker)
12
- "Player '#{marker}': Make your move."
13
- end
14
-
15
- def winning_game_message(marker)
16
- "GAME OVER! Player '#{marker}' wins!"
17
- end
18
-
19
- def tie_game_message
20
- "GAME OVER! It's a tie!"
21
- end
22
-
23
- end
24
-
25
- class CLIUI < UI
26
-
27
- def request_player_type(marker)
28
- player_type_message(marker)
29
- io.gets.chomp.downcase
30
- end
31
-
32
- def request_difficulty_level
33
- difficulty_level_message
34
- io.gets.chomp.downcase
35
- end
36
-
37
- def request_board_size
38
- board_size_message
39
- io.gets.chomp.downcase
40
- end
41
-
42
- def request_human_move
43
- standardize(io.gets.chomp)
44
- end
45
-
46
- def standardize(input)
47
- input.split('').sort.join('').upcase
48
- end
1
+ module RubyTictactoe
49
2
 
50
- def difficulty_level_message
51
- io.print "Select computer difficulty level: Enter 'easy' or 'hard.'\n"
52
- end
53
-
54
- def board_size_message
55
- io.print "Enter the number of rows you want your board to have (3-5).\n"
56
- end
3
+ class UI
4
+ attr_accessor :io
5
+ def initialize
6
+ @io = Kernel
7
+ end
57
8
 
58
- def level_assigned_message(level)
59
- io.print "You selected difficulty level #{level.upcase}.\n"
60
- end
9
+ def first_move_message(marker)
10
+ "Player '#{marker}' goes first."
11
+ end
61
12
 
62
- def board_assigned_message(rows)
63
- io.print "You selected a board with #{rows} rows.\n"
64
- end
13
+ def next_move_message(marker)
14
+ "Player '#{marker}': Make your move."
15
+ end
65
16
 
66
- def invalid_input_message(input)
67
- io.print "#{input} is not a valid option.\n"
68
- end
17
+ def winning_game_message(marker)
18
+ "GAME OVER! Player '#{marker}' wins!"
19
+ end
69
20
 
70
- def player_type_message(marker)
71
- io.print "For player " + "'#{marker}'," + " enter 'human' or 'computer.'\n"
72
- end
73
-
74
- def type_assigned_message(type, marker)
75
- io.print "Player " + "'#{marker}' " + "is #{type}.\n"
76
- end
77
-
78
- def first_move_message(marker)
79
- output = super
80
- io.print "\n\n************ New Game ************\n"
81
- io.print output + "\n"
82
- end
83
-
84
- def next_move_message(marker)
85
- io.print "Player '#{marker}': Enter open cell ID.\n"
86
- end
87
-
88
- def winning_game_message(marker)
89
- output = super
90
- io.print output + "\n"
91
- end
92
-
93
- def tie_game_message
94
- output = super
95
- io.print output + "\n"
96
- end
97
-
98
- def taken_cell_message(cell)
99
- io.print "#{cell} has already been taken!\n"
100
- end
101
-
102
- def bad_cell_message(cell)
103
- io.print "#{cell} is not a valid cell ID!\n"
104
- end
21
+ def tie_game_message
22
+ "GAME OVER! It's a tie!"
23
+ end
105
24
 
106
- def early_exit_message
107
- io.print "\nExiting Tic-Tac-Toe..."
108
- io.print "...\n"
109
- io.print "Goodbye!\n\n"
110
25
  end
111
26
 
112
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_ttt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-26 00:00:00.000000000 Z
12
+ date: 2013-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -36,11 +36,13 @@ extensions: []
36
36
  extra_rdoc_files: []
37
37
  files:
38
38
  - lib/ai.rb
39
+ - lib/alpha_beta_player.rb
39
40
  - lib/board.rb
41
+ - lib/game.rb
40
42
  - lib/game_setup.rb
41
43
  - lib/player.rb
42
44
  - lib/player_factory.rb
43
- - lib/ruby_ttt.rb
45
+ - lib/tictactoe_constants.rb
44
46
  - lib/ui.rb
45
47
  homepage: http://rubygems.org/gems/ruby_ttt
46
48
  licenses: []
data/lib/ruby_ttt.rb DELETED
@@ -1,125 +0,0 @@
1
- require 'ai'
2
- require 'board'
3
- require 'player_factory'
4
- require 'game_setup'
5
- require 'player'
6
- require 'ui'
7
-
8
- class Game
9
- attr_accessor :board, :player_one, :player_two, :ui, :player_first_move
10
- def initialize(settings)
11
- @board = settings[:board]
12
- @player_one = settings[:player_one]
13
- @player_two = settings[:player_two]
14
- @player_first_move = settings[:player_first_move]
15
- @ui = UI.new
16
- end
17
-
18
- def advance_game
19
- game_status_check(current_player.opponent.marker)
20
- ui.next_move_message(current_player.marker) unless board.game_over?
21
- end
22
-
23
- def game_status_check(marker)
24
- if board.winner?(marker)
25
- ui.winning_game_message(marker)
26
- elsif !board.moves_remaining?
27
- ui.tie_game_message
28
- end
29
- end
30
-
31
- def verify_move(cell)
32
- return false if !board.available_cell?(cell)
33
- current_player.add_marker(board, cell)
34
- true
35
- end
36
-
37
- def current_player
38
- if total_markers(MARKER_X) > total_markers(MARKER_O)
39
- player_two
40
- elsif total_markers(MARKER_O) > total_markers(MARKER_X)
41
- player_one
42
- else
43
- player_first_move
44
- end
45
- end
46
-
47
- def total_markers(marker)
48
- board.all_cells.select { |cell, value| value == marker }.count
49
- end
50
-
51
- end
52
-
53
- class CLIGame < Game
54
- attr_accessor :board, :player_one, :player_two, :ui, :player_first_move
55
- def initialize(settings)
56
- super
57
- @ui = CLIUI.new
58
- end
59
-
60
- def start_game!
61
- begin
62
- play!
63
- rescue Interrupt
64
- ui.early_exit_message
65
- exit
66
- end
67
- end
68
-
69
- def play!
70
- ui.first_move_message(current_player.marker)
71
- until board.game_over?
72
- board.display_board
73
- get_next_move
74
- end
75
- exit_game
76
- end
77
-
78
- def get_next_move
79
- if current_player.is_a?(HumanPlayer)
80
- move = ui.request_human_move
81
- verify_move(move) ? advance_game : invalid_move(move)
82
- else
83
- current_player.make_move(board)
84
- advance_game
85
- end
86
- end
87
-
88
- def invalid_move(cell)
89
- board.valid_cell?(cell) ? ui.taken_cell_message(cell) : ui.bad_cell_message(cell)
90
- end
91
-
92
- def exit_game
93
- board.display_board
94
- ui.io.exit
95
- end
96
-
97
- end
98
-
99
- class WebGame < Game
100
- attr_accessor :board, :player_one, :player_two, :ui, :player_first_move
101
- def initialize(settings)
102
- super
103
- @ui = UI.new
104
- end
105
-
106
- def get_message(player)
107
- return ui.first_move_message(player.marker) if board.empty?
108
- if board.winner?(current_player.opponent.marker)
109
- ui.winning_game_message(current_player.opponent.marker)
110
- elsif !board.moves_remaining?
111
- ui.tie_game_message
112
- else
113
- ui.next_move_message(current_player.marker)
114
- end
115
- end
116
-
117
- def get_first_move_player(type_one, type_two)
118
- PlayerFactory.new(type_one, type_two).player_goes_first
119
- end
120
-
121
- def computer_player?(type_one, type_two)
122
- ((type_one.downcase) == COMPUTER_PLAYER) || ((type_two.downcase) == COMPUTER_PLAYER)
123
- end
124
-
125
- end