tic_tac_toes 0.0.7 → 0.0.8
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/Rakefile +2 -2
- data/bin/tic_tac_toes +18 -22
- data/lib/tic_tac_toes/command_line/menu.rb +55 -0
- data/lib/tic_tac_toes/command_line/prompt.rb +29 -0
- data/lib/tic_tac_toes/command_line/runner.rb +38 -0
- data/lib/tic_tac_toes/core/board.rb +64 -0
- data/lib/tic_tac_toes/core/board_factory.rb +11 -0
- data/lib/tic_tac_toes/core/game_state.rb +40 -0
- data/lib/tic_tac_toes/core/game_state_factory.rb +15 -0
- data/lib/tic_tac_toes/core/history.rb +29 -0
- data/lib/tic_tac_toes/core/io.rb +93 -0
- data/lib/tic_tac_toes/core/move_strategies/easy_ai.rb +13 -0
- data/lib/tic_tac_toes/core/move_strategies/hard_ai.rb +89 -0
- data/lib/tic_tac_toes/core/move_strategies/human.rb +20 -0
- data/lib/tic_tac_toes/core/move_strategies/medium_ai.rb +15 -0
- data/lib/tic_tac_toes/core/player.rb +22 -0
- data/lib/tic_tac_toes/core/player_factory.rb +30 -0
- data/lib/tic_tac_toes/core/rules.rb +66 -0
- data/lib/tic_tac_toes/core/strings.rb +106 -0
- data/lib/tic_tac_toes/database/pg_wrapper.rb +74 -0
- data/lib/tic_tac_toes/ui/adapter.rb +96 -0
- data/lib/tic_tac_toes/ui/serializer.rb +0 -0
- data/lib/tic_tac_toes/version.rb +1 -1
- data/spec/{command_line → tic_tac_toes/command_line}/menu_spec.rb +14 -14
- data/spec/{command_line → tic_tac_toes/command_line}/runner_spec.rb +7 -10
- data/spec/tic_tac_toes/{board_factory_spec.rb → core/board_factory_spec.rb} +3 -3
- data/spec/tic_tac_toes/{board_spec.rb → core/board_spec.rb} +23 -23
- data/spec/tic_tac_toes/{game_state_factory_spec.rb → core/game_state_factory_spec.rb} +3 -3
- data/spec/tic_tac_toes/core/game_state_spec.rb +129 -0
- data/spec/tic_tac_toes/{history_spec.rb → core/history_spec.rb} +3 -3
- data/spec/tic_tac_toes/{io_spec.rb → core/io_spec.rb} +7 -7
- data/spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb +19 -0
- data/spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb +121 -0
- data/spec/tic_tac_toes/{move_strategies → core/move_strategies}/human_spec.rb +3 -3
- data/spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb +21 -0
- data/spec/tic_tac_toes/{player_factory_spec.rb → core/player_factory_spec.rb} +7 -7
- data/spec/tic_tac_toes/{player_spec.rb → core/player_spec.rb} +5 -5
- data/spec/tic_tac_toes/{rules_spec.rb → core/rules_spec.rb} +34 -34
- data/spec/tic_tac_toes/{strings_spec.rb → core/strings_spec.rb} +8 -8
- data/spec/{database → tic_tac_toes/database}/pg_wrapper_spec.rb +3 -3
- data/spec/tic_tac_toes/test_board_generator.rb +17 -0
- data/spec/{ui → tic_tac_toes/ui}/adapter_spec.rb +13 -13
- data/spec/tic_tac_toes/ui/serializer_spec.rb +0 -0
- metadata +64 -61
- data/lib/command_line/menu.rb +0 -53
- data/lib/command_line/prompt.rb +0 -27
- data/lib/command_line/runner.rb +0 -37
- data/lib/database/pg_wrapper.rb +0 -72
- data/lib/tic_tac_toes/board.rb +0 -62
- data/lib/tic_tac_toes/board_factory.rb +0 -9
- data/lib/tic_tac_toes/game_state.rb +0 -27
- data/lib/tic_tac_toes/game_state_factory.rb +0 -14
- data/lib/tic_tac_toes/history.rb +0 -27
- data/lib/tic_tac_toes/io.rb +0 -91
- data/lib/tic_tac_toes/move_strategies/easy_ai.rb +0 -11
- data/lib/tic_tac_toes/move_strategies/hard_ai.rb +0 -87
- data/lib/tic_tac_toes/move_strategies/human.rb +0 -18
- data/lib/tic_tac_toes/move_strategies/medium_ai.rb +0 -13
- data/lib/tic_tac_toes/player.rb +0 -20
- data/lib/tic_tac_toes/player_factory.rb +0 -28
- data/lib/tic_tac_toes/rules.rb +0 -64
- data/lib/tic_tac_toes/strings.rb +0 -104
- data/lib/ui/adapter.rb +0 -94
- data/spec/test_board_generator.rb +0 -15
- data/spec/tic_tac_toes/game_state_spec.rb +0 -66
- data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +0 -19
- data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +0 -121
- data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +0 -21
- /data/lib/{tasks → tic_tac_toes/tasks}/destroy_databases.rake +0 -0
- /data/lib/{tasks → tic_tac_toes/tasks}/set_up_databases.rake +0 -0
@@ -1,18 +0,0 @@
|
|
1
|
-
module TicTacToes
|
2
|
-
module MoveStrategies
|
3
|
-
class Human
|
4
|
-
def initialize(io)
|
5
|
-
@io = io
|
6
|
-
end
|
7
|
-
|
8
|
-
def move(board, players)
|
9
|
-
@io.move_solicitation
|
10
|
-
|
11
|
-
Integer(@io.solicit_input)
|
12
|
-
rescue ArgumentError
|
13
|
-
@io.not_an_integer_error
|
14
|
-
move(board, players)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/move_strategies/easy_ai'
|
2
|
-
require 'tic_tac_toes/move_strategies/hard_ai'
|
3
|
-
|
4
|
-
module TicTacToes
|
5
|
-
module MoveStrategies
|
6
|
-
module MediumAI
|
7
|
-
def self.move(board, players)
|
8
|
-
move_strategy = [EasyAI, HardAI, HardAI].sample
|
9
|
-
move_strategy.move(board, players)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/lib/tic_tac_toes/player.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
module TicTacToes
|
2
|
-
class Player
|
3
|
-
attr_reader :move_strategy, :token, :needs_to_think
|
4
|
-
|
5
|
-
def initialize(move_strategy, token, needs_to_think, io)
|
6
|
-
@move_strategy = move_strategy
|
7
|
-
@token = token
|
8
|
-
@needs_to_think = needs_to_think
|
9
|
-
@io = io
|
10
|
-
end
|
11
|
-
|
12
|
-
def place_and_return_move(board, players)
|
13
|
-
loop do
|
14
|
-
space = @move_strategy.move(board, players)
|
15
|
-
break [@token, space] if board.place(self, space)
|
16
|
-
@io.invalid_move_error
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/move_strategies/human'
|
2
|
-
require 'tic_tac_toes/move_strategies/easy_ai'
|
3
|
-
require 'tic_tac_toes/move_strategies/medium_ai'
|
4
|
-
require 'tic_tac_toes/move_strategies/hard_ai'
|
5
|
-
require 'tic_tac_toes/player'
|
6
|
-
|
7
|
-
module TicTacToes
|
8
|
-
class PlayerFactory
|
9
|
-
def initialize(io)
|
10
|
-
@io = io
|
11
|
-
end
|
12
|
-
|
13
|
-
AIS = {
|
14
|
-
easy: ::TicTacToes::MoveStrategies::EasyAI,
|
15
|
-
medium: ::TicTacToes::MoveStrategies::MediumAI,
|
16
|
-
hard: ::TicTacToes::MoveStrategies::HardAI }
|
17
|
-
|
18
|
-
def generate_human_player(token)
|
19
|
-
needs_to_think = false
|
20
|
-
Player.new(TicTacToes::MoveStrategies::Human.new(@io), token, needs_to_think, @io)
|
21
|
-
end
|
22
|
-
|
23
|
-
def generate_computer_player(token, difficulty)
|
24
|
-
needs_to_think = true
|
25
|
-
Player.new(AIS[difficulty], token, needs_to_think, @io)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/tic_tac_toes/rules.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/player_factory'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
module Rules
|
5
|
-
ROW_SIZE_RANGE = (2..10)
|
6
|
-
|
7
|
-
def self.row_size_valid?(row_size)
|
8
|
-
row_size.between?(ROW_SIZE_RANGE.min, ROW_SIZE_RANGE.max)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.token_valid?(token, taken_tokens)
|
12
|
-
correct_length = token.length == 1
|
13
|
-
untaken = !taken_tokens.include?(token)
|
14
|
-
|
15
|
-
correct_length && untaken
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.difficulty_valid?(difficulty)
|
19
|
-
PlayerFactory::AIS.include? difficulty
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.game_over?(board, players)
|
23
|
-
winner = !determine_winner(board, players).nil?
|
24
|
-
tie = board.full?
|
25
|
-
|
26
|
-
winner || tie
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.determine_winner(board, players)
|
30
|
-
winner = nil
|
31
|
-
|
32
|
-
players.each do |player|
|
33
|
-
player_has_won = win?(board, player)
|
34
|
-
winner = player.token if player_has_won
|
35
|
-
end
|
36
|
-
|
37
|
-
winner
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.win?(board, player)
|
41
|
-
diagonal_win?(board, player) ||
|
42
|
-
horizontal_win?(board, player) ||
|
43
|
-
vertical_win?(board, player)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def self.diagonal_win?(board, player)
|
49
|
-
set_win?(board.diagonals, player)
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.horizontal_win?(board, player)
|
53
|
-
set_win?(board.rows, player)
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.vertical_win?(board, player)
|
57
|
-
set_win?(board.columns, player)
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.set_win?(sets, player)
|
61
|
-
sets.any? { |set| set.all? { |space| space.token == player.token unless space.nil? } }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/tic_tac_toes/strings.rb
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'command_line/prompt'
|
2
|
-
require 'tic_tac_toes/rules'
|
3
|
-
|
4
|
-
module TicTacToes
|
5
|
-
module Strings
|
6
|
-
smallest_row_size = Rules::ROW_SIZE_RANGE.min
|
7
|
-
largest_row_size = Rules::ROW_SIZE_RANGE.max
|
8
|
-
|
9
|
-
NOT_AN_INTEGER = "Input must be an integer"
|
10
|
-
INVALID_ROW_SIZE = "Input must be between #{smallest_row_size} and #{largest_row_size}"
|
11
|
-
INVALID_TOKEN = "Input must be a single, untaken character"
|
12
|
-
INVALID_DIFFICULTY = "Input must be a valid difficulty"
|
13
|
-
INVALID_MOVE = "Input must be a space that is on the board and untaken"
|
14
|
-
ROW_SIZE_SOLICITATION = "Pick row size of board:"
|
15
|
-
DIFFICULTY_SOLICITATION = "Pick difficulty (easy, medium, hard):"
|
16
|
-
MOVE_SOLICITATION = "Pick a space:"
|
17
|
-
THINKING = "Thinking..."
|
18
|
-
|
19
|
-
def self.token_solicitation(player)
|
20
|
-
"Pick #{player} token:"
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.game_over_notification(winner)
|
24
|
-
"#{winner} wins!"
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.board(board)
|
28
|
-
board_string = ""
|
29
|
-
board.rows.each_with_index do |row, index|
|
30
|
-
row_start_index = (index * board.row_size).to_i
|
31
|
-
at_last_row = index == board.row_size - 1
|
32
|
-
board_string << "\n"
|
33
|
-
board_string << row(row, row_start_index, board.size)
|
34
|
-
board_string << "\n"
|
35
|
-
board_string << horizontal_divider(board.row_size, board.size) unless at_last_row
|
36
|
-
end
|
37
|
-
board_string << "\n"
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def self.row(row, row_start_index, board_size)
|
43
|
-
row_array = []
|
44
|
-
row.each_with_index do |space, index|
|
45
|
-
if space.nil?
|
46
|
-
board_index = index + row_start_index
|
47
|
-
row_array << empty_space(board_index, board_size)
|
48
|
-
else
|
49
|
-
row_array << token(space, board_size)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
row_array.join("|")
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.empty_space(board_index, board_size)
|
56
|
-
if space_needs_buffer?(board_index, board_size)
|
57
|
-
"[ #{board_index}]"
|
58
|
-
else
|
59
|
-
"[#{board_index}]"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.token(space, board_size)
|
64
|
-
token = get_colored_token(space)
|
65
|
-
|
66
|
-
if double_digit_board?(board_size)
|
67
|
-
" #{token} "
|
68
|
-
else
|
69
|
-
" #{token} "
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.get_colored_token(player)
|
74
|
-
if player.needs_to_think
|
75
|
-
return CommandLine::Prompt.red(player.token)
|
76
|
-
else
|
77
|
-
return CommandLine::Prompt.blue(player.token)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def self.space_needs_buffer?(board_index, board_size)
|
82
|
-
is_double_digit_board = double_digit_board?(board_size)
|
83
|
-
is_single_digit_space = board_index < 10
|
84
|
-
|
85
|
-
is_double_digit_board && is_single_digit_space
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.double_digit_board?(board_size)
|
89
|
-
board_size > 10
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.horizontal_divider(row_size, board_size)
|
93
|
-
horizontal_divider = ""
|
94
|
-
divider_unit = "-"
|
95
|
-
divider_units_per_space = double_digit_board?(board_size) ? 5 : 4
|
96
|
-
extra_units_per_row = 1
|
97
|
-
|
98
|
-
raw_length = (row_size * divider_units_per_space).to_i
|
99
|
-
truncated_length = raw_length - extra_units_per_row
|
100
|
-
truncated_length.times { horizontal_divider << divider_unit }
|
101
|
-
horizontal_divider
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/lib/ui/adapter.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/board'
|
2
|
-
require 'tic_tac_toes/game_state'
|
3
|
-
require 'tic_tac_toes/player_factory'
|
4
|
-
require 'tic_tac_toes/rules'
|
5
|
-
|
6
|
-
module UI
|
7
|
-
module Adapter
|
8
|
-
def self.new_board_structure
|
9
|
-
board = TicTacToes::Board.new
|
10
|
-
board.spaces
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.make_move(board_structure, move, listener)
|
14
|
-
move = move.to_i
|
15
|
-
game_state = game_state_from_board_structure(board_structure)
|
16
|
-
|
17
|
-
human_player = game_state.players.first
|
18
|
-
game_state.board.place(human_player, move)
|
19
|
-
|
20
|
-
if TicTacToes::Rules.game_over?(game_state.board, game_state.players)
|
21
|
-
return listener.game_is_over(game_state_to_board_structure(game_state), "Game over")
|
22
|
-
end
|
23
|
-
|
24
|
-
game_state.turn_over(move)
|
25
|
-
|
26
|
-
computer_player = game_state.players.first
|
27
|
-
computer_player.place_and_return_move(game_state.board, game_state.players)
|
28
|
-
|
29
|
-
if TicTacToes::Rules.game_over?(game_state.board, game_state.players)
|
30
|
-
return listener.game_is_over(game_state_to_board_structure(game_state), "Game over")
|
31
|
-
end
|
32
|
-
|
33
|
-
listener.move_was_valid(game_state_to_board_structure(game_state))
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.game_state_from_board_structure(board_structure)
|
37
|
-
player_factory = TicTacToes::PlayerFactory.new('unused_io')
|
38
|
-
human_player = player_factory.generate_human_player('X')
|
39
|
-
computer_player = player_factory.generate_computer_player('O', :hard)
|
40
|
-
players = [human_player, computer_player]
|
41
|
-
|
42
|
-
board_structure_with_players = replace_tokens_with_players(board_structure, human_player, computer_player)
|
43
|
-
board = board_from_structure(board_structure_with_players)
|
44
|
-
|
45
|
-
TicTacToes::GameState.new(board, players, NullHistory.new)
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.game_state_to_board_structure(game_state)
|
49
|
-
structure_with_players = game_state.board.spaces
|
50
|
-
|
51
|
-
replace_players_with_tokens(structure_with_players)
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def self.replace_tokens_with_players(board_structure, human_player, computer_player)
|
57
|
-
board_structure.map do |space|
|
58
|
-
case space
|
59
|
-
when 'X'
|
60
|
-
human_player
|
61
|
-
when 'O'
|
62
|
-
computer_player
|
63
|
-
else
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.replace_players_with_tokens(board_structure)
|
70
|
-
board_structure.map { |space| space.token unless space.nil? }
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.board_from_structure(board_structure)
|
74
|
-
row_size = Math.sqrt(board_structure.count).to_i
|
75
|
-
board = TicTacToes::Board.new(row_size: row_size)
|
76
|
-
|
77
|
-
board_structure.each_with_index do |player, index|
|
78
|
-
board.place(player, index)
|
79
|
-
end
|
80
|
-
|
81
|
-
board
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
class NullHistory
|
86
|
-
def record_board_size(size)
|
87
|
-
nil
|
88
|
-
end
|
89
|
-
|
90
|
-
def record_move(move)
|
91
|
-
nil
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/board'
|
2
|
-
|
3
|
-
module TestBoardGenerator
|
4
|
-
def self.generate(structure)
|
5
|
-
board_size = structure.count
|
6
|
-
row_size = Math.sqrt(board_size)
|
7
|
-
board = TicTacToes::Board.new(row_size: row_size)
|
8
|
-
|
9
|
-
structure.each_with_index do |player, index|
|
10
|
-
board.place(player, index)
|
11
|
-
end
|
12
|
-
|
13
|
-
board
|
14
|
-
end
|
15
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/game_state'
|
2
|
-
|
3
|
-
describe TicTacToes::GameState do
|
4
|
-
describe '@initialize' do
|
5
|
-
it "records its board's size" do
|
6
|
-
size = 5
|
7
|
-
board = double(size: size)
|
8
|
-
history = double
|
9
|
-
|
10
|
-
expect(history).to receive(:record_board_size).with(size)
|
11
|
-
TicTacToes::GameState.new(board, 'players', history)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe '#current_player' do
|
16
|
-
it 'returns the first item of its players array' do
|
17
|
-
history = double(record_board_size: true)
|
18
|
-
players = ['first_player', 'second_player']
|
19
|
-
game_state = TicTacToes::GameState.new('board', players, history)
|
20
|
-
|
21
|
-
current_player = game_state.current_player
|
22
|
-
expect(current_player).to eq('first_player')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe '#turn_over' do
|
27
|
-
it 'records the last move' do
|
28
|
-
move = double
|
29
|
-
players = double(rotate!: true)
|
30
|
-
history = double(record_board_size: true)
|
31
|
-
game_state = TicTacToes::GameState.new('board', players, history)
|
32
|
-
|
33
|
-
expect(history).to receive(:record_move).with(move)
|
34
|
-
game_state.turn_over(move)
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'rotates its player array' do
|
38
|
-
players = ['first_player', 'second_player']
|
39
|
-
history = double(record_board_size: true, record_move: true)
|
40
|
-
game_state = TicTacToes::GameState.new('board', players, history)
|
41
|
-
|
42
|
-
game_state.turn_over('move')
|
43
|
-
expect(game_state.current_player).to eq('second_player')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
describe '#game_over' do
|
48
|
-
it 'records the winner' do
|
49
|
-
winner = double
|
50
|
-
history = double(record_board_size: true, persist: true)
|
51
|
-
game_state = TicTacToes::GameState.new('board', 'players', history)
|
52
|
-
|
53
|
-
expect(history).to receive(:record_winner).with(winner)
|
54
|
-
game_state.game_over(winner)
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'persists its history' do
|
58
|
-
winner = double
|
59
|
-
history = double(record_board_size: true, record_winner: true)
|
60
|
-
game_state = TicTacToes::GameState.new('board', 'players', history)
|
61
|
-
|
62
|
-
expect(history).to receive(:persist)
|
63
|
-
game_state.game_over(winner)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,19 +0,0 @@
|
|
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
|
@@ -1,121 +0,0 @@
|
|
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
|
-
|
22
|
-
context "when playing on a 3x3 board" do
|
23
|
-
it "returns 4 when the opponent’s first move was a corner" do
|
24
|
-
board = TestBoardGenerator.generate([nil, nil, nil,
|
25
|
-
nil, nil, nil,
|
26
|
-
nil, nil, x])
|
27
|
-
|
28
|
-
expect(hard_ai.move(board, players)).to eq(4)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "returns 4 when the opponent’s first move was an edge" do
|
32
|
-
board = TestBoardGenerator.generate([nil, nil, nil,
|
33
|
-
nil, nil, x,
|
34
|
-
nil, nil, nil])
|
35
|
-
|
36
|
-
expect(hard_ai.move(board, players)).to eq(4)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "returns 0 when the opponent’s first move was the center" do
|
40
|
-
board = TestBoardGenerator.generate([nil, nil, nil,
|
41
|
-
nil, x, nil,
|
42
|
-
nil, nil, nil])
|
43
|
-
|
44
|
-
expect(hard_ai.move(board, players)).to eq(0)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
describe '#minimax' do
|
51
|
-
it "returns the correct score for a pre-win board" do
|
52
|
-
board = TestBoardGenerator.generate([x, nil, nil,
|
53
|
-
o, o, nil,
|
54
|
-
x, nil, x])
|
55
|
-
win_score = 1
|
56
|
-
|
57
|
-
expect(hard_ai.minimax(board, :max, players)).to eql(win_score)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "returns the correct score for a pre-loss board" do
|
61
|
-
board = TestBoardGenerator.generate([ o, o, x,
|
62
|
-
nil, nil, nil,
|
63
|
-
x, nil, x])
|
64
|
-
loss_score = -1
|
65
|
-
|
66
|
-
expect(hard_ai.minimax(board, :max, players)).to eql(loss_score)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "returns the correct score for a pre-draw board" do
|
70
|
-
board = TestBoardGenerator.generate([x, x, o,
|
71
|
-
o, nil, x,
|
72
|
-
x, o, x])
|
73
|
-
draw_score = 0
|
74
|
-
|
75
|
-
expect(hard_ai.minimax(board, :max, players)).to eql(draw_score)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
describe '#generate_board' do
|
81
|
-
it "returns a board based on a token, a space, and an existing board" do
|
82
|
-
token, space = o, 3
|
83
|
-
board = TestBoardGenerator.generate([ x, nil, nil,
|
84
|
-
nil, o, nil,
|
85
|
-
x, nil, nil])
|
86
|
-
|
87
|
-
new_board = hard_ai.generate_board(token, space, board)
|
88
|
-
expect(new_board.space(space)).to eql(token)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
describe '#score' do
|
94
|
-
it "returns the correct score when HardAI has won" do
|
95
|
-
board = TestBoardGenerator.generate([ o, nil, nil,
|
96
|
-
nil, o, nil,
|
97
|
-
nil, nil, o])
|
98
|
-
win_score = 1
|
99
|
-
|
100
|
-
expect(hard_ai.score(board, players)).to eql(win_score)
|
101
|
-
end
|
102
|
-
|
103
|
-
it "returns the correct score when no one has won" do
|
104
|
-
board = TestBoardGenerator.generate([o, o, x,
|
105
|
-
x, x, o,
|
106
|
-
o, x, o])
|
107
|
-
draw_score = 0
|
108
|
-
|
109
|
-
expect(hard_ai.score(board, players)).to eql(draw_score)
|
110
|
-
end
|
111
|
-
|
112
|
-
it "returns the correct score when the opponent has won" do
|
113
|
-
board = TestBoardGenerator.generate([ x, nil, nil,
|
114
|
-
nil, x, nil,
|
115
|
-
nil, nil, x])
|
116
|
-
loss_score = -1
|
117
|
-
|
118
|
-
expect(hard_ai.score(board, players)).to eql(loss_score)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,21 +0,0 @@
|
|
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
|
File without changes
|
File without changes
|