tic_tac_toes 0.1.4 → 0.1.6
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/Gemfile.lock +1 -1
- data/bin/game_histories +2 -1
- data/lib/tic_tac_toes/core/game_state.rb +8 -1
- data/lib/tic_tac_toes/core/history.rb +1 -1
- data/lib/tic_tac_toes/core/io.rb +13 -13
- data/lib/tic_tac_toes/core/{strings.rb → presenter.rb} +17 -1
- data/lib/tic_tac_toes/ui/adapter.rb +38 -30
- data/lib/tic_tac_toes/ui/serializer.rb +34 -7
- data/lib/tic_tac_toes/version.rb +1 -1
- data/spec/tic_tac_toes/core/game_state_spec.rb +19 -0
- data/spec/tic_tac_toes/core/history_spec.rb +8 -1
- data/spec/tic_tac_toes/core/io_spec.rb +2 -2
- data/spec/tic_tac_toes/core/presenter_spec.rb +95 -0
- data/spec/tic_tac_toes/ui/adapter_spec.rb +21 -6
- data/spec/tic_tac_toes/ui/serializer_spec.rb +18 -8
- metadata +5 -8
- data/lib/tic_tac_toes/command_line/history_reader.rb +0 -28
- data/spec/tic_tac_toes/command_line/history_reader_spec.rb +0 -48
- data/spec/tic_tac_toes/core/strings_spec.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 973d4205866381b247cee7397ef538d46d26dbd3
|
4
|
+
data.tar.gz: 8383fa3bd6d89f0e1dc082bf15d58e22315c4d16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38e1a5192560eb071120b41079ae56a77e442b5fefe60659537868e45eb76277f05ffd74b4510c1d707009c3a5a9bb22582bd30cd66a9fdc67ec6524d471a92f
|
7
|
+
data.tar.gz: 81602fbe1f20241e27b4400815bc4ee3fdbbb1bdaa9622de099134e4b043db4dd640bb6880258b072f7d8e16b0827dc9717904749ff8ae94ca5d4518cbfa0177
|
data/Gemfile.lock
CHANGED
data/bin/game_histories
CHANGED
@@ -4,10 +4,11 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
|
4
4
|
|
5
5
|
require 'tic_tac_toes/database/pg_wrapper'
|
6
6
|
require 'tic_tac_toes/command_line/history_reader'
|
7
|
+
require 'tic_tac_toes/command_line/prompt'
|
7
8
|
|
8
9
|
database = 'tic_tac_toes'
|
9
10
|
|
10
11
|
wrapper = TicTacToes::Database::PGWrapper.new(database)
|
11
12
|
reader = TicTacToes::CommandLine::HistoryReader.new(wrapper)
|
12
13
|
|
13
|
-
reader.
|
14
|
+
reader.game_history_strings.each { |history| TicTacToes::CommandLine::Prompt.display(history) }
|
@@ -29,11 +29,19 @@ module TicTacToes
|
|
29
29
|
@players[1]
|
30
30
|
end
|
31
31
|
|
32
|
+
def computer_player
|
33
|
+
@players.detect { |player| player.move_strategy.type == MoveStrategies::COMPUTER }
|
34
|
+
end
|
35
|
+
|
32
36
|
def turn_over(move)
|
33
37
|
@history.record_move(move)
|
34
38
|
@players.rotate!
|
35
39
|
end
|
36
40
|
|
41
|
+
def moves
|
42
|
+
@history.moves
|
43
|
+
end
|
44
|
+
|
37
45
|
def end_game(winner)
|
38
46
|
@history.record_winner(winner)
|
39
47
|
@history.persist
|
@@ -54,7 +62,6 @@ module TicTacToes
|
|
54
62
|
end
|
55
63
|
|
56
64
|
def record_difficulty
|
57
|
-
computer_player = @players.detect { |player| player.move_strategy.type == MoveStrategies::COMPUTER }
|
58
65
|
@history.record_difficulty(computer_player.move_strategy)
|
59
66
|
end
|
60
67
|
end
|
data/lib/tic_tac_toes/core/io.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'tic_tac_toes/core/
|
1
|
+
require 'tic_tac_toes/core/presenter'
|
2
2
|
|
3
3
|
module TicTacToes
|
4
4
|
module Core
|
@@ -27,41 +27,41 @@ module TicTacToes
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def draw_board(board)
|
30
|
-
@io_strategy.display(
|
30
|
+
@io_strategy.display(Presenter.board(board))
|
31
31
|
end
|
32
32
|
|
33
33
|
def invalid_row_size_error
|
34
|
-
@io_strategy.display_red(
|
34
|
+
@io_strategy.display_red(Presenter::INVALID_ROW_SIZE)
|
35
35
|
end
|
36
36
|
|
37
37
|
def invalid_token_error
|
38
|
-
@io_strategy.display_red(
|
38
|
+
@io_strategy.display_red(Presenter::INVALID_TOKEN)
|
39
39
|
end
|
40
40
|
|
41
41
|
def invalid_difficulty_error
|
42
|
-
@io_strategy.display_red(
|
42
|
+
@io_strategy.display_red(Presenter::INVALID_DIFFICULTY)
|
43
43
|
end
|
44
44
|
|
45
45
|
def invalid_move_error
|
46
|
-
@io_strategy.display_red(
|
46
|
+
@io_strategy.display_red(Presenter::INVALID_MOVE)
|
47
47
|
end
|
48
48
|
|
49
49
|
def thinking_notification
|
50
|
-
@io_strategy.display_red(
|
50
|
+
@io_strategy.display_red(Presenter::THINKING)
|
51
51
|
end
|
52
52
|
|
53
53
|
def game_over_notification(winner)
|
54
54
|
winner = "Nobody" if winner.nil?
|
55
55
|
winner = winner.token unless winner.is_a? String
|
56
|
-
@io_strategy.display(
|
56
|
+
@io_strategy.display(Presenter.game_over_notification(winner))
|
57
57
|
end
|
58
58
|
|
59
59
|
def move_solicitation
|
60
|
-
@io_strategy.display(
|
60
|
+
@io_strategy.display(Presenter::MOVE_SOLICITATION)
|
61
61
|
end
|
62
62
|
|
63
63
|
def not_an_integer_error
|
64
|
-
@io_strategy.display_red(
|
64
|
+
@io_strategy.display_red(Presenter::NOT_AN_INTEGER)
|
65
65
|
end
|
66
66
|
|
67
67
|
def solicit_input
|
@@ -79,15 +79,15 @@ module TicTacToes
|
|
79
79
|
private
|
80
80
|
|
81
81
|
def row_size_solicitation
|
82
|
-
@io_strategy.display(
|
82
|
+
@io_strategy.display(Presenter::ROW_SIZE_SOLICITATION)
|
83
83
|
end
|
84
84
|
|
85
85
|
def token_solicitation(player)
|
86
|
-
@io_strategy.display(
|
86
|
+
@io_strategy.display(Presenter.token_solicitation(player))
|
87
87
|
end
|
88
88
|
|
89
89
|
def difficulty_solicitation
|
90
|
-
@io_strategy.display(
|
90
|
+
@io_strategy.display(Presenter::DIFFICULTY_SOLICITATION)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -3,7 +3,7 @@ require 'tic_tac_toes/core/rules'
|
|
3
3
|
|
4
4
|
module TicTacToes
|
5
5
|
module Core
|
6
|
-
module
|
6
|
+
module Presenter
|
7
7
|
smallest_row_size = Rules::ROW_SIZE_RANGE.min
|
8
8
|
largest_row_size = Rules::ROW_SIZE_RANGE.max
|
9
9
|
|
@@ -101,6 +101,22 @@ module TicTacToes
|
|
101
101
|
truncated_length.times { horizontal_divider << divider_unit }
|
102
102
|
horizontal_divider
|
103
103
|
end
|
104
|
+
|
105
|
+
def self.game_history_strings(wrapper)
|
106
|
+
game_histories = wrapper.read_game_histories
|
107
|
+
game_histories.map { |history| game_history_string(history) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.game_history_string(history)
|
111
|
+
history_string = ''
|
112
|
+
history_string << "Board size: #{history.board_size.to_s}\n"
|
113
|
+
history_string << "Difficulty: #{history.difficulty}\n"
|
114
|
+
history_string << "Winner: #{history.winner}\n"
|
115
|
+
history_string << "Move history:\n"
|
116
|
+
history.moves.each { |move| history_string << " #{move.join(' -> ')}\n" }
|
117
|
+
history_string << "\n"
|
118
|
+
history_string
|
119
|
+
end
|
104
120
|
end
|
105
121
|
end
|
106
122
|
end
|
@@ -1,37 +1,29 @@
|
|
1
1
|
require 'tic_tac_toes/core/player_factory'
|
2
2
|
require 'tic_tac_toes/core/move_strategies/hard_ai'
|
3
|
+
require 'tic_tac_toes/core/move_strategies/types'
|
3
4
|
|
4
5
|
module TicTacToes
|
5
6
|
module UI
|
6
7
|
module Adapter
|
7
8
|
def self.start_game(order, game_state, listener)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
when 'second'
|
12
|
-
make_move(game_state, nil, listener)
|
13
|
-
end
|
9
|
+
user_is_first = order == 'first'
|
10
|
+
|
11
|
+
user_is_first ? listener.moves_were_made(game_state) : make_move(game_state, nil, listener)
|
14
12
|
end
|
15
13
|
|
16
14
|
def self.make_move(game_state, move, listener)
|
17
|
-
|
18
|
-
move = move.to_i
|
19
|
-
game_state.place_move(move)
|
20
|
-
end
|
21
|
-
|
22
|
-
if game_state.game_over?
|
23
|
-
tell_listener_game_is_over(game_state, listener)
|
24
|
-
return
|
25
|
-
end
|
15
|
+
computer_first = move.nil?
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
17
|
+
if computer_first
|
18
|
+
game_state.turn_over(nil)
|
19
|
+
computer_move(game_state, listener)
|
20
|
+
return game_over(game_state, listener) if game_state.game_over?
|
21
|
+
else
|
22
|
+
human_move(game_state, move, listener)
|
23
|
+
return game_over(game_state, listener) if game_state.game_over?
|
24
|
+
computer_move(game_state, listener)
|
25
|
+
return game_over(game_state, listener) if game_state.game_over?
|
33
26
|
end
|
34
|
-
|
35
27
|
listener.moves_were_made(game_state)
|
36
28
|
end
|
37
29
|
|
@@ -43,7 +35,7 @@ module TicTacToes
|
|
43
35
|
potential_game_state = generate_game_state(game_state, space)
|
44
36
|
next predictions << predicted_result(potential_game_state) if potential_game_state.game_over?
|
45
37
|
|
46
|
-
potential_game_state.turn_over(
|
38
|
+
potential_game_state.turn_over(nil)
|
47
39
|
potential_game_state.place_move(Core::MoveStrategies::HardAI.move(potential_game_state))
|
48
40
|
next predictions << predicted_result(potential_game_state) if potential_game_state.game_over?
|
49
41
|
|
@@ -54,20 +46,36 @@ module TicTacToes
|
|
54
46
|
|
55
47
|
private
|
56
48
|
|
57
|
-
def self.
|
49
|
+
def self.computer_move(game_state, listener)
|
50
|
+
computer_player = game_state.computer_player
|
51
|
+
computer_move = computer_player.place_and_return_move(game_state)
|
52
|
+
game_state.turn_over(computer_move)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.human_move(game_state, move, listener)
|
56
|
+
human_player = game_state.current_player
|
57
|
+
game_state.place_move(move.to_i)
|
58
|
+
game_state.turn_over([human_player.token, move.to_i])
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.game_over(game_state, listener)
|
58
62
|
winning_player = game_state.determine_winner
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
listener.game_ended_in_draw(game_state)
|
63
|
-
else
|
64
|
+
record_game_history(game_state, winning_player)
|
65
|
+
if winning_player
|
64
66
|
listener.game_ended_in_winner(game_state, winning_player.token)
|
67
|
+
else
|
68
|
+
listener.game_ended_in_draw(game_state)
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
72
|
+
def self.record_game_history(game_state, winning_player)
|
73
|
+
game_state.start_game
|
74
|
+
game_state.end_game(winning_player)
|
75
|
+
end
|
76
|
+
|
68
77
|
def self.generate_game_state(game_state, space)
|
69
78
|
new_game_state = Marshal.load(Marshal.dump(game_state))
|
70
|
-
new_game_state.turn_over([])
|
71
79
|
new_game_state.place_move(space)
|
72
80
|
new_game_state
|
73
81
|
end
|
@@ -77,7 +85,7 @@ module TicTacToes
|
|
77
85
|
|
78
86
|
if winner.nil?
|
79
87
|
"tie"
|
80
|
-
elsif winner.move_strategy.
|
88
|
+
elsif winner.move_strategy.type == Core::MoveStrategies::HUMAN
|
81
89
|
"win"
|
82
90
|
else
|
83
91
|
"loss"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'tic_tac_toes/core/board'
|
2
2
|
require 'tic_tac_toes/core/game_state'
|
3
3
|
require 'tic_tac_toes/core/player_factory'
|
4
|
+
require 'tic_tac_toes/database/pg_wrapper'
|
4
5
|
|
5
6
|
module TicTacToes
|
6
7
|
module UI
|
@@ -13,30 +14,35 @@ module TicTacToes
|
|
13
14
|
board.spaces
|
14
15
|
end
|
15
16
|
|
16
|
-
def self.
|
17
|
+
def self.game_state(board_structure, computer_type, move_history)
|
17
18
|
player_factory = Core::PlayerFactory.new('unused_io')
|
18
19
|
human_player = player_factory.generate_player(X, Core::PlayerFactory::HUMAN)
|
19
20
|
computer_player = player_factory.generate_player(O, Core::PlayerFactory.const_get(computer_type))
|
20
21
|
players = [human_player, computer_player]
|
21
22
|
|
22
23
|
board_structure_with_players = replace_tokens_with_players(board_structure, human_player, computer_player)
|
23
|
-
board =
|
24
|
+
board = board(board_structure_with_players)
|
24
25
|
|
25
|
-
|
26
|
+
history = history(move_history)
|
27
|
+
|
28
|
+
Core::GameState.new(board, players, history)
|
26
29
|
end
|
27
30
|
|
28
|
-
def self.
|
31
|
+
def self.board_structure(game_state)
|
29
32
|
structure_with_players = game_state.board.spaces
|
30
|
-
|
31
33
|
replace_players_with_tokens(structure_with_players)
|
32
34
|
end
|
33
35
|
|
34
|
-
def self.
|
36
|
+
def self.ai_type(game_state)
|
35
37
|
computer = Core::MoveStrategies::COMPUTER
|
36
38
|
computer_player = game_state.players.detect { |player| player.move_strategy.type == computer }
|
37
39
|
computer_player.move_strategy.ai_type
|
38
40
|
end
|
39
41
|
|
42
|
+
def self.move_history(game_state)
|
43
|
+
game_state.moves.flatten.join if game_state.moves
|
44
|
+
end
|
45
|
+
|
40
46
|
private
|
41
47
|
|
42
48
|
def self.replace_tokens_with_players(board_structure, human_player, computer_player)
|
@@ -56,7 +62,7 @@ module TicTacToes
|
|
56
62
|
board_structure.map { |space| space.token unless space.nil? }
|
57
63
|
end
|
58
64
|
|
59
|
-
def self.
|
65
|
+
def self.board(board_structure)
|
60
66
|
row_size = Math.sqrt(board_structure.count).to_i
|
61
67
|
board = Core::Board.new(row_size: row_size)
|
62
68
|
|
@@ -66,6 +72,19 @@ module TicTacToes
|
|
66
72
|
|
67
73
|
board
|
68
74
|
end
|
75
|
+
|
76
|
+
def self.history(move_history)
|
77
|
+
storage_wrapper = Database::PGWrapper.new('tic_tac_toes')
|
78
|
+
history = Core::History.new(storage_wrapper)
|
79
|
+
if move_history
|
80
|
+
moves = move_history.split(//).each_slice(2).to_a
|
81
|
+
moves.each do |move|
|
82
|
+
move = [move.first, move.last.to_i]
|
83
|
+
history.record_move(move)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
history
|
87
|
+
end
|
69
88
|
end
|
70
89
|
|
71
90
|
class NullHistory
|
@@ -80,6 +99,14 @@ module TicTacToes
|
|
80
99
|
def record_move(move)
|
81
100
|
nil
|
82
101
|
end
|
102
|
+
|
103
|
+
def record_winner(winner)
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def persist
|
108
|
+
nil
|
109
|
+
end
|
83
110
|
end
|
84
111
|
end
|
85
112
|
end
|
data/lib/tic_tac_toes/version.rb
CHANGED
@@ -88,6 +88,16 @@ describe TicTacToes::Core::GameState do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
describe '#computer_player' do
|
92
|
+
it 'returns the game’s computer player' do
|
93
|
+
human_player = TicTacToes::Core::PlayerFactory.new('unused_io').generate_player('X', :human)
|
94
|
+
computer_player = TicTacToes::Core::PlayerFactory.new('unused_io').generate_player('O', :hard)
|
95
|
+
players = [human_player, computer_player]
|
96
|
+
game_state = TicTacToes::Core::GameState.new('board', players, 'history')
|
97
|
+
expect(game_state.computer_player).to eq(computer_player)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
91
101
|
describe '#turn_over' do
|
92
102
|
it 'records the last move' do
|
93
103
|
move = double
|
@@ -109,6 +119,15 @@ describe TicTacToes::Core::GameState do
|
|
109
119
|
end
|
110
120
|
end
|
111
121
|
|
122
|
+
describe '#moves' do
|
123
|
+
it 'returns it’s move history' do
|
124
|
+
moves = [['X', 0], ['O', 4]]
|
125
|
+
history = double(moves: moves)
|
126
|
+
game_state = TicTacToes::Core::GameState.new('board', 'players', history)
|
127
|
+
expect(game_state.moves).to eq(moves)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
112
131
|
describe '#end_game' do
|
113
132
|
it 'records the winner' do
|
114
133
|
winning_player = double
|
@@ -28,7 +28,14 @@ describe TicTacToes::Core::History do
|
|
28
28
|
move = ["X", 0]
|
29
29
|
|
30
30
|
history.record_move(move)
|
31
|
-
expect(history.moves.first).to
|
31
|
+
expect(history.moves.first).to eq(move)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn’t record anything if move is nil" do
|
35
|
+
move = nil
|
36
|
+
|
37
|
+
history.record_move(move)
|
38
|
+
expect(history.moves).to eq([])
|
32
39
|
end
|
33
40
|
end
|
34
41
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'tic_tac_toes/core/board'
|
2
2
|
require 'tic_tac_toes/core/io'
|
3
|
-
require 'tic_tac_toes/core/
|
3
|
+
require 'tic_tac_toes/core/presenter'
|
4
4
|
|
5
5
|
describe TicTacToes::Core::IO do
|
6
|
-
let(:strings) { TicTacToes::Core::
|
6
|
+
let(:strings) { TicTacToes::Core::Presenter }
|
7
7
|
let(:prompt) { double("prompt", solicit_input: 0, display: true, display_red: true) }
|
8
8
|
let(:io) { TicTacToes::Core::IO.new(prompt) }
|
9
9
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'tic_tac_toes/core/board'
|
2
|
+
require 'tic_tac_toes/core/rules'
|
3
|
+
require 'tic_tac_toes/core/presenter'
|
4
|
+
|
5
|
+
describe TicTacToes::Core::Presenter do
|
6
|
+
before do
|
7
|
+
@presenter = TicTacToes::Core::Presenter
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#invalid_row_size' do
|
11
|
+
it "returns a string containing the minimum and maximum allowed row sizes" do
|
12
|
+
smallest_row_size = TicTacToes::Core::Rules::ROW_SIZE_RANGE.min.to_s
|
13
|
+
largest_row_size = TicTacToes::Core::Rules::ROW_SIZE_RANGE.max.to_s
|
14
|
+
|
15
|
+
expect(@presenter::INVALID_ROW_SIZE).to include(smallest_row_size, largest_row_size)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#token_solicitation' do
|
20
|
+
it "returns a string containing the name of the player whose token is being picked" do
|
21
|
+
player = "human"
|
22
|
+
expect(@presenter.token_solicitation(player)).to include(player)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#game_over_notification' do
|
27
|
+
it "returns a string containing the name of the winner" do
|
28
|
+
winner = "X"
|
29
|
+
expect(@presenter.game_over_notification(winner)).to include(winner)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#board' do
|
34
|
+
it "returns a board string" do
|
35
|
+
board = TicTacToes::Core::Board.new(row_size: 3)
|
36
|
+
|
37
|
+
leading_spaces_regexp = /^[[:blank:]]+/
|
38
|
+
board_string = <<-END.gsub(leading_spaces_regexp, '')
|
39
|
+
|
40
|
+
[0]|[1]|[2]
|
41
|
+
-----------
|
42
|
+
[3]|[4]|[5]
|
43
|
+
-----------
|
44
|
+
[6]|[7]|[8]
|
45
|
+
|
46
|
+
END
|
47
|
+
|
48
|
+
expect(@presenter.board(board)).to eql(board_string)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#game_history_strings' do
|
53
|
+
it 'Returns an array with a string for each game history' do
|
54
|
+
history_double = double('history',
|
55
|
+
board_size: 9,
|
56
|
+
difficulty: 'Medium AI',
|
57
|
+
winner: 'X',
|
58
|
+
moves: [['A', 0], ['B', 4]])
|
59
|
+
histories_array = [history_double, history_double]
|
60
|
+
wrapper = double('wrapper', read_game_histories: histories_array)
|
61
|
+
|
62
|
+
expect(@presenter.game_history_strings(wrapper).count).to eq(2)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#game_history_string' do
|
67
|
+
before do
|
68
|
+
@history_double = double('history',
|
69
|
+
board_size: 9,
|
70
|
+
difficulty: 'Medium AI',
|
71
|
+
winner: 'X',
|
72
|
+
moves: [['A', 0], ['B', 4]])
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'Returns a string that includes the game history’s board size' do
|
76
|
+
history_string = @presenter.game_history_string(@history_double)
|
77
|
+
expect(history_string).to include('9')
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'Returns a string that includes the game history’s difficulty' do
|
81
|
+
history_string = @presenter.game_history_string(@history_double)
|
82
|
+
expect(history_string).to include('Medium AI')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'Returns a string that includes the game history’s winner' do
|
86
|
+
history_string = @presenter.game_history_string(@history_double)
|
87
|
+
expect(history_string).to include('X')
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'Returns a string that includes the game history’s move history' do
|
91
|
+
history_string = @presenter.game_history_string(@history_double)
|
92
|
+
expect(history_string).to include('A', '0', 'B', '4')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -19,8 +19,8 @@ describe TicTacToes::UI::Adapter do
|
|
19
19
|
context 'when passed an order of “second”' do
|
20
20
|
it 'sends its listener #moves_were_made with the game state' do
|
21
21
|
order = 'second'
|
22
|
-
computer_player = double(place_and_return_move: 0)
|
23
|
-
game_state = double(game_over?: false, turn_over: true,
|
22
|
+
computer_player = double(place_and_return_move: 0, token: 'O')
|
23
|
+
game_state = double(game_over?: false, turn_over: true, computer_player: computer_player)
|
24
24
|
listener = double
|
25
25
|
|
26
26
|
expect(listener).to receive(:moves_were_made).with(game_state)
|
@@ -56,6 +56,21 @@ describe TicTacToes::UI::Adapter do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
context 'when the game is over' do
|
60
|
+
it 'records the game history' do
|
61
|
+
board = TicTacToes::TestBoardGenerator.generate([ x, x, nil,
|
62
|
+
nil, nil, nil,
|
63
|
+
nil, nil, nil])
|
64
|
+
game_state = TicTacToes::Core::GameState.new(board, players, history)
|
65
|
+
move = '2'
|
66
|
+
listener = double(game_ended_in_winner: true)
|
67
|
+
|
68
|
+
expect(history).to receive(:persist)
|
69
|
+
TicTacToes::UI::Adapter.make_move(game_state, move, listener)
|
70
|
+
board.place(x, 2)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
59
74
|
context 'when the game has ended with a winner' do
|
60
75
|
it 'sends its listener #game_ended_in_winner with the updated game state and the winning token' do
|
61
76
|
board = TicTacToes::TestBoardGenerator.generate([ x, x, nil,
|
@@ -98,7 +113,7 @@ describe TicTacToes::UI::Adapter do
|
|
98
113
|
player_factory = TicTacToes::Core::PlayerFactory.new('unused_io')
|
99
114
|
let(:x) { player_factory.generate_player('x', TicTacToes::Core::PlayerFactory::HUMAN) }
|
100
115
|
let(:o) { player_factory.generate_player('o', TicTacToes::Core::PlayerFactory::HARD_AI) }
|
101
|
-
let(:players) { [
|
116
|
+
let(:players) { [x, o] }
|
102
117
|
|
103
118
|
it 'returns an array indicating which moves will result in a win' do
|
104
119
|
board = TicTacToes::TestBoardGenerator.generate([ x, x, nil,
|
@@ -139,9 +154,9 @@ describe TicTacToes::UI::Adapter do
|
|
139
154
|
nil, nil, x])
|
140
155
|
game_state = TicTacToes::Core::GameState.new(board, players, history)
|
141
156
|
|
142
|
-
expect(TicTacToes::UI::Adapter.predictions(game_state)).to eq([
|
143
|
-
|
144
|
-
|
157
|
+
expect(TicTacToes::UI::Adapter.predictions(game_state)).to eq([nil, nil, nil,
|
158
|
+
nil, nil, "loss",
|
159
|
+
nil, "loss", nil])
|
145
160
|
end
|
146
161
|
end
|
147
162
|
end
|
@@ -11,13 +11,14 @@ describe TicTacToes::UI::Serializer do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
describe '#
|
15
|
-
it 'returns a game state object based on a board structure and
|
14
|
+
describe '#game_state' do
|
15
|
+
it 'returns a game state object based on a board structure and AI type' do
|
16
16
|
board_structure = [nil, nil, nil, nil, 'X', nil, nil, nil, nil]
|
17
|
-
|
17
|
+
ai_type = 'EASY_AI'
|
18
|
+
move_history = 'X0O4'
|
18
19
|
ai = TicTacToes::Core::PlayerFactory::AIS.fetch(TicTacToes::Core::PlayerFactory::EASY_AI)
|
19
20
|
|
20
|
-
game_state = TicTacToes::UI::Serializer.
|
21
|
+
game_state = TicTacToes::UI::Serializer.game_state(board_structure, ai_type, move_history)
|
21
22
|
middle_space = game_state.board.space(4)
|
22
23
|
first_player = game_state.players.first
|
23
24
|
second_player = game_state.players.last
|
@@ -26,10 +27,11 @@ describe TicTacToes::UI::Serializer do
|
|
26
27
|
expect(first_player.token).to eq(TicTacToes::UI::Serializer::X)
|
27
28
|
expect(second_player.token).to eq(TicTacToes::UI::Serializer::O)
|
28
29
|
expect(second_player.move_strategy).to eq(ai)
|
30
|
+
expect(game_state.moves).to eq([['X', 0], ['O', 4]])
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
describe '#
|
34
|
+
describe '#board_structure' do
|
33
35
|
it 'returns a board structure based on a game state object' do
|
34
36
|
player_factory = TicTacToes::Core::PlayerFactory.new('unused_io')
|
35
37
|
first_player = player_factory.generate_player(TicTacToes::UI::Serializer::X, TicTacToes::Core::PlayerFactory::HUMAN)
|
@@ -40,17 +42,25 @@ describe TicTacToes::UI::Serializer do
|
|
40
42
|
|
41
43
|
game_state = TicTacToes::Core::GameState.new(board, players, TicTacToes::UI::NullHistory.new)
|
42
44
|
board_structure = [nil, nil, nil, nil, 'X', nil, nil, nil, nil]
|
43
|
-
expect(TicTacToes::UI::Serializer.
|
45
|
+
expect(TicTacToes::UI::Serializer.board_structure(game_state)).to eq(board_structure)
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
describe '#
|
49
|
+
describe '#ai_type' do
|
48
50
|
it 'returns the type of a game state’s computer player' do
|
49
51
|
move_strategy = TicTacToes::Core::PlayerFactory::AIS.fetch(TicTacToes::Core::PlayerFactory::EASY_AI)
|
50
52
|
computer_player = double(move_strategy: move_strategy)
|
51
53
|
players = [computer_player]
|
52
54
|
game_state = double(players: players)
|
53
|
-
expect(TicTacToes::UI::Serializer.
|
55
|
+
expect(TicTacToes::UI::Serializer.ai_type(game_state)).to eq('EASY_AI')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#move_history' do
|
60
|
+
it 'returns a move history structure based on a game state object' do
|
61
|
+
moves = [['X', 0], ['O', 4]]
|
62
|
+
game_state = double('game_state', moves: moves)
|
63
|
+
expect(TicTacToes::UI::Serializer.move_history(game_state)).to eq('X0O4')
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tic_tac_toes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Spatafora
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -87,7 +87,6 @@ files:
|
|
87
87
|
- bin/game_histories
|
88
88
|
- bin/set_up_ttt_databases
|
89
89
|
- bin/tic_tac_toes
|
90
|
-
- lib/tic_tac_toes/command_line/history_reader.rb
|
91
90
|
- lib/tic_tac_toes/command_line/menu.rb
|
92
91
|
- lib/tic_tac_toes/command_line/prompt.rb
|
93
92
|
- lib/tic_tac_toes/command_line/runner.rb
|
@@ -102,15 +101,14 @@ files:
|
|
102
101
|
- lib/tic_tac_toes/core/move_strategies/types.rb
|
103
102
|
- lib/tic_tac_toes/core/player.rb
|
104
103
|
- lib/tic_tac_toes/core/player_factory.rb
|
104
|
+
- lib/tic_tac_toes/core/presenter.rb
|
105
105
|
- lib/tic_tac_toes/core/rules.rb
|
106
|
-
- lib/tic_tac_toes/core/strings.rb
|
107
106
|
- lib/tic_tac_toes/database/pg_wrapper.rb
|
108
107
|
- lib/tic_tac_toes/tasks/destroy_databases.rake
|
109
108
|
- lib/tic_tac_toes/tasks/set_up_databases.rake
|
110
109
|
- lib/tic_tac_toes/ui/adapter.rb
|
111
110
|
- lib/tic_tac_toes/ui/serializer.rb
|
112
111
|
- lib/tic_tac_toes/version.rb
|
113
|
-
- spec/tic_tac_toes/command_line/history_reader_spec.rb
|
114
112
|
- spec/tic_tac_toes/command_line/menu_spec.rb
|
115
113
|
- spec/tic_tac_toes/core/board_spec.rb
|
116
114
|
- spec/tic_tac_toes/core/game_state_spec.rb
|
@@ -122,8 +120,8 @@ files:
|
|
122
120
|
- spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb
|
123
121
|
- spec/tic_tac_toes/core/player_factory_spec.rb
|
124
122
|
- spec/tic_tac_toes/core/player_spec.rb
|
123
|
+
- spec/tic_tac_toes/core/presenter_spec.rb
|
125
124
|
- spec/tic_tac_toes/core/rules_spec.rb
|
126
|
-
- spec/tic_tac_toes/core/strings_spec.rb
|
127
125
|
- spec/tic_tac_toes/database/pg_wrapper_spec.rb
|
128
126
|
- spec/tic_tac_toes/test_board_generator.rb
|
129
127
|
- spec/tic_tac_toes/ui/adapter_spec.rb
|
@@ -154,7 +152,6 @@ signing_key:
|
|
154
152
|
specification_version: 4
|
155
153
|
summary: The game Tic-tac-toe, featuring an unbeatable AI
|
156
154
|
test_files:
|
157
|
-
- spec/tic_tac_toes/command_line/history_reader_spec.rb
|
158
155
|
- spec/tic_tac_toes/command_line/menu_spec.rb
|
159
156
|
- spec/tic_tac_toes/core/board_spec.rb
|
160
157
|
- spec/tic_tac_toes/core/game_state_spec.rb
|
@@ -166,8 +163,8 @@ test_files:
|
|
166
163
|
- spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb
|
167
164
|
- spec/tic_tac_toes/core/player_factory_spec.rb
|
168
165
|
- spec/tic_tac_toes/core/player_spec.rb
|
166
|
+
- spec/tic_tac_toes/core/presenter_spec.rb
|
169
167
|
- spec/tic_tac_toes/core/rules_spec.rb
|
170
|
-
- spec/tic_tac_toes/core/strings_spec.rb
|
171
168
|
- spec/tic_tac_toes/database/pg_wrapper_spec.rb
|
172
169
|
- spec/tic_tac_toes/test_board_generator.rb
|
173
170
|
- spec/tic_tac_toes/ui/adapter_spec.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/command_line/prompt'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
module CommandLine
|
5
|
-
class HistoryReader
|
6
|
-
def initialize(wrapper)
|
7
|
-
@wrapper = wrapper
|
8
|
-
end
|
9
|
-
|
10
|
-
def display_game_histories
|
11
|
-
game_histories = @wrapper.read_game_histories
|
12
|
-
game_histories.each { |history| Prompt.display(game_history_string(history)) }
|
13
|
-
nil
|
14
|
-
end
|
15
|
-
|
16
|
-
def game_history_string(history)
|
17
|
-
history_string = ''
|
18
|
-
history_string << "Board size: #{history.board_size.to_s}\n"
|
19
|
-
history_string << "Difficulty: #{history.difficulty}\n"
|
20
|
-
history_string << "Winner: #{history.winner}\n"
|
21
|
-
history_string << "Move history:\n"
|
22
|
-
history.moves.each { |move| history_string << " #{move.join(' -> ')}\n" }
|
23
|
-
history_string << "\n"
|
24
|
-
history_string
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/command_line/history_reader'
|
2
|
-
|
3
|
-
describe TicTacToes::CommandLine::HistoryReader do
|
4
|
-
before do
|
5
|
-
@history_double = double('history',
|
6
|
-
board_size: 9,
|
7
|
-
difficulty: 'Medium AI',
|
8
|
-
winner: 'X',
|
9
|
-
moves: [['A', 0], ['B', 4]])
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '#display_game_histories' do
|
13
|
-
it 'Displays something for each game history' do
|
14
|
-
histories_array = [@history_double, @history_double]
|
15
|
-
wrapper = double('wrapper', read_game_histories: histories_array)
|
16
|
-
|
17
|
-
expect(TicTacToes::CommandLine::Prompt).to receive(:display).twice
|
18
|
-
TicTacToes::CommandLine::HistoryReader.new(wrapper).display_game_histories
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '#game_history_string' do
|
23
|
-
before do
|
24
|
-
wrapper_double = double('wrapper')
|
25
|
-
@history_reader = TicTacToes::CommandLine::HistoryReader.new(wrapper_double)
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'Returns a string that includes the game history’s board size' do
|
29
|
-
history_string = @history_reader.game_history_string(@history_double)
|
30
|
-
expect(history_string).to include('9')
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'Returns a string that includes the game history’s difficulty' do
|
34
|
-
history_string = @history_reader.game_history_string(@history_double)
|
35
|
-
expect(history_string).to include('Medium AI')
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'Returns a string that includes the game history’s winner' do
|
39
|
-
history_string = @history_reader.game_history_string(@history_double)
|
40
|
-
expect(history_string).to include('X')
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'Returns a string that includes the game history’s move history' do
|
44
|
-
history_string = @history_reader.game_history_string(@history_double)
|
45
|
-
expect(history_string).to include('A', '0', 'B', '4')
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/core/board'
|
2
|
-
require 'tic_tac_toes/core/rules'
|
3
|
-
require 'tic_tac_toes/core/strings'
|
4
|
-
|
5
|
-
describe TicTacToes::Core::Strings do
|
6
|
-
let(:strings) { TicTacToes::Core::Strings }
|
7
|
-
|
8
|
-
describe '#invalid_row_size' do
|
9
|
-
it "returns a string containing the minimum and maximum allowed row sizes" do
|
10
|
-
smallest_row_size = TicTacToes::Core::Rules::ROW_SIZE_RANGE.min.to_s
|
11
|
-
largest_row_size = TicTacToes::Core::Rules::ROW_SIZE_RANGE.max.to_s
|
12
|
-
|
13
|
-
expect(strings::INVALID_ROW_SIZE).to include(smallest_row_size, largest_row_size)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '#token_solicitation' do
|
18
|
-
it "returns a string containing the name of the player whose token is being picked" do
|
19
|
-
player = "human"
|
20
|
-
expect(strings.token_solicitation(player)).to include(player)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#game_over_notification' do
|
25
|
-
it "returns a string containing the name of the winner" do
|
26
|
-
winner = "X"
|
27
|
-
expect(strings.game_over_notification(winner)).to include(winner)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe '#board' do
|
32
|
-
it "returns a board string" do
|
33
|
-
board = TicTacToes::Core::Board.new(row_size: 3)
|
34
|
-
|
35
|
-
leading_spaces_regexp = /^[[:blank:]]+/
|
36
|
-
board_string = <<-END.gsub(leading_spaces_regexp, '')
|
37
|
-
|
38
|
-
[0]|[1]|[2]
|
39
|
-
-----------
|
40
|
-
[3]|[4]|[5]
|
41
|
-
-----------
|
42
|
-
[6]|[7]|[8]
|
43
|
-
|
44
|
-
END
|
45
|
-
|
46
|
-
expect(strings.board(board)).to eql(board_string)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|