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