diff --git a/week1/homework/questions.txt b/week1/homework/questions.txt index 0adfd69..77d9123 100644 --- a/week1/homework/questions.txt +++ b/week1/homework/questions.txt @@ -1,24 +1,28 @@ -Please read: -Chapter 3 Classes, Objects, and Variables +Please read: +Chapter 3 Classes, Objects, and Variables p.90-94 Strings 1. What is an object? -An object is a representation in memory of a specific concept or thing that the Ruby interpreter knows about. +Everything that you can manipulate in Ruby is an object and every object in Ruby was generated directly or indirectly from a class. [Thomas p.29] +Therefore, all variables reference objects. An object typically holds some type of data or state. Each object in Ruby has a unique identifier. 2. What is a variable? -A variable is a name for a location in memory. It can contain, or point to, any type of object. +A name used to reference a value or an allocated space for a value. +Variables are used to keep track of objects: each variable holds a reference to an object. [Thomas p.43] +A variable is simply a reference to an object. [Thomas p.43] 3. What is the difference between an object and a class? -An object is an instance of a class, or a specific thing of that class's type in memory. The class is the specifics that are common to all things of that type. The classification of a concept or a thing is a class. A specific thing or concept of a class's type in memory is an object. For example: All books have titles (Class). This book's title is "Harry Potter and the Goblet of Fire" (Object). +Everything that you can manipulate in Ruby is an object. [Thomas p.29] +A class is an object that can be used to instantiate a number of objects with similar attributes, while each being independent (having different identities) of the other objects instantiated with the class. 4. What is a String? -A string is how Ruby understands text. It is a collection of characters (Bytes), and can be created by making an instance of the String class (String.new) or as a string literal ("",'', %Q[]). +Ruby strings are simply sequences of characters. They normally hold printable characters but that is not a requirement; a string can also hold binary data. Strings are objects of class String. [Thomas p.90] 5. What are three messages that I can send to a string object? Hint: think methods -chomp! - removes newline characters, or the specified characters, from the end of a string -strip! - removes leading or trailing whitespace from a string -split - returns an array of strings made up of the original string separated on whitespace or the specified characters or regexp +squeeze +split +chomp 6. What are two ways of defining a String literal? Bonus: What is the difference between the two? -Single quotes ex: '' and Double quotes ex: "". The single qoutes allow for 2 escape characters: \' and \\ . The double qouted string literal allows for many different escaped special characters (like \n is a line break) and allows for string interpolation, or the injection of evaluated Ruby code into the string ex: "Hello #{my_name}". The single qouted string takes up much less memory than a doulbe qouted string with interpolation. Without interpolation, both are about the same. - +Strings can be defined via single quotes, double quotes, %q, %Q, or with a here document. +Strings defined with double quotes can escape backslashes and single quotes while double quoted strings support many more escape sequences. diff --git a/week1/homework/strings_and_rspec_spec.rb b/week1/homework/strings_and_rspec_spec.rb index 2f188f6..8a24c67 100644 --- a/week1/homework/strings_and_rspec_spec.rb +++ b/week1/homework/strings_and_rspec_spec.rb @@ -3,24 +3,24 @@ # Please make these examples all pass # You will need to change the 3 pending tests # You will need to write a passing test for the first example -# (Hint: If you need help refer to the in-class exercises) +# (Hint: If you need help refer to the in-class exercises) # The two tests with the pending keyword, require some ruby code to be written -# (Hint: You should do the reading on Strings first) +# (Hint: You should do the reading on Strings first) describe String do - context "When a string is defined" do - before(:all) do - @my_string = "Renée is a fun teacher. Ruby is a really cool programming language" - end - it "should be able to count the charaters" do - @my_string.should have(@my_string.size).characters - end - it "should be able to split on the . charater" do - result = @my_string.split('.') - result.should have(2).items - end - it "should be able to give the encoding of the string" do - @my_string.encoding.should eq (Encoding.find("UTF-8")) - end - end + context "When a string is defined" do + before(:all) do + @my_string = "Renée is a fun teacher. Ruby is a really cool programming language" + end + it "should be able to count the characters" do + @my_string.length.should eq 66 + end + it "should be able to split on the . character" do + result = @my_string.split('.') + result.should have(2).items + end + it "should be able to give the encoding of the string" do + @my_string.encoding.should eq (Encoding.find("UTF-8")) + end + end end diff --git a/week2/homework/questions.txt b/week2/homework/questions.txt index 4cfc0a3..70faf30 100644 --- a/week2/homework/questions.txt +++ b/week2/homework/questions.txt @@ -1,18 +1,18 @@ -Please Read The Chapters on: -Containers, Blocks, and Iterators +Please Read The Chapters on: +Containers, Blocks, and Iterators Sharing Functionality: Inheritance, Modules, and Mixins 1. What is the difference between a Hash and an Array? -An array is an ordered list of items that are referenced by their index (order), a hash is a collection of items that can be referenced by a key and have no order. +The class Array holds a collection of object references. Each object reference occupies a position in the array, identified by a non-negative integer index. [Thomas p47] Hashes are similar to arrays in that they are indexed collections of object references. However, although you index arrays with integers, you can index a hash with objects of any type. [Thomas p50] 2. When would you use an Array over a Hash and vice versa? -When the items have an inherent order I would use an array, when I want to reference the items in my collection by a name or key and their order does not matter I would use a hash. +Use a hash when you would like to use any object as an index (key). Use an array when you don't need or don't want to have to index values by a custom key. As of Ruby 1.9, hashes remember the order in which you add items to the hash, like arrays do innately. 3. What is a module? Enumerable is a built in Ruby module, what is it? -A module is a way to group code that you can use across multiple classes. Enumerable is a Ruby module that provides collection functionality; iteration, searching, and sorting. It requires an implementation of the each method. +Enumerable is a module that defines useful iterator methods (methods that invoke a block of code) that can be included by classes that define collections such as the Array class. 4. Can you inherit more than one thing in Ruby? How could you get around this problem? -No, multiple inheritance is not allowed in Ruby. You can include multiple modules if you wanted to mix-in different functionality into your code. Code that is related with a hierarchical nature should be subclassed (inherited). A class can only have 1 direct parent, but can have lots of ancestors. +Because Ruby has single inheritance for classes, if you need to inherit functionality from more than one class, you might instead consider breaking that functionality into multiple modules which can then be included. 5. What is the difference between a Module and a Class? -A class can be instantiated into an object, a module cannot. A module is code that can be used across many classes. +Classes and modules both define methods. A class can hold state using self and provide behavior using methods that may or may not act on self. If the ability to hold state is not needed, a module could instead be used, which only provides behavior via methods and does not provide a mechanism for keeping state. diff --git a/week2/homework/simon_says.rb b/week2/homework/simon_says.rb index fa35ccd..a1de714 100644 --- a/week2/homework/simon_says.rb +++ b/week2/homework/simon_says.rb @@ -1,22 +1,32 @@ module SimonSays - def echo(st) - st - end - - def shout(st) - st.upcase - end - def first_word(st) - st.split.first - end + # Returns the given str. + def echo(str) + str + end - def start_of_word(st,i) - st[0...i] - end - - def repeat(st, t=2) - return "Go Away!" if t==0 - ([st]*t).join(' ') - end + # Returns a copy of str with all lowercase letters replaced with their uppercase counterparts. + def shout(str) + str.upcase + end + + # Returns a copy of str, repeated count times and separated with a single space. + def repeat(str, count = 2) + raise ArgumentError, "count must be a positive integer" if !(count.is_a? Integer) || count < 1 + + (1..count).collect{ str }.join(' ') + end + + # Returns the portion of word from the beginning of word to the given length. + def start_of_word(word, length) + raise ArgumentError, "length must be an integer greater or equal to 0" if !(length.is_a? Integer) || length < 0 + raise ArgumentError, "length is greater than the length of word" if length > word.length + + word[0, length] + end + + # Returns a new string of the first word in str. + def first_word(str) + str.split(' ')[0] + end end diff --git a/week3/class_materials/resources.txt b/week3/class_materials/resources.txt index 0a1ff81..0c3ef4c 100644 --- a/week3/class_materials/resources.txt +++ b/week3/class_materials/resources.txt @@ -5,4 +5,4 @@ Array: http://www.ruby-doc.org/core-1.9.3/Array.html Hash: http://www.ruby-doc.org/core-1.9.3/Hash.html Range: http://ruby-doc.org/core-1.9.3/Range.html -Why's Poignant Guide: http://mislav.uniqpath.com/poignant-guide/ +Why's Poignant Guide: http://mislav.uniqpath.com/poignant-guide/ \ No newline at end of file diff --git a/week3/homework/calculator.rb b/week3/homework/calculator.rb index f8916e3..7e6c9d2 100644 --- a/week3/homework/calculator.rb +++ b/week3/homework/calculator.rb @@ -1,24 +1,49 @@ class Calculator - def sum(array) - array.inject(0){|sum, x| sum +x} - end - - def multiply(*numbers) - puts numbers.inspect - numbers.flatten.inject(:*) - end - - def pow(base, p) - #(1...p).to_a.inject(base){|r,v| r *= base} - pow_fac(base, p) - end - - def fac(n) - #(1..n).to_a.inject(1){|f,v| f *= v} - pow_fac(n) - end -private - def pow_fac(base=nil, p) - (1..p).to_a.inject(1){|f,v| f *= base || v} - end + + # Initializes the calculator. + def initialize + end + + # Returns the sum of the given numbers or an array of numbers. + def sum(*arr) + arr.flatten! + + arr.each{ |n| raise ArgumentError, "arguments must be of type Number or an + Array of variables all of type Numeric" unless n.is_a? Numeric } + + if arr.length > 0 + arr.inject(:+) + else + 0 + end + end + + # Returns the product of the given numbers or an array of numbers. + def product(*arr) + arr.flatten! + + arr.each{ |n| raise ArgumentError, "arguments must be of type Numeric or an + Array of variables all of type Numeric" unless n.is_a? Numeric } + + arr.inject(:+) + end + + # Returns the result of the base number raised to the power of the exponent number. + def pow(base, exponent) + raise ArgumentError, "base must be of type Numeric" unless base.is_a? Numeric + raise ArgumentError, "exponent must be of type Numeric" unless exponent.is_a? Numeric + + base ** exponent + end + + # Returns the factorial of the specified number. + def factorial(num) + raise ArgumentError, "argument must be a non-negative integer" if !(num.is_a? Integer) || num < 0 + + if num == 0 + 1 + else + num * factorial(num - 1) + end + end end diff --git a/week3/homework/calculator_spec.rb b/week3/homework/calculator_spec.rb index 5a418ed..48ec205 100644 --- a/week3/homework/calculator_spec.rb +++ b/week3/homework/calculator_spec.rb @@ -1,7 +1,7 @@ require "#{File.dirname(__FILE__)}/calculator" describe Calculator do - + before do @calculator = Calculator.new end @@ -10,59 +10,51 @@ it "computes the sum of an empty array" do @calculator.sum([]).should == 0 end - + it "computes the sum of an array of one number" do @calculator.sum([7]).should == 7 end - + it "computes the sum of an array of two numbers" do @calculator.sum([7,11]).should == 18 end - + it "computes the sum of an array of many numbers" do @calculator.sum([1,3,5,7,9]).should == 25 end end - - # Once the above tests pass, - # write tests and code for the following: - describe "#multiply" do - it "multiplies two numbers" do - @calculator.multiply(2,2).should eq 4 - end - it "multiplies an array of numbers" do - @calculator.multiply([2,2]).should eq 4 - end + describe "#product" do + it "multiplies two numbers" do + @calculator.product(2, 3) == 6 + end + + it "multiplies an array of numbers" do + @calculator.product([2, 3, 4]) == 24 + end end - - it "raises one number to the power of another number" do - p = 1 - 32.times{ p *= 2 } - @calculator.pow(2,32).should eq p + + describe "#pow" do + it "raises one number to the power of another number" do + @calculator.pow(2, 4) == 32 + end end - - # http://en.wikipedia.org/wiki/Factorial + describe "#factorial" do it "computes the factorial of 0" do - @calculator.fac(0).should eq 1 + @calculator.factorial(0) == 1 end it "computes the factorial of 1" do - @calculator.fac(1).should eq 1 + @calculator.factorial(1) == 1 end - it "computes the factorial of 2" do - @calculator.fac(2).should eq 2 + @calculator.factorial(2) == 2 end - it "computes the factorial of 5" do - @calculator.fac(5).should eq 120 + @calculator.factorial(5) == 120 end - it "computes the factorial of 10" do - @calculator.fac(10).should eq 3628800 + @calculator.factorial(10) == 3628800 end - end - end diff --git a/week3/homework/questions.txt b/week3/homework/questions.txt index 08067b8..29e76ad 100644 --- a/week3/homework/questions.txt +++ b/week3/homework/questions.txt @@ -5,24 +5,30 @@ Please Read: - Chapter 22 The Ruby Language: basic types (symbols), variables and constants 1. What is a symbol? -A symbol is a static name or identifier. +Symbols are "scalar value objects used as identifiers, mapping immutable strings to fixed internal values." [Peter Cooper, Ruby Reloaded] 2. What is the difference between a symbol and a string? -A string is a collection of characters whereas a symbol is a static identifier. A string is not static no matter what the contents of the string are. So the strings "hello" and "hello" are two different ojects, whereas the symbol :hello and :hello are the exact same object. If you think of 1 as a FixNum or fixed number, you can think of the symbol :hello as the "FixStr" or fixed string :hello. +Symbols are immutable objects and if having the same name are the same object in memory while strings are mutable objects which have their own place in memory and can be manipulated by methods in the String class. 3. What is a block and how do I call a block? -A block is an anonymous function, or some code snipt that you can define and then call at a later time. To call a block you can use the yield keyword. +A block is simply a chunk of code enclosed in either braces or the keywords do and end. [Thomas p.55] Blocks are like anonymous methods. [Thomas p.66] Alternatively, blocks can be converted into an object having closure. You call a block using one of the ways outlined in the next answer. 4. How do I pass a block to a method? What is the method signature? -To pass a block to a method you define the block after the method call with either the curly bracket enclosure {} or the do/end syntax. An example of passing a block to the each method of an array: +You pass a block to a method in two ways: -my_array.each {|a| puts a} +Oneline: +my_receiver.my_method(args) { |v| v } -Any method in Ruby can take a block. You can explicitly add a block to a method by putting an ampersand & before the variable name in the method definition. An example of this would be: +Multiline: +my_receiver.my_method(args) do |v| + v +end + +You could then write the method signature like this that would act upon the block variables and return the output using the yield keyword: -def my_method(&my_block) - my_block.call +def my_method + yield(v + 1) end 5. Where would you use regular expressions? -Regular expressions are used for pattern matching and replacement with strings. An example would be if I wanted to write a syntax checker for some text that checked if each sentance ended with a period, started with a space and then a capital letter. +One might use regular expressions to test a string to see whether it mathces a pattern, extract sections of a string that match all or part of a pattern, or change a string, replacing parts that match a pattern. [Thomas p.99] diff --git a/week4/homework/questions.txt b/week4/homework/questions.txt index bc1ab7c..e16118d 100644 --- a/week4/homework/questions.txt +++ b/week4/homework/questions.txt @@ -3,7 +3,35 @@ Chapter 10 Basic Input and Output The Rake Gem: http://rake.rubyforge.org/ 1. How does Ruby read files? +Ruby reads files using the File class which is a subclass of the IO class. + +Two common ways to read files are using the each_line and each_byte methods of the File class like this [Thomas p.164]: + +File.open("testfile") do |file| + file.each_byte {|ch| print "#{ch.chr}:#{ch} " } +end + +File.open("testfile") do |file| + file.each_line {|line| puts "Got #{line.dump}" } +end + + 2. How would you output "Hello World!" to a file called my_output.txt? + +File.open("testfile", "w") do |file| + file.puts "Hello World!" +end + + 3. What is the Directory class and what is it used for? +"Objects of class Dir are directory streams representing directories in the underlying file system. They provide a variety of ways to list directories and their contents." [ruby-doc.org/core-1.9.3/Dir.html] + +The Dir class provides methods to open, create, delete, and check for the existence of directories among other actions. + + 4. What is an IO object? +An IO object is an instance of class IO that can be used for reading or writing binary data to and from a file using methods such as IO#print, IO#sysread, or IO#syswrite. + + 5. What is rake and what is it used for? What is a rake task? +Rake is Ruby's equivalent to the Unix program make which can take a list of commands for manipulating files and processes which is typically used in builds. With rake, one can create rakefiles, specify tasks with prerequisites, create filelists to manipulate file names and paths, and execute tasks in parallel. "Tasks are the main unit of work in a Rakefile. Tasks have a name (usually given as a symbol or a string), a list of prerequisites (more symbols or strings) and a list of actions (given as a block)." [https://github.com/jimweirich/rake/blob/master/doc/rakefile.rdoc] diff --git a/week6/class_materials/test_gem/bin/test_gem_gs b/week6/class_materials/test_gem/bin/test_gem_gs new file mode 100644 index 0000000..044c5e3 --- /dev/null +++ b/week6/class_materials/test_gem/bin/test_gem_gs @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +puts "Hello There!" +puts ARGV[0] \ No newline at end of file diff --git a/week6/class_materials/test_gem/lib/test_gem_gs.rb b/week6/class_materials/test_gem/lib/test_gem_gs.rb new file mode 100644 index 0000000..1bd5f23 --- /dev/null +++ b/week6/class_materials/test_gem/lib/test_gem_gs.rb @@ -0,0 +1,3 @@ +puts "Hello World!" +class Hello +end \ No newline at end of file diff --git a/week6/class_materials/test_gem/test_gem_gs.gemspec b/week6/class_materials/test_gem/test_gem_gs.gemspec new file mode 100644 index 0000000..d0fcee8 --- /dev/null +++ b/week6/class_materials/test_gem/test_gem_gs.gemspec @@ -0,0 +1,12 @@ +Gem::Specification.new do |s| + s.name = 'gs_hello_gem' + s.version = '0.0.1' + s.date = '2012-11-13' + s.summary = "Making a Test Gem" + s.description = "A gem to explain how to make gems" + s.authors = ["Greg Stallings"] + s.email = 'gregstallings@gmail.com' + s.homepage = 'http://rubygems.org/gems/test_gem_gs' + s.files = ["lib/test_gem_gs.rb"] + s.executables << 'test_gem_gs' +end \ No newline at end of file diff --git a/week7/homework/features/step_definitions/pirate.rb b/week7/homework/features/step_definitions/pirate.rb index d2a6f6a..a2949d8 100644 --- a/week7/homework/features/step_definitions/pirate.rb +++ b/week7/homework/features/step_definitions/pirate.rb @@ -1,17 +1,19 @@ -class PirateTranslator - PIRATE_WORDS = { - "Hello Friend" => "Ahoy Matey" - } - def say(str) - @said = lookup_pirate(str).to_s - end +class Pirate + def initialize(text='') + @text = text + end - def translate - @said + "\n Shiber Me Timbers You Scurvey Dogs!!" - end + # Sets the text + def text(text) + @text = text + end -private - def lookup_pirate(str) - PIRATE_WORDS[str] - end -end \ No newline at end of file + # Translates text into pirate speak + def translate(str=@text) + if str == 'Hello Friend' + 'Ahoy Matey' + else + 'Shiber Me Timbers You Scurvey Dogs!!' + end + end +end diff --git a/week7/homework/features/step_definitions/pirate_steps.rb b/week7/homework/features/step_definitions/pirate_steps.rb index faf1a7f..fe0fea5 100644 --- a/week7/homework/features/step_definitions/pirate_steps.rb +++ b/week7/homework/features/step_definitions/pirate_steps.rb @@ -1,19 +1,23 @@ -Gangway /^I have a (\w+)$/ do |arg| - @translator = Kernel.const_get(arg).new +Gangway /^I have a PirateTranslator$/ do + @pirate = Pirate.new end -Blimey /^I (\w+) '(.+)'$/ do |method, arg| - @translator.send(method, arg) +Blimey /^I say 'Hello Friend'$/ do + @text = @pirate.text('Hello Friend') end -Letgoandhaul /^I hit (\w+)$/ do |arg| - @result = @translator.send(arg) +Blimey /^I hit translate$/ do + @result = @pirate.translate end -Letgoandhaul /^it prints out '(.+)'$/ do |arg| - @result.split("\n ").first.should == arg +Letgoandhaul /^it prints out 'Ahoy Matey'$/ do + if @text == 'Hello Friend' + @result.should eq 'Ahoy Matey' + end end -Letgoandhaul /^it also prints '(.+)'$/ do |arg| - @result.split("\n ").last.should == arg +Letgoandhaul /^it also prints 'Shiber Me Timbers You Scurvey Dogs!!'$/ do + if @text != 'Hello Friend' + @result.should eq 'Shiber Me Timbers You Scurvey Dogs!!' + end end diff --git a/week7/homework/features/step_definitions/tic-tac-toe-steps.rb b/week7/homework/features/step_definitions/tic-tac-toe-steps.rb index b353120..d00e957 100644 --- a/week7/homework/features/step_definitions/tic-tac-toe-steps.rb +++ b/week7/homework/features/step_definitions/tic-tac-toe-steps.rb @@ -35,7 +35,7 @@ Then /^the computer prints "(.*?)"$/ do |arg1| @game.should_receive(:puts).with(arg1) - @game.indicate_palyer_turn + @game.indicate_player_turn end Then /^waits for my input of "(.*?)"$/ do |arg1| diff --git a/week7/homework/features/step_definitions/tic-tac-toe.rb b/week7/homework/features/step_definitions/tic-tac-toe.rb new file mode 100644 index 0000000..5d3a597 --- /dev/null +++ b/week7/homework/features/step_definitions/tic-tac-toe.rb @@ -0,0 +1,298 @@ +class TicTacToe + SYMBOLS = [:X, :O] + FRESH_BOARD = { + :A1 => ' ', :A2 => ' ', :A3 => ' ', + :B1 => ' ', :B2 => ' ', :B3 => ' ', + :C1 => ' ', :C2 => ' ', :C3 => ' ' + } + + # Initializes game + def initialize(player=nil, symbol=nil) + @players = {computer: 'Computer', player: nil} + + if player + raise ArgumentError, "starting player must be one of #{@players.keys}" unless + @players.keys.include? player + + # Explicitly set the starting player + @current_player_key = player + else + # Randomly assign a starting player + @current_player_key = @players.keys.sample + end + + if symbol + raise ArgumentError, "symbol must be one of #{SYMBOLS}" unless + SYMBOLS.include? symbol + + # Explicitly set the symbol for the player + @player_symbol = symbol + else + # Randomly assign a symbol for the player + @player_symbol = SYMBOLS.sample + end + + # The starting player and symbol have been assigned so assign the + # opposite symbol to the computer + @computer_symbol = SYMBOLS.select {|value| value if value != @player_symbol }.first + + @board = FRESH_BOARD + @winner = nil + end + + # Player methods + ################ + + # Gets the player's name + def player + @players[:player] + end + + # Sets the player + def player=(player) + @players[:player] = player + end + + # Gets the computer's name + def computer + @players[:computer] + end + + attr_reader :player_symbol + + # Sets the player's symbol + def player_symbol=(symbol) + raise ArgumentError, "player symbol must be one of #{SYMBOLS}" unless + SYMBOLS.include? symbol + @player_symbol = symbol + end + + attr_reader :computer_symbol + + # Sets the computer's symbol + def computer_symbol=(symbol) + raise ArgumentError, "computer symbol must be one of #{SYMBOLS}" unless + SYMBOLS.include? symbol + @computer_symbol = symbol + end + + # Prompts the standard input for the player's name + def get_player_name + puts "Enter your name:" + name = gets.chomp + player = name + end + + # Outputs the welcome message to the player + def welcome_player + if @players[:player].nil? + @players[:player] = get_player_name + end + "Welcome #{@players[:player]}" + end + + # Gets the current player name + def current_player + "#{@players[@current_player_key]}" + end + + # Gets the next player key + def next_player_key + @players.keys.select {|value| value if value != @current_player_key }.first + end + + # Outputs to the user that it is the player's turn + def indicate_player_turn + puts "#{player}'s Move:" + end + + # Prompts the standard input for the player's move + def get_player_move + move = gets.chomp.to_sym + end + + # Does the player's move and returns the spot + def player_move + # Causes stack overflow due to infinite recursion on tests + move = get_player_move.to_sym + if open_spots.include? move + # Enter the player's symbol into the chosen spot + @board[move] = @player_symbol + @current_player_key = :computer + move + else + # Remove for testing + #puts "You must enter one of the available spots: #{open_spots.map {|v| v.to_s}.join(', ') }" + player_move + end + + # This way won't cause stack overflow but tests are not compatible + #until open_spots.include?(move = get_player_move.to_sym) + # puts "You must enter one of the available spots: #{open_spots.map {|v| v.to_s}.join(', ') }" + #end + #@board[move] = @player_symbol + #@current_player_key = :computer + #move + end + + # Does the computer's move and returns the spot + def computer_move + puts "Computer's Move:" + + # Randomly choose an open board spot and enter the computer's symbol + move = open_spots.sample + @board[move] = @computer_symbol + @current_player_key = :player + move + end + + # Game-state methods + #################### + + # Determines the winner of the game and sets the winner + def determine_winner + winning_symbol = nil + + # For each symbol + SYMBOLS.each do |symbol| + # Find all the board keys that have symbol as its value + keys = @board.select {|key, value| key if value == symbol }.keys + + # Don't bother checking if there aren't at least three keys with the same symbol + if keys.length >= 3 + # Gather the letter parts of the keys + letters = {} + keys.each do |value| + letter = value.to_s[0] + if letters[letter] + letters[letter] += 1 + else + letters[letter] = 1 + end + end + + # If any of the values in the letters hash equal three + # then this symbol is the winner + selected_letters = letters.select {|key, value| key if value == 3 } + winning_symbol = symbol if selected_letters.length > 0 + + # Gather the number parts of the keys + numbers = {} + keys.each do |value| + number = value.to_s[1] + if numbers[number] + numbers[number] += 1 + else + numbers[number] = 1 + end + end + + # If any of the values in the numbers hash equal three + # then this symbol is the winner + selected_numbers = numbers.select {|key, value| key if value == 3 } + winning_symbol = symbol if selected_numbers.length > 0 + + # Manually check for diagonal matches since there are only two possibilities + winning_symbol = symbol if (keys.include? :A1) && + (keys.include? :B2) && + (keys.include? :C3) + winning_symbol = symbol if (keys.include? :A3) && + (keys.include? :B2) && + (keys.include? :C1) + end + end + + # Set the @winner + if player_symbol == winning_symbol + @winner = :player + elsif computer_symbol == winning_symbol + @winner = :computer + end + @winner + end + + # Returns true if the game is over + def over? + if @winner || draw? + true + else + false + end + end + + # Returns true if the player won the game + def player_won? + if @winner == :player + return true + else + return false + end + end + + # Returns true if the computer won the game + def computer_won? + if @winner == :computer + return true + else + return false + end + end + + # Returns true if the game is a draw + # A draw is when there isn't a winner and there aren't any open spaces left + # on the board + def draw? + if !@winner && !spots_open? + true + else + false + end + end + + # Board methods + ############### + + attr_reader :board + + # Sets the board + def board=(board) + # Raise argument exception unless board is a hash with 9 keys + raise ArgumentError, "board must be of type Hash" unless board.is_a? Hash + raise ArgumentError, "board must have 9 spots" unless board.keys.length == 9 + + # Ensure each value in required keys is present in board keys exactly once + board_keys = [:A1, :A2, :A3, :B1, :B2, :B3, :C1, :C2, :C3] + board_keys.each do |value| + raise ArgumentError, "board must have exactly one key of #{value}" unless + board.keys.count(value) == 1 + end + + # Ensure all board values are valid + board.values.each do |value| + raise ArgumentError, "board values must be one of #{SYMBOLS.dup << ' '}" unless + (SYMBOLS.dup << ' ').include? value + end + + @board = board + end + + # Returns an array of open spots available on the board + def open_spots + @board.select {|key, value| key if value == ' ' }.keys + end + + # Returns true if there are any open spots on the board + def spots_open? + open_spots.length > 0 + end + + # Outputs the board in a human readable state + def current_state + "\n + #{@board[:A1]} | #{@board[:A2]} | #{@board[:A3]} \n +-----------------\n + #{@board[:B1]} | #{@board[:B2]} | #{@board[:B3]} \n +-----------------\n + #{@board[:C1]} | #{@board[:C2]} | #{@board[:C3]} \n\n" + end +end diff --git a/week7/homework/play_game.rb b/week7/homework/play_game.rb index cf7847f..f64b949 100644 --- a/week7/homework/play_game.rb +++ b/week7/homework/play_game.rb @@ -8,7 +8,7 @@ when "Computer" @game.computer_move when @game.player - @game.indicate_palyer_turn + @game.indicate_player_turn @game.player_move end puts @game.current_state diff --git a/week7/homework/questions.txt b/week7/homework/questions.txt index d55387d..0a7a539 100644 --- a/week7/homework/questions.txt +++ b/week7/homework/questions.txt @@ -3,7 +3,23 @@ Please Read Chapters 23 and 24 DuckTyping and MetaProgramming Questions: 1. What is method_missing and how can it be used? +method_missing is a method on BasicObject which is invoked when an object is sent a message that it cannot handle. The method can be overrided in a subclass to provide more dynamic behavior such as providing the developer with a more helpful error message when a method that does not exist is invoked on an object of the subclass. In another use, Rails' ActiveRecord uses method_missing on models to create dynamic finder methods. + 2. What is and Eigenclass and what is it used for? Where Do Singleton methods live? -3. When would you use DuckTypeing? How would you use it to improve your code? +An eigenclass or singleton class is a new anonymous class that Ruby creates when a method is defined on an object which is not yet an instance of a class. Singleton methods live in the eigenclass. + +3. When would you use DuckTyping? How would you use it to improve your code? +Duck Typing is the practice of taking advantage of the dynamically typed nature of a language such as Ruby to build methods or other functionality that are able to handle more than one variable type in order to make one's program more usable or adaptable to changes in implementation or testing. For example, since a Ruby method does not inherently check the type if its arguments, one could build a method that accepts either a string or an array for the same argument. In the example below, since Array and String both respond to the << method, the argument file could be either a string or an array. + +def(file, line) + file << line +end + 4. What is the difference between a class method and an instance method? What is the difference between instance_eval and class_eval? +A class method is a method that is associated with the class object (where the current object self is set to the class object of the class being defined), while an instance method is a method that is associated with instances of the class (where the current object self is set to the explicit receiver). + +"class_eval sets things up as if you were in the body of a class definition, so method definitions will define instance methods" [Thomas p.395] +"instance_eval sets things up as if you were working inside the singleton class of self. Therefore, any methods you define will become class methods." [Thomas p.395] + 5. What is the difference between a singleton class and a singleton method? +A singleton class is an anonymous class created by Ruby when a method is defined on an object which is not yet an instance of a class, while a singleton method is any method defined in a singleton class or on the current object self (a method that is specific to a particular object).