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.
Files changed (131) hide show
  1. data/bin/play.rb +10 -0
  2. data/lib/bashrw-ttt.rb +3 -0
  3. data/lib/ttt/ai.rb +33 -0
  4. data/lib/ttt/ai_easy.rb +17 -0
  5. data/lib/ttt/ai_hard.rb +21 -0
  6. data/lib/ttt/ai_medium.rb +21 -0
  7. data/lib/ttt/ai_medium_back.rb +58 -0
  8. data/lib/ttt/board.rb +37 -0
  9. data/lib/ttt/config_helper.rb +53 -0
  10. data/lib/ttt/config_options.rb +27 -0
  11. data/lib/ttt/context.rb +214 -0
  12. data/lib/ttt/four_by_four.rb +62 -0
  13. data/lib/ttt/game.rb +94 -0
  14. data/lib/ttt/game_history.rb +37 -0
  15. data/lib/ttt/game_interactor.rb +126 -0
  16. data/lib/ttt/human.rb +12 -0
  17. data/lib/ttt/interfaces/cli/lib/cli/board_selection.rb +34 -0
  18. data/lib/ttt/interfaces/cli/lib/cli/cli_game.rb +186 -0
  19. data/lib/ttt/interfaces/cli/lib/cli/cli_presenter.rb +216 -0
  20. data/lib/ttt/interfaces/cli/lib/cli/clio.rb +17 -0
  21. data/lib/ttt/interfaces/cli/lib/cli/play_again.rb +38 -0
  22. data/lib/ttt/interfaces/cli/lib/cli/player_selection.rb +43 -0
  23. data/lib/ttt/interfaces/cli/lib/cli/selection.rb +8 -0
  24. data/lib/ttt/interfaces/cli/lib/cli/walk_history.rb +48 -0
  25. data/lib/ttt/interfaces/cli/spec/cli/board_selection_spec.rb +29 -0
  26. data/lib/ttt/interfaces/cli/spec/cli/cli_game_spec.rb +376 -0
  27. data/lib/ttt/interfaces/cli/spec/cli/cli_presenter_spec.rb +147 -0
  28. data/lib/ttt/interfaces/cli/spec/cli/play_again_spec.rb +39 -0
  29. data/lib/ttt/interfaces/cli/spec/cli/player_selection_spec.rb +62 -0
  30. data/lib/ttt/interfaces/cli/spec/cli/walk_history_spec.rb +115 -0
  31. data/lib/ttt/interfaces/cli/spec/spec_helper.rb +14 -0
  32. data/lib/ttt/interfaces/limelight/four_by_four/props.rb +1 -0
  33. data/lib/ttt/interfaces/limelight/four_by_four/styles.rb +74 -0
  34. data/lib/ttt/interfaces/limelight/four_by_four_finished/props.rb +1 -0
  35. data/lib/ttt/interfaces/limelight/four_by_four_finished/styles.rb +74 -0
  36. data/lib/ttt/interfaces/limelight/game_list/players/load_button.rb +10 -0
  37. data/lib/ttt/interfaces/limelight/game_list/props.rb +17 -0
  38. data/lib/ttt/interfaces/limelight/game_list/styles.rb +8 -0
  39. data/lib/ttt/interfaces/limelight/main_menu/players/exit_ttt.rb +3 -0
  40. data/lib/ttt/interfaces/limelight/main_menu/players/load_game.rb +3 -0
  41. data/lib/ttt/interfaces/limelight/main_menu/players/new_game.rb +3 -0
  42. data/lib/ttt/interfaces/limelight/main_menu/props.rb +17 -0
  43. data/lib/ttt/interfaces/limelight/new_game/players/board_type.rb +1 -0
  44. data/lib/ttt/interfaces/limelight/new_game/players/new_game.rb +5 -0
  45. data/lib/ttt/interfaces/limelight/new_game/players/player_type.rb +1 -0
  46. data/lib/ttt/interfaces/limelight/new_game/players/setup_button.rb +21 -0
  47. data/lib/ttt/interfaces/limelight/new_game/props.rb +21 -0
  48. data/lib/ttt/interfaces/limelight/new_game/styles.rb +23 -0
  49. data/lib/ttt/interfaces/limelight/partials/board_partial.rb +14 -0
  50. data/lib/ttt/interfaces/limelight/partials/menu_button_partial.rb +5 -0
  51. data/lib/ttt/interfaces/limelight/partials/move_history_partial.rb +6 -0
  52. data/lib/ttt/interfaces/limelight/partials/title_history_partial.rb +10 -0
  53. data/lib/ttt/interfaces/limelight/players/board.rb +3 -0
  54. data/lib/ttt/interfaces/limelight/players/game.rb +4 -0
  55. data/lib/ttt/interfaces/limelight/players/generic_move_history.rb +5 -0
  56. data/lib/ttt/interfaces/limelight/players/generic_player.rb +8 -0
  57. data/lib/ttt/interfaces/limelight/players/left_button.rb +3 -0
  58. data/lib/ttt/interfaces/limelight/players/main_menu.rb +3 -0
  59. data/lib/ttt/interfaces/limelight/players/right_button.rb +3 -0
  60. data/lib/ttt/interfaces/limelight/playscripts/game_playscript.rb +169 -0
  61. data/lib/ttt/interfaces/limelight/production.rb +14 -0
  62. data/lib/ttt/interfaces/limelight/spec/game_playscript_spec.rb +218 -0
  63. data/lib/ttt/interfaces/limelight/spec/main_menu/main_menu_spec.rb +38 -0
  64. data/lib/ttt/interfaces/limelight/spec/new_game/new_game_spec.rb +45 -0
  65. data/lib/ttt/interfaces/limelight/spec/spec_helper.rb +8 -0
  66. data/lib/ttt/interfaces/limelight/stages.rb +6 -0
  67. data/lib/ttt/interfaces/limelight/styles.rb +119 -0
  68. data/lib/ttt/interfaces/limelight/three_by_three/props.rb +1 -0
  69. data/lib/ttt/interfaces/limelight/three_by_three/styles.rb +39 -0
  70. data/lib/ttt/interfaces/limelight/three_by_three_by_three/props.rb +1 -0
  71. data/lib/ttt/interfaces/limelight/three_by_three_by_three/styles.rb +119 -0
  72. data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/props.rb +1 -0
  73. data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/styles.rb +119 -0
  74. data/lib/ttt/interfaces/limelight/three_by_three_finished/props.rb +1 -0
  75. data/lib/ttt/interfaces/limelight/three_by_three_finished/styles.rb +39 -0
  76. data/lib/ttt/interfaces/rails/app/controllers/application_controller.rb +3 -0
  77. data/lib/ttt/interfaces/rails/app/controllers/ttt_games_controller.rb +86 -0
  78. data/lib/ttt/interfaces/rails/app/helpers/application_helper.rb +2 -0
  79. data/lib/ttt/interfaces/rails/app/models/ttt_game.rb +34 -0
  80. data/lib/ttt/interfaces/rails/app/presenters/web_game_presenter.rb +159 -0
  81. data/lib/ttt/interfaces/rails/autotest/discover.rb +1 -0
  82. data/lib/ttt/interfaces/rails/config/application.rb +67 -0
  83. data/lib/ttt/interfaces/rails/config/boot.rb +6 -0
  84. data/lib/ttt/interfaces/rails/config/environment.rb +5 -0
  85. data/lib/ttt/interfaces/rails/config/environments/development.rb +37 -0
  86. data/lib/ttt/interfaces/rails/config/environments/production.rb +67 -0
  87. data/lib/ttt/interfaces/rails/config/environments/test.rb +37 -0
  88. data/lib/ttt/interfaces/rails/config/initializers/backtrace_silencers.rb +7 -0
  89. data/lib/ttt/interfaces/rails/config/initializers/inflections.rb +15 -0
  90. data/lib/ttt/interfaces/rails/config/initializers/mime_types.rb +5 -0
  91. data/lib/ttt/interfaces/rails/config/initializers/secret_token.rb +7 -0
  92. data/lib/ttt/interfaces/rails/config/initializers/session_store.rb +8 -0
  93. data/lib/ttt/interfaces/rails/config/initializers/wrap_parameters.rb +14 -0
  94. data/lib/ttt/interfaces/rails/config/routes.rb +49 -0
  95. data/lib/ttt/interfaces/rails/db/schema.rb +28 -0
  96. data/lib/ttt/interfaces/rails/db/seeds.rb +7 -0
  97. data/lib/ttt/interfaces/rails/spec/controllers/ttt_games_controller_spec.rb +211 -0
  98. data/lib/ttt/interfaces/rails/spec/models/ttt_game_spec.rb +76 -0
  99. data/lib/ttt/interfaces/rails/spec/presenters/web_game_presenter_spec.rb +52 -0
  100. data/lib/ttt/interfaces/rails/spec/spec_helper.rb +13 -0
  101. data/lib/ttt/interfaces/web_interface/html_generator.rb +76 -0
  102. data/lib/ttt/interfaces/web_interface/web_game_presenter.rb +0 -0
  103. data/lib/ttt/minimax.rb +46 -0
  104. data/lib/ttt/move_history.rb +10 -0
  105. data/lib/ttt/move_traverser.rb +49 -0
  106. data/lib/ttt/player.rb +15 -0
  107. data/lib/ttt/riak_db.rb +53 -0
  108. data/lib/ttt/setup.rb +59 -0
  109. data/lib/ttt/three_by_three.rb +35 -0
  110. data/lib/ttt/three_by_three_by_three.rb +37 -0
  111. data/spec/spec_helper.rb +22 -0
  112. data/spec/ttt/ai_easy_spec.rb +15 -0
  113. data/spec/ttt/ai_hard_spec.rb +76 -0
  114. data/spec/ttt/ai_medium_spec.rb +65 -0
  115. data/spec/ttt/ai_spec.rb +50 -0
  116. data/spec/ttt/board_spec.rb +96 -0
  117. data/spec/ttt/config_helper_spec.rb +54 -0
  118. data/spec/ttt/context_spec.rb +398 -0
  119. data/spec/ttt/four_by_four_spec.rb +112 -0
  120. data/spec/ttt/game_history_spec.rb +41 -0
  121. data/spec/ttt/game_interactor_spec.rb +197 -0
  122. data/spec/ttt/game_spec.rb +246 -0
  123. data/spec/ttt/human_spec.rb +18 -0
  124. data/spec/ttt/move_history_spec.rb +13 -0
  125. data/spec/ttt/move_traverser_spec.rb +75 -0
  126. data/spec/ttt/player_spec.rb +13 -0
  127. data/spec/ttt/riak_db_spec.rb +64 -0
  128. data/spec/ttt/setup_spec.rb +63 -0
  129. data/spec/ttt/three_by_three_by_three_spec.rb +130 -0
  130. data/spec/ttt/three_by_three_spec.rb +110 -0
  131. 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