bashrw_ttt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/play.rb +10 -0
- data/lib/ttt/ai.rb +33 -0
- data/lib/ttt/ai_easy.rb +17 -0
- data/lib/ttt/ai_hard.rb +21 -0
- data/lib/ttt/ai_medium.rb +21 -0
- data/lib/ttt/ai_medium_back.rb +58 -0
- data/lib/ttt/board.rb +37 -0
- data/lib/ttt/config_helper.rb +53 -0
- data/lib/ttt/config_options.rb +27 -0
- data/lib/ttt/context.rb +214 -0
- data/lib/ttt/four_by_four.rb +62 -0
- data/lib/ttt/game.rb +94 -0
- data/lib/ttt/game_history.rb +37 -0
- data/lib/ttt/game_interactor.rb +126 -0
- data/lib/ttt/human.rb +12 -0
- data/lib/ttt/interfaces/cli/lib/cli/board_selection.rb +34 -0
- data/lib/ttt/interfaces/cli/lib/cli/cli_game.rb +186 -0
- data/lib/ttt/interfaces/cli/lib/cli/cli_presenter.rb +216 -0
- data/lib/ttt/interfaces/cli/lib/cli/clio.rb +17 -0
- data/lib/ttt/interfaces/cli/lib/cli/play_again.rb +38 -0
- data/lib/ttt/interfaces/cli/lib/cli/player_selection.rb +43 -0
- data/lib/ttt/interfaces/cli/lib/cli/selection.rb +8 -0
- data/lib/ttt/interfaces/cli/lib/cli/walk_history.rb +48 -0
- data/lib/ttt/interfaces/cli/spec/cli/board_selection_spec.rb +29 -0
- data/lib/ttt/interfaces/cli/spec/cli/cli_game_spec.rb +376 -0
- data/lib/ttt/interfaces/cli/spec/cli/cli_presenter_spec.rb +147 -0
- data/lib/ttt/interfaces/cli/spec/cli/play_again_spec.rb +39 -0
- data/lib/ttt/interfaces/cli/spec/cli/player_selection_spec.rb +62 -0
- data/lib/ttt/interfaces/cli/spec/cli/walk_history_spec.rb +115 -0
- data/lib/ttt/interfaces/cli/spec/spec_helper.rb +14 -0
- data/lib/ttt/interfaces/limelight/four_by_four/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/four_by_four/styles.rb +74 -0
- data/lib/ttt/interfaces/limelight/four_by_four_finished/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/four_by_four_finished/styles.rb +74 -0
- data/lib/ttt/interfaces/limelight/game_list/players/load_button.rb +10 -0
- data/lib/ttt/interfaces/limelight/game_list/props.rb +17 -0
- data/lib/ttt/interfaces/limelight/game_list/styles.rb +8 -0
- data/lib/ttt/interfaces/limelight/main_menu/players/exit_ttt.rb +3 -0
- data/lib/ttt/interfaces/limelight/main_menu/players/load_game.rb +3 -0
- data/lib/ttt/interfaces/limelight/main_menu/players/new_game.rb +3 -0
- data/lib/ttt/interfaces/limelight/main_menu/props.rb +17 -0
- data/lib/ttt/interfaces/limelight/new_game/players/board_type.rb +1 -0
- data/lib/ttt/interfaces/limelight/new_game/players/new_game.rb +5 -0
- data/lib/ttt/interfaces/limelight/new_game/players/player_type.rb +1 -0
- data/lib/ttt/interfaces/limelight/new_game/players/setup_button.rb +21 -0
- data/lib/ttt/interfaces/limelight/new_game/props.rb +21 -0
- data/lib/ttt/interfaces/limelight/new_game/styles.rb +23 -0
- data/lib/ttt/interfaces/limelight/partials/board_partial.rb +14 -0
- data/lib/ttt/interfaces/limelight/partials/menu_button_partial.rb +5 -0
- data/lib/ttt/interfaces/limelight/partials/move_history_partial.rb +6 -0
- data/lib/ttt/interfaces/limelight/partials/title_history_partial.rb +10 -0
- data/lib/ttt/interfaces/limelight/players/board.rb +3 -0
- data/lib/ttt/interfaces/limelight/players/game.rb +4 -0
- data/lib/ttt/interfaces/limelight/players/generic_move_history.rb +5 -0
- data/lib/ttt/interfaces/limelight/players/generic_player.rb +8 -0
- data/lib/ttt/interfaces/limelight/players/left_button.rb +3 -0
- data/lib/ttt/interfaces/limelight/players/main_menu.rb +3 -0
- data/lib/ttt/interfaces/limelight/players/right_button.rb +3 -0
- data/lib/ttt/interfaces/limelight/playscripts/game_playscript.rb +169 -0
- data/lib/ttt/interfaces/limelight/production.rb +14 -0
- data/lib/ttt/interfaces/limelight/spec/game_playscript_spec.rb +218 -0
- data/lib/ttt/interfaces/limelight/spec/main_menu/main_menu_spec.rb +38 -0
- data/lib/ttt/interfaces/limelight/spec/new_game/new_game_spec.rb +45 -0
- data/lib/ttt/interfaces/limelight/spec/spec_helper.rb +8 -0
- data/lib/ttt/interfaces/limelight/stages.rb +6 -0
- data/lib/ttt/interfaces/limelight/styles.rb +119 -0
- data/lib/ttt/interfaces/limelight/three_by_three/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/three_by_three/styles.rb +39 -0
- data/lib/ttt/interfaces/limelight/three_by_three_by_three/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/three_by_three_by_three/styles.rb +119 -0
- data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/styles.rb +119 -0
- data/lib/ttt/interfaces/limelight/three_by_three_finished/props.rb +1 -0
- data/lib/ttt/interfaces/limelight/three_by_three_finished/styles.rb +39 -0
- data/lib/ttt/interfaces/rails/app/controllers/application_controller.rb +3 -0
- data/lib/ttt/interfaces/rails/app/controllers/ttt_games_controller.rb +86 -0
- data/lib/ttt/interfaces/rails/app/helpers/application_helper.rb +2 -0
- data/lib/ttt/interfaces/rails/app/models/ttt_game.rb +34 -0
- data/lib/ttt/interfaces/rails/app/presenters/web_game_presenter.rb +159 -0
- data/lib/ttt/interfaces/rails/autotest/discover.rb +1 -0
- data/lib/ttt/interfaces/rails/config/application.rb +67 -0
- data/lib/ttt/interfaces/rails/config/boot.rb +6 -0
- data/lib/ttt/interfaces/rails/config/environment.rb +5 -0
- data/lib/ttt/interfaces/rails/config/environments/development.rb +37 -0
- data/lib/ttt/interfaces/rails/config/environments/production.rb +67 -0
- data/lib/ttt/interfaces/rails/config/environments/test.rb +37 -0
- data/lib/ttt/interfaces/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/lib/ttt/interfaces/rails/config/initializers/inflections.rb +15 -0
- data/lib/ttt/interfaces/rails/config/initializers/mime_types.rb +5 -0
- data/lib/ttt/interfaces/rails/config/initializers/secret_token.rb +7 -0
- data/lib/ttt/interfaces/rails/config/initializers/session_store.rb +8 -0
- data/lib/ttt/interfaces/rails/config/initializers/wrap_parameters.rb +14 -0
- data/lib/ttt/interfaces/rails/config/routes.rb +49 -0
- data/lib/ttt/interfaces/rails/db/schema.rb +28 -0
- data/lib/ttt/interfaces/rails/db/seeds.rb +7 -0
- data/lib/ttt/interfaces/rails/spec/controllers/ttt_games_controller_spec.rb +211 -0
- data/lib/ttt/interfaces/rails/spec/models/ttt_game_spec.rb +76 -0
- data/lib/ttt/interfaces/rails/spec/presenters/web_game_presenter_spec.rb +52 -0
- data/lib/ttt/interfaces/rails/spec/spec_helper.rb +13 -0
- data/lib/ttt/interfaces/web_interface/html_generator.rb +76 -0
- data/lib/ttt/interfaces/web_interface/web_game_presenter.rb +0 -0
- data/lib/ttt/minimax.rb +46 -0
- data/lib/ttt/move_history.rb +10 -0
- data/lib/ttt/move_traverser.rb +49 -0
- data/lib/ttt/player.rb +15 -0
- data/lib/ttt/riak_db.rb +53 -0
- data/lib/ttt/setup.rb +59 -0
- data/lib/ttt/three_by_three.rb +35 -0
- data/lib/ttt/three_by_three_by_three.rb +37 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/ttt/ai_easy_spec.rb +15 -0
- data/spec/ttt/ai_hard_spec.rb +76 -0
- data/spec/ttt/ai_medium_spec.rb +65 -0
- data/spec/ttt/ai_spec.rb +50 -0
- data/spec/ttt/board_spec.rb +96 -0
- data/spec/ttt/config_helper_spec.rb +54 -0
- data/spec/ttt/context_spec.rb +398 -0
- data/spec/ttt/four_by_four_spec.rb +112 -0
- data/spec/ttt/game_history_spec.rb +41 -0
- data/spec/ttt/game_interactor_spec.rb +197 -0
- data/spec/ttt/game_spec.rb +246 -0
- data/spec/ttt/human_spec.rb +18 -0
- data/spec/ttt/move_history_spec.rb +13 -0
- data/spec/ttt/move_traverser_spec.rb +75 -0
- data/spec/ttt/player_spec.rb +13 -0
- data/spec/ttt/riak_db_spec.rb +64 -0
- data/spec/ttt/setup_spec.rb +63 -0
- data/spec/ttt/three_by_three_by_three_spec.rb +130 -0
- data/spec/ttt/three_by_three_spec.rb +110 -0
- metadata +238 -0
@@ -0,0 +1,216 @@
|
|
1
|
+
module CLI
|
2
|
+
class CLIPresenter
|
3
|
+
attr_accessor :io
|
4
|
+
def initialize(input, output)
|
5
|
+
self.io = CLIO.new(input, output)
|
6
|
+
end
|
7
|
+
|
8
|
+
def output(args)
|
9
|
+
io.output args
|
10
|
+
end
|
11
|
+
|
12
|
+
def padding
|
13
|
+
output "\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
def input
|
17
|
+
io.input
|
18
|
+
end
|
19
|
+
|
20
|
+
def welcome_prompt
|
21
|
+
output "Welcome to TTT!"
|
22
|
+
end
|
23
|
+
|
24
|
+
def command_list
|
25
|
+
output "\nPlease select an option from the following list:"
|
26
|
+
padding
|
27
|
+
output "1. Play TTT!\n\n"
|
28
|
+
output "2. Load game\n\n"
|
29
|
+
output "3. Quit\n\n"
|
30
|
+
padding
|
31
|
+
end
|
32
|
+
|
33
|
+
def menu
|
34
|
+
welcome_prompt
|
35
|
+
command_list
|
36
|
+
selection = input.chomp
|
37
|
+
if valid_menu?(selection)
|
38
|
+
selection
|
39
|
+
else
|
40
|
+
error
|
41
|
+
menu
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def valid_menu?(selection)
|
46
|
+
selection.to_i >= 1 && selection.to_i <= 3
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_game_list(list)
|
50
|
+
clear
|
51
|
+
puts "Please choose one of the following games:\n\n"
|
52
|
+
list.each do |key|
|
53
|
+
output key
|
54
|
+
end
|
55
|
+
output "\n"
|
56
|
+
input.chomp
|
57
|
+
end
|
58
|
+
|
59
|
+
def sort_list(list)
|
60
|
+
list.map(&:to_i).sort.map(&:to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def no_games
|
64
|
+
output "No games were found."
|
65
|
+
end
|
66
|
+
|
67
|
+
def player_type_prompt(num, players)
|
68
|
+
output "\nPlease select an option for Player #{num}:\n\n"
|
69
|
+
display_options_with_index(players)
|
70
|
+
input
|
71
|
+
end
|
72
|
+
|
73
|
+
def select_game_prompt
|
74
|
+
output "\nPlease choose one of the following games:\n\n"
|
75
|
+
end
|
76
|
+
|
77
|
+
def player_prompt(board, history, player)
|
78
|
+
output_help(board)
|
79
|
+
output_move_history(history)
|
80
|
+
output_board(board)
|
81
|
+
output "\n#{player.prompt}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def output_move_history(moves)
|
85
|
+
output "\n"
|
86
|
+
output "Move History:".rjust(10)
|
87
|
+
move_num = "#"
|
88
|
+
move_side = "side"
|
89
|
+
move_square = "square"
|
90
|
+
output "#{move_num} #{move_side.rjust(7)} #{move_square.rjust(7)}"
|
91
|
+
moves.each_with_index do |move, n|
|
92
|
+
output "#{n}. #{(move.side).to_s.rjust(5)} #{(move.move).to_s.rjust(5)}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def process_winner(board, history, player)
|
97
|
+
clear
|
98
|
+
output_move_history(history)
|
99
|
+
output_board(board)
|
100
|
+
winner_prompt(player)
|
101
|
+
end
|
102
|
+
|
103
|
+
def process_draw(board, history)
|
104
|
+
clear
|
105
|
+
output_move_history(history)
|
106
|
+
output_board(board)
|
107
|
+
draw_prompt
|
108
|
+
end
|
109
|
+
|
110
|
+
def board_selection_prompt(boards)
|
111
|
+
clear
|
112
|
+
output "\nPlease select which board you'd like to play:\n"
|
113
|
+
display_options_with_index(boards)
|
114
|
+
end
|
115
|
+
|
116
|
+
def display_options_with_index(options)
|
117
|
+
options.each_with_index do |option, index|
|
118
|
+
output "#{index + 1}: #{option}\n\n"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def clear
|
123
|
+
system('clear')
|
124
|
+
#output "\e[2J"
|
125
|
+
#output "\e[0;0H"
|
126
|
+
end
|
127
|
+
|
128
|
+
def ai_difficulty_prompt(ai_options)
|
129
|
+
clear
|
130
|
+
output "\nChoose a difficulty level for the computer:"
|
131
|
+
ai_options.inject(1) do |index, difficulty|
|
132
|
+
output "#{index}. #{difficulty.to_s.gsub(/TTT::AI/, '')}"
|
133
|
+
index += 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def post_game_msg
|
138
|
+
output "\nWould you like to review the game history (y or n)?"
|
139
|
+
end
|
140
|
+
|
141
|
+
def play_again_msg
|
142
|
+
output "\nPlay again (y or n)?"
|
143
|
+
end
|
144
|
+
|
145
|
+
def walk_msg
|
146
|
+
output "\nEnter -1 to go back, 0 for main menu, or 1 to go forward"
|
147
|
+
end
|
148
|
+
|
149
|
+
def output_board(board)
|
150
|
+
output "\n"
|
151
|
+
case board.length
|
152
|
+
when 9
|
153
|
+
output_three_by_three(board)
|
154
|
+
when 16
|
155
|
+
output_four_by_four(board)
|
156
|
+
when 27
|
157
|
+
output_3d(board)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def output_help(board)
|
162
|
+
clear
|
163
|
+
output "\nMove options:"
|
164
|
+
case board.length
|
165
|
+
when 9
|
166
|
+
regular_move_prompt
|
167
|
+
when 16
|
168
|
+
four_by_four_move_prompt
|
169
|
+
when 27
|
170
|
+
three_d_prompt
|
171
|
+
end
|
172
|
+
output "\n"
|
173
|
+
end
|
174
|
+
|
175
|
+
def error
|
176
|
+
output "\nI'm sorry, I didn't understand. Please try again."
|
177
|
+
end
|
178
|
+
|
179
|
+
def winner_prompt(winner)
|
180
|
+
output "\n#{winner} is the winner!"
|
181
|
+
end
|
182
|
+
|
183
|
+
def draw_prompt
|
184
|
+
output "\nIt's a draw!"
|
185
|
+
end
|
186
|
+
|
187
|
+
def output_three_by_three(board)
|
188
|
+
output("\n #{board[0]} | #{board[1]} | #{board[2]} \n-----------------\n #{board[3]} | #{board[4]} | #{board[5]} \n-----------------\n #{board[6]} | #{board[7]} | #{board[8]} ")
|
189
|
+
end
|
190
|
+
|
191
|
+
def output_four_by_four(board)
|
192
|
+
output("\n #{board[0]} | #{board[1]} | #{board[2]} | #{board[3]} \n-----------------------\n #{board[4]} | #{board[5]} | #{board[6]} | #{board[7]} \n-----------------------\n #{board[8]} | #{board[9]} | #{board[10]} | #{board[11]} \n-----------------------\n #{board[12]} | #{board[13]} | #{board[14]} | #{board[15]} ")
|
193
|
+
end
|
194
|
+
|
195
|
+
def output_3d(board)
|
196
|
+
output("\n #{board[0]} | #{board[1]} | #{board[2]} #{board[9]} | #{board[10]} | #{board[11]} #{board[18]} | #{board[19]} | #{board[20]} \n----------------- ----------------- -----------------\n #{board[3]} | #{board[4]} | #{board[5]} #{board[12]} | #{board[13]} | #{board[14]} #{board[21]} | #{board[22]} | #{board[23]} \n----------------- ----------------- -----------------\n #{board[6]} | #{board[7]} | #{board[8]} #{board[15]} | #{board[16]} | #{board[17]} #{board[24]} | #{board[25]} | #{board[26]} ")
|
197
|
+
end
|
198
|
+
|
199
|
+
def three_d_prompt
|
200
|
+
output("\n 0 | 1 | 2 9 | 10 | 11 18 | 19 | 20 \n----------------- ----------------- -----------------\n 3 | 4 | 5 12 | 13 | 14 21 | 22 | 23 \n----------------- ----------------- -----------------\n 6 | 7 | 8 15 | 16 | 17 24 | 25 | 26 ")
|
201
|
+
end
|
202
|
+
|
203
|
+
def regular_move_prompt
|
204
|
+
output("\n 0 | 1 | 2 \n-----------------\n 3 | 4 | 5 \n-----------------\n 6 | 7 | 8 ")
|
205
|
+
end
|
206
|
+
|
207
|
+
def four_by_four_move_prompt
|
208
|
+
output("\n 0 | 1 | 2 | 3 \n-----------------------\n 4 | 5 | 6 | 7 \n-----------------------\n 8 | 9 | 10 | 11 \n-----------------------\n 12 | 13 | 14 | 15 ")
|
209
|
+
end
|
210
|
+
|
211
|
+
def welcome_prompt
|
212
|
+
clear
|
213
|
+
output("Welcome to TTT!")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CLI
|
2
|
+
class CLIO
|
3
|
+
attr_accessor :outstream, :instream
|
4
|
+
def initialize(input, output)
|
5
|
+
self.outstream = output
|
6
|
+
self.instream = input
|
7
|
+
end
|
8
|
+
|
9
|
+
def output(message)
|
10
|
+
outstream.puts message
|
11
|
+
end
|
12
|
+
|
13
|
+
def input
|
14
|
+
instream.gets
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CLI
|
2
|
+
class PlayAgain
|
3
|
+
|
4
|
+
def initialize(presenter)
|
5
|
+
self.presenter = presenter
|
6
|
+
end
|
7
|
+
|
8
|
+
def play_again?
|
9
|
+
play_again_msg
|
10
|
+
process_reply input
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_reply reply
|
14
|
+
if valid_reply?(reply)
|
15
|
+
return false if reply.chomp == "n"
|
16
|
+
true
|
17
|
+
else
|
18
|
+
presenter.error
|
19
|
+
play_again?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def input
|
24
|
+
presenter.input
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid_reply?(response)
|
28
|
+
response =~ (/(y|n)/i)
|
29
|
+
end
|
30
|
+
|
31
|
+
def play_again_msg
|
32
|
+
presenter.play_again_msg
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
attr_accessor :presenter
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'cli/selection'
|
2
|
+
|
3
|
+
module CLI
|
4
|
+
class PlayerSelection < Selection
|
5
|
+
attr_accessor :presenter, :players, :player1, :player2
|
6
|
+
def initialize(players, presenter)
|
7
|
+
self.presenter = presenter
|
8
|
+
self.players = players
|
9
|
+
end
|
10
|
+
|
11
|
+
def process
|
12
|
+
process_player_type_input(1, presenter.player_type_prompt(1, players))
|
13
|
+
process_player_type_input(2, presenter.player_type_prompt(2, players))
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_player_type_input(num, selection)
|
18
|
+
if player_selection_valid? selection.chomp.to_i
|
19
|
+
set_player_string(num, selection.chomp.to_i)
|
20
|
+
else
|
21
|
+
presenter.error
|
22
|
+
process_player_type_input(num, presenter.player_type_prompt(num, players))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_player_string(num, selection)
|
27
|
+
case num
|
28
|
+
when 1
|
29
|
+
self.player1 = _player(selection)
|
30
|
+
when 2
|
31
|
+
self.player2 = _player(selection)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def _player(selection)
|
36
|
+
players[selection - 1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def player_selection_valid?(selection)
|
40
|
+
0 < selection.to_i && selection <= players.length
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module CLI
|
2
|
+
class WalkHistory
|
3
|
+
attr_accessor :presenter, :game, :move_traverser, :board
|
4
|
+
def initialize(presenter, game)
|
5
|
+
self.presenter = presenter
|
6
|
+
self.move_traverser = game.history.move_traverser
|
7
|
+
self.game = game
|
8
|
+
end
|
9
|
+
|
10
|
+
def post_game_msg
|
11
|
+
presenter.post_game_msg
|
12
|
+
process(presenter.input.chomp)
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(selection)
|
16
|
+
if selection =~ (/y|n/i)
|
17
|
+
walk_msg if selection =~ (/y/i)
|
18
|
+
else
|
19
|
+
presenter.error
|
20
|
+
post_game_msg
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def walk_msg
|
25
|
+
presenter.clear
|
26
|
+
presenter.output_board(build_board)
|
27
|
+
presenter.walk_msg
|
28
|
+
diff_index = presenter.input.chomp
|
29
|
+
case diff_index
|
30
|
+
when "-1" then walk_history(-1)
|
31
|
+
when "0" then return
|
32
|
+
when "1" then walk_history(1)
|
33
|
+
else
|
34
|
+
presenter.error
|
35
|
+
walk_msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def walk_history(diff_index)
|
40
|
+
move_traverser.adjust_move_index(diff_index)
|
41
|
+
walk_msg
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_board(board = game.board, move_index = move_traverser.move_index)
|
45
|
+
move_traverser.history_board_builder(board, move_index)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CLI
|
4
|
+
describe BoardSelection do
|
5
|
+
let(:view) { CLIO.new(StringIO.new, StringIO.new) }
|
6
|
+
let(:boards) { TTT::Setup.new.players }
|
7
|
+
let(:presenter) { CLIPresenter.new(StringIO.new, StringIO.new) }
|
8
|
+
let(:boardio) { BoardSelection.new(boards, presenter) }
|
9
|
+
|
10
|
+
describe "#board_selection_input_valid?" do
|
11
|
+
it "returns true when input is within valid range of options" do
|
12
|
+
boardio.board_selection_input_valid?(1).should == true
|
13
|
+
boardio.board_selection_input_valid?(2).should == true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns false when input is not within valid range of options" do
|
17
|
+
boardio.board_selection_input_valid?(6).should == false
|
18
|
+
boardio.board_selection_input_valid?(1.5).should == false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#process" do
|
23
|
+
it "displays a generic error message if the user inputs invalid input" do
|
24
|
+
presenter.io.instream = StringIO.new("9\n1\n")
|
25
|
+
boardio.process
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,376 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'ttt/context'
|
4
|
+
|
5
|
+
module CLI
|
6
|
+
describe CLIGame do
|
7
|
+
let(:setup) { TTT::Setup.new }
|
8
|
+
let(:context) { TTT::Context.instance }
|
9
|
+
let(:players) { setup.players }
|
10
|
+
let(:boards) { setup.boards }
|
11
|
+
let(:db) { setup.new_db }
|
12
|
+
let(:presenter) { CLIPresenter.new(input, output) }
|
13
|
+
let(:output) { StringIO.new }
|
14
|
+
let(:input1) { StringIO.new("1\n1\n1\n0\n3\n1\n4\n2\nn\n") }
|
15
|
+
let(:input) { StringIO.new }
|
16
|
+
let(:game) { setup.new_game(player1: players.first, player2: players.first, board: boards.first) }
|
17
|
+
let(:ai_game) { setup.new_game(player1: players.last, player2: players.last, board: boards.first) }
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
context.setup = TTT::Setup
|
21
|
+
@cli_game = described_class.new(context, input, output)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#play" do
|
25
|
+
it "displays the main game menu to the user" do
|
26
|
+
@cli_game.stub(:process_command).and_return(nil)
|
27
|
+
@cli_game.presenter.should_receive(:menu)
|
28
|
+
@cli_game.play
|
29
|
+
end
|
30
|
+
|
31
|
+
it "chooses the game made" do
|
32
|
+
@cli_game.presenter.stub(:menu).and_return(1)
|
33
|
+
@cli_game.should_receive(:process_command).with(1)
|
34
|
+
@cli_game.play
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#process_command" do
|
39
|
+
context "matches user game mode selection with the proper game play option" do
|
40
|
+
it "starts a new game when the user selects option 1" do
|
41
|
+
@cli_game.should_receive(:init_game)
|
42
|
+
@cli_game.process_command("1\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "prompts the user to load a previously played game when the user selects option 2" do
|
46
|
+
@cli_game.should_receive(:game_list)
|
47
|
+
@cli_game.process_command("2\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "exits the game when the user selects option 3" do
|
51
|
+
@cli_game.should_receive(:exit)
|
52
|
+
@cli_game.process_command("3\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "reprompts the user with the main menu if invalid input is received" do
|
56
|
+
@cli_game.should_receive(:play)
|
57
|
+
@cli_game.process_command("aoivoijv\n")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#init_game" do
|
63
|
+
before(:each) do
|
64
|
+
@cli_game.presenter.io.instream = StringIO.new("1\n1\n1\n")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "presents a welcome prompt to the user" do
|
68
|
+
@cli_game.presenter.should_receive(:welcome_prompt)
|
69
|
+
@cli_game.stub(:get_player_selection => nil)
|
70
|
+
@cli_game.stub(:get_board_selection => nil)
|
71
|
+
@cli_game.stub(:build_game => nil)
|
72
|
+
@cli_game.context.stub(:add_game => nil)
|
73
|
+
@cli_game.stub(:play_game => nil)
|
74
|
+
@cli_game.init_game
|
75
|
+
end
|
76
|
+
|
77
|
+
it "asks the user to select player types for player 1 and player 2" do
|
78
|
+
@cli_game.should_receive(:get_player_selection)
|
79
|
+
@cli_game.stub(:get_board_selection => nil)
|
80
|
+
@cli_game.stub(:build_game => nil)
|
81
|
+
@cli_game.context.stub(:add_game => nil)
|
82
|
+
@cli_game.stub(:play_game => nil)
|
83
|
+
@cli_game.init_game
|
84
|
+
end
|
85
|
+
|
86
|
+
it "asks the user to select the board type for the new game" do
|
87
|
+
@cli_game.stub(:get_player_selection => nil)
|
88
|
+
@cli_game.should_receive(:get_board_selection)
|
89
|
+
@cli_game.stub(:build_game => nil)
|
90
|
+
@cli_game.context.stub(:add_game => nil)
|
91
|
+
@cli_game.stub(:play_game => nil)
|
92
|
+
@cli_game.init_game
|
93
|
+
end
|
94
|
+
|
95
|
+
it "builds a new game based on the player type and board type selections of the user" do
|
96
|
+
@cli_game.should_receive(:build_game)
|
97
|
+
@cli_game.context.stub(:add_game => nil)
|
98
|
+
@cli_game.stub(:play_game => nil)
|
99
|
+
@cli_game.init_game
|
100
|
+
end
|
101
|
+
|
102
|
+
it "a new game is added to the datastore" do
|
103
|
+
@cli_game.context.should_receive(:add_game)
|
104
|
+
@cli_game.stub(:play_game => nil)
|
105
|
+
@cli_game.init_game
|
106
|
+
end
|
107
|
+
|
108
|
+
it "begins to play the game after a new game has been successfully created" do
|
109
|
+
@cli_game.should_receive(:play_game)
|
110
|
+
@cli_game.init_game
|
111
|
+
@cli_game.id.should_not be_nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#get_player_selection" do
|
116
|
+
it "selects the player type for player1 and player2 based on the context.players list" do
|
117
|
+
context.players.length.times do |n|
|
118
|
+
@cli_game.presenter.io.instream = StringIO.new("#{n + 1}\n#{n + 1}\n")
|
119
|
+
players_selection = @cli_game.get_player_selection
|
120
|
+
players_selection.player1.should == context.players[n]
|
121
|
+
players_selection.player2.should == context.players[n]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#get_board_selection" do
|
127
|
+
it "selects the board type based on the context.boards list" do
|
128
|
+
context.boards.length.times do |n|
|
129
|
+
@cli_game.presenter.io.instream = StringIO.new("#{n + 1}\n#{n + 1}\n")
|
130
|
+
board_selection = @cli_game.get_board_selection
|
131
|
+
board_selection.board.should == context.boards[n]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#build_game" do
|
137
|
+
it "constructs a new game based on the player types and board type selected by the user" do
|
138
|
+
p1 = [*1..context.players.length].sample
|
139
|
+
p2 = [*1..context.players.length].sample
|
140
|
+
b = [*1..context.boards.length].sample
|
141
|
+
@cli_game.presenter.io.instream = StringIO.new("#{p1}\n#{p2}\n#{b}\n")
|
142
|
+
player_selection = @cli_game.get_player_selection
|
143
|
+
board_selection = @cli_game.get_board_selection
|
144
|
+
game = @cli_game.build_game(player_selection, board_selection)
|
145
|
+
game.player1.should_not be_nil
|
146
|
+
game.player2.should_not be_nil
|
147
|
+
game.board.should_not be_nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#play_game" do
|
152
|
+
before(:each) do
|
153
|
+
@cli_game.presenter.io.instream = StringIO.new("1\n1\n1\n0\n1\n4\n5\n8\n")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "plays a game until the game is finished" do
|
157
|
+
@cli_game.should_receive(:game_over)
|
158
|
+
@cli_game.init_game
|
159
|
+
@cli_game.game.board.board.should == ['x', 'o', ' ', ' ', 'x', 'o', ' ', ' ', 'x']
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#game_over" do
|
164
|
+
it "prompts the user to walk the previously played game's history" do
|
165
|
+
@cli_game.should_receive(:walk_history)
|
166
|
+
@cli_game.stub(:play_again)
|
167
|
+
@cli_game.game_over
|
168
|
+
end
|
169
|
+
|
170
|
+
it "prompts the user to play again" do
|
171
|
+
@cli_game.should_receive(:play_again)
|
172
|
+
@cli_game.stub(:walk_history)
|
173
|
+
@cli_game.game_over
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#play_again" do
|
178
|
+
it "exits the program if the user doesn't want to play again" do
|
179
|
+
PlayAgain.stub_chain(:new, :play_again?).and_return(nil)
|
180
|
+
@cli_game.should_receive(:exit)
|
181
|
+
@cli_game.stub(:play => nil)
|
182
|
+
@cli_game.play_again
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#game" do
|
187
|
+
it "returns the current game from the datastore" do
|
188
|
+
@cli_game.presenter.io.instream = StringIO.new("1\n1\n1\n")
|
189
|
+
@cli_game.stub(:play_game => nil)
|
190
|
+
@cli_game.game.should == context.get_game(@cli_game.id)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#move_cycle" do
|
195
|
+
it "prompts the current player to move" do
|
196
|
+
@cli_game.id = context.add_game(game)
|
197
|
+
@cli_game.stub(:process_next_move => nil)
|
198
|
+
@cli_game.presenter.should_receive(:player_prompt).with(game.board.board, game.show_history, game.current_player)
|
199
|
+
@cli_game.move_cycle
|
200
|
+
end
|
201
|
+
|
202
|
+
it "receives the next move from the current_player" do
|
203
|
+
@cli_game.id = context.add_game(ai_game)
|
204
|
+
@cli_game.stub(:game => context.get_game(@cli_game.id))
|
205
|
+
@cli_game.presenter.stub(:player_prompt => nil)
|
206
|
+
@cli_game.should_receive(:process_next_move)
|
207
|
+
@cli_game.move_cycle
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "#player_prompt" do
|
212
|
+
it "calls player_prompt on @presenter" do
|
213
|
+
@cli_game.id = context.add_game(game)
|
214
|
+
@cli_game.presenter.should_receive(:player_prompt)
|
215
|
+
@cli_game.player_prompt(context.get_game(@cli_game.id))
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#process_next_move" do
|
220
|
+
context "When an AI player move is processed" do
|
221
|
+
it "returns the AI player's move if the current player is an AI player" do
|
222
|
+
@cli_game.id = context.add_game(ai_game)
|
223
|
+
original_board = @cli_game.game.board.board
|
224
|
+
@cli_game.process_next_move
|
225
|
+
original_board.should_not == @cli_game.game.board.board
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "When a Human player move is processed" do
|
230
|
+
before(:each) do
|
231
|
+
@cli_game.id = context.add_game(game)
|
232
|
+
@cli_game.presenter.io.instream = StringIO.new("1\n")
|
233
|
+
end
|
234
|
+
|
235
|
+
it "gets the human player's move" do
|
236
|
+
@cli_game.process_next_move
|
237
|
+
@cli_game.game.board.board.should == [' ', 'x', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
|
238
|
+
end
|
239
|
+
|
240
|
+
it "validates the human player's move" do
|
241
|
+
@cli_game.should_receive(:validate_move).with("1\n").and_return(true)
|
242
|
+
@cli_game.process_next_move
|
243
|
+
end
|
244
|
+
|
245
|
+
it "updates the game with the human player's move" do
|
246
|
+
@cli_game.game.board.board.should == [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
|
247
|
+
@cli_game.process_next_move
|
248
|
+
@cli_game.game.board.board.should == [' ', 'x', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
it "evaluates the game after the move has been processed" do
|
253
|
+
@cli_game.id = context.add_game(game)
|
254
|
+
game = @cli_game.game
|
255
|
+
game.board.board = ['x', 'o', ' ', ' ', 'x', 'o', ' ', ' ', ' ']
|
256
|
+
context.save_game(@cli_game.id, game)
|
257
|
+
@cli_game.presenter.io.instream = StringIO.new("8\n") # win for 'x'
|
258
|
+
@cli_game.presenter.should_receive(:process_winner)
|
259
|
+
@cli_game.process_next_move
|
260
|
+
@cli_game.game.winner?.should == true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "#get_next_move" do
|
265
|
+
it "prompts the human player for input on the command line if the current player is human" do
|
266
|
+
@cli_game.id = context.add_game(game)
|
267
|
+
@cli_game.presenter.io.instream = StringIO.new("1\n")
|
268
|
+
@cli_game.presenter.should_receive(:input).and_return("1\n")
|
269
|
+
@cli_game.process_next_move
|
270
|
+
end
|
271
|
+
|
272
|
+
it "gets the AI player's move when the current player is an AI player" do
|
273
|
+
@cli_game.id = context.add_game(ai_game)
|
274
|
+
original_board = @cli_game.game.board.board
|
275
|
+
@cli_game.get_next_move
|
276
|
+
original_board.should_not == @cli_game.game.board.board
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "#get_games" do
|
281
|
+
it "asks the context object for alist of games saved to the datastore" do
|
282
|
+
games = context.game_list
|
283
|
+
@cli_game.get_games
|
284
|
+
@cli_game.games.length.should == games.length
|
285
|
+
end
|
286
|
+
|
287
|
+
it "sets @games equal to nil if there are no games in the datastore" do
|
288
|
+
@cli_game.context.stub(:game_list => [])
|
289
|
+
@cli_game.get_games
|
290
|
+
@cli_game.games.should == nil
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe "#load_game" do
|
295
|
+
before(:each) do
|
296
|
+
@cli_game.stub(:game_list => nil)
|
297
|
+
end
|
298
|
+
it "prompts the user to select a game id from the game list" do
|
299
|
+
@cli_game.presenter.should_receive(:process_game_list).with(@cli_game.games).and_return("1\n")
|
300
|
+
@cli_game.context.stub(:get_game => nil)
|
301
|
+
@cli_game.load_game
|
302
|
+
end
|
303
|
+
|
304
|
+
it "loads a game with a given id" do
|
305
|
+
@cli_game.id = context.add_game(game)
|
306
|
+
@cli_game.presenter.stub(:process_game_list => @cli_game.id)
|
307
|
+
@cli_game.context.stub(:get_game => @cli_game.game)
|
308
|
+
@cli_game.should_receive(:resume_game)
|
309
|
+
@cli_game.load_game
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "#no_games" do
|
314
|
+
it "prompts the users that no games exist in the datastore and returns to the main menu" do
|
315
|
+
@cli_game.presenter.should_receive(:no_games)
|
316
|
+
@cli_game.should_receive(:play)
|
317
|
+
@cli_game.no_games
|
318
|
+
end
|
319
|
+
|
320
|
+
it "sleeps for one second" do
|
321
|
+
@cli_game.should_receive(:sleep).with(1)
|
322
|
+
@cli_game.stub(:play => nil)
|
323
|
+
@cli_game.no_games
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
describe "#resume_game" do
|
328
|
+
it "prompts the player to resume playing an unfinished game loaded from the datastore" do
|
329
|
+
@cli_game.id = context.add_game(game)
|
330
|
+
@cli_game.presenter.should_receive(:player_prompt).with(@cli_game.game.board.board, @cli_game.game.show_history, @cli_game.game.current_player)
|
331
|
+
@cli_game.stub(:play_game => nil)
|
332
|
+
@cli_game.resume_game
|
333
|
+
end
|
334
|
+
|
335
|
+
it "displasy a loaded game's final state when a finished game is loaded from the db" do
|
336
|
+
@cli_game.id = context.add_game(game)
|
337
|
+
game = context.get_game(@cli_game.id)
|
338
|
+
game.board.board = %w(a b c d e f g h i)
|
339
|
+
@cli_game.context.save_game(@cli_game.id, game)
|
340
|
+
@cli_game.stub(:play_game => nil)
|
341
|
+
@cli_game.presenter.io.outstream.include? "It's a draw"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe "#game_list" do
|
346
|
+
it "gets a list of games" do
|
347
|
+
@cli_game.should_receive(:get_games)
|
348
|
+
@cli_game.stub(:load_game)
|
349
|
+
@cli_game.stub(:no_games)
|
350
|
+
@cli_game.game_list
|
351
|
+
end
|
352
|
+
|
353
|
+
it "calls #load_game if there are games stored in the datastore" do
|
354
|
+
@cli_game.should_receive(:load_game)
|
355
|
+
@cli_game.game_list
|
356
|
+
end
|
357
|
+
|
358
|
+
it "calls #no_games if there are no games stored in the datastore" do
|
359
|
+
@cli_game.stub(:get_games => nil)
|
360
|
+
@cli_game.games = nil
|
361
|
+
@cli_game.should_receive(:no_games)
|
362
|
+
@cli_game.game_list
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
describe "#walk_history" do
|
367
|
+
it "makes a new WalkHistory object" do
|
368
|
+
@cli_game.id = context.add_game(game)
|
369
|
+
@cli_game.presenter.stub(:input => "n")
|
370
|
+
@cli_game.stub(:play_again => nil)
|
371
|
+
@cli_game.presenter.should_receive(:post_game_msg)
|
372
|
+
@cli_game.walk_history
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|