ruby_ttt 0.1.5 → 0.1.9
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 +11 -7
- data/lib/board.rb +87 -13
- data/lib/game_setup.rb +111 -0
- data/lib/player.rb +36 -24
- data/lib/player_factory.rb +31 -0
- data/lib/ruby_ttt.rb +48 -114
- data/lib/ui.rb +26 -82
- metadata +4 -2
data/lib/ai.rb
CHANGED
@@ -5,6 +5,10 @@ LOSE = -1
|
|
5
5
|
TIE = 0
|
6
6
|
|
7
7
|
class AI
|
8
|
+
attr_reader :current_player
|
9
|
+
def initialize(player)
|
10
|
+
@current_player = player
|
11
|
+
end
|
8
12
|
|
9
13
|
def computer_move(board, player)
|
10
14
|
test_board = board.dup
|
@@ -28,39 +32,39 @@ class AI
|
|
28
32
|
end
|
29
33
|
|
30
34
|
def get_move_score(board, player, cell)
|
31
|
-
|
35
|
+
player.add_marker(board, cell)
|
32
36
|
best_score = apply_minimax(board, player, cell, depth=0, NEG_INF, POS_INF)
|
33
37
|
board.remove_marker(cell)
|
34
38
|
best_score
|
35
39
|
end
|
36
40
|
|
37
41
|
def get_score(board, player)
|
38
|
-
return WIN if board.winner?(player.marker) && player
|
42
|
+
return WIN if board.winner?(player.marker) && (player == current_player)
|
39
43
|
return LOSE if board.winner?(player.marker)
|
40
44
|
TIE
|
41
45
|
end
|
42
46
|
|
43
47
|
def apply_minimax(board, player, cell, depth, alpha, beta)
|
44
48
|
return get_score(board, player) if board.game_over?
|
45
|
-
if player
|
46
|
-
maximizing_player =
|
49
|
+
if (player == current_player)
|
50
|
+
maximizing_player = MaximizingPlayer.new(player)
|
47
51
|
alphabeta(board, maximizing_player, depth, alpha, beta)
|
48
52
|
else
|
49
|
-
minimizing_player =
|
53
|
+
minimizing_player = MinimizingPlayer.new(player)
|
50
54
|
alphabeta(board, minimizing_player, depth, alpha, beta)
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
58
|
def alphabeta(board, player, depth, alpha, beta)
|
55
59
|
board.open_cells.each_key do |cell|
|
56
|
-
|
60
|
+
player.opponent.add_marker(board, cell)
|
57
61
|
score = (apply_minimax(board, player.opponent, cell, depth += 1, alpha, beta) / depth.to_f)
|
58
62
|
alpha = player.get_alpha(alpha, score)
|
59
63
|
beta = player.get_beta(beta, score)
|
60
64
|
board.remove_marker(cell)
|
61
65
|
break if alpha >= beta
|
62
66
|
end
|
63
|
-
player.
|
67
|
+
player.return_best_score(alpha, beta)
|
64
68
|
end
|
65
69
|
|
66
70
|
end
|
data/lib/board.rb
CHANGED
@@ -46,22 +46,10 @@ class Board
|
|
46
46
|
rows
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def add_test_marker(marker, cell)
|
50
50
|
all_cells[cell] = marker
|
51
51
|
end
|
52
52
|
|
53
|
-
def winner?(marker)
|
54
|
-
board_markers = all_cells.select { |k,v| v == marker }.keys
|
55
|
-
winning_lines.each do |line|
|
56
|
-
return true if (line & board_markers).length == num_of_rows
|
57
|
-
end
|
58
|
-
false
|
59
|
-
end
|
60
|
-
|
61
|
-
def game_over?
|
62
|
-
!moves_remaining? || winner?(MARKER_X)|| winner?(MARKER_O)
|
63
|
-
end
|
64
|
-
|
65
53
|
def available_cell?(cell)
|
66
54
|
valid_cell?(cell) && all_cells[cell].nil?
|
67
55
|
end
|
@@ -78,6 +66,18 @@ class Board
|
|
78
66
|
all_cells.has_value?(nil)
|
79
67
|
end
|
80
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
|
73
|
+
end
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def game_over?
|
78
|
+
!moves_remaining? || winner?(MARKER_X) || winner?(MARKER_O)
|
79
|
+
end
|
80
|
+
|
81
81
|
def open_cells
|
82
82
|
all_cells.select { |k,v| v.nil? }
|
83
83
|
end
|
@@ -143,4 +143,78 @@ class Board
|
|
143
143
|
diagonal
|
144
144
|
end
|
145
145
|
|
146
|
+
end
|
147
|
+
|
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
|
154
|
+
|
155
|
+
def print_board_numbers
|
156
|
+
num = 1
|
157
|
+
io.print " "
|
158
|
+
num_of_rows.times do
|
159
|
+
io.print "--#{num}-- "
|
160
|
+
num += 1
|
161
|
+
end
|
162
|
+
io.print "\n"
|
163
|
+
end
|
164
|
+
|
165
|
+
def print_divider
|
166
|
+
io.print " "
|
167
|
+
num_of_rows.times { io.print "------" }
|
168
|
+
io.print "\n"
|
169
|
+
end
|
170
|
+
|
171
|
+
def print_board_rows
|
172
|
+
alpha = 'A'
|
173
|
+
all_rows.each do |row|
|
174
|
+
show_row(alpha, row)
|
175
|
+
alpha = alpha.next
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
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
|
185
|
+
|
186
|
+
def show_marker(cell)
|
187
|
+
all_cells[cell].nil? ? ' ' : all_cells[cell]
|
188
|
+
end
|
189
|
+
|
190
|
+
def display_board
|
191
|
+
print_board_numbers
|
192
|
+
print_board_rows
|
193
|
+
print_board_numbers
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
class WebBoard < Board
|
199
|
+
|
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>"
|
206
|
+
end
|
207
|
+
board_string
|
208
|
+
end
|
209
|
+
|
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>"
|
216
|
+
end
|
217
|
+
board_string
|
218
|
+
end
|
219
|
+
|
146
220
|
end
|
data/lib/game_setup.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
COMPUTER_PLAYER = 'computer'
|
2
|
+
HUMAN_PLAYER = 'human'
|
3
|
+
AI_PLAYER = 'hard computer'
|
4
|
+
HARD_LEVEL = 'hard'
|
5
|
+
EASY_LEVEL = 'easy'
|
6
|
+
|
7
|
+
class GameSetup; end
|
8
|
+
|
9
|
+
class CLIGameSetup < GameSetup
|
10
|
+
attr_accessor :ui
|
11
|
+
def initialize
|
12
|
+
@ui = CLIUI.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_settings
|
16
|
+
settings = {}
|
17
|
+
begin
|
18
|
+
players = set_up_players
|
19
|
+
settings[:board] = get_board
|
20
|
+
settings[:player_one] = players.player_one
|
21
|
+
settings[:player_two] = players.player_two
|
22
|
+
settings[:player_first_move] = players.player_goes_first
|
23
|
+
settings
|
24
|
+
rescue Interrupt
|
25
|
+
ui.early_exit_message
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_up_players
|
31
|
+
player_one_type = get_player_type(MARKER_X)
|
32
|
+
player_two_type = get_player_type(MARKER_O)
|
33
|
+
PlayerFactory.new(player_one_type, player_two_type)
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_player_type(marker)
|
37
|
+
type = ui.request_player_type(marker)
|
38
|
+
validated_type = valid_type?(type) ? get_difficulty_level(type) : invalid_type(type, marker)
|
39
|
+
ui.type_assigned_message(validated_type, marker)
|
40
|
+
validated_type
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_difficulty_level(type)
|
44
|
+
return type if type == HUMAN_PLAYER
|
45
|
+
level = ui.request_difficulty_level
|
46
|
+
valid_level?(level) ? ui.level_assigned_message(level) : invalid_level(level)
|
47
|
+
player_type_by_level(level)
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_type?(type)
|
51
|
+
(type == HUMAN_PLAYER) || (type == COMPUTER_PLAYER)
|
52
|
+
end
|
53
|
+
|
54
|
+
def invalid_type(type, marker)
|
55
|
+
ui.invalid_input_message(type)
|
56
|
+
get_player_type(marker)
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_level?(level)
|
60
|
+
(level == HARD_LEVEL) || (level == EASY_LEVEL)
|
61
|
+
end
|
62
|
+
|
63
|
+
def invalid_level(level)
|
64
|
+
ui.invalid_input_message(level)
|
65
|
+
get_difficulty_level(COMPUTER_PLAYER)
|
66
|
+
end
|
67
|
+
|
68
|
+
def player_type_by_level(level)
|
69
|
+
level == HARD_LEVEL ? AI_PLAYER : COMPUTER_PLAYER
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_board
|
73
|
+
rows = ui.request_board_size
|
74
|
+
valid_board_size?(rows) ? ui.board_assigned_message(rows) : invalid_board_size(rows)
|
75
|
+
CLIBoard.new(rows.to_i)
|
76
|
+
end
|
77
|
+
|
78
|
+
def valid_board_size?(input)
|
79
|
+
rows = input.to_i
|
80
|
+
rows.is_a?(Integer) && (rows > 2 && rows < 6)
|
81
|
+
end
|
82
|
+
|
83
|
+
def invalid_board_size(rows)
|
84
|
+
ui.invalid_input_message(rows)
|
85
|
+
get_board
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class WebGameSetup < GameSetup
|
91
|
+
|
92
|
+
# def set_up_players(player_one_type, player_two_type)
|
93
|
+
# PlayerFactory.new(player_one_type, player_two_type)
|
94
|
+
# end
|
95
|
+
|
96
|
+
# def get_board
|
97
|
+
# board = Board.new(session[:board_size])
|
98
|
+
# board.all_cells = session[:current_board]
|
99
|
+
# end
|
100
|
+
|
101
|
+
# def get_settings
|
102
|
+
# settings = {}
|
103
|
+
# players = set_up_players
|
104
|
+
# settings[:board] = get_board
|
105
|
+
# settings[:player_one] = players.player_one
|
106
|
+
# settings[:player_two] = players.player_two
|
107
|
+
# settings[:player_goes_first] = players.player_goes_first
|
108
|
+
# settings
|
109
|
+
# end
|
110
|
+
|
111
|
+
end
|
data/lib/player.rb
CHANGED
@@ -1,19 +1,12 @@
|
|
1
1
|
class Player
|
2
|
-
attr_accessor :marker, :
|
2
|
+
attr_accessor :marker, :opponent
|
3
3
|
def initialize(marker)
|
4
|
-
@marker
|
5
|
-
@
|
6
|
-
@turn = 0
|
7
|
-
@opponent = nil
|
4
|
+
@marker = marker
|
5
|
+
@opponent = nil
|
8
6
|
end
|
9
7
|
|
10
|
-
def
|
11
|
-
|
12
|
-
self.opponent.turn = 1
|
13
|
-
end
|
14
|
-
|
15
|
-
def current_player?
|
16
|
-
self.turn == 1
|
8
|
+
def add_marker(board, cell)
|
9
|
+
board.all_cells[cell] = self.marker
|
17
10
|
end
|
18
11
|
|
19
12
|
def get_alpha(alpha, score)
|
@@ -25,36 +18,55 @@ class Player
|
|
25
18
|
end
|
26
19
|
end
|
27
20
|
|
28
|
-
class
|
29
|
-
|
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
|
30
|
+
|
31
|
+
class ComputerPlayer < Player
|
32
|
+
|
33
|
+
def make_move(board)
|
34
|
+
cell = board.random_cell
|
35
|
+
add_marker(board, cell)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class HumanPlayer < Player; end
|
41
|
+
|
42
|
+
class MinimizingPlayer < Player
|
43
|
+
attr_accessor :marker, :opponent
|
30
44
|
def initialize(player)
|
31
|
-
@marker
|
32
|
-
@
|
33
|
-
@opponent = player.opponent
|
45
|
+
@marker = player.marker
|
46
|
+
@opponent = player.opponent
|
34
47
|
end
|
35
48
|
|
36
49
|
def get_alpha(alpha, score)
|
37
50
|
score > alpha ? score : alpha
|
38
51
|
end
|
39
52
|
|
40
|
-
def
|
53
|
+
def return_best_score(alpha, beta)
|
41
54
|
alpha
|
42
55
|
end
|
43
56
|
end
|
44
57
|
|
45
|
-
class
|
46
|
-
attr_accessor :marker, :
|
58
|
+
class MaximizingPlayer < Player
|
59
|
+
attr_accessor :marker, :opponent
|
47
60
|
def initialize(player)
|
48
|
-
@marker
|
49
|
-
@
|
50
|
-
@opponent = player.opponent
|
61
|
+
@marker = player.marker
|
62
|
+
@opponent = player.opponent
|
51
63
|
end
|
52
64
|
|
53
65
|
def get_beta(beta, score)
|
54
66
|
score < beta ? score : beta
|
55
67
|
end
|
56
68
|
|
57
|
-
def
|
69
|
+
def return_best_score(alpha, beta)
|
58
70
|
beta
|
59
71
|
end
|
60
72
|
end
|
@@ -0,0 +1,31 @@
|
|
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
|
8
|
+
|
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"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def player_goes_first
|
23
|
+
rand(0..1) == 1 ? player_one : player_two
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_opponents
|
27
|
+
player_one.opponent = player_two
|
28
|
+
player_two.opponent = player_one
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/ruby_ttt.rb
CHANGED
@@ -1,92 +1,64 @@
|
|
1
1
|
require 'ai'
|
2
2
|
require 'board'
|
3
|
+
require 'player_factory'
|
4
|
+
require 'game_setup'
|
3
5
|
require 'player'
|
4
6
|
require 'ui'
|
5
7
|
|
6
|
-
EASY_LEVEL = 'easy'
|
7
|
-
HARD_LEVEL = 'hard'
|
8
|
-
COMPUTER_PLAYER = 'computer'
|
9
|
-
HUMAN_PLAYER = 'human'
|
10
|
-
|
11
8
|
class Game
|
12
|
-
attr_accessor :board, :player_one, :player_two, :
|
13
|
-
def initialize(
|
14
|
-
@board
|
15
|
-
@player_one = player_one
|
16
|
-
@player_two = player_two
|
17
|
-
@
|
18
|
-
@
|
19
|
-
end
|
20
|
-
|
21
|
-
def set_opponents
|
22
|
-
player_one.opponent = player_two
|
23
|
-
player_two.opponent = player_one
|
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
|
24
16
|
end
|
25
17
|
|
26
|
-
def
|
27
|
-
(
|
18
|
+
def advance_game
|
19
|
+
game_status_check(current_player.opponent.marker)
|
20
|
+
ui.next_move_message(current_player.marker) unless board.game_over?
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
|
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
|
32
29
|
end
|
33
30
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
31
|
+
def verify_move(cell)
|
32
|
+
return false if !board.available_cell?(cell)
|
33
|
+
current_player.add_marker(board, cell)
|
34
|
+
true
|
37
35
|
end
|
38
36
|
|
39
|
-
def
|
40
|
-
if
|
41
|
-
|
42
|
-
|
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
|
43
42
|
else
|
44
|
-
|
43
|
+
player_first_move
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
48
|
-
def
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
def computer_move?
|
53
|
-
current_player.player_type == COMPUTER_PLAYER
|
47
|
+
def total_markers(marker)
|
48
|
+
board.all_cells.select { |cell, value| value == marker }.count
|
54
49
|
end
|
55
50
|
|
56
|
-
def advance_game
|
57
|
-
game_status_check
|
58
|
-
current_player.next_player_turn
|
59
|
-
ui.next_move_message(current_player) unless board.game_over?
|
60
|
-
end
|
61
|
-
|
62
|
-
def game_status_check
|
63
|
-
if board.winner?(current_player.marker)
|
64
|
-
ui.winning_game_message(current_player)
|
65
|
-
elsif !board.moves_remaining?
|
66
|
-
ui.tie_game_message
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def current_player
|
71
|
-
player_one.current_player? ? player_one : player_two
|
72
|
-
end
|
73
51
|
end
|
74
52
|
|
75
53
|
class CLIGame < Game
|
76
|
-
attr_accessor :ui
|
77
|
-
|
78
|
-
def initialize(board, player_one, player_two)
|
54
|
+
attr_accessor :board, :player_one, :player_two, :ui, :player_first_move
|
55
|
+
def initialize(settings)
|
79
56
|
super
|
80
|
-
@ui = CLIUI.new
|
57
|
+
@ui = CLIUI.new
|
81
58
|
end
|
82
59
|
|
83
|
-
def
|
60
|
+
def start_game!
|
84
61
|
begin
|
85
|
-
set_opponents
|
86
|
-
get_player_type(player_one)
|
87
|
-
get_player_type(player_two)
|
88
|
-
level = get_difficulty_level
|
89
|
-
who_goes_first
|
90
62
|
play!
|
91
63
|
rescue Interrupt
|
92
64
|
ui.early_exit_message
|
@@ -94,71 +66,33 @@ class CLIGame < Game
|
|
94
66
|
end
|
95
67
|
end
|
96
68
|
|
97
|
-
def get_player_type(player)
|
98
|
-
type = ui.request_player_type(player.marker)
|
99
|
-
validate_type(type, player) ? set_player_type(type, player) : invalid_type(type, player)
|
100
|
-
end
|
101
|
-
|
102
|
-
def get_difficulty_level
|
103
|
-
return nil unless computer_player_selected?
|
104
|
-
level = ui.request_difficulty_level
|
105
|
-
validate_level(level) ? ui.level_assigned_message(level) : invalid_level(level)
|
106
|
-
end
|
107
|
-
|
108
|
-
def validate_type(type, player)
|
109
|
-
(type == HUMAN_PLAYER) || (type == COMPUTER_PLAYER)
|
110
|
-
end
|
111
|
-
|
112
|
-
def set_player_type(type, player)
|
113
|
-
player.player_type = type
|
114
|
-
ui.type_assigned_message(type, player.marker)
|
115
|
-
end
|
116
|
-
|
117
|
-
def invalid_type(type, player)
|
118
|
-
ui.invalid_input_message(type)
|
119
|
-
get_player_type(player)
|
120
|
-
end
|
121
|
-
|
122
|
-
def validate_level(level)
|
123
|
-
(level == HARD_LEVEL) || (level == EASY_LEVEL)
|
124
|
-
end
|
125
|
-
|
126
|
-
def invalid_level(level)
|
127
|
-
ui.invalid_input_message(level)
|
128
|
-
get_difficulty_level
|
129
|
-
end
|
130
|
-
|
131
69
|
def play!
|
132
70
|
until board.game_over?
|
133
|
-
|
134
|
-
|
135
|
-
advance_game if verify_move(move)
|
71
|
+
board.display_board
|
72
|
+
get_next_move
|
136
73
|
end
|
137
74
|
exit_game
|
138
75
|
end
|
139
76
|
|
77
|
+
def get_next_move
|
78
|
+
if current_player.is_a?(HumanPlayer)
|
79
|
+
move = ui.request_human_move
|
80
|
+
verify_move(move) ? advance_game : invalid_move(move)
|
81
|
+
else
|
82
|
+
current_player.make_move(board)
|
83
|
+
advance_game
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
140
87
|
def invalid_move(cell)
|
141
88
|
board.valid_cell?(cell) ? ui.taken_cell_message(cell) : ui.bad_cell_message(cell)
|
142
89
|
end
|
143
90
|
|
144
91
|
def exit_game
|
145
|
-
|
92
|
+
board.display_board
|
146
93
|
ui.io.exit
|
147
94
|
end
|
148
95
|
|
149
96
|
end
|
150
97
|
|
151
|
-
class WebGame < Game
|
152
|
-
attr_accessor :ui
|
153
|
-
|
154
|
-
def initialize(board, player_one, player_two)
|
155
|
-
super
|
156
|
-
@ui = WebUI.new(board)
|
157
|
-
end
|
158
|
-
|
159
|
-
def set_player_types(player_one_type, player_two_type)
|
160
|
-
player_one.player_type = player_one_type
|
161
|
-
player_two.player_type = player_two_type
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
98
|
+
class WebGame < Game; end
|
data/lib/ui.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
class UI
|
2
|
-
|
3
|
-
def initialize(board)
|
4
|
-
@board = board
|
5
|
-
end
|
2
|
+
def initialize; end
|
6
3
|
|
7
|
-
def first_move_message(
|
8
|
-
"Player '#{
|
4
|
+
def first_move_message(marker)
|
5
|
+
"Player '#{marker}' goes first."
|
9
6
|
end
|
10
7
|
|
11
|
-
def next_move_message(
|
12
|
-
"Player '#{
|
8
|
+
def next_move_message(marker)
|
9
|
+
"Player '#{marker}': Make your move."
|
13
10
|
end
|
14
11
|
|
15
|
-
def winning_game_message(
|
16
|
-
"GAME OVER! Player '#{
|
12
|
+
def winning_game_message(marker)
|
13
|
+
"GAME OVER! Player '#{marker}' wins!"
|
17
14
|
end
|
18
15
|
|
19
16
|
def tie_game_message
|
@@ -24,8 +21,7 @@ end
|
|
24
21
|
|
25
22
|
class CLIUI < UI
|
26
23
|
attr_accessor :io
|
27
|
-
def initialize
|
28
|
-
super
|
24
|
+
def initialize
|
29
25
|
@io = Kernel
|
30
26
|
end
|
31
27
|
|
@@ -39,6 +35,11 @@ class CLIUI < UI
|
|
39
35
|
io.gets.chomp.downcase
|
40
36
|
end
|
41
37
|
|
38
|
+
def request_board_size
|
39
|
+
board_size_message
|
40
|
+
io.gets.chomp.downcase
|
41
|
+
end
|
42
|
+
|
42
43
|
def request_human_move
|
43
44
|
standardize(io.gets.chomp)
|
44
45
|
end
|
@@ -51,12 +52,20 @@ class CLIUI < UI
|
|
51
52
|
io.print "Select computer difficulty level: Enter 'easy' or 'hard.'\n"
|
52
53
|
end
|
53
54
|
|
55
|
+
def board_size_message
|
56
|
+
io.print "Enter the number of rows you want your board to have (3-5).\n"
|
57
|
+
end
|
58
|
+
|
54
59
|
def level_assigned_message(level)
|
55
60
|
io.print "You selected difficulty level #{level.upcase}.\n"
|
56
61
|
end
|
57
62
|
|
63
|
+
def board_assigned_message(rows)
|
64
|
+
io.print "You selected a board with #{rows} rows.\n"
|
65
|
+
end
|
66
|
+
|
58
67
|
def invalid_input_message(input)
|
59
|
-
io.print "
|
68
|
+
io.print "#{input} is not a valid option.\n"
|
60
69
|
end
|
61
70
|
|
62
71
|
def player_type_message(marker)
|
@@ -67,58 +76,17 @@ class CLIUI < UI
|
|
67
76
|
io.print "Player " + "'#{marker}' " + "is #{type}.\n"
|
68
77
|
end
|
69
78
|
|
70
|
-
def
|
71
|
-
num = 1
|
72
|
-
io.print " "
|
73
|
-
board.num_of_rows.times do
|
74
|
-
io.print "--#{num}-- "
|
75
|
-
num += 1
|
76
|
-
end
|
77
|
-
io.print "\n"
|
78
|
-
end
|
79
|
-
|
80
|
-
def print_divider
|
81
|
-
io.print " "
|
82
|
-
board.num_of_rows.times { io.print "------" }
|
83
|
-
io.print "\n"
|
84
|
-
end
|
85
|
-
|
86
|
-
def print_board_rows
|
87
|
-
alpha = 'A'
|
88
|
-
board.all_rows.each do |row|
|
89
|
-
show_row(alpha, row)
|
90
|
-
alpha = alpha.next
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def show_row(letter, cells)
|
95
|
-
io.print "#{letter}"
|
96
|
-
cells.each { |cell| io.print " | " + show_marker(cell) }
|
97
|
-
io.print " | #{letter}\n"
|
98
|
-
print_divider
|
99
|
-
end
|
100
|
-
|
101
|
-
def show_marker(cell)
|
102
|
-
board.all_cells[cell].nil? ? ' ' : board.all_cells[cell]
|
103
|
-
end
|
104
|
-
|
105
|
-
def display_board
|
106
|
-
print_board_numbers
|
107
|
-
print_board_rows
|
108
|
-
print_board_numbers
|
109
|
-
end
|
110
|
-
|
111
|
-
def first_move_message(player)
|
79
|
+
def first_move_message(marker)
|
112
80
|
output = super
|
113
81
|
io.print "\n\n************ New Game ************\n"
|
114
82
|
io.print output + "\n"
|
115
83
|
end
|
116
84
|
|
117
|
-
def next_move_message(
|
118
|
-
io.print "Player '#{
|
85
|
+
def next_move_message(marker)
|
86
|
+
io.print "Player '#{marker}': Enter open cell ID.\n"
|
119
87
|
end
|
120
88
|
|
121
|
-
def winning_game_message(
|
89
|
+
def winning_game_message(marker)
|
122
90
|
output = super
|
123
91
|
io.print output + "\n"
|
124
92
|
end
|
@@ -142,28 +110,4 @@ class CLIUI < UI
|
|
142
110
|
io.print "Goodbye!\n\n"
|
143
111
|
end
|
144
112
|
|
145
|
-
end
|
146
|
-
|
147
|
-
class WebUI < UI
|
148
|
-
|
149
|
-
def print_active_board
|
150
|
-
board_string = ''
|
151
|
-
board.all_rows.each do |row|
|
152
|
-
board_string += "<div class='row'>"
|
153
|
-
row.each { |cell| board_string += "<button name='move' value='#{cell}'> #{board.all_cells[cell]} <span class='cell'>.</span></button>" }
|
154
|
-
board_string += "</div>"
|
155
|
-
end
|
156
|
-
board_string
|
157
|
-
end
|
158
|
-
|
159
|
-
def print_inactive_board
|
160
|
-
board_string = ''
|
161
|
-
board.all_rows.each do |row|
|
162
|
-
board_string += "<div class='row'>"
|
163
|
-
row.each { |cell| board_string += "<button> #{board.all_cells[cell]} <span class='cell'>.</span></button>" }
|
164
|
-
board_string += "</div>"
|
165
|
-
end
|
166
|
-
board_string
|
167
|
-
end
|
168
|
-
|
169
113
|
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.1.
|
4
|
+
version: 0.1.9
|
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-
|
12
|
+
date: 2013-12-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -37,7 +37,9 @@ extra_rdoc_files: []
|
|
37
37
|
files:
|
38
38
|
- lib/ai.rb
|
39
39
|
- lib/board.rb
|
40
|
+
- lib/game_setup.rb
|
40
41
|
- lib/player.rb
|
42
|
+
- lib/player_factory.rb
|
41
43
|
- lib/ruby_ttt.rb
|
42
44
|
- lib/ui.rb
|
43
45
|
homepage: http://rubygems.org/gems/ruby_ttt
|