pszals_ttt 0.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e94776de494fef81265f1603ad67dbb72ac6d9fd
4
+ data.tar.gz: 6567cdf5a42a5f46dd674bc40e3179985b152dba
5
+ SHA512:
6
+ metadata.gz: 2c97013a4575ddd44fe2ca9a3dbece254d3f8babf6862d3740f5950afb5b424a19ff2a0bf0ed2c8234f28728333125960b874206526f6c7467553eb81b14ac46
7
+ data.tar.gz: 26419f52c36fc0e988730b29322c7ddbe2fcb605c87abc6beeebf30ac862bb1ff44e0ff0a102ff6b41761b8f5e8a5411290aa46ec3df8cb71cc5ccfcfa5b6997
data/lib/board.rb ADDED
@@ -0,0 +1,135 @@
1
+ class Board
2
+
3
+ attr_accessor :width, :current_board, :player_1, :player_2
4
+
5
+ def initialize(player_1, player_2)
6
+ @width = 3
7
+ @current_board = squares_with_integers
8
+ @player_1 = player_1
9
+ @player_2 = player_2
10
+ end
11
+
12
+ def whose_turn
13
+ number_of_empty_squares%2 == 0 ? @player_2.marker : @player_1.marker
14
+ end
15
+
16
+ def get_opponent
17
+ number_of_empty_squares%2 != 0 ? @player_2.marker : @player_1.marker
18
+ end
19
+
20
+ def integer_board
21
+ (1..@width**2).to_a
22
+ end
23
+
24
+ def squares_with_integers
25
+ string_board = integer_board.map {|square| square.to_s}
26
+ end
27
+
28
+ def display_board
29
+ if @width == 3
30
+ @current_board.each_slice(@width).
31
+ map { |a,b,c| " #{a} | #{b} | #{c} \n" }.
32
+ join("---|---|---\n")
33
+ else
34
+ @current_board.each_slice(@width).
35
+ map { |a,b,c,d| " #{a} | #{b} | #{c} | #{d} \n" }.
36
+ join("---|---|---|---\n")
37
+ end
38
+ end
39
+
40
+ def reset_board
41
+ @current_board = squares_with_integers
42
+ end
43
+
44
+ def set_square(square, marker)
45
+ @current_board[square.to_i - 1] = marker
46
+ end
47
+
48
+ def undo_set_square(square)
49
+ set_square(square, square.to_s)
50
+ end
51
+
52
+ def square_empty?(square)
53
+ @current_board[square.to_i - 1] == square.to_s ? true : false
54
+ end
55
+
56
+ def list_of_open_squares
57
+ @current_board.select {|square| square_empty?(square.to_i)}
58
+ end
59
+
60
+ def number_of_empty_squares
61
+ list_of_open_squares.length
62
+ end
63
+
64
+ def board_open?
65
+ number_of_empty_squares >= 1? true : false
66
+ end
67
+
68
+ def number_of_filled_squares
69
+ @current_board.length - list_of_open_squares.length
70
+ end
71
+
72
+ def winner_on_board?
73
+ winning_marker = false
74
+ gather_winning_combinations.each do |combo|
75
+ marker = @current_board[combo[0]]
76
+ winner = []
77
+ positions = (0...@width).to_a
78
+ positions.each {|position| winner << @current_board[combo[position]]}
79
+ if winner.all? { |square| square == marker }
80
+ winning_marker = marker
81
+ end
82
+ end
83
+ return winning_marker
84
+ end
85
+
86
+ def game_over?
87
+ (!board_open? or winner_on_board?) ? true : false
88
+ end
89
+
90
+ def game_won?(marker)
91
+ winner_on_board? == marker ? true : false
92
+ end
93
+
94
+ def board_indices
95
+ (0...@width**2).to_a
96
+ end
97
+
98
+ def winning_rows
99
+ board_indices.each_slice(@width).reduce([]) {|rows, slice| rows << slice}
100
+ end
101
+
102
+ def generate_column(starting_index)
103
+ (0...@width).reduce([]) {|column, index|
104
+ column << (index*@width+starting_index)
105
+ }
106
+ end
107
+
108
+ def winning_columns
109
+ (0...@width).reduce([]) {|set_of_columns, index|
110
+ set_of_columns << generate_column(index)
111
+ }
112
+ end
113
+
114
+ def diagonal_down
115
+ (0...@width).reduce([]) {|diagonal, index|
116
+ diagonal << index*(@width + 1)
117
+ }
118
+ end
119
+
120
+ def diagonal_up
121
+ (0...@width).reduce([]) {|diagonal, index|
122
+ diagonal << (index + 1)*(@width - 1)
123
+ }
124
+ end
125
+
126
+ def gather_winning_combinations
127
+ combos = []
128
+ winning_rows.each {|row| combos << row } and
129
+ combos << diagonal_down and
130
+ combos << diagonal_up and
131
+ winning_columns.each {|column| combos << column}
132
+ combos
133
+ end
134
+
135
+ end
@@ -0,0 +1,68 @@
1
+ require 'console_ui'
2
+ require 'board'
3
+ require 'game'
4
+ require 'unbeatable_ai'
5
+ require 'player'
6
+
7
+ class Configuration
8
+
9
+ attr_accessor :ai, :ui, :board_width
10
+
11
+ def initialize(user_interface)
12
+ @ui = user_interface
13
+ @ai = Unbeatable_AI.new
14
+ @board_width = false
15
+ end
16
+
17
+ def configure_opponent
18
+ @ui.ask_for_opponent
19
+ opponent_type = @ui.get_opponent
20
+ if opponent_type == 1
21
+ @ai.opponent = true
22
+ @ai
23
+ else
24
+ @ai.opponent = false
25
+ @ai
26
+ end
27
+ end
28
+
29
+ def get_marker
30
+ @ui.ask_for_marker_type
31
+ marker = @ui.get_marker_type
32
+ return marker
33
+ end
34
+
35
+ def configure_player_1(marker)
36
+ if marker.length == 1 and marker =~ /[A-Za-z]/
37
+ player_1 = Player.new(marker)
38
+ else
39
+ @ui.marker_error
40
+ configure_player_1(get_marker)
41
+ end
42
+ end
43
+
44
+ def configure_player_2(marker)
45
+ if marker.length == 1 and marker =~ /[A-Za-z]/
46
+ player_2 = Player.new(marker)
47
+ else
48
+ @ui.marker_error
49
+ configure_player_2(get_marker)
50
+ end
51
+ end
52
+
53
+ def configure_board(player_1, player_2)
54
+ board = Board.new(player_1, player_2)
55
+ @ui.ask_for_width_of_board
56
+ board_width = @ui.get_width_of_board
57
+ if board_width == 3
58
+ board.width = 3
59
+ board
60
+ elsif board_width == 4
61
+ board.width = 4
62
+ board.current_board = board.squares_with_integers
63
+ board
64
+ else
65
+ configure_board(player_1, player_2)
66
+ end
67
+ end
68
+ end
data/lib/console_ui.rb ADDED
@@ -0,0 +1,75 @@
1
+ class Console_UI
2
+
3
+ def ask_for_width_of_board
4
+ put_to_console("Enter 3 for a 3x3 board or 4 for a 4x4 board.")
5
+ end
6
+
7
+ def get_width_of_board
8
+ get_input
9
+ end
10
+
11
+ def ask_for_first_player
12
+ put_to_console("Enter 'X' or 'O.'")
13
+ end
14
+
15
+ def ask_for_marker_type
16
+ put_to_console("Enter a character A-Z to serve as your marker type")
17
+ end
18
+
19
+ def get_marker_type
20
+ gets.chomp
21
+ end
22
+
23
+ def get_first_player
24
+ get_input
25
+ end
26
+
27
+ def ask_for_opponent
28
+ put_to_console("Enter 1 for computer opponent or any key for human opponent")
29
+ end
30
+
31
+ def get_opponent
32
+ get_input
33
+ end
34
+
35
+ def puts_turn(marker)
36
+ put_to_console("It is #{marker}'s turn.")
37
+ end
38
+
39
+ def ask_for_square_to_mark
40
+ put_to_console('Pick an empty square to mark: ')
41
+ end
42
+
43
+ def marker_error
44
+ put_to_console("Input error.")
45
+ end
46
+
47
+ def get_square_to_mark
48
+ get_input
49
+ end
50
+
51
+ def puts_winner(winning_marker)
52
+ put_to_console("Player #{winning_marker} wins!")
53
+ end
54
+
55
+ def puts_tie
56
+ put_to_console("Tie Game!")
57
+ end
58
+
59
+ def ask_to_restart
60
+ put_to_console("Enter 1 to restart or any key to exit.")
61
+ end
62
+
63
+ def print_board(board)
64
+ put_to_console(board)
65
+ end
66
+
67
+ def put_to_console(output)
68
+ puts output
69
+ end
70
+
71
+ def get_input
72
+ gets.chomp.to_i
73
+ end
74
+
75
+ end
data/lib/game.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'runner'
2
+
3
+ class Game
4
+
5
+ attr_accessor :player_1, :player_2
6
+ attr_reader :ui, :board, :ai, :unbeatable_ai, :runner
7
+
8
+ def initialize(board, ui, ai, player_1, player_2)
9
+ @player_1 = player_1
10
+ @player_2 = player_2
11
+ @board = board
12
+ @ui = ui
13
+ @ai = ai
14
+ @unbeatable_ai = ai
15
+ @runner = Runner.new(ui)
16
+ end
17
+
18
+ def declare_turn(marker)
19
+ @ui.puts_turn(marker)
20
+ @ui.print_board(@board.display_board)
21
+ @ui.ask_for_square_to_mark
22
+ end
23
+
24
+ def select_square
25
+ square = nil
26
+ marker = @board.whose_turn
27
+ declare_turn(marker)
28
+
29
+ if marker == player_2.marker and @ai.opponent == true
30
+ square = @unbeatable_ai.make_move(@board, marker)
31
+ else
32
+ square = @ui.get_square_to_mark
33
+ end
34
+
35
+ place_marker(square, marker)
36
+ end
37
+
38
+ def place_marker(square, marker)
39
+
40
+ if @board.square_empty?(square) == false
41
+ @ui.marker_error
42
+ select_square
43
+ end
44
+
45
+ @board.set_square(square, marker)
46
+ find_winner
47
+ end
48
+
49
+ def find_winner
50
+ winner = @board.winner_on_board?
51
+ open_board = @board.board_open?
52
+
53
+ if winner == false and open_board
54
+ select_square
55
+ elsif winner != false
56
+ game_over(@ui.puts_winner(winner))
57
+ else
58
+ game_over(@ui.puts_tie)
59
+ end
60
+
61
+ end
62
+
63
+ def game_over(final_game_message)
64
+ @ui.print_board(@board.display_board)
65
+ final_game_message
66
+ @ui.ask_to_restart
67
+ choice = @ui.get_input
68
+ restart?(choice)
69
+ end
70
+
71
+ def restart?(input)
72
+ input == 1 ? @runner.call : exit
73
+ end
74
+
75
+ def play_game
76
+ select_square
77
+ end
78
+
79
+ end
data/lib/player.rb ADDED
@@ -0,0 +1,9 @@
1
+ class Player
2
+
3
+ attr_reader :marker
4
+
5
+ def initialize(selection)
6
+ @marker = selection
7
+ end
8
+
9
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'board'
2
+ require 'game'
3
+ require 'configuration'
4
+
5
+ class Runner
6
+
7
+ def initialize(ui)
8
+ @ui = ui
9
+ end
10
+
11
+ def call
12
+ configure_game
13
+ play_game
14
+ end
15
+
16
+ def configure_game
17
+ configuration = Configuration.new(@ui)
18
+ ai = configuration.configure_opponent
19
+ player_1 = configuration.configure_player_1(configuration.get_marker)
20
+ player_2 = configuration.configure_player_2(configuration.get_marker)
21
+ board = configuration.configure_board(player_1, player_2)
22
+ @game = Game.new(board, @ui, ai, player_1, player_2)
23
+ end
24
+
25
+ def play_game
26
+ @game.play_game
27
+ end
28
+
29
+ end
@@ -0,0 +1,54 @@
1
+ require 'board'
2
+
3
+ class Unbeatable_AI
4
+
5
+ attr_accessor :opponent
6
+
7
+ def initialize
8
+ @opponent = false
9
+ end
10
+
11
+ def score_board(board, marker)
12
+ if !board.board_open? and !board.winner_on_board?
13
+ return 0
14
+ elsif board.game_won?(marker)
15
+ 1.0
16
+ else
17
+ -1.0
18
+ end
19
+ end
20
+
21
+ def minimax(board, marker, depth, alpha, beta)
22
+ opponent = board.get_opponent
23
+ score = 0
24
+ best_score = -1.0/0
25
+ if board.game_over?
26
+ return score_board(board, marker) / depth
27
+ else
28
+ new_alpha = alpha
29
+ board.list_of_open_squares.each do |square|
30
+ board.set_square(square, marker)
31
+ score = -minimax(board, opponent, depth + 1, -new_alpha, -beta)
32
+ board.undo_set_square(square)
33
+ best_score = score if score > best_score
34
+ end
35
+ return best_score
36
+ end
37
+ end
38
+
39
+ def make_move(board, marker)
40
+ best_square = nil
41
+ best_score = -1.0/0
42
+ opponent = board.get_opponent
43
+ board.list_of_open_squares.each do |square|
44
+ board.set_square(square, marker)
45
+ score = -minimax(board, opponent, 1, -1.0/0, 1.0/0)
46
+ board.undo_set_square(square)
47
+ if score > best_score
48
+ best_score = score
49
+ best_square = square
50
+ end
51
+ end
52
+ return best_square
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pszals_ttt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Philip Szalwinski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-25 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: my tic tac toe game engine
14
+ email: pszalwinski@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/board.rb
20
+ - lib/configuration.rb
21
+ - lib/console_ui.rb
22
+ - lib/game.rb
23
+ - lib/player.rb
24
+ - lib/runner.rb
25
+ - lib/unbeatable_ai.rb
26
+ homepage:
27
+ licenses: []
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.0.4
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: a simple tic tac toe game
49
+ test_files: []