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
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_board_generator'
|
2
|
+
require 'tic_tac_toes/move_strategies/easy_ai'
|
3
|
+
|
4
|
+
describe TicTacToes::MoveStrategies::EasyAI do
|
5
|
+
describe '#move' do
|
6
|
+
let(:players) { double("players") }
|
7
|
+
let(:easy_ai) { TicTacToes::MoveStrategies::EasyAI }
|
8
|
+
|
9
|
+
it "returns a randomly-selected valid move" do
|
10
|
+
board = TestBoardGenerator.generate([ :O, nil, nil,
|
11
|
+
nil, :X, nil,
|
12
|
+
nil, :X, nil])
|
13
|
+
valid_moves = [1, 2, 3, 5, 6, 8]
|
14
|
+
|
15
|
+
move = easy_ai.move(board, players)
|
16
|
+
expect(valid_moves).to include(move)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'test_board_generator'
|
2
|
+
require 'tic_tac_toes/move_strategies/hard_ai'
|
3
|
+
require 'tic_tac_toes/player'
|
4
|
+
|
5
|
+
describe TicTacToes::MoveStrategies::HardAI do
|
6
|
+
let(:hard_ai) { TicTacToes::MoveStrategies::HardAI }
|
7
|
+
let(:x) { TicTacToes::Player.new("human", "x", false, "io") }
|
8
|
+
let(:o) { TicTacToes::Player.new(hard_ai, "o", true, "io") }
|
9
|
+
let(:players) { [o, x] }
|
10
|
+
|
11
|
+
|
12
|
+
describe '#move' do
|
13
|
+
it "returns the best move" do
|
14
|
+
board = TestBoardGenerator.generate([x, nil, nil,
|
15
|
+
o, o, nil,
|
16
|
+
x, nil, x])
|
17
|
+
best_move = 5
|
18
|
+
|
19
|
+
expect(hard_ai.move(board, players)).to eql(best_move)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe '#minimax' do
|
25
|
+
it "returns the correct score for a pre-win board" do
|
26
|
+
board = TestBoardGenerator.generate([x, nil, nil,
|
27
|
+
o, o, nil,
|
28
|
+
x, nil, x])
|
29
|
+
win_score = 1
|
30
|
+
|
31
|
+
expect(hard_ai.minimax(board, :max, players)).to eql(win_score)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns the correct score for a pre-loss board" do
|
35
|
+
board = TestBoardGenerator.generate([ o, o, x,
|
36
|
+
nil, nil, nil,
|
37
|
+
x, nil, x])
|
38
|
+
loss_score = -1
|
39
|
+
|
40
|
+
expect(hard_ai.minimax(board, :max, players)).to eql(loss_score)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns the correct score for a pre-draw board" do
|
44
|
+
board = TestBoardGenerator.generate([x, x, o,
|
45
|
+
o, nil, x,
|
46
|
+
x, o, x])
|
47
|
+
draw_score = 0
|
48
|
+
|
49
|
+
expect(hard_ai.minimax(board, :max, players)).to eql(draw_score)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe '#generate_board' do
|
55
|
+
it "returns a board based on a token, a space, and an existing board" do
|
56
|
+
token, space = o, 3
|
57
|
+
board = TestBoardGenerator.generate([ x, nil, nil,
|
58
|
+
nil, o, nil,
|
59
|
+
x, nil, nil])
|
60
|
+
|
61
|
+
new_board = hard_ai.generate_board(token, space, board)
|
62
|
+
expect(new_board.space(space)).to eql(token)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
describe '#score' do
|
68
|
+
it "returns the correct score when HardAI has won" do
|
69
|
+
board = TestBoardGenerator.generate([ o, nil, nil,
|
70
|
+
nil, o, nil,
|
71
|
+
nil, nil, o])
|
72
|
+
win_score = 1
|
73
|
+
|
74
|
+
expect(hard_ai.score(board, players)).to eql(win_score)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns the correct score when no one has won" do
|
78
|
+
board = TestBoardGenerator.generate([o, o, x,
|
79
|
+
x, x, o,
|
80
|
+
o, x, o])
|
81
|
+
draw_score = 0
|
82
|
+
|
83
|
+
expect(hard_ai.score(board, players)).to eql(draw_score)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns the correct score when the opponent has won" do
|
87
|
+
board = TestBoardGenerator.generate([ x, nil, nil,
|
88
|
+
nil, x, nil,
|
89
|
+
nil, nil, x])
|
90
|
+
loss_score = -1
|
91
|
+
|
92
|
+
expect(hard_ai.score(board, players)).to eql(loss_score)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'tic_tac_toes/move_strategies/human'
|
2
|
+
|
3
|
+
describe TicTacToes::MoveStrategies::Human do
|
4
|
+
describe '#move' do
|
5
|
+
let(:io) { double("io", solicit_input: 0, move_solicitation: true, not_an_integer_error: true) }
|
6
|
+
let(:human) { TicTacToes::MoveStrategies::Human.new(io) }
|
7
|
+
|
8
|
+
it "displays a move solicitation" do
|
9
|
+
expect(io).to receive(:move_solicitation)
|
10
|
+
human.move("board", "players")
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when given not-integer-like input' do
|
14
|
+
let(:not_integer_like) { "string" }
|
15
|
+
let(:integer_like) { "100" }
|
16
|
+
|
17
|
+
it "displays a not an integer error" do
|
18
|
+
allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
|
19
|
+
|
20
|
+
expect(io).to receive(:not_an_integer_error).once
|
21
|
+
human.move("board", "players")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "only returns a move (converted to integer) once it gets integer-like input" do
|
25
|
+
allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
|
26
|
+
|
27
|
+
expect(human.move("board", "players")).to eq(100)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_board_generator'
|
2
|
+
require 'tic_tac_toes/move_strategies/medium_ai'
|
3
|
+
|
4
|
+
describe TicTacToes::MoveStrategies::MediumAI do
|
5
|
+
let(:medium_ai) { TicTacToes::MoveStrategies::MediumAI }
|
6
|
+
let(:x) { TicTacToes::Player.new("human", "x", false, "io") }
|
7
|
+
let(:o) { TicTacToes::Player.new(medium_ai, "o", true, "io") }
|
8
|
+
let(:players) { [x, o] }
|
9
|
+
|
10
|
+
describe '#move' do
|
11
|
+
it "returns a valid move (based on either EasyAI or HardAI)" do
|
12
|
+
board = TestBoardGenerator.generate([ o, o, x,
|
13
|
+
nil, x, nil,
|
14
|
+
nil, x, nil])
|
15
|
+
valid_moves = [3, 5, 6, 8]
|
16
|
+
|
17
|
+
move = medium_ai.move(board, players)
|
18
|
+
expect(valid_moves).to include(move)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,33 +1,30 @@
|
|
1
|
-
require '
|
2
|
-
require 'tic_tac_toes/
|
3
|
-
require 'tic_tac_toes/medium_ai'
|
1
|
+
require 'tic_tac_toes/move_strategies/human'
|
2
|
+
require 'tic_tac_toes/move_strategies/medium_ai'
|
4
3
|
require 'tic_tac_toes/player_factory'
|
5
|
-
require 'tic_tac_toes/spec_helper'
|
6
4
|
|
7
5
|
describe TicTacToes::PlayerFactory do
|
8
|
-
let(:
|
9
|
-
let(:io_interface) { TicTacToes::IOInterface.new(io) }
|
10
|
-
let(:player_factory) { TicTacToes::PlayerFactory.new(io_interface) }
|
11
|
-
|
6
|
+
let(:player_factory) { TicTacToes::PlayerFactory.new("io") }
|
12
7
|
|
13
8
|
describe '#generate_human_player' do
|
14
|
-
it "returns a player with the correct token and
|
15
|
-
|
9
|
+
it "returns a player with the correct token and move strategy" do
|
10
|
+
strategy = TicTacToes::MoveStrategies::Human
|
11
|
+
token = 'X'
|
16
12
|
|
17
13
|
human_player = player_factory.generate_human_player(token)
|
18
|
-
expect(human_player.
|
14
|
+
expect(human_player.move_strategy).to be_a strategy
|
19
15
|
expect(human_player.token).to eq(token)
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
19
|
describe '#generate_computer_player' do
|
25
|
-
it
|
26
|
-
token =
|
20
|
+
it 'returns a player with the correct token and move strategy' do
|
21
|
+
token = 'O'
|
27
22
|
difficulty = :medium
|
23
|
+
strategy = TicTacToes::MoveStrategies::MediumAI
|
28
24
|
|
29
|
-
computer_player = player_factory.generate_computer_player(token,
|
30
|
-
|
25
|
+
computer_player = player_factory.generate_computer_player(token,
|
26
|
+
difficulty)
|
27
|
+
expect(computer_player.move_strategy).to eq(strategy)
|
31
28
|
expect(computer_player.token).to eq(token)
|
32
29
|
end
|
33
30
|
end
|
@@ -1,34 +1,32 @@
|
|
1
1
|
require 'tic_tac_toes/board'
|
2
|
-
require 'tic_tac_toes/io_interface'
|
3
2
|
require 'tic_tac_toes/player'
|
4
|
-
require 'tic_tac_toes/spec_helper'
|
5
3
|
|
6
4
|
describe TicTacToes::Player do
|
7
|
-
let(:board)
|
8
|
-
let(:
|
9
|
-
let(:players)
|
5
|
+
let(:board) { TicTacToes::Board.new(row_size: 3) }
|
6
|
+
let(:io) { double("io", invalid_move_error: true) }
|
7
|
+
let(:players) { double("players") }
|
10
8
|
|
11
|
-
describe '#
|
12
|
-
let(:
|
13
|
-
let(:token)
|
14
|
-
let(:needs_to_think)
|
9
|
+
describe '#place_and_return_move' do
|
10
|
+
let(:move_strategy) { double("move_strategy") }
|
11
|
+
let(:token) { "X" }
|
12
|
+
let(:needs_to_think) { false }
|
15
13
|
|
16
|
-
let(:player) { TicTacToes::Player.new(
|
14
|
+
let(:player) { TicTacToes::Player.new(move_strategy, token, needs_to_think, io) }
|
17
15
|
|
18
|
-
it "only returns a move once it receives a valid move" do
|
16
|
+
it "only places/returns a move once it receives a valid move" do
|
19
17
|
invalid_space, valid_space = 9, 0
|
20
|
-
allow(
|
18
|
+
allow(move_strategy).to receive(:move).and_return(invalid_space, valid_space)
|
21
19
|
|
22
|
-
player.
|
20
|
+
player.place_and_return_move(board, players)
|
23
21
|
expect(board.space(valid_space)).to eq(player)
|
24
22
|
end
|
25
23
|
|
26
24
|
it "displays an invalid move error when given an invalid move" do
|
27
25
|
invalid_space, valid_space = 9, 0
|
28
|
-
allow(
|
26
|
+
allow(move_strategy).to receive(:move).and_return(invalid_space, valid_space)
|
29
27
|
|
30
|
-
expect(
|
31
|
-
player.
|
28
|
+
expect(io).to receive(:invalid_move_error)
|
29
|
+
player.place_and_return_move(board, players)
|
32
30
|
end
|
33
31
|
end
|
34
32
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'test_board_generator'
|
2
2
|
require 'tic_tac_toes/rules'
|
3
3
|
|
4
4
|
describe TicTacToes::Rules do
|
@@ -60,28 +60,25 @@ describe TicTacToes::Rules do
|
|
60
60
|
|
61
61
|
describe '#game_over?' do
|
62
62
|
it "returns false if there is not yet a winner and the board is not full" do
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
board = generate_board(structure)
|
63
|
+
board = TestBoardGenerator.generate([x, x, o,
|
64
|
+
o, o, nil,
|
65
|
+
x, o, x])
|
67
66
|
|
68
67
|
expect(rules.game_over?(board, players)).to be false
|
69
68
|
end
|
70
69
|
|
71
70
|
it "returns true if any player has won" do
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
board = generate_board(structure)
|
71
|
+
board = TestBoardGenerator.generate([ x, nil, nil,
|
72
|
+
nil, x, nil,
|
73
|
+
nil, nil, x])
|
76
74
|
|
77
75
|
expect(rules.game_over?(board, players)).to be true
|
78
76
|
end
|
79
77
|
|
80
78
|
it "returns true if the board is full" do
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
board = generate_board(structure)
|
79
|
+
board = TestBoardGenerator.generate([x, x, o,
|
80
|
+
o, o, x,
|
81
|
+
x, o, x])
|
85
82
|
|
86
83
|
expect(rules.game_over?(board, players)).to be true
|
87
84
|
end
|
@@ -90,20 +87,18 @@ describe TicTacToes::Rules do
|
|
90
87
|
|
91
88
|
describe '#determine_winner' do
|
92
89
|
it "returns the winning token when there is a winner" do
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
board = generate_board(structure)
|
90
|
+
board = TestBoardGenerator.generate([ o, nil, nil,
|
91
|
+
nil, o, nil,
|
92
|
+
nil, nil, o])
|
97
93
|
winning_token = "o"
|
98
94
|
|
99
95
|
expect(rules.determine_winner(board, players)).to eql(winning_token)
|
100
96
|
end
|
101
97
|
|
102
98
|
it "returns nil if there is not a winner" do
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
board = generate_board(structure)
|
99
|
+
board = TestBoardGenerator.generate([x, x, o,
|
100
|
+
o, o, nil,
|
101
|
+
x, o, x])
|
107
102
|
|
108
103
|
expect(rules.determine_winner(board, players)).to be_nil
|
109
104
|
end
|
@@ -112,46 +107,41 @@ describe TicTacToes::Rules do
|
|
112
107
|
|
113
108
|
describe '#win?' do
|
114
109
|
it "returns false if the given token has not won" do
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
board = generate_board(structure)
|
110
|
+
board = TestBoardGenerator.generate([x, x, o,
|
111
|
+
o, o, nil,
|
112
|
+
x, o, x])
|
119
113
|
|
120
114
|
expect(rules.win?(board, x)).to be false
|
121
115
|
end
|
122
116
|
|
123
117
|
it "returns true if the given token has achieved a back diagonal win" do
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
board = generate_board(structure)
|
118
|
+
board = TestBoardGenerator.generate([ x, nil, nil,
|
119
|
+
nil, x, nil,
|
120
|
+
nil, nil, x])
|
128
121
|
|
129
122
|
expect(rules.win?(board, x)).to be true
|
130
123
|
end
|
131
124
|
|
132
125
|
it "returns true if the given token has achieved a front diagonal win" do
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
board = generate_board(structure)
|
126
|
+
board = TestBoardGenerator.generate([nil, nil, x,
|
127
|
+
nil, x, nil,
|
128
|
+
x, nil, nil])
|
137
129
|
|
138
130
|
expect(rules.win?(board, x)).to be true
|
139
131
|
end
|
140
132
|
|
141
133
|
it "returns true if the given token has achieved a horizonatal win" do
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
board = generate_board(structure)
|
134
|
+
board = TestBoardGenerator.generate([nil, nil, nil,
|
135
|
+
x, x, x,
|
136
|
+
nil, nil, nil])
|
146
137
|
|
147
138
|
expect(rules.win?(board, x)).to be true
|
148
139
|
end
|
149
140
|
|
150
141
|
it "returns true if the given token has achieved a vertical win" do
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
board = generate_board(structure)
|
142
|
+
board = TestBoardGenerator.generate([nil, x, nil,
|
143
|
+
nil, x, nil,
|
144
|
+
nil, x, nil])
|
155
145
|
|
156
146
|
expect(rules.win?(board, x)).to be true
|
157
147
|
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.0.
|
4
|
+
version: 0.0.3
|
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-
|
11
|
+
date: 2014-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,6 +77,7 @@ extensions: []
|
|
77
77
|
extra_rdoc_files: []
|
78
78
|
files:
|
79
79
|
- ".gitignore"
|
80
|
+
- ".travis.yml"
|
80
81
|
- Gemfile
|
81
82
|
- Gemfile.lock
|
82
83
|
- LICENSE.txt
|
@@ -85,36 +86,44 @@ files:
|
|
85
86
|
- bin/destroy_ttt_databases
|
86
87
|
- bin/set_up_ttt_databases
|
87
88
|
- bin/tic_tac_toes
|
88
|
-
- lib/command_line/io.rb
|
89
89
|
- lib/command_line/menu.rb
|
90
|
+
- lib/command_line/prompt.rb
|
90
91
|
- lib/command_line/runner.rb
|
91
92
|
- lib/database/pg_wrapper.rb
|
93
|
+
- lib/tasks/destroy_databases.rake
|
94
|
+
- lib/tasks/set_up_databases.rake
|
92
95
|
- lib/tic_tac_toes/board.rb
|
93
|
-
- lib/tic_tac_toes/
|
94
|
-
- lib/tic_tac_toes/
|
96
|
+
- lib/tic_tac_toes/board_factory.rb
|
97
|
+
- lib/tic_tac_toes/game_state.rb
|
98
|
+
- lib/tic_tac_toes/game_state_factory.rb
|
95
99
|
- lib/tic_tac_toes/history.rb
|
96
|
-
- lib/tic_tac_toes/
|
97
|
-
- lib/tic_tac_toes/
|
100
|
+
- lib/tic_tac_toes/io.rb
|
101
|
+
- lib/tic_tac_toes/move_strategies/easy_ai.rb
|
102
|
+
- lib/tic_tac_toes/move_strategies/hard_ai.rb
|
103
|
+
- lib/tic_tac_toes/move_strategies/human.rb
|
104
|
+
- lib/tic_tac_toes/move_strategies/medium_ai.rb
|
98
105
|
- lib/tic_tac_toes/player.rb
|
99
106
|
- lib/tic_tac_toes/player_factory.rb
|
100
107
|
- lib/tic_tac_toes/rules.rb
|
101
108
|
- lib/tic_tac_toes/strings.rb
|
102
|
-
- lib/tic_tac_toes/tasks/destroy_databases.rake
|
103
|
-
- lib/tic_tac_toes/tasks/set_up_databases.rake
|
104
109
|
- lib/tic_tac_toes/version.rb
|
105
110
|
- spec/command_line/menu_spec.rb
|
106
111
|
- spec/command_line/runner_spec.rb
|
107
112
|
- spec/database/pg_wrapper_spec.rb
|
113
|
+
- spec/test_board_generator.rb
|
114
|
+
- spec/tic_tac_toes/board_factory_spec.rb
|
108
115
|
- spec/tic_tac_toes/board_spec.rb
|
109
|
-
- spec/tic_tac_toes/
|
110
|
-
- spec/tic_tac_toes/
|
116
|
+
- spec/tic_tac_toes/game_state_factory_spec.rb
|
117
|
+
- spec/tic_tac_toes/game_state_spec.rb
|
111
118
|
- spec/tic_tac_toes/history_spec.rb
|
112
|
-
- spec/tic_tac_toes/
|
113
|
-
- spec/tic_tac_toes/
|
119
|
+
- spec/tic_tac_toes/io_spec.rb
|
120
|
+
- spec/tic_tac_toes/move_strategies/easy_ai_spec.rb
|
121
|
+
- spec/tic_tac_toes/move_strategies/hard_ai_spec.rb
|
122
|
+
- spec/tic_tac_toes/move_strategies/human_spec.rb
|
123
|
+
- spec/tic_tac_toes/move_strategies/medium_ai_spec.rb
|
114
124
|
- spec/tic_tac_toes/player_factory_spec.rb
|
115
125
|
- spec/tic_tac_toes/player_spec.rb
|
116
126
|
- spec/tic_tac_toes/rules_spec.rb
|
117
|
-
- spec/tic_tac_toes/spec_helper.rb
|
118
127
|
- spec/tic_tac_toes/strings_spec.rb
|
119
128
|
- tic_tac_toes.gemspec
|
120
129
|
homepage: http://github.com/bspatafora/tic_tac_toes
|
@@ -145,14 +154,18 @@ test_files:
|
|
145
154
|
- spec/command_line/menu_spec.rb
|
146
155
|
- spec/command_line/runner_spec.rb
|
147
156
|
- spec/database/pg_wrapper_spec.rb
|
157
|
+
- spec/test_board_generator.rb
|
158
|
+
- spec/tic_tac_toes/board_factory_spec.rb
|
148
159
|
- spec/tic_tac_toes/board_spec.rb
|
149
|
-
- spec/tic_tac_toes/
|
150
|
-
- spec/tic_tac_toes/
|
160
|
+
- spec/tic_tac_toes/game_state_factory_spec.rb
|
161
|
+
- spec/tic_tac_toes/game_state_spec.rb
|
151
162
|
- spec/tic_tac_toes/history_spec.rb
|
152
|
-
- spec/tic_tac_toes/
|
153
|
-
- spec/tic_tac_toes/
|
163
|
+
- spec/tic_tac_toes/io_spec.rb
|
164
|
+
- spec/tic_tac_toes/move_strategies/easy_ai_spec.rb
|
165
|
+
- spec/tic_tac_toes/move_strategies/hard_ai_spec.rb
|
166
|
+
- spec/tic_tac_toes/move_strategies/human_spec.rb
|
167
|
+
- spec/tic_tac_toes/move_strategies/medium_ai_spec.rb
|
154
168
|
- spec/tic_tac_toes/player_factory_spec.rb
|
155
169
|
- spec/tic_tac_toes/player_spec.rb
|
156
170
|
- spec/tic_tac_toes/rules_spec.rb
|
157
|
-
- spec/tic_tac_toes/spec_helper.rb
|
158
171
|
- spec/tic_tac_toes/strings_spec.rb
|
data/lib/tic_tac_toes/easy_ai.rb
DELETED
data/lib/tic_tac_toes/hard_ai.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/rules'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
module HardAI
|
5
|
-
def self.make_move(board, players)
|
6
|
-
open_spaces = Hash[board.open_spaces.map { |space| [space, nil] }]
|
7
|
-
|
8
|
-
open_spaces.each do |space, score|
|
9
|
-
score = minimax(generate_board(players.first, space, board), :min, players)
|
10
|
-
open_spaces[space] = score
|
11
|
-
end
|
12
|
-
|
13
|
-
best_score = open_spaces.values.max
|
14
|
-
open_spaces.each { |space, score| return space if score == best_score }
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.minimax(board, current_player, players)
|
18
|
-
return score(board, players) if Rules.game_over?(board, players)
|
19
|
-
|
20
|
-
if current_player == :max
|
21
|
-
best_score = -1
|
22
|
-
board.open_spaces.each do |space|
|
23
|
-
score = minimax(generate_board(players.first, space, board), :min, players)
|
24
|
-
best_score = [best_score, score].max
|
25
|
-
end
|
26
|
-
best_score
|
27
|
-
|
28
|
-
elsif current_player == :min
|
29
|
-
best_score = 1
|
30
|
-
board.open_spaces.each do |space|
|
31
|
-
score = minimax(generate_board(players.last, space, board), :max, players)
|
32
|
-
best_score = [best_score, score].min
|
33
|
-
end
|
34
|
-
best_score
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.generate_board(player, space, board)
|
39
|
-
new_board = Marshal.load(Marshal.dump(board))
|
40
|
-
new_board.place(player, space)
|
41
|
-
new_board
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.score(board, players)
|
45
|
-
own_token, opponent_token = players.first.token, players.last.token
|
46
|
-
|
47
|
-
case Rules.determine_winner(board, players)
|
48
|
-
when own_token
|
49
|
-
1
|
50
|
-
when opponent_token
|
51
|
-
-1
|
52
|
-
else
|
53
|
-
0
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/strings'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
class IOInterface
|
5
|
-
def initialize(io)
|
6
|
-
@io = io
|
7
|
-
end
|
8
|
-
|
9
|
-
def make_move(_board, _players)
|
10
|
-
move_solicitation
|
11
|
-
|
12
|
-
Integer(@io.solicit_input)
|
13
|
-
rescue ArgumentError
|
14
|
-
not_an_integer_error
|
15
|
-
make_move(_board, _players)
|
16
|
-
end
|
17
|
-
|
18
|
-
def get_row_size
|
19
|
-
row_size_solicitation
|
20
|
-
|
21
|
-
Integer(@io.solicit_input)
|
22
|
-
rescue ArgumentError
|
23
|
-
not_an_integer_error
|
24
|
-
get_row_size
|
25
|
-
end
|
26
|
-
|
27
|
-
def get_token(player)
|
28
|
-
token_solicitation(player)
|
29
|
-
@io.solicit_input
|
30
|
-
end
|
31
|
-
|
32
|
-
def get_difficulty
|
33
|
-
difficulty_solicitation
|
34
|
-
@io.solicit_input.downcase.to_sym
|
35
|
-
end
|
36
|
-
|
37
|
-
def draw_board(board)
|
38
|
-
@io.display(Strings.board(board))
|
39
|
-
end
|
40
|
-
|
41
|
-
def invalid_row_size_error
|
42
|
-
@io.display_red(Strings::INVALID_ROW_SIZE)
|
43
|
-
end
|
44
|
-
|
45
|
-
def invalid_token_error
|
46
|
-
@io.display_red(Strings::INVALID_TOKEN)
|
47
|
-
end
|
48
|
-
|
49
|
-
def invalid_difficulty_error
|
50
|
-
@io.display_red(Strings::INVALID_DIFFICULTY)
|
51
|
-
end
|
52
|
-
|
53
|
-
def invalid_move_error
|
54
|
-
@io.display_red(Strings::INVALID_MOVE)
|
55
|
-
end
|
56
|
-
|
57
|
-
def thinking_notification
|
58
|
-
@io.display_red(Strings::THINKING)
|
59
|
-
end
|
60
|
-
|
61
|
-
def game_over_notification(winner)
|
62
|
-
winner = "Nobody" if winner.nil?
|
63
|
-
@io.display(Strings.game_over_notification(winner))
|
64
|
-
end
|
65
|
-
|
66
|
-
def red(message)
|
67
|
-
@io.red(message)
|
68
|
-
end
|
69
|
-
|
70
|
-
def blue(message)
|
71
|
-
@io.blue(message)
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def move_solicitation
|
77
|
-
@io.display(Strings::MOVE_SOLICITATION)
|
78
|
-
end
|
79
|
-
|
80
|
-
def row_size_solicitation
|
81
|
-
@io.display(Strings::ROW_SIZE_SOLICITATION)
|
82
|
-
end
|
83
|
-
|
84
|
-
def token_solicitation(player)
|
85
|
-
@io.display(Strings.token_solicitation(player))
|
86
|
-
end
|
87
|
-
|
88
|
-
def difficulty_solicitation
|
89
|
-
@io.display(Strings::DIFFICULTY_SOLICITATION)
|
90
|
-
end
|
91
|
-
|
92
|
-
def not_an_integer_error
|
93
|
-
@io.display_red(Strings::NOT_AN_INTEGER)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|