Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Apr 20th, 2006, 11:05 PM   #1
Jessehk
The Oblivious One
 
Jessehk's Avatar
 
Join Date: May 2005
Location: Ontario, Canada
Posts: 648
Rep Power: 4 Jessehk is on a distinguished road
[Ruby]Tic-Tac-Toe

I thought I'd learn Ruby a bit better by writing a short little tic-tac-toe game.

I have written the actual game, but it is not elegant. What I will do is post the file containing the classes I wrote to write the game. I know very few people here know Ruby, but comments would be great regardless.

tictactoe.rb
#!/usr/bin/env ruby

#Created by Jesse H-k on April 20, 2006

#Raised when more then 2 players are created.
class TooManyPlayersError < Exception
	def initialize(message)
		super(message)
	end
end

#A player in a tic-tac-toe game.
class Player
	@@x_taken = false
	@@number = 0
	
	def initialize
		if (@@number += 1) > 2
			raise TooManyPlayersError, "No more then 2 \"Player\" instances are allowed to be created."
		end

		if @@x_taken
			@icon = 'O'
		else
			@icon = 'X'
			@@x_taken = true
		end
		
		@wins, @losses = 0, 0
	end	

	attr_reader :icon
	attr_accessor :wins, :losses
end

# A board in a tic-tac-toe game.
class Board
	def initialize
		@spaces = [' '] * 9
		@winner = nil
		@filled = 0
	end

	#Make a move on the board. player must be an instance of the Player class, and is the player who
	#is making the move. spot is the position on the board where the move is being made.
	#example usage:
	#	board, player = Board.new, Player.new #create a board and player
	#	board.make_move(player, 3)            #have player make a move on the space labeled 3
	#
	def make_move(player, spot)
		if not (1..9) === spot
			raise ArgumentError, "Invalid spot given. Spot must be [1-9]"
		elsif not player.is_a?(Player)
			raise TypeError, "player must be of \"Player\" class. Object of class \"%s\" provided." % player.class.to_s
		elsif filled?(spot)
			raise ArgumentError, "The spot (#{spot}) has been filled already!"
		else
			@spaces[spot - 1] = player.icon	#place the player's icon on the spot
			@filled += 1					#increase the number of filled spaces on the board
		end
	end
	
	#return true if the board is full. false otherwise.
	def full?
		@filled == 9
	end

	#This method will calculate and return the winner (either "X", or "O").
	#nil is returned when there is no winner.
	def winner

		#return the winner if has been already calculated. The serves 2 purposes:
		#	(1) Avoiding lots of unnecessary looping.
		#	(2) Only return the first winner. If two players eventually win, only the first to do so is returned.
		return @winner if @winner != nil 
		
		['X', 'O'].each do |p|	#for each icon
			win = [p] * 3		#set the winning combination for each player
			
			#rows
			[0, 3, 6].each do |s|					#the starting spots of any rows
				if @spaces.slice(s..(s + 2)) == win	#if the given row matches 3 icons of the player
					return (@winner = p)			#assign @winner and return the appropriate icon.
				end
			end
			
			#see above
			(0...3).each do |s| 
				if [@spaces[s], @spaces[s + 3], @spaces[s + 6]] == win 
					return (@winner = p)
				end
			end

			#represents each diagonal on the board
			[[0, 4, 8], [2, 4, 6]].each do |dia|
				if dia.inject([]) { |result, elem| result << @spaces[elem] } == win				
					return (@winner = p)
				end
			end
		end
		nil
	end

	#Return a String representation of the board. An example could
	#be the following:
	#
	#	-----------
	#	 X |   |   
	#	-----------
	#	   |   | X  
	#	-----------
	#	 O |   |   
	#	-----------
	def to_s
		result = '-' * 11

		@spaces.join.scan(/.../).each do |triple|
			result << "\n "
			result << triple.scan(/./).join(' | ')
			result << " \n"
			result << "-" * 11
		end

		result
	end

	#Similar to Board#to_s, but will display a board above the actual board which indicates the position
	#of the moves. The top and bottom of the string is padded with newlines.
	#here is an example of its display:
	#
	#	-----------
	#	 1 | 2 | 3
	#	-----------
	#	 4 | 5 | 6
	#	-----------
	#	 7 | 8 | 9
	#	-----------
	#	
	#	-----------
	#	 X |   |
	#	-----------
	#	   | X |
	#	-----------
	#	 O |   |
	#	-----------
	def display
		#start with an empty board
		empty = to_s.gsub(/( )(X|O)( )/, ' ' * 3) 

		#sequentialy fill the board with [1-9]
		result += (1..9).inject(empty) { |result, x| result.sub(/ {3}/, " #{x} ") }
		
		#append padding, add the board using to_s, and more padding.
		result + "\n\n" + to_s + "\n\n"													
	end
		
	#Reset the Board. This is required when starting a new game, or re-playing a previous one.
	def reset
		@spaces, @winner, @filled = [' '] * 9, nil, 0
	end
		
	#Return true if a spot is occupied by either Player. false otherwise.
	def filled?(spot)
		%w(X O).include?(@spaces[spot - 1])
	end
end

I tried to comment as best I could.
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS!
Jessehk is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 2:25 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC