bashrw-ttt 0.1.1
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/bashrw-ttt.rb +3 -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 +239 -0
@@ -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
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CLI
|
4
|
+
describe CLIPresenter do
|
5
|
+
let(:instream) { StringIO.new }
|
6
|
+
let(:outstream) { StringIO.new }
|
7
|
+
let(:presenter) { CLIPresenter.new(instream, outstream) }
|
8
|
+
|
9
|
+
describe "#new" do
|
10
|
+
it "returns a new presenter object" do
|
11
|
+
presenter.should_not be_nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#output" do
|
16
|
+
it "sends an output message to the io" do
|
17
|
+
presenter.output "hello"
|
18
|
+
presenter.io.outstream.include? "Hello"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#welcome_prompt" do
|
23
|
+
it "displays 'Welcome to TTT!'" do
|
24
|
+
presenter.welcome_prompt
|
25
|
+
presenter.io.outstream.include? "Welcome to TTT!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#command_list" do
|
30
|
+
it "sends a command list message to the io" do
|
31
|
+
presenter.io.should_receive(:output).at_least(4).times
|
32
|
+
presenter.command_list
|
33
|
+
presenter.io.outstream.include? "Please select an option from the following list:"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#menu" do
|
38
|
+
it "sends a welcome prompt message to the io" do
|
39
|
+
presenter.io.outstream.include? "Welcome to TTT!"
|
40
|
+
presenter.io.outstream.include? "1. Play ttt!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#input" do
|
45
|
+
it "receives input from the command line" do
|
46
|
+
presenter.io.instream = StringIO.new("Hello\n")
|
47
|
+
presenter.input.chomp.should == "Hello"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#process_game_list" do
|
52
|
+
it "sends an output message to the io" do
|
53
|
+
list = %w(5 4 3 2 1)
|
54
|
+
presenter.io.outstream = StringIO.new(list.join("\n"))
|
55
|
+
presenter.io.instream = StringIO.new("1\n")
|
56
|
+
presenter.io.should_receive(:output).at_least(2)
|
57
|
+
presenter.process_game_list(list)
|
58
|
+
presenter.io.outstream.string.should == "5\n4\n3\n2\n1"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#sort_list" do
|
63
|
+
it "sorts an array of game id strings into integers" do
|
64
|
+
ary = %w(5 4 3 2 1)
|
65
|
+
presenter.sort_list(ary).should == %w(1 2 3 4 5)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#no_games" do
|
70
|
+
it "displays 'No games were found.' to the user" do
|
71
|
+
presenter.no_games
|
72
|
+
presenter.io.outstream.include? "No games were found"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#player_prompt" do
|
77
|
+
it "displays the prompt for the player type" do
|
78
|
+
history = stub(:each_with_index => [])
|
79
|
+
board = Array.new(9, ' ')
|
80
|
+
player = double('player')
|
81
|
+
player.stub(:prompt).and_return("Entere move:")
|
82
|
+
presenter.player_prompt(board, history, player)
|
83
|
+
presenter.io.outstream.include? "Enter move:"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "#output_three_by_three" do
|
88
|
+
it "prints game board" do
|
89
|
+
board = Array.new(9, ' ')
|
90
|
+
presenter.output_three_by_three(board)
|
91
|
+
presenter.io.outstream.string.chomp.should == "\n | | \n"+
|
92
|
+
"-----------------\n"+
|
93
|
+
" | | \n"+
|
94
|
+
"-----------------\n"+
|
95
|
+
" | | "
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#output_four_by_four" do
|
100
|
+
it "displays a four by four board" do
|
101
|
+
board = Array.new(16, ' ')
|
102
|
+
presenter.output_four_by_four(board)
|
103
|
+
presenter.io.outstream.string.chomp.should == "\n | | | \n"+
|
104
|
+
"-----------------------\n"+
|
105
|
+
" | | | \n"+
|
106
|
+
"-----------------------\n"+
|
107
|
+
" | | | \n"+
|
108
|
+
"-----------------------\n"+
|
109
|
+
" | | | "
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#winner_prompt" do
|
114
|
+
it "displays 'Player 1 is the winner!" do
|
115
|
+
presenter.winner_prompt("Player 1")
|
116
|
+
presenter.io.outstream.string.split("\n").include? "Player 1 is the winner!"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#draw_prompt" do
|
121
|
+
it "displays 'It's a draw!'" do
|
122
|
+
presenter.draw_prompt
|
123
|
+
presenter.io.outstream.string.split("\n").include? "It's a draw!"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#four_by_four_move_prompt" do
|
128
|
+
it "displays a help menu to indicate how the user can move on a 4x4 board" do
|
129
|
+
presenter.four_by_four_move_prompt
|
130
|
+
presenter.io.outstream.string.chomp.should == "\n 0 | 1 | 2 | 3 \n"+
|
131
|
+
"-----------------------\n"+
|
132
|
+
" 4 | 5 | 6 | 7 \n"+
|
133
|
+
"-----------------------\n"+
|
134
|
+
" 8 | 9 | 10 | 11 \n"+
|
135
|
+
"-----------------------\n"+
|
136
|
+
" 12 | 13 | 14 | 15 "
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#post_game_msg" do
|
141
|
+
it "prompts the user if they 'Would like to review the game history'?" do
|
142
|
+
presenter.post_game_msg
|
143
|
+
presenter.io.outstream.include? "\nWould you like to review the game history (y or n)?"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CLI
|
4
|
+
describe PlayAgain do
|
5
|
+
let(:presenter) { CLIPresenter.new(StringIO.new, StringIO.new) }
|
6
|
+
let(:againio) { PlayAgain.new(presenter) }
|
7
|
+
|
8
|
+
describe "#valid_reply?" do
|
9
|
+
it "returns nil if the play again response is not 'y' or 'n'" do
|
10
|
+
againio.valid_reply?("1\n").should == nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns zero if the play again response is 'y' or 'n'" do
|
14
|
+
againio.valid_reply?("n\n").should == 0
|
15
|
+
againio.valid_reply?("y\n").should == 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#play_again_msg" do
|
20
|
+
it "sends a play_again_msg message to gameio" do
|
21
|
+
presenter.should_receive(:play_again_msg)
|
22
|
+
againio.play_again_msg
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#play_again?" do
|
27
|
+
it "prompts the user to play again" do
|
28
|
+
presenter.io.instream = StringIO.new("n\n")
|
29
|
+
presenter.io.outstream.string.split("\n").include? "Play again (y or n)?"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "prompts the user to re-enter input when invalid input is received" do
|
33
|
+
presenter.io.instream = StringIO.new("i\nn\n")
|
34
|
+
againio.play_again?
|
35
|
+
presenter.io.outstream.string.split("\n").include? "I'm sorry, I didn't understand you. Please try again."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|