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