tic_tac_toes 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/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
|