bashrw-ttt 0.1.1
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/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
|