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.
- checksums.yaml +4 -4
- data/.travis.yml +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/Rakefile +2 -2
- data/bin/tic_tac_toes +21 -9
- data/lib/command_line/menu.rb +11 -12
- data/lib/command_line/{io.rb → prompt.rb} +1 -1
- data/lib/command_line/runner.rb +18 -18
- data/lib/tic_tac_toes/board_factory.rb +9 -0
- data/lib/tic_tac_toes/game_state.rb +27 -0
- data/lib/tic_tac_toes/game_state_factory.rb +14 -0
- data/lib/tic_tac_toes/history.rb +3 -3
- data/lib/tic_tac_toes/io.rb +91 -0
- data/lib/tic_tac_toes/move_strategies/easy_ai.rb +11 -0
- data/lib/tic_tac_toes/move_strategies/hard_ai.rb +59 -0
- data/lib/tic_tac_toes/move_strategies/human.rb +18 -0
- data/lib/tic_tac_toes/move_strategies/medium_ai.rb +13 -0
- data/lib/tic_tac_toes/player.rb +7 -7
- data/lib/tic_tac_toes/player_factory.rb +13 -9
- data/lib/tic_tac_toes/rules.rb +1 -1
- data/lib/tic_tac_toes/strings.rb +3 -2
- data/lib/tic_tac_toes/version.rb +1 -1
- data/spec/command_line/menu_spec.rb +40 -44
- data/spec/command_line/runner_spec.rb +23 -101
- data/spec/database/pg_wrapper_spec.rb +0 -1
- data/spec/test_board_generator.rb +15 -0
- data/spec/tic_tac_toes/board_factory_spec.rb +13 -0
- data/spec/tic_tac_toes/board_spec.rb +20 -25
- data/spec/tic_tac_toes/game_state_factory_spec.rb +15 -0
- data/spec/tic_tac_toes/game_state_spec.rb +66 -0
- data/spec/tic_tac_toes/history_spec.rb +3 -4
- data/spec/tic_tac_toes/io_spec.rb +163 -0
- data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +19 -0
- data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +95 -0
- data/spec/tic_tac_toes/move_strategies/human_spec.rb +31 -0
- data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +21 -0
- data/spec/tic_tac_toes/player_factory_spec.rb +13 -16
- data/spec/tic_tac_toes/player_spec.rb +14 -16
- data/spec/tic_tac_toes/rules_spec.rb +31 -41
- data/spec/tic_tac_toes/strings_spec.rb +0 -1
- metadata +32 -19
- data/lib/tic_tac_toes/easy_ai.rb +0 -9
- data/lib/tic_tac_toes/hard_ai.rb +0 -57
- data/lib/tic_tac_toes/io_interface.rb +0 -96
- data/lib/tic_tac_toes/medium_ai.rb +0 -11
- data/spec/tic_tac_toes/easy_ai_spec.rb +0 -20
- data/spec/tic_tac_toes/hard_ai_spec.rb +0 -103
- data/spec/tic_tac_toes/io_interface_spec.rb +0 -175
- data/spec/tic_tac_toes/medium_ai_spec.rb +0 -22
- data/spec/tic_tac_toes/spec_helper.rb +0 -13
- /data/lib/{tic_tac_toes/tasks → tasks}/destroy_databases.rake +0 -0
- /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/
|
4
|
-
require 'tic_tac_toes/
|
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
|
8
|
-
let(:
|
9
|
-
let(:
|
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
|
17
|
-
allow(
|
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(
|
21
|
+
expect(io).to receive(:invalid_row_size_error)
|
20
22
|
menu.get_board
|
21
23
|
end
|
22
24
|
|
23
|
-
it
|
24
|
-
allow(
|
25
|
-
allow(
|
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
|
-
|
35
|
-
|
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(
|
44
|
-
allow(
|
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.
|
48
|
-
expect(computer_player.
|
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) {
|
53
|
-
let(:first_token) {
|
54
|
-
let(:second_token) {
|
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(
|
54
|
+
allow(io).to receive(:get_difficulty) { valid_difficulty }
|
59
55
|
end
|
60
56
|
|
61
|
-
it
|
62
|
-
allow(
|
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(
|
60
|
+
expect(io).to receive(:invalid_token_error)
|
65
61
|
menu.get_players
|
66
62
|
end
|
67
63
|
|
68
|
-
it
|
69
|
-
allow(
|
70
|
-
allow(
|
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 =
|
84
|
-
allow(
|
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
|
88
|
-
allow(
|
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(
|
86
|
+
expect(io).to receive(:invalid_difficulty_error)
|
91
87
|
menu.get_players
|
92
88
|
end
|
93
89
|
|
94
|
-
it
|
95
|
-
allow(
|
96
|
-
allow(
|
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.
|
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
|
-
|
26
|
-
|
27
|
-
|
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
|
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
|
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
|
@@ -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 '
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
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(:
|
6
|
-
let(:history)
|
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(
|
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
|