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