bashrw_ttt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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