tttazures 0.0.4 → 0.0.9
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/lib/board.rb +49 -0
- data/lib/board_printer_three_dim.rb +40 -0
- data/lib/board_printer_two_dim.rb +32 -0
- data/lib/computer_ai.rb +78 -0
- data/lib/config.rb +78 -0
- data/lib/console_ui.rb +107 -0
- data/lib/driver.rb +3 -0
- data/lib/game_controller.rb +76 -0
- data/lib/human_player.rb +17 -0
- data/lib/three_dimensional_board.rb +197 -0
- data/lib/two_dimensional_board.rb +67 -0
- data/spec/board_3x3_spec.rb +108 -0
- data/spec/board_3x3x3_spec.rb +173 -0
- data/spec/board_4x4_spec.rb +95 -0
- data/spec/board_5x5_spec.rb +74 -0
- data/spec/computer_ai_3x3_board_spec.rb +62 -0
- data/spec/computer_ai_spec.rb +10 -0
- data/spec/configurer_spec.rb +140 -0
- data/spec/console_ui_spec.rb +115 -0
- data/spec/coverage/assets/0.7.1/application.css +1110 -0
- data/spec/coverage/assets/0.7.1/application.js +626 -0
- data/spec/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/spec/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/spec/coverage/assets/0.7.1/favicon_green.png +0 -0
- data/spec/coverage/assets/0.7.1/favicon_red.png +0 -0
- data/spec/coverage/assets/0.7.1/favicon_yellow.png +0 -0
- data/spec/coverage/assets/0.7.1/loading.gif +0 -0
- data/spec/coverage/assets/0.7.1/magnify.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/spec/coverage/index.html +72 -0
- data/spec/game_controller_spec.rb +50 -0
- data/spec/human_player_spec.rb +20 -0
- data/spec/long_tests.rb +265 -0
- data/spec/spec_helper.rb +4 -0
- metadata +121 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 460e19f29c18852dacd3caa80532ca13dfad0420
|
4
|
+
data.tar.gz: f1219fe9ebdd295aebd4368b58d29a98346c9dbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5cbc1431951539ce91812d3e4464d02972b66421b67cc62c5114583c9c9b5e2e6ade8751fb5d7857e498944b46a5f2705f7b7acd214264ec200f717d35ae50f
|
7
|
+
data.tar.gz: a80d5113273b3f26f936b14bdfc2ae1565738015a7c64624e12fde150ca23a4e25b9ee8bc4c7fddca9584bd0252e3b1ece363e34607fe2941db8a210fe374087
|
data/lib/board.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Board
|
2
|
+
|
3
|
+
attr_reader :row_length
|
4
|
+
|
5
|
+
def get_print_results
|
6
|
+
#row length d because
|
7
|
+
#it is needed to show correct 2d version of @board_array array
|
8
|
+
[@board_array, @row_length]
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_open_spots
|
12
|
+
open_spots = []
|
13
|
+
@board_array.each_index {|index| open_spots << index if @board_array[index] == :open}
|
14
|
+
open_spots
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_choice choice
|
18
|
+
@board_array[choice] = :open if valid_choice? choice
|
19
|
+
end
|
20
|
+
|
21
|
+
def record_choice choice, player
|
22
|
+
if valid_choice?(choice) && @board_array[choice] == :open
|
23
|
+
@board_array[choice] = player
|
24
|
+
return true
|
25
|
+
else return false end
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_choice? choice
|
29
|
+
choice = choice.to_i
|
30
|
+
if choice >= 0 && choice <= @board_array.length
|
31
|
+
return true else return false end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
#deprecated, write tests for board_is_full?
|
37
|
+
def check_for_tie
|
38
|
+
if @board_array.include?(:open) then return :continue_game else return :tie end
|
39
|
+
end
|
40
|
+
|
41
|
+
def win_is_found a
|
42
|
+
a.size == 1 && !a.include?(:open)
|
43
|
+
end
|
44
|
+
|
45
|
+
def board_is_full?
|
46
|
+
!@board_array.include?(:open)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class BoardPrinterThreeDim
|
2
|
+
|
3
|
+
def initialize output= STDOUT
|
4
|
+
@output = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def print print_results
|
8
|
+
board = print_results[0]
|
9
|
+
row_length = print_results[1].to_i
|
10
|
+
|
11
|
+
start_index = 0
|
12
|
+
row_length.times do
|
13
|
+
index = start_index
|
14
|
+
row_length.times do
|
15
|
+
row_length.times do
|
16
|
+
symbol = get_element_symbol board, index
|
17
|
+
if symbol.to_s.size == 1
|
18
|
+
@output.print " #{symbol} "
|
19
|
+
else
|
20
|
+
@output.print " #{symbol} "
|
21
|
+
end
|
22
|
+
index += 1
|
23
|
+
end
|
24
|
+
@output.print " "
|
25
|
+
index += 6
|
26
|
+
end
|
27
|
+
@output.puts
|
28
|
+
start_index += row_length
|
29
|
+
end
|
30
|
+
@output.puts
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_element_symbol board_array, index
|
34
|
+
case board_array[index]
|
35
|
+
when :x then symbol = "X"
|
36
|
+
when :o then symbol = "O"
|
37
|
+
else symbol = index end
|
38
|
+
symbol
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class BoardPrinterTwoDim
|
2
|
+
|
3
|
+
def initialize output= STDOUT
|
4
|
+
@output = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def print print_results
|
8
|
+
board = print_results[0]
|
9
|
+
row_length = print_results[1]
|
10
|
+
|
11
|
+
board.to_enum.with_index.each do |value, index|
|
12
|
+
symbol = get_element_symbol board, index
|
13
|
+
|
14
|
+
if symbol.to_s.size == 1
|
15
|
+
@output.print " #{symbol}"
|
16
|
+
else
|
17
|
+
@output.print " #{symbol}"
|
18
|
+
end
|
19
|
+
|
20
|
+
@output.puts "" if (index+1) % row_length == 0 && (index+1) != (row_length * row_length)
|
21
|
+
end
|
22
|
+
@output.puts "\n \n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_element_symbol board_array, index
|
26
|
+
case board_array[index]
|
27
|
+
when :x then symbol = "X"
|
28
|
+
when :o then symbol = "O"
|
29
|
+
else symbol = index end
|
30
|
+
symbol
|
31
|
+
end
|
32
|
+
end
|
data/lib/computer_ai.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class ComputerAI
|
2
|
+
|
3
|
+
MIN_ALPHA = -1.0/0
|
4
|
+
MAX_BETA = 1.0/0
|
5
|
+
TOP_DEPTH = 0
|
6
|
+
|
7
|
+
attr_reader :player, :threshold
|
8
|
+
|
9
|
+
def initialize player, threshold
|
10
|
+
@threshold = threshold
|
11
|
+
@player = player
|
12
|
+
end
|
13
|
+
|
14
|
+
def make_move board
|
15
|
+
update_depth_limit(board)
|
16
|
+
if use_mini_max? then best_options = mini_max_top(board, @player)
|
17
|
+
else best_options = make_random_move(board) end
|
18
|
+
choice = best_options.sample
|
19
|
+
board.record_choice(choice, @player)
|
20
|
+
choice
|
21
|
+
end
|
22
|
+
|
23
|
+
def mini_max_top board, player
|
24
|
+
best_moves = []
|
25
|
+
best_score = -1.0/0
|
26
|
+
open_moves = board.get_open_spots
|
27
|
+
open_moves.each do |move|
|
28
|
+
board.record_choice(move,player)
|
29
|
+
score = -mini_max(board, opponent(player), TOP_DEPTH+1, MIN_ALPHA, MAX_BETA)
|
30
|
+
board.remove_choice(move)
|
31
|
+
if score > best_score
|
32
|
+
best_score = score
|
33
|
+
best_moves.clear << move
|
34
|
+
elsif score == best_score
|
35
|
+
best_moves << move
|
36
|
+
end
|
37
|
+
end
|
38
|
+
best_moves
|
39
|
+
end
|
40
|
+
|
41
|
+
def mini_max board,player, depth, alpha, beta
|
42
|
+
if depth >= @depth_limit || alpha >= beta then return 0 end
|
43
|
+
|
44
|
+
if board.check_board_status == :tie
|
45
|
+
return 0
|
46
|
+
elsif board.check_board_status != :continue_game
|
47
|
+
return -(@depth_limit/depth)
|
48
|
+
end
|
49
|
+
|
50
|
+
open_moves = board.get_open_spots
|
51
|
+
open_moves.each do |move|
|
52
|
+
board.record_choice(move,player)
|
53
|
+
score = -mini_max(board, opponent(player), depth+1, -beta,-alpha)
|
54
|
+
board.remove_choice(move)
|
55
|
+
alpha = score if score > alpha
|
56
|
+
end
|
57
|
+
alpha
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_depth_limit board
|
61
|
+
if board.get_open_spots.size > 18 then @depth_limit = 4
|
62
|
+
elsif board.get_open_spots.size > 14 then @depth_limit = 5
|
63
|
+
elsif board.get_open_spots.size > 8 then @depth_limit = 6
|
64
|
+
else @depth_limit = 7 end
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_random_move board
|
68
|
+
board.get_open_spots
|
69
|
+
end
|
70
|
+
|
71
|
+
def use_mini_max?
|
72
|
+
if rand(100) < @threshold then true else false end
|
73
|
+
end
|
74
|
+
|
75
|
+
def opponent player
|
76
|
+
if player == :o then return :x else return :o end
|
77
|
+
end
|
78
|
+
end
|
data/lib/config.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative './computer_ai'
|
2
|
+
require_relative './human_player'
|
3
|
+
require_relative './two_dimensional_board'
|
4
|
+
require_relative './three_dimensional_board'
|
5
|
+
|
6
|
+
class Configurer
|
7
|
+
|
8
|
+
def initialize console
|
9
|
+
@console = console
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_board
|
13
|
+
choice = get_board_choice
|
14
|
+
case choice
|
15
|
+
when 0 then TwoDimensionalBoard.new 3
|
16
|
+
when 1 then TwoDimensionalBoard.new 4
|
17
|
+
when 2 then TwoDimensionalBoard.new 5
|
18
|
+
when 3 then ThreeDimensionalBoard.new 3
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_players
|
23
|
+
user_input = @console.query_user_for_game_type
|
24
|
+
players = {}
|
25
|
+
case user_input
|
26
|
+
when 0
|
27
|
+
players[:player1] = HumanPlayer.new(:x, @console)
|
28
|
+
players[:player2] = HumanPlayer.new(:o, @console)
|
29
|
+
when 1
|
30
|
+
players[:player1] = HumanPlayer.new(:x, @console)
|
31
|
+
players[:player2] = set_ai_difficulty(:o)
|
32
|
+
when 2
|
33
|
+
players[:player1] = set_ai_difficulty(:o)
|
34
|
+
players[:player2] = HumanPlayer.new(:x, @console)
|
35
|
+
when 3
|
36
|
+
players[:player1] = set_ai_difficulty(:x)
|
37
|
+
players[:player2] = set_ai_difficulty(:o)
|
38
|
+
end
|
39
|
+
players
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_ai_difficulty symbol
|
43
|
+
ai_difficulty_choice = get_ai_choice_from_user(symbol)
|
44
|
+
case ai_difficulty_choice
|
45
|
+
when 0 then ComputerAI.new(symbol, 10)
|
46
|
+
when 1 then ComputerAI.new(symbol, 50)
|
47
|
+
when 2 then ComputerAI.new(symbol, 100)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_board_choice
|
52
|
+
valid_choice = false
|
53
|
+
while valid_choice == false
|
54
|
+
choice = @console.query_user_for_board_type
|
55
|
+
if choice >= 0 && choice < 4 then valid_choice = true
|
56
|
+
else @console.print_user_input_error end
|
57
|
+
end
|
58
|
+
choice
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_ai_choice_from_user symbol
|
62
|
+
valid_choice = false
|
63
|
+
while(!valid_choice)
|
64
|
+
choice = @console.query_user_for_ai_difficulty(symbol)
|
65
|
+
if choice < 3 && choice >= 0 then valid_choice = true end
|
66
|
+
end
|
67
|
+
choice
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_board_printer board
|
71
|
+
if board.get_type == :two_dimensional_board
|
72
|
+
BoardPrinterTwoDim.new
|
73
|
+
elsif board.get_type == :three_dimensional_board
|
74
|
+
BoardPrinterThreeDim.new
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/lib/console_ui.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
class ConsoleUI
|
2
|
+
|
3
|
+
def initialize output= STDOUT
|
4
|
+
@output = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def query_user_for_choice user
|
8
|
+
@output.puts "User #{user}: Choose!"
|
9
|
+
receive_user_choice.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_user_for_new_game
|
13
|
+
print_new_game_prompt
|
14
|
+
choice = receive_user_choice
|
15
|
+
return choice == "y" ? true : false
|
16
|
+
end
|
17
|
+
|
18
|
+
def receive_user_choice
|
19
|
+
begin
|
20
|
+
choice = gets.chomp.downcase
|
21
|
+
rescue Interrupt => e
|
22
|
+
@output.puts e
|
23
|
+
end
|
24
|
+
say_goodbye if choice == 'q'
|
25
|
+
return choice
|
26
|
+
end
|
27
|
+
|
28
|
+
def print_player_move move, player
|
29
|
+
puts "Player #{player.player} chose #{move}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to_choice choice_accepted, choice
|
33
|
+
if choice_accepted then @output.puts "You Chose: #{choice}"
|
34
|
+
else print_user_input_error end
|
35
|
+
end
|
36
|
+
|
37
|
+
def query_user_for_board_type
|
38
|
+
print_board_options
|
39
|
+
receive_user_choice.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def query_user_for_ai_difficulty symbol
|
43
|
+
print_ai_difficulty_options(symbol)
|
44
|
+
receive_user_choice.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def query_user_for_game_type
|
48
|
+
valid_choice = false
|
49
|
+
while valid_choice == false
|
50
|
+
print_game_options
|
51
|
+
choice = receive_user_choice.to_i
|
52
|
+
if choice >= 0 && choice < 4 then valid_choice = true
|
53
|
+
else print_user_input_error end
|
54
|
+
end
|
55
|
+
choice
|
56
|
+
end
|
57
|
+
|
58
|
+
def print_board_options
|
59
|
+
@output.puts "What type of board size would you like to use?"
|
60
|
+
@output.puts " Choose 0 for 3x3 board"
|
61
|
+
@output.puts " Choose 1 for 4x4 board"
|
62
|
+
@output.puts " Choose 2 for 5x5 board"
|
63
|
+
@output.puts " Choose 3 for 3x3x3 board"
|
64
|
+
end
|
65
|
+
|
66
|
+
def print_ai_difficulty_options player
|
67
|
+
@output.puts "How difficult will Player #{player} AI be?"
|
68
|
+
@output.puts " Choose 0: AI is easy to beat"
|
69
|
+
@output.puts " Choose 1: AI is medium difficulty"
|
70
|
+
@output.puts " Choose 2: AI is unbeatable (except for 3x3x3)"
|
71
|
+
end
|
72
|
+
|
73
|
+
def print_game_options
|
74
|
+
@output.puts "Choose Game Type"
|
75
|
+
@output.puts " Choose 0 for Human v. Human"
|
76
|
+
@output.puts " Choose 1 for Human v. Computer, Human goes first"
|
77
|
+
@output.puts " Choose 2 for Human v. Computer, Computer goes first"
|
78
|
+
@output.puts " Choose 3 for Computer v. Computer"
|
79
|
+
end
|
80
|
+
|
81
|
+
def present_game_result game_result
|
82
|
+
if game_result == :tie then @output.puts "Game Over: Tie"
|
83
|
+
else @output.puts "Game Over. Player #{game_result} Wins" end
|
84
|
+
end
|
85
|
+
|
86
|
+
def greet_user
|
87
|
+
@output.puts "---Welcome to Tic Tac Toe---"
|
88
|
+
@output.puts "Press 'q' at any time to exit game"
|
89
|
+
end
|
90
|
+
|
91
|
+
def say_goodbye
|
92
|
+
@output.puts "Thanks for playing!"
|
93
|
+
exit
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_user_input_error
|
97
|
+
@output.puts "Whoops! That choice wasn't valid, try again:"
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_new_game_prompt
|
101
|
+
@output.puts "Would you like to start another game? Press 'Y' if yes. Press Any Other Key to Exit"
|
102
|
+
end
|
103
|
+
|
104
|
+
def print printString
|
105
|
+
@output.puts printString;
|
106
|
+
end
|
107
|
+
end
|
data/lib/driver.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative './console_ui'
|
2
|
+
require_relative './computer_ai'
|
3
|
+
require_relative './human_player'
|
4
|
+
require_relative './two_dimensional_board'
|
5
|
+
require_relative './three_dimensional_board'
|
6
|
+
require_relative './board_printer_three_dim'
|
7
|
+
require_relative './board_printer_two_dim'
|
8
|
+
require_relative "./config"
|
9
|
+
|
10
|
+
class GameController
|
11
|
+
|
12
|
+
attr_reader :console, :ai
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@console = ConsoleUI.new
|
16
|
+
@config = Configurer.new(@console)
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
|
21
|
+
@console.greet_user
|
22
|
+
new_game = true
|
23
|
+
while new_game
|
24
|
+
|
25
|
+
setup_game
|
26
|
+
player = @player1
|
27
|
+
while @board.check_board_status == :continue_game
|
28
|
+
display_board
|
29
|
+
move = player.make_move(@board)
|
30
|
+
@console.print_player_move(move,player)
|
31
|
+
player = opponent(player)
|
32
|
+
end
|
33
|
+
|
34
|
+
display_board
|
35
|
+
present_game_result
|
36
|
+
new_game = play_again?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_game
|
41
|
+
set_players
|
42
|
+
@board = @config.get_board
|
43
|
+
@board_printer = @config.get_board_printer(@board)
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_players
|
47
|
+
players = @config.get_players
|
48
|
+
@player1 = players[:player1]
|
49
|
+
@player2 = players[:player2]
|
50
|
+
end
|
51
|
+
|
52
|
+
def display_board
|
53
|
+
@board_printer.print(@board.get_print_results)
|
54
|
+
end
|
55
|
+
|
56
|
+
def present_game_result
|
57
|
+
@console.present_game_result(@board.check_board_status)
|
58
|
+
end
|
59
|
+
|
60
|
+
def play_again?
|
61
|
+
response = @console.query_user_for_new_game
|
62
|
+
|
63
|
+
if response then true
|
64
|
+
else
|
65
|
+
@console.say_goodbye
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def opponent player
|
71
|
+
if player == @player1 then @player2 else @player1 end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
data/lib/human_player.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class HumanPlayer
|
2
|
+
|
3
|
+
attr_reader :player
|
4
|
+
|
5
|
+
def initialize player, console
|
6
|
+
@console = console
|
7
|
+
@player = player
|
8
|
+
end
|
9
|
+
|
10
|
+
def make_move board
|
11
|
+
valid_choice = false
|
12
|
+
while !valid_choice
|
13
|
+
choice = @console.query_user_for_choice(@player)
|
14
|
+
valid_choice = board.record_choice(choice, @player)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require_relative "./board"
|
2
|
+
|
3
|
+
class ThreeDimensionalBoard < Board
|
4
|
+
|
5
|
+
def initialize size
|
6
|
+
@board_array = Array.new(size**3, :open)
|
7
|
+
@row_length = size
|
8
|
+
@row_length_squared = size**2
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_print_results
|
12
|
+
#row length needed to show correct 2d version of @board_array array
|
13
|
+
[@board_array, 3]
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_type
|
17
|
+
:three_dimensional_board
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_board_status
|
21
|
+
result = :continue_game
|
22
|
+
if (column_result = check_column_win(@board_array)) != :continue_game
|
23
|
+
result = column_result
|
24
|
+
elsif (row_result = check_rows_for_win(@board_array)) != :continue_game
|
25
|
+
result = row_result
|
26
|
+
elsif (diag_result = check_diagonal_wins(@board_array)) != :continue_game
|
27
|
+
result = diag_result
|
28
|
+
elsif (tie_result = check_for_tie) != :continue_game
|
29
|
+
result = tie_result
|
30
|
+
end
|
31
|
+
return result
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_column_win board
|
35
|
+
start_index = 0
|
36
|
+
@row_length.times do
|
37
|
+
@row_length.times do
|
38
|
+
if board[start_index] != :open
|
39
|
+
if board[start_index] == board[start_index + 3] && board[start_index+3] == board[start_index + 6]
|
40
|
+
return board[start_index]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
start_index += 1
|
44
|
+
end
|
45
|
+
start_index += 6
|
46
|
+
end
|
47
|
+
return :continue_game
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_rows_for_win board
|
51
|
+
if (result = check_left_right_row_for_win(board)) != :continue_game
|
52
|
+
return result
|
53
|
+
elsif (result = check_front_to_back_row_win(board)) != :continue_game
|
54
|
+
return result
|
55
|
+
else return :continue_game end
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_left_right_row_for_win board
|
59
|
+
for row_number in 0...(@row_length**2)
|
60
|
+
row = get_row(row_number, board).uniq
|
61
|
+
return row.pop if win_is_found(row)
|
62
|
+
end
|
63
|
+
:continue_game
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_row row_number, board
|
67
|
+
start_index = row_number * @row_length
|
68
|
+
board.slice(start_index, @row_length)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_front_back_row start_index, board
|
72
|
+
board.select.with_index{|x,i| x if (i-start_index) % (@row_length**2) == 0}
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_front_to_back_row_win board
|
76
|
+
for row_number in 0..(@row_length**2)
|
77
|
+
row = get_front_back_row(row_number,board).uniq
|
78
|
+
return row.pop if win_is_found(row)
|
79
|
+
end
|
80
|
+
:continue_game
|
81
|
+
end
|
82
|
+
|
83
|
+
def check_diagonal_wins board
|
84
|
+
if (result = check_top_bottom_diagonal(board)) != :continue_game
|
85
|
+
return result
|
86
|
+
elsif (result = check_bottom_top_diagonal(board)) != :continue_game
|
87
|
+
return result
|
88
|
+
elsif (result = horizontal_left_right_diagonal_check(board)) != :continue_game
|
89
|
+
return result
|
90
|
+
elsif (result = horizontal_right_left_diagonal_check(board)) != :continue_game
|
91
|
+
return result
|
92
|
+
elsif (result = vertical_top_bottom_diagonal_check(board)) != :continue_game
|
93
|
+
return result
|
94
|
+
elsif (result = vertical_bottom_top_diagonal_check(board)) != :continue_game
|
95
|
+
return result
|
96
|
+
elsif (result = angled_through_center_diagonals_check(board)) != :continue_game
|
97
|
+
return result
|
98
|
+
else return :continue_game end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
def check_top_bottom_diagonal board
|
104
|
+
for diag_number in 0...@row_length
|
105
|
+
index = diag_number*@row_length_squared
|
106
|
+
sub_board = board.slice(index, @row_length_squared)
|
107
|
+
diagonal = get_top_bottom_diagonal(index, sub_board).uniq
|
108
|
+
return diagonal.pop if win_is_found(diagonal)
|
109
|
+
end
|
110
|
+
:continue_game
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_top_bottom_diagonal start_index, board
|
114
|
+
board.select.with_index {|x,i| x if i % (@row_length+1) == 0}
|
115
|
+
end
|
116
|
+
|
117
|
+
def check_bottom_top_diagonal board
|
118
|
+
index = 6
|
119
|
+
@row_length.times do
|
120
|
+
if board[index] != :open
|
121
|
+
if board[index] == board[index-2] && board[index-2] == board[index-4]
|
122
|
+
return board[index]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
index += 9
|
126
|
+
end
|
127
|
+
return :continue_game
|
128
|
+
end
|
129
|
+
|
130
|
+
def horizontal_left_right_diagonal_check board
|
131
|
+
index = 0
|
132
|
+
@row_length.times do
|
133
|
+
if board[index] != :open
|
134
|
+
if board[index] == board[index+10] && board[index+10] == board[index+20]
|
135
|
+
return board[index]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
index += 3
|
139
|
+
end
|
140
|
+
return :continue_game
|
141
|
+
end
|
142
|
+
|
143
|
+
def horizontal_right_left_diagonal_check board
|
144
|
+
index = 2
|
145
|
+
@row_length.times do
|
146
|
+
if board[index] != :open
|
147
|
+
if board[index] == board[index+8] && board[index+8] == board[index+16]
|
148
|
+
return board[index]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
index += 3
|
152
|
+
end
|
153
|
+
return :continue_game
|
154
|
+
end
|
155
|
+
|
156
|
+
def vertical_top_bottom_diagonal_check board
|
157
|
+
index = 0
|
158
|
+
@row_length.times do
|
159
|
+
if board[index] != :open
|
160
|
+
if board[index] == board[index+12] && board[index+12] == board[index+24]
|
161
|
+
return board[index]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
index += 1
|
165
|
+
end
|
166
|
+
return :continue_game
|
167
|
+
end
|
168
|
+
|
169
|
+
def vertical_bottom_top_diagonal_check board
|
170
|
+
index = 6
|
171
|
+
@row_length.times do
|
172
|
+
if board[index] != :open
|
173
|
+
if board[index] == board[index+6] && board[index+6] == board[index+12]
|
174
|
+
return board[index]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
index += 1
|
178
|
+
end
|
179
|
+
return :continue_game
|
180
|
+
end
|
181
|
+
|
182
|
+
def angled_through_center_diagonals_check board
|
183
|
+
#scores diagonals starting at a front corner, angling through center,
|
184
|
+
#ending at back, opposite corner of cube
|
185
|
+
if board[0] != :open && board[0] == board[13] && board[13] == board[26]
|
186
|
+
return board[0]
|
187
|
+
elsif board[2] != :open && board[2] == board[13] && board[13] == board[24]
|
188
|
+
return board[2]
|
189
|
+
elsif board[6] != :open && board[6] == board[13] && board[13] == board[20]
|
190
|
+
return board[6]
|
191
|
+
elsif board[8] != :open && board[8] == board[13] && board[13] == board[18]
|
192
|
+
return board[8]
|
193
|
+
end
|
194
|
+
return :continue_game
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|