Class: TicTacToe::Game
- Inherits:
-
Object
- Object
- TicTacToe::Game
- Defined in:
- lib/tic_tac_toe.rb
Constant Summary collapse
- NotPlayable =
Class.new(StandardError)
Instance Attribute Summary collapse
-
#current_mark_pos ⇒ Object
Returns the value of attribute current_mark_pos.
-
#current_player ⇒ Object
Returns the value of attribute current_player.
-
#game_over ⇒ Object
Returns the value of attribute game_over.
-
#names ⇒ Object
readonly
Returns the value of attribute names.
-
#play_table ⇒ Object
Returns the value of attribute play_table.
-
#players ⇒ Object
Returns the value of attribute players.
-
#size ⇒ Object
readonly
Returns the value of attribute size.
-
#symbols ⇒ Object
readonly
Returns the value of attribute symbols.
-
#unique_columns ⇒ Object
Returns the value of attribute unique_columns.
-
#unique_diags ⇒ Object
Returns the value of attribute unique_diags.
-
#unique_rows ⇒ Object
Returns the value of attribute unique_rows.
Instance Method Summary collapse
- #boldize(text) ⇒ Object
- #colorize(text) ⇒ Object
- #dim(text) ⇒ Object
- #end_game(symbol = nil) ⇒ Object
- #flash(text) ⇒ Object
- #forced_to_quit ⇒ Object
- #init_players ⇒ Object
-
#initialize(size, symbols, player_names) ⇒ Game
constructor
A new instance of Game.
- #italicize(text) ⇒ Object
- #no_longer_winnable? ⇒ Boolean
- #play ⇒ Object
- #playable? ⇒ Boolean
- #prepare_screen ⇒ Object
- #prompt ⇒ Object
- #refresh_screen ⇒ Object
- #run_some_checks ⇒ Object
-
#show_note ⇒ Object
Screen related methods.
- #show_play_table ⇒ Object
-
#solved? ⇒ Boolean
This method will run faster every ‘play` loop due to the decreasing number of `unique_<lines>` members.
- #style(text) ⇒ Object
- #term_width ⇒ Object
- #wrong_input?(input) ⇒ Boolean
Constructor Details
#initialize(size, symbols, player_names) ⇒ Game
Returns a new instance of Game.
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/tic_tac_toe.rb', line 41 def initialize size, symbols, player_names @symbols = eval(symbols || "") || ['x', 'o'] @names = eval(player_names || "") || [] @size = (size || 3).to_i raise NotPlayable, "Game is not playable. See examples of possible arguments in source code!"\ unless playable? play end |
Instance Attribute Details
#current_mark_pos ⇒ Object
Returns the value of attribute current_mark_pos.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def current_mark_pos @current_mark_pos end |
#current_player ⇒ Object
Returns the value of attribute current_player.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def current_player @current_player end |
#game_over ⇒ Object
Returns the value of attribute game_over.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def game_over @game_over end |
#names ⇒ Object (readonly)
Returns the value of attribute names.
39 40 41 |
# File 'lib/tic_tac_toe.rb', line 39 def names @names end |
#play_table ⇒ Object
Returns the value of attribute play_table.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def play_table @play_table end |
#players ⇒ Object
Returns the value of attribute players.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def players @players end |
#size ⇒ Object (readonly)
Returns the value of attribute size.
39 40 41 |
# File 'lib/tic_tac_toe.rb', line 39 def size @size end |
#symbols ⇒ Object (readonly)
Returns the value of attribute symbols.
39 40 41 |
# File 'lib/tic_tac_toe.rb', line 39 def symbols @symbols end |
#unique_columns ⇒ Object
Returns the value of attribute unique_columns.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def unique_columns @unique_columns end |
#unique_diags ⇒ Object
Returns the value of attribute unique_diags.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def unique_diags @unique_diags end |
#unique_rows ⇒ Object
Returns the value of attribute unique_rows.
36 37 38 |
# File 'lib/tic_tac_toe.rb', line 36 def unique_rows @unique_rows end |
Instance Method Details
#boldize(text) ⇒ Object
291 292 293 294 295 |
# File 'lib/tic_tac_toe.rb', line 291 def boldize text style text do 1 end end |
#colorize(text) ⇒ Object
279 280 281 282 283 |
# File 'lib/tic_tac_toe.rb', line 279 def colorize text style text do current_player.color end end |
#dim(text) ⇒ Object
297 298 299 300 301 |
# File 'lib/tic_tac_toe.rb', line 297 def dim text style text do 2 end end |
#end_game(symbol = nil) ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/tic_tac_toe.rb', line 205 def end_game symbol = nil self.game_over = true refresh_screen show_play_table puts print "Game Over. " if symbol == :draw puts "It's a draw." else current_player.wins end end |
#flash(text) ⇒ Object
313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/tic_tac_toe.rb', line 313 def flash text print "\r\e[A" + " " * term_width prompt if STYLE == :off print text else print "\e[33m#{text}\e[0m" end sleep 2 puts end |
#forced_to_quit ⇒ Object
271 272 273 274 275 276 277 |
# File 'lib/tic_tac_toe.rb', line 271 def forced_to_quit puts refresh_screen print "Game Over. " puts italicize("Forced to quit.") exit end |
#init_players ⇒ Object
58 59 60 61 62 63 64 65 |
# File 'lib/tic_tac_toe.rb', line 58 def init_players self.players = [] symbols.compact.uniq.size.times do name = self.names.shift symbol = self.symbols.shift self.players << Player.new({name: name, symbol: symbol}) end end |
#italicize(text) ⇒ Object
285 286 287 288 289 |
# File 'lib/tic_tac_toe.rb', line 285 def italicize text style text do 3 end end |
#no_longer_winnable? ⇒ Boolean
92 93 94 |
# File 'lib/tic_tac_toe.rb', line 92 def no_longer_winnable? (unique_rows + unique_columns + unique_diags).empty? end |
#play ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/tic_tac_toe.rb', line 145 def play init_players player = self.players.cycle.each self.play_table = Array.new(@size){ Array.new(@size) } self.unique_rows = [*(0...size)] self.unique_columns = [*(0...size)] self.unique_diags = [0, 1] self.game_over = false prepare_screen until game_over do self.current_player = player.next pos_marked = false until pos_marked do refresh_screen show_play_table show_note prompt # Get input begin input = gets if input.nil? # Ctrl + D pressed raise Interrupt else typed_text = input.chomp.strip end rescue SystemExit, Interrupt # Ctrl + C pressed or program/ruby failure forced_to_quit end wrong_input = wrong_input? typed_text if wrong_input flash wrong_input next end # The row and column is 1 based self.current_mark_pos = typed_text.split(/,\s*/).map{|c| c.to_i-1} mark_row, mark_col = *self.current_mark_pos symbol = self.current_player.symbol unless play_table[mark_row][mark_col] play_table[mark_row][mark_col] = symbol pos_marked = true run_some_checks else flash "Already marked. Choose another position!" end end end end |
#playable? ⇒ Boolean
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/tic_tac_toe.rb', line 67 def playable? size > 2 && size <= 50 && symbols.compact.uniq.size > 1 && names.compact.uniq.size <= symbols.compact.uniq.size && symbols.compact.uniq.all? do |m| m.is_a?(String) && # each should be string m.chars.count == 1 && # each should contain 1 character m.bytes.count == 1 # each should occupy 1 byte end end |
#prepare_screen ⇒ Object
259 260 261 |
# File 'lib/tic_tac_toe.rb', line 259 def prepare_screen (size+5).times{ puts } end |
#prompt ⇒ Object
230 231 232 |
# File 'lib/tic_tac_toe.rb', line 230 def prompt print "\r #{colorize(current_player.name)}: " end |
#refresh_screen ⇒ Object
263 264 265 266 267 268 269 |
# File 'lib/tic_tac_toe.rb', line 263 def refresh_screen print "\r\e[#{size+5}A" (size+5).times do print " " * term_width + "\n" end print "\r\e[#{size+5}A" end |
#run_some_checks ⇒ Object
200 201 202 203 |
# File 'lib/tic_tac_toe.rb', line 200 def run_some_checks end_game if solved? end_game :draw if no_longer_winnable? end |
#show_note ⇒ Object
Screen related methods
225 226 227 228 |
# File 'lib/tic_tac_toe.rb', line 225 def show_note puts dim("Type: <#{italicize("row")}>\,<#{italicize("column")}>") puts end |
#show_play_table ⇒ Object
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/tic_tac_toe.rb', line 234 def show_play_table size_width = size.to_s.length col_label = "COLUMN".center(3*size) row_label = "ROW".center(size) print " "*(size_width + 4) print "#{italicize(col_label)}" + "\n" print " "*(size_width + 4) 1.upto(size) do |cnum| print "#{boldize(cnum.to_s.center(3))}" end print "\n" play_table.each.with_index(1) do |row, rnum| print " #{italicize(row_label[rnum-1])}" print " #{boldize(rnum.to_s.rjust(size_width))} " row.each do |el| if STYLE == :off print "[#{el || " "}]" else print el ? "[#{el || " "}]\e[0m" : "[#{el || " "}]" end end puts end end |
#solved? ⇒ Boolean
This method will run faster every ‘play` loop due to the decreasing number of `unique_<lines>` members
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/tic_tac_toe.rb', line 98 def solved? mark_row, mark_col = *current_mark_pos symbol = current_player.symbol # Check if solved horizontally if unique_rows.any? and unique_rows.include? mark_row row = play_table[mark_row] return true if row.all?{|m| m == symbol } # Remove row from unique list if no longer unique unique_rows.delete(mark_row) if row.compact.uniq.size > 1 end # Check if solved vertically if unique_columns.any? and unique_columns.include? mark_col column = [] size.times do |i| column << play_table[i][mark_col] end return true if column.all?{|m| m == symbol } # Remove column unique list if no longer unique unique_columns.delete(mark_col) if column.compact.uniq.size > 1 end # Check if solved diagonally if (mark_row == mark_col || mark_row == size - mark_col - 1) && unique_diags.any? # Initialize diagonals with negative and positive gradients neg_diag = [] pos_diag = [] size.times do |i| neg_diag << play_table[i][i] pos_diag << play_table[size-i-1][i] end return true if neg_diag.all?{|m| m == symbol } || pos_diag.all?{|m| m == symbol } # Remove diagonal if no longer unique unique_diags.delete(0) if neg_diag.compact.uniq.size > 1 unique_diags.delete(1) if pos_diag.compact.uniq.size > 1 end # Not yet solved return false end |
#style(text) ⇒ Object
303 304 305 306 307 308 309 310 311 |
# File 'lib/tic_tac_toe.rb', line 303 def style text unless STYLE == :off text = text.gsub(/\e\[0m/,"") closing = "\e[0m" opening_code = yield text = "\e[#{opening_code}m#{text}#{closing}" end return text end |
#term_width ⇒ Object
218 219 220 |
# File 'lib/tic_tac_toe.rb', line 218 def term_width @term_width ||= `tput cols`.to_i end |
#wrong_input?(input) ⇒ Boolean
78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/tic_tac_toe.rb', line 78 def wrong_input? input = "" if m = input.match(/^(\d+),\s*(\d+)$/) if m.to_a.all?{|el| el.to_i.between?(1,size) } return false else = "Input number should be between 1 and #{size}." end else = "Make sure input is in the following format: <number>,<number>" end return end |