tic_tac_toes 0.0.2 → 0.0.3

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +2 -0
  5. data/Rakefile +2 -2
  6. data/bin/tic_tac_toes +21 -9
  7. data/lib/command_line/menu.rb +11 -12
  8. data/lib/command_line/{io.rb → prompt.rb} +1 -1
  9. data/lib/command_line/runner.rb +18 -18
  10. data/lib/tic_tac_toes/board_factory.rb +9 -0
  11. data/lib/tic_tac_toes/game_state.rb +27 -0
  12. data/lib/tic_tac_toes/game_state_factory.rb +14 -0
  13. data/lib/tic_tac_toes/history.rb +3 -3
  14. data/lib/tic_tac_toes/io.rb +91 -0
  15. data/lib/tic_tac_toes/move_strategies/easy_ai.rb +11 -0
  16. data/lib/tic_tac_toes/move_strategies/hard_ai.rb +59 -0
  17. data/lib/tic_tac_toes/move_strategies/human.rb +18 -0
  18. data/lib/tic_tac_toes/move_strategies/medium_ai.rb +13 -0
  19. data/lib/tic_tac_toes/player.rb +7 -7
  20. data/lib/tic_tac_toes/player_factory.rb +13 -9
  21. data/lib/tic_tac_toes/rules.rb +1 -1
  22. data/lib/tic_tac_toes/strings.rb +3 -2
  23. data/lib/tic_tac_toes/version.rb +1 -1
  24. data/spec/command_line/menu_spec.rb +40 -44
  25. data/spec/command_line/runner_spec.rb +23 -101
  26. data/spec/database/pg_wrapper_spec.rb +0 -1
  27. data/spec/test_board_generator.rb +15 -0
  28. data/spec/tic_tac_toes/board_factory_spec.rb +13 -0
  29. data/spec/tic_tac_toes/board_spec.rb +20 -25
  30. data/spec/tic_tac_toes/game_state_factory_spec.rb +15 -0
  31. data/spec/tic_tac_toes/game_state_spec.rb +66 -0
  32. data/spec/tic_tac_toes/history_spec.rb +3 -4
  33. data/spec/tic_tac_toes/io_spec.rb +163 -0
  34. data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +19 -0
  35. data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +95 -0
  36. data/spec/tic_tac_toes/move_strategies/human_spec.rb +31 -0
  37. data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +21 -0
  38. data/spec/tic_tac_toes/player_factory_spec.rb +13 -16
  39. data/spec/tic_tac_toes/player_spec.rb +14 -16
  40. data/spec/tic_tac_toes/rules_spec.rb +31 -41
  41. data/spec/tic_tac_toes/strings_spec.rb +0 -1
  42. metadata +32 -19
  43. data/lib/tic_tac_toes/easy_ai.rb +0 -9
  44. data/lib/tic_tac_toes/hard_ai.rb +0 -57
  45. data/lib/tic_tac_toes/io_interface.rb +0 -96
  46. data/lib/tic_tac_toes/medium_ai.rb +0 -11
  47. data/spec/tic_tac_toes/easy_ai_spec.rb +0 -20
  48. data/spec/tic_tac_toes/hard_ai_spec.rb +0 -103
  49. data/spec/tic_tac_toes/io_interface_spec.rb +0 -175
  50. data/spec/tic_tac_toes/medium_ai_spec.rb +0 -22
  51. data/spec/tic_tac_toes/spec_helper.rb +0 -13
  52. /data/lib/{tic_tac_toes/tasks → tasks}/destroy_databases.rake +0 -0
  53. /data/lib/{tic_tac_toes/tasks → tasks}/set_up_databases.rake +0 -0
@@ -1,73 +1,69 @@
1
- require 'tic_tac_toes/io_interface'
2
1
  require 'command_line/menu'
3
- require 'tic_tac_toes/medium_ai'
4
- require 'tic_tac_toes/spec_helper'
2
+ require 'tic_tac_toes/board_factory'
3
+ require 'tic_tac_toes/player_factory'
4
+ require 'tic_tac_toes/io'
5
+ require 'tic_tac_toes/move_strategies/human'
6
+ require 'tic_tac_toes/move_strategies/medium_ai'
5
7
 
6
8
  describe CommandLine::Menu do
7
- let(:io) { double("io") }
8
- let(:io_interface) { TicTacToes::IOInterface.new(io) }
9
- let(:menu) { CommandLine::Menu.new(io_interface) }
10
-
9
+ let(:io) { double }
10
+ let(:board_factory) { TicTacToes::BoardFactory.new }
11
+ let(:player_factory) { TicTacToes::PlayerFactory.new(io) }
12
+ let(:menu) { CommandLine::Menu.new(io, board_factory, player_factory) }
11
13
 
12
14
  describe '#get_board' do
13
15
  let(:invalid_row_size) { 11 }
14
16
  let(:valid_row_size) { 7 }
15
17
 
16
- it "displays an invalid row size error when given an invalid (outside 2-10) row size" do
17
- allow(io_interface).to receive(:get_row_size).and_return(invalid_row_size, valid_row_size)
18
+ it 'displays an invalid row size error when given a row size outside 2-10' do
19
+ allow(io).to receive(:get_row_size).and_return(invalid_row_size, valid_row_size)
18
20
 
19
- expect(io_interface).to receive(:invalid_row_size_error)
21
+ expect(io).to receive(:invalid_row_size_error)
20
22
  menu.get_board
21
23
  end
22
24
 
23
- it "only returns a row size once it receives a valid (2-10) row size" do
24
- allow(io_interface).to receive(:invalid_row_size_error)
25
- allow(io_interface).to receive(:get_row_size).and_return(invalid_row_size, valid_row_size)
25
+ it 'only returns a row size once it receives a valid (2-10) row size' do
26
+ allow(io).to receive(:invalid_row_size_error)
27
+ allow(io).to receive(:get_row_size).and_return(invalid_row_size, valid_row_size)
26
28
 
27
29
  board = menu.get_board
28
30
  expect(board.row_size).to eq(valid_row_size)
29
31
  end
30
32
  end
31
33
 
32
-
33
34
  describe '#get_players' do
34
- before(:each) do
35
- allow(io).to receive(:blue) { |argument| argument }
36
- allow(io).to receive(:red) { |argument| argument }
37
- end
38
-
39
- it "returns an array with a human (IO decider) player and a computer (AI decider) player" do
40
- first_token, second_token = "X", "O"
35
+ it 'returns an array with a human player and a computer player' do
36
+ first_token, second_token = 'X', 'O'
41
37
  difficulty = :medium
42
38
 
43
- allow(io_interface).to receive(:get_token).and_return(first_token, second_token)
44
- allow(io_interface).to receive(:get_difficulty).and_return(difficulty)
39
+ allow(io).to receive(:get_token).and_return(first_token, second_token)
40
+ allow(io).to receive(:get_difficulty).and_return(difficulty)
45
41
 
46
42
  human_player, computer_player = menu.get_players
47
- expect(human_player.decider).to be_a TicTacToes::IOInterface
48
- expect(computer_player.decider).to eq(TicTacToes::MediumAI)
43
+ expect(human_player.move_strategy).to be_a TicTacToes::MoveStrategies::Human
44
+ expect(computer_player.move_strategy).to eq(TicTacToes::MoveStrategies::MediumAI)
49
45
  end
50
46
 
51
47
  context 'when given an invalid (non-single-character) token' do
52
- let(:invalid_token) { "invalid" }
53
- let(:first_token) { "X" }
54
- let(:second_token) { "O" }
48
+ let(:invalid_token) { 'invalid' }
49
+ let(:first_token) { 'X' }
50
+ let(:second_token) { 'O' }
55
51
 
56
52
  before(:each) do
57
53
  valid_difficulty = :medium
58
- allow(io_interface).to receive(:get_difficulty) { valid_difficulty }
54
+ allow(io).to receive(:get_difficulty) { valid_difficulty }
59
55
  end
60
56
 
61
- it "displays an invalid token error" do
62
- allow(io_interface).to receive(:get_token).and_return(invalid_token, first_token, second_token)
57
+ it 'displays an invalid token error' do
58
+ allow(io).to receive(:get_token).and_return(invalid_token, first_token, second_token)
63
59
 
64
- expect(io_interface).to receive(:invalid_token_error)
60
+ expect(io).to receive(:invalid_token_error)
65
61
  menu.get_players
66
62
  end
67
63
 
68
- it "only returns an array of players (with correct tokens) once it receives a valid token" do
69
- allow(io_interface).to receive(:invalid_token_error)
70
- allow(io_interface).to receive(:get_token).and_return(invalid_token, first_token, second_token)
64
+ it 'only returns an array of players (with correct tokens) once it receives a valid token' do
65
+ allow(io).to receive(:invalid_token_error)
66
+ allow(io).to receive(:get_token).and_return(invalid_token, first_token, second_token)
71
67
 
72
68
  first_player, second_player = menu.get_players
73
69
  expect(first_player.token).to eq(first_token)
@@ -80,23 +76,23 @@ describe CommandLine::Menu do
80
76
  let(:valid_difficulty) { :medium }
81
77
 
82
78
  before(:each) do
83
- first_token, second_token = "X", "O"
84
- allow(io_interface).to receive(:get_token).and_return(first_token, second_token)
79
+ first_token, second_token = 'X', 'O'
80
+ allow(io).to receive(:get_token).and_return(first_token, second_token)
85
81
  end
86
82
 
87
- it "displays an invalid difficulty error" do
88
- allow(io_interface).to receive(:get_difficulty).and_return(invalid_difficulty, valid_difficulty)
83
+ it 'displays an invalid difficulty error' do
84
+ allow(io).to receive(:get_difficulty).and_return(invalid_difficulty, valid_difficulty)
89
85
 
90
- expect(io_interface).to receive(:invalid_difficulty_error)
86
+ expect(io).to receive(:invalid_difficulty_error)
91
87
  menu.get_players
92
88
  end
93
89
 
94
- it "only returns an array of players (with correct difficulty) once it receives a valid difficulty" do
95
- allow(io_interface).to receive(:invalid_difficulty_error)
96
- allow(io_interface).to receive(:get_difficulty).and_return(invalid_difficulty, valid_difficulty)
90
+ it 'only returns an array of players (with correct difficulty) once it receives a valid difficulty' do
91
+ allow(io).to receive(:invalid_difficulty_error)
92
+ allow(io).to receive(:get_difficulty).and_return(invalid_difficulty, valid_difficulty)
97
93
 
98
94
  computer_player = menu.get_players.last
99
- expect(computer_player.decider).to eq(TicTacToes::MediumAI)
95
+ expect(computer_player.move_strategy).to eq(TicTacToes::MoveStrategies::MediumAI)
100
96
  end
101
97
  end
102
98
  end
@@ -1,119 +1,41 @@
1
1
  require 'command_line/runner'
2
- require 'tic_tac_toes/board'
3
- require 'tic_tac_toes/history'
4
- require 'tic_tac_toes/spec_helper'
5
2
 
6
3
  describe CommandLine::Runner do
7
- let(:io_interface) { double("io interface",
8
- :draw_board => true,
9
- :thinking_notification => true,
10
- :game_over_notification => true) }
11
- let(:board) { TicTacToes::Board.new(row_size: 3) }
12
- let(:menu) { double("menu",
13
- :get_board => board,
14
- :get_players => true) }
15
- let(:rules) { double("rules",
16
- :game_over? => true,
17
- :determine_winner => "X") }
18
- let(:database_interface) { double("database interface", :record_game_history => true) }
19
- let(:history) { TicTacToes::History.new(database_interface) }
20
-
21
- let(:runner) { CommandLine::Runner.new(io_interface, menu, rules, history) }
22
-
23
-
24
4
  describe '#run' do
25
- it "gets a board and players" do
26
- expect(menu).to receive(:get_board)
27
- expect(menu).to receive(:get_players)
5
+ let(:game_state) { double(board: 'board', players: 'players', game_over: true) }
6
+
7
+ let(:io) { double(draw_board: true,
8
+ thinking_notification: true,
9
+ game_over_notification: true) }
10
+ let(:menu) { double(get_board: true,
11
+ get_players: true) }
12
+ let(:game_state) { double(board: 'board',
13
+ players: 'players',
14
+ game_over: true) }
15
+ let(:game_state_factory) { double(generate_game_state: game_state) }
16
+ let(:rules) { double(game_over?: true,
17
+ determine_winner: true) }
18
+
19
+ let(:runner) { CommandLine::Runner.new(io,
20
+ menu,
21
+ game_state_factory,
22
+ rules) }
23
+
24
+ it 'gets an initial game state' do
25
+ expect(game_state_factory).to receive(:generate_game_state)
28
26
  runner.run
29
27
  end
30
28
 
31
- it "has its history object record the board size" do
32
- runner.run
33
- expect(history.board_size).to eq(board.size)
34
- end
35
-
36
- it "takes turns until the game is over" do
29
+ it 'takes turns until the game is over' do
37
30
  allow(rules).to receive(:game_over?).and_return(false, true)
38
31
 
39
32
  expect(runner).to receive(:take_turn).once
40
33
  runner.run
41
34
  end
42
35
 
43
- it "ends the game when the game is over" do
36
+ it 'ends the game when the game is over' do
44
37
  expect(runner).to receive(:end_game)
45
38
  runner.run
46
39
  end
47
40
  end
48
-
49
-
50
- describe '#take_turn' do
51
- move = ["X", 0]
52
- let(:board) { double("board") }
53
- let(:first_player) { double("first player", make_move: move, needs_to_think: true) }
54
- let(:second_player) { double("second player", make_move: move, needs_to_think: false) }
55
- let(:players) { [first_player, second_player] }
56
-
57
- it "draws the board" do
58
- expect(io_interface).to receive(:draw_board)
59
- runner.take_turn(board, players)
60
- end
61
-
62
- it "displays a thinking notification if the current player needs to think" do
63
- expect(io_interface).to receive(:thinking_notification)
64
- runner.take_turn(board, players)
65
-
66
- expect(io_interface).not_to receive(:thinking_notification)
67
- runner.take_turn(board, players)
68
- end
69
-
70
- it "asks the first player to make a move" do
71
- expect(first_player).to receive(:make_move)
72
- runner.take_turn(board, players)
73
- end
74
-
75
- it "has its history object record the move" do
76
- runner.take_turn(board, players)
77
- expect(history.moves.first).to eql(move)
78
- end
79
-
80
- it "keeps track of the current player by rotating the players" do
81
- runner.take_turn(board, players)
82
- expect(second_player).to receive(:make_move)
83
- runner.take_turn(board, players)
84
- end
85
- end
86
-
87
-
88
- describe '#end_game' do
89
- let(:board) { double("board") }
90
- let(:players) { double("players") }
91
-
92
- it "draws the board" do
93
- expect(io_interface).to receive(:draw_board)
94
- runner.end_game(board, players)
95
- end
96
-
97
- it "determines the winner" do
98
- expect(rules).to receive(:determine_winner)
99
- runner.end_game(board, players)
100
- end
101
-
102
- it "displays the winner" do
103
- allow(rules).to receive(:determine_winner) { :winner }
104
-
105
- expect(io_interface).to receive(:game_over_notification).with(:winner)
106
- runner.end_game(board, players)
107
- end
108
-
109
- it "has its history object record the winner" do
110
- runner.end_game(board, players)
111
- expect(history.winner).to eq("X")
112
- end
113
-
114
- it "has its history object persist the game history" do
115
- expect(history).to receive(:persist)
116
- runner.end_game(board, players)
117
- end
118
- end
119
41
  end
@@ -1,6 +1,5 @@
1
1
  require 'database/pg_wrapper'
2
2
  require 'pg'
3
- require 'tic_tac_toes/spec_helper'
4
3
 
5
4
  describe Database::PGWrapper do
6
5
  database = "tic_tac_toes_test"
@@ -0,0 +1,15 @@
1
+ require 'tic_tac_toes/board'
2
+
3
+ module TestBoardGenerator
4
+ def self.generate(structure)
5
+ board_size = structure.count
6
+ row_size = Math.sqrt(board_size)
7
+ board = TicTacToes::Board.new(row_size: row_size)
8
+
9
+ structure.each_with_index do |player, index|
10
+ board.place(player, index)
11
+ end
12
+
13
+ board
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'tic_tac_toes/board_factory'
2
+
3
+ describe TicTacToes::BoardFactory do
4
+ describe '#generate_board' do
5
+ it 'returns a board object generated from a row size' do
6
+ board_factory = TicTacToes::BoardFactory.new
7
+ row_size = 5
8
+
9
+ board = board_factory.generate_board(row_size)
10
+ expect(board.row_size).to eq(row_size)
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require 'tic_tac_toes/spec_helper'
1
+ require 'test_board_generator'
2
2
  require 'tic_tac_toes/board'
3
3
 
4
4
  describe TicTacToes::Board do
@@ -28,10 +28,9 @@ describe TicTacToes::Board do
28
28
 
29
29
  describe '#open_spaces' do
30
30
  it "returns an array of the board's nil spaces" do
31
- structure = [:X, :O, nil,
32
- :O, :O, :X,
33
- :X, :X, nil]
34
- board = generate_board(structure)
31
+ board = TestBoardGenerator.generate([:X, :O, nil,
32
+ :O, :O, :X,
33
+ :X, :X, nil])
35
34
  open_spaces = [2, 8]
36
35
 
37
36
  expect(board.open_spaces).to eql(open_spaces)
@@ -41,11 +40,10 @@ describe TicTacToes::Board do
41
40
 
42
41
  describe '#rows' do
43
42
  it "returns an array of row arrays based on the board's spaces" do
44
- structure = [ :X, :X, :X, :X,
45
- nil, nil, nil, nil,
46
- nil, nil, nil, nil,
47
- nil, nil, nil, nil]
48
- board = generate_board(structure)
43
+ board = TestBoardGenerator.generate([ :X, :X, :X, :X,
44
+ nil, nil, nil, nil,
45
+ nil, nil, nil, nil,
46
+ nil, nil, nil, nil])
49
47
  first_row = [:X, :X, :X, :X]
50
48
 
51
49
  expect(board.rows.size).to eq(4)
@@ -56,11 +54,10 @@ describe TicTacToes::Board do
56
54
 
57
55
  describe '#columns' do
58
56
  it "returns an array of column arrays based on the board's spaces" do
59
- structure = [:X, nil, nil, nil,
60
- :X, nil, nil, nil,
61
- :X, nil, nil, nil,
62
- :X, nil, nil, nil]
63
- board = generate_board(structure)
57
+ board = TestBoardGenerator.generate([:X, nil, nil, nil,
58
+ :X, nil, nil, nil,
59
+ :X, nil, nil, nil,
60
+ :X, nil, nil, nil])
64
61
  first_column = [:X, :X, :X, :X]
65
62
 
66
63
  expect(board.columns.size).to eq(4)
@@ -71,12 +68,11 @@ describe TicTacToes::Board do
71
68
 
72
69
  describe '#diagonals' do
73
70
  it "returns an array of diagonal arrays based on the board's spaces" do
74
- structure = [ :X, nil, nil, :O,
75
- nil, :X, :O, nil,
76
- nil, :O, :X, nil,
77
- :O, nil, nil, :X]
78
- board = generate_board(structure)
79
- back_diagonal = [:X, :X, :X, :X]
71
+ board = TestBoardGenerator.generate([ :X, nil, nil, :O,
72
+ nil, :X, :O, nil,
73
+ nil, :O, :X, nil,
74
+ :O, nil, nil, :X])
75
+ back_diagonal = [:X, :X, :X, :X]
80
76
  front_diagonal = [:O, :O, :O, :O]
81
77
 
82
78
  expect(board.diagonals.size).to eq(2)
@@ -93,10 +89,9 @@ describe TicTacToes::Board do
93
89
  end
94
90
 
95
91
  it "returns true if all spaces are non-nil" do
96
- structure = [:X, :O, :X,
97
- :O, :X, :O,
98
- :X, :O, :X]
99
- board = generate_board(structure)
92
+ board = TestBoardGenerator.generate([:X, :O, :X,
93
+ :O, :X, :O,
94
+ :X, :O, :X])
100
95
 
101
96
  expect(board.full?).to be true
102
97
  end
@@ -0,0 +1,15 @@
1
+ require 'tic_tac_toes/game_state_factory'
2
+
3
+ describe TicTacToes::GameStateFactory do
4
+ describe '#generate_game_state' do
5
+ it 'returns a game state object generated from a board object and player array' do
6
+ history = double(record_board_size: true)
7
+ game_state_factory = TicTacToes::GameStateFactory.new(history)
8
+ board, players = double(size: 3), double
9
+
10
+ game_state = game_state_factory.generate_game_state(board, players)
11
+ expect(game_state.board).to eq(board)
12
+ expect(game_state.players).to eq(players)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,66 @@
1
+ require 'tic_tac_toes/game_state'
2
+
3
+ describe TicTacToes::GameState do
4
+ describe '@initialize' do
5
+ it "records its board's size" do
6
+ size = 5
7
+ board = double(size: size)
8
+ history = double
9
+
10
+ expect(history).to receive(:record_board_size).with(size)
11
+ TicTacToes::GameState.new(board, 'players', history)
12
+ end
13
+ end
14
+
15
+ describe '#current_player' do
16
+ it 'returns the first item of its players array' do
17
+ history = double(record_board_size: true)
18
+ players = ['first_player', 'second_player']
19
+ game_state = TicTacToes::GameState.new('board', players, history)
20
+
21
+ current_player = game_state.current_player
22
+ expect(current_player).to eq('first_player')
23
+ end
24
+ end
25
+
26
+ describe '#turn_over' do
27
+ it 'records the last move' do
28
+ move = double
29
+ players = double(rotate!: true)
30
+ history = double(record_board_size: true)
31
+ game_state = TicTacToes::GameState.new('board', players, history)
32
+
33
+ expect(history).to receive(:record_move).with(move)
34
+ game_state.turn_over(move)
35
+ end
36
+
37
+ it 'rotates its player array' do
38
+ players = ['first_player', 'second_player']
39
+ history = double(record_board_size: true, record_move: true)
40
+ game_state = TicTacToes::GameState.new('board', players, history)
41
+
42
+ game_state.turn_over('move')
43
+ expect(game_state.current_player).to eq('second_player')
44
+ end
45
+ end
46
+
47
+ describe '#game_over' do
48
+ it 'records the winner' do
49
+ winner = double
50
+ history = double(record_board_size: true, persist: true)
51
+ game_state = TicTacToes::GameState.new('board', 'players', history)
52
+
53
+ expect(history).to receive(:record_winner).with(winner)
54
+ game_state.game_over(winner)
55
+ end
56
+
57
+ it 'persists its history' do
58
+ winner = double
59
+ history = double(record_board_size: true, record_winner: true)
60
+ game_state = TicTacToes::GameState.new('board', 'players', history)
61
+
62
+ expect(history).to receive(:persist)
63
+ game_state.game_over(winner)
64
+ end
65
+ end
66
+ end
@@ -1,9 +1,8 @@
1
- require 'tic_tac_toes/spec_helper'
2
1
  require 'tic_tac_toes/history'
3
2
 
4
3
  describe TicTacToes::History do
5
- let(:database_interface) { double("database interface", :record_game_history => true) }
6
- let(:history) { TicTacToes::History.new(database_interface) }
4
+ let(:database_wrapper) { double("database wrapper", :record_game_history => true) }
5
+ let(:history) { TicTacToes::History.new(database_wrapper) }
7
6
 
8
7
  describe '#record_board_size' do
9
8
  it "records the passed board size" do
@@ -34,7 +33,7 @@ describe TicTacToes::History do
34
33
 
35
34
  describe '#persist' do
36
35
  it "sends the history instance to its database interface for storage" do
37
- expect(database_interface).to receive(:record_game_history).with history
36
+ expect(database_wrapper).to receive(:record_game_history).with history
38
37
  history.persist
39
38
  end
40
39
  end
@@ -0,0 +1,163 @@
1
+ require 'tic_tac_toes/board'
2
+ require 'tic_tac_toes/io'
3
+ require 'tic_tac_toes/strings'
4
+
5
+ describe TicTacToes::IO do
6
+ let(:strings) { TicTacToes::Strings }
7
+ let(:prompt) { double("prompt", solicit_input: 0, display: true, display_red: true) }
8
+ let(:io) { TicTacToes::IO.new(prompt) }
9
+
10
+ describe '#get_row_size' do
11
+ it "displays a row size solicitation" do
12
+ expect(io).to receive(:row_size_solicitation)
13
+ io.get_row_size
14
+ end
15
+
16
+ context 'when given not-integer-like input' do
17
+ let(:not_integer_like) { "string" }
18
+ let(:integer_like) { "100" }
19
+
20
+ it "displays a not an integer error" do
21
+ allow(prompt).to receive(:solicit_input).and_return(not_integer_like, integer_like)
22
+
23
+ expect(io).to receive(:not_an_integer_error).once
24
+ io.get_row_size
25
+ end
26
+
27
+ it "only returns a row size (converted to integer) once it gets integer-like input" do
28
+ allow(prompt).to receive(:solicit_input).and_return(not_integer_like, integer_like)
29
+
30
+ expect(io.get_row_size).to eq(100)
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ describe '#get_token' do
37
+ let(:player) { double("player") }
38
+
39
+ it "displays a token solicitation with the name of the player whose token is to be set" do
40
+ expect(io).to receive(:token_solicitation).with(player)
41
+ io.get_token(player)
42
+ end
43
+
44
+ it "returns the user input" do
45
+ token = "X"
46
+ allow(prompt).to receive(:solicit_input) { token }
47
+
48
+ expect(io.get_token(player)).to equal(token)
49
+ end
50
+ end
51
+
52
+
53
+ describe '#get_difficulty' do
54
+ let(:difficulty) { "HARD" }
55
+
56
+ it "displays a difficulty solicitation" do
57
+ allow(prompt).to receive(:solicit_input) { difficulty }
58
+
59
+ expect(io).to receive(:difficulty_solicitation)
60
+ io.get_difficulty
61
+ end
62
+
63
+ it "returns the user input (downcased and converted into a symbol)" do
64
+ allow(prompt).to receive(:solicit_input) { difficulty }
65
+
66
+ expect(io.get_difficulty).to equal(:hard)
67
+ end
68
+ end
69
+
70
+
71
+ describe '#draw_board' do
72
+ it "displays a board string" do
73
+ board = TicTacToes::Board.new
74
+ board_string = strings.board(board)
75
+
76
+ expect(prompt).to receive(:display).with(board_string)
77
+ io.draw_board(board)
78
+ end
79
+ end
80
+
81
+
82
+ describe '#invalid_row_size_error' do
83
+ it "displays a red invalid row size message" do
84
+ expect(prompt).to receive(:display_red).with(strings::INVALID_ROW_SIZE)
85
+ io.invalid_row_size_error
86
+ end
87
+ end
88
+
89
+
90
+ describe '#invalid_token_error' do
91
+ it "displays a red invalid token message" do
92
+ expect(prompt).to receive(:display_red).with(strings::INVALID_TOKEN)
93
+ io.invalid_token_error
94
+ end
95
+ end
96
+
97
+
98
+ describe '#invalid_difficulty_error' do
99
+ it "displays a red invalid difficulty message" do
100
+ expect(prompt).to receive(:display_red).with(strings::INVALID_DIFFICULTY)
101
+ io.invalid_difficulty_error
102
+ end
103
+ end
104
+
105
+
106
+ describe '#invalid_move_error' do
107
+ it "displays a red invalid move message" do
108
+ expect(prompt).to receive(:display_red).with(strings::INVALID_MOVE)
109
+ io.invalid_move_error
110
+ end
111
+ end
112
+
113
+
114
+ describe '#thinking_notification' do
115
+ it "displays a red thinking message" do
116
+ expect(prompt).to receive(:display_red).with(strings::THINKING)
117
+ io.thinking_notification
118
+ end
119
+ end
120
+
121
+
122
+ describe '#game_over_notification' do
123
+ it "displays a game over message with the name of the winner" do
124
+ winner = :X
125
+ game_over_message = strings.game_over_notification(winner)
126
+
127
+ expect(prompt).to receive(:display).with(game_over_message)
128
+ io.game_over_notification(winner)
129
+ end
130
+ end
131
+
132
+
133
+ describe '#move_solicitation' do
134
+ it "displays a move solicitation message" do
135
+ expect(prompt).to receive(:display).with(strings::MOVE_SOLICITATION)
136
+ io.move_solicitation
137
+ end
138
+ end
139
+
140
+
141
+ describe '#not_an_integer_error' do
142
+ it "displays a red not an integer message" do
143
+ expect(prompt).to receive(:display_red).with(strings::NOT_AN_INTEGER)
144
+ io.not_an_integer_error
145
+ end
146
+ end
147
+
148
+
149
+ describe '#red' do
150
+ it "calls its IO's red method with the passed message" do
151
+ expect(io).to receive(:red).with("message")
152
+ io.red("message")
153
+ end
154
+ end
155
+
156
+
157
+ describe '#blue' do
158
+ it "calls its IO's blue method with the passed message" do
159
+ expect(io).to receive(:blue).with("message")
160
+ io.blue("message")
161
+ end
162
+ end
163
+ end