bashrw_ttt 0.1.0

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