ruby_ttt 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
2
+ require 'game'
3
+
4
+ # board = Board.new(3)
5
+ # player_one = Player.new(MARKER_X)
6
+ # player_two = Player.new(MARKER_O)
7
+ # GameSetup.new(board, player_one, player_two).start!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_ttt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -35,18 +35,13 @@ executables: []
35
35
  extensions: []
36
36
  extra_rdoc_files: []
37
37
  files:
38
- - lib/ai.rb
39
- - lib/board.rb
40
- - lib/game.rb
41
- - lib/game_setup.rb
42
- - lib/player.rb
43
- - lib/ui.rb
38
+ - ruby_ttt.rb
44
39
  homepage: http://rubygems.org/gems/ruby_ttt
45
40
  licenses: []
46
41
  post_install_message:
47
42
  rdoc_options: []
48
43
  require_paths:
49
- - lib
44
+ - .
50
45
  required_ruby_version: !ruby/object:Gem::Requirement
51
46
  none: false
52
47
  requirements:
data/lib/ai.rb DELETED
@@ -1,66 +0,0 @@
1
- POS_INF = 999
2
- NEG_INF = -999
3
- WIN = 1
4
- LOSE = -1
5
- TIE = 0
6
-
7
- class AI
8
-
9
- def computer_move(board, player)
10
- test_board = board.dup
11
- test_board.all_cells = board.all_cells.dup
12
- get_best_move(test_board, player)
13
- end
14
-
15
- def get_best_move(board, player)
16
- ranked_moves = rank_possible_moves(board, player)
17
- move = ranked_moves.max_by {|cell, score| score}
18
- move.first
19
- end
20
-
21
- private
22
-
23
- def rank_possible_moves(board, player)
24
- possible_moves = board.open_cells
25
- possible_moves.each_key do |cell|
26
- possible_moves[cell] = get_move_score(board, player, cell)
27
- end
28
- end
29
-
30
- def get_move_score(board, player, cell)
31
- board.add_marker(player.marker, cell)
32
- best_score = apply_minimax(board, player, cell, depth=0, NEG_INF, POS_INF)
33
- board.remove_marker(cell)
34
- best_score
35
- end
36
-
37
- def get_score(board, player)
38
- return WIN if board.winner?(player.marker) && player.current_player?
39
- return LOSE if board.winner?(player.marker)
40
- TIE
41
- end
42
-
43
- def apply_minimax(board, player, cell, depth, alpha, beta)
44
- return get_score(board, player) if board.game_over?
45
- if player.current_player?
46
- maximizing_player = Maximizing.new(player)
47
- alphabeta(board, maximizing_player, depth, alpha, beta)
48
- else
49
- minimizing_player = Minimizing.new(player)
50
- alphabeta(board, minimizing_player, depth, alpha, beta)
51
- end
52
- end
53
-
54
- def alphabeta(board, player, depth, alpha, beta)
55
- board.open_cells.each_key do |cell|
56
- board.add_marker(player.opponent.marker, cell)
57
- score = (apply_minimax(board, player.opponent, cell, depth += 1, alpha, beta) / depth.to_f)
58
- alpha = player.get_alpha(alpha, score)
59
- beta = player.get_beta(beta, score)
60
- board.remove_marker(cell)
61
- break if alpha >= beta
62
- end
63
- player.return_value(alpha, beta)
64
- end
65
-
66
- end
@@ -1,145 +0,0 @@
1
- MARKER_X = 'X'
2
- MARKER_O = 'O'
3
- class Board
4
- attr_accessor :all_cells, :num_of_rows, :winning_lines
5
- def initialize(num_of_rows)
6
- @num_of_rows = num_of_rows
7
- @all_cells = create_board_hash
8
- @winning_lines = get_winning_lines
9
- end
10
-
11
- def create_board_hash
12
- new_board = Hash.new
13
- alpha = 'A'
14
- numeric = 1
15
- num_of_rows.times do
16
- num_of_rows.times do
17
- cellID = numeric.to_s + alpha
18
- numeric += 1
19
- new_board[cellID] = nil
20
- end
21
- alpha = alpha.next
22
- numeric = 1
23
- end
24
- new_board
25
- end
26
-
27
- def get_winning_lines
28
- lines = []
29
- all_rows.each { |row| lines << row }
30
- all_cols.each { |col| lines << col }
31
- diagonals.each { |diagonal| lines << diagonal }
32
- lines
33
- end
34
-
35
- def all_rows
36
- rows = []
37
- cellIDs = all_cells.keys
38
- beg = 0
39
- ending = num_of_rows - 1
40
- until rows.length == num_of_rows
41
- rows << cellIDs[beg..ending]
42
- beg += num_of_rows
43
- ending += num_of_rows
44
- end
45
- rows
46
- end
47
-
48
- def add_marker(marker, cell)
49
- all_cells[cell] = marker
50
- end
51
-
52
- def winner?(marker)
53
- board_markers = all_cells.select { |k,v| v == marker }.keys
54
- winning_lines.each do |line|
55
- return true if (line & board_markers).length == num_of_rows
56
- end
57
- false
58
- end
59
-
60
- def game_over?
61
- !moves_remaining? || winner?(MARKER_X)|| winner?(MARKER_O)
62
- end
63
-
64
- def available_cell?(cell)
65
- valid_cell?(cell) && all_cells[cell].nil?
66
- end
67
-
68
- def valid_cell?(cell)
69
- all_cells.has_key?(cell)
70
- end
71
-
72
- def remove_marker(cell)
73
- all_cells[cell] = nil
74
- end
75
-
76
- def moves_remaining?
77
- all_cells.has_value?(nil)
78
- end
79
-
80
- def open_cells
81
- all_cells.select { |k,v| v.nil? }
82
- end
83
-
84
- def empty?
85
- open_cells.length == (num_of_rows * num_of_rows)
86
- end
87
-
88
- def random_cell
89
- cells = open_cells.keys
90
- cells_count = cells.length - 1
91
- cells[rand(cells_count)]
92
- end
93
-
94
- private
95
- def all_cols
96
- cols = []
97
- index = 0
98
- num_of_rows.times do
99
- cols << get_column(index)
100
- index += 1
101
- end
102
- cols
103
- end
104
-
105
- def get_column(index)
106
- column = []
107
- cellIDs = all_cells.keys
108
- num_of_rows.times do
109
- column << cellIDs[index]
110
- index += num_of_rows
111
- end
112
- column
113
- end
114
-
115
- def diagonals
116
- diagonals = []
117
- diagonals << diagonal_one
118
- diagonals << diagonal_two
119
- end
120
-
121
- def diagonal_one
122
- diagonal = []
123
- alpha = 'A'
124
- numeric = 1
125
- num_of_rows.times do
126
- diagonal << numeric.to_s + alpha
127
- alpha = alpha.next
128
- numeric += 1
129
- end
130
- diagonal
131
- end
132
-
133
- def diagonal_two
134
- diagonal = []
135
- alpha = 'A'
136
- numeric = num_of_rows
137
- num_of_rows.times do
138
- diagonal << numeric.to_s + alpha
139
- alpha = alpha.next
140
- numeric -= 1
141
- end
142
- diagonal
143
- end
144
-
145
- end
@@ -1,59 +0,0 @@
1
- require 'ai'
2
- require 'board'
3
- require 'player'
4
- require 'ui'
5
- require 'game_setup'
6
- class Game
7
- attr_accessor :board, :ui, :player_one, :player_two, :ai, :difficulty_level
8
- def initialize(board, player_one, player_two, difficulty_level)
9
- @board = board
10
- @ui = UI.new(@board)
11
- @player_one = player_one
12
- @player_two = player_two
13
- @ai = AI.new
14
- @difficulty_level = difficulty_level
15
- end
16
-
17
- def play!
18
- until board.game_over?
19
- ui.display_board
20
- move = get_next_move
21
- board.available_cell?(move) ? advance_game(move, current_player) : invalid_move(move)
22
- end
23
- exit_game
24
- end
25
-
26
- def get_next_move
27
- return ui.request_human_move if current_player.player_type == HUMAN_PLAYER
28
- difficulty_level == HARD_LEVEL ? ai.computer_move(board, current_player) : board.random_cell
29
- end
30
-
31
- def advance_game(cell, player)
32
- board.add_marker(player.marker, cell)
33
- game_status_check
34
- player.next_player_turn
35
- ui.next_move_message(current_player) unless board.game_over?
36
- end
37
-
38
- def game_status_check
39
- if board.winner?(current_player.marker)
40
- ui.winning_game_message(current_player)
41
- elsif !board.moves_remaining?
42
- ui.tie_game_message
43
- end
44
- end
45
-
46
- def invalid_move(cell)
47
- board.valid_cell?(cell) ? ui.taken_cell_message(cell) : ui.bad_cell_message(cell)
48
- end
49
-
50
- def current_player
51
- player_one.current_player? ? player_one : player_two
52
- end
53
-
54
- def exit_game
55
- ui.display_board
56
- ui.io.exit
57
- end
58
-
59
- end
@@ -1,76 +0,0 @@
1
- EASY_LEVEL = 'easy'
2
- HARD_LEVEL = 'hard'
3
- COMPUTER_PLAYER = 'computer'
4
- HUMAN_PLAYER = 'human'
5
- class GameSetup
6
- attr_accessor :ui, :board, :player_one, :player_two
7
- def initialize(board, player_one, player_two)
8
- @board = board
9
- @ui = UI.new(@board)
10
- @player_one = player_one
11
- @player_two = player_two
12
- end
13
-
14
- def start!
15
- begin
16
- set_opponents
17
- get_player_type(player_one)
18
- get_player_type(player_two)
19
- level = get_difficulty_level
20
- who_goes_first
21
- Game.new(board, player_one, player_two, level).play!
22
- rescue Interrupt
23
- ui.early_exit_message
24
- exit
25
- end
26
- end
27
-
28
- def set_opponents
29
- player_one.opponent = player_two
30
- player_two.opponent = player_one
31
- end
32
-
33
- def get_player_type(player)
34
- type = ui.request_player_type(player.marker)
35
- validate_type(type, player) ? set_player_type(type, player) : invalid_type(type, player)
36
- end
37
-
38
- def get_difficulty_level
39
- return nil unless player_one.player_type == COMPUTER_PLAYER || player_two.player_type == COMPUTER_PLAYER
40
- level = ui.request_difficulty_level
41
- validate_level(level) ? ui.level_assigned_message(level) : invalid_level(level)
42
- end
43
-
44
- def validate_type(type, player)
45
- (type == HUMAN_PLAYER) || (type == COMPUTER_PLAYER)
46
- end
47
-
48
- def set_player_type(type, player)
49
- player.player_type = type
50
- ui.type_assigned_message(type, player.marker)
51
- end
52
-
53
- def invalid_type(type, player)
54
- ui.invalid_input_message(type)
55
- get_player_type(player)
56
- end
57
-
58
- def validate_level(level)
59
- (level == HARD_LEVEL) || (level == EASY_LEVEL)
60
- end
61
-
62
- def invalid_level(level)
63
- ui.invalid_input_message(level)
64
- get_difficulty_level
65
- end
66
-
67
- def who_goes_first
68
- rand(0..1) == 1 ? set_first_turn(player_one) : set_first_turn(player_two)
69
- end
70
-
71
- def set_first_turn(player)
72
- player.turn = 1
73
- ui.first_move_message(player)
74
- end
75
-
76
- end
@@ -1,63 +0,0 @@
1
- class Player
2
- attr_accessor :marker, :turn, :player_type, :opponent
3
- def initialize(marker)
4
- @marker = marker
5
- @player_type = 'human'
6
- @turn = 0
7
- @opponent = nil
8
- end
9
-
10
- def next_player_turn
11
- self.turn = 0
12
- self.opponent.turn = 1
13
- end
14
-
15
- def current_player?
16
- self.turn == 1
17
- end
18
-
19
- def get_alpha(alpha, score)
20
- alpha
21
- end
22
-
23
- def get_beta(beta, score)
24
- beta
25
- end
26
- end
27
-
28
- class Minimizing < Player
29
- attr_accessor :marker, :turn, :opponent
30
- def initialize(player)
31
- @marker = player.marker
32
- @turn = player.turn
33
- @opponent = player.opponent
34
- end
35
-
36
- def get_alpha(alpha, score)
37
- score > alpha ? score : alpha
38
- end
39
-
40
- def return_value(alpha, beta)
41
- alpha
42
- end
43
-
44
- end
45
-
46
- class Maximizing < Player
47
- attr_accessor :marker, :turn, :opponent
48
- def initialize(player)
49
- @marker = player.marker
50
- @turn = player.turn
51
- @opponent = player.opponent
52
- end
53
-
54
- def get_beta(beta, score)
55
- score < beta ? score : beta
56
- end
57
-
58
- def return_value(alpha, beta)
59
- beta
60
- end
61
-
62
- end
63
-
data/lib/ui.rb DELETED
@@ -1,118 +0,0 @@
1
- class UI
2
- attr_accessor :board, :io
3
- def initialize(board)
4
- @board = board
5
- @io = Kernel
6
- end
7
-
8
- def request_player_type(marker)
9
- player_type_message(marker)
10
- io.gets.chomp.downcase
11
- end
12
-
13
- def request_difficulty_level
14
- difficulty_level_message
15
- io.gets.chomp.downcase
16
- end
17
-
18
- def request_human_move
19
- standardize(io.gets.chomp)
20
- end
21
-
22
- def standardize(input)
23
- input.split('').sort.join('').upcase
24
- end
25
-
26
- def difficulty_level_message
27
- io.print "Select computer difficulty level: Enter 'easy' or 'hard.'\n"
28
- end
29
-
30
- def level_assigned_message(level)
31
- io.print "You selected difficulty level #{level.upcase}.\n"
32
- end
33
-
34
- def invalid_input_message(input)
35
- io.print " #{input} is not a valid option.\n"
36
- end
37
-
38
- def player_type_message(marker)
39
- io.print "For player " + "'#{marker}'," + " enter 'human' or 'computer.'\n"
40
- end
41
-
42
- def type_assigned_message(type, marker)
43
- io.print "Player " + "'#{marker}' " + "is #{type}.\n"
44
- end
45
-
46
- def print_board_numbers
47
- num = 1
48
- io.print " "
49
- board.num_of_rows.times do
50
- io.print "--#{num}-- "
51
- num += 1
52
- end
53
- io.print "\n"
54
- end
55
-
56
- def print_divider
57
- io.print " "
58
- board.num_of_rows.times { io.print "------" }
59
- io.print "\n"
60
- end
61
-
62
- def print_board_rows
63
- alpha = 'A'
64
- board.all_rows.each do |row|
65
- show_row(alpha, row)
66
- alpha = alpha.next
67
- end
68
- end
69
-
70
- def show_row(letter, cells)
71
- io.print "#{letter}"
72
- cells.each { |cell| io.print " | " + show_marker(cell) }
73
- io.print " | #{letter}\n"
74
- print_divider
75
- end
76
-
77
- def show_marker(cell)
78
- board.all_cells[cell].nil? ? ' ' : board.all_cells[cell]
79
- end
80
-
81
- def display_board
82
- print_board_numbers
83
- print_board_rows
84
- print_board_numbers
85
- end
86
-
87
- def first_move_message(player)
88
- io.print "\n\n************ New Game ************\n"
89
- io.print "Player '#{player.marker}' goes first.\n"
90
- end
91
-
92
- def next_move_message(player)
93
- io.print "Player '#{player.marker}': Enter open cell ID.\n"
94
- end
95
-
96
- def winning_game_message(player)
97
- io.print "GAME OVER! Player '#{player.marker}' wins!\n"
98
- end
99
-
100
- def tie_game_message
101
- io.print "GAME OVER! It's a tie!\n"
102
- end
103
-
104
- def taken_cell_message(cell)
105
- io.print "#{cell} has already been taken!\n"
106
- end
107
-
108
- def bad_cell_message(cell)
109
- io.print "#{cell} is not a valid cell ID!\n"
110
- end
111
-
112
- def early_exit_message
113
- io.print "\nExiting Tic-Tac-Toe..."
114
- io.print "...\n"
115
- io.print "Goodbye!\n\n"
116
- end
117
-
118
- end