pszals_ttt 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []