ruby_ttt 0.3.1 → 0.3.2

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.
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