# 007

No need for a solution

# 009

Solution provided as a part of the chapter, see 009.txt

# 010

Step 1. Run REPL, for example, by typing irb Step 2. Just type 60 * 60 * 24 * 1000 to see the result.

# 012

No need for a solution

# 014

No need for a solution

# 015

### Exercise 1

1) ls - lah 2) mkdir my_directory 3) ls -lah 4) echo blabla > example.txt (creating a sample file) 5) cp example.txt my_directory 6) ls -lah my_directory

### Exercise 2

find ~ -type f -name '*.log'

### Exercise 3

1) touch file.txt - create empty file or “touch” (change date for) existing, optional step 2) ls -lah 3) echo Walt > file.txt 4) cat file.txt

# 016

No need for a solution

# 017

### Exercise 1

puts "Say a note on a 0 fret?" # The right answer is E
gets
puts "Say a note on a 1st fret?" # The right answer is F
gets
puts "Say a note on a 2nd fret?" # The right answer is F#
gets
puts "Say a note on a 3rd fret?" # G
gets
puts "Say a note on a 4th fret?" # G#
gets
puts "Say a note on a 5th fret?" # A
gets
puts "Say a note on a 6th fret?" # A#
gets
puts "Say a note on a 7th fret?" # B
gets
puts "Say a note on a 8th fret?" # C
gets
puts "Say a note on a 9th fret?" # C#
gets
puts "Say a note on a 10th fret?" # D
gets
puts "Say a note on a 11th fret?" # D#
gets
puts "Say a note on a 12th fret?" # E
gets


### Exercise 2

No need for a solution

Skip for now

# 024

### Exercise 1

No need for a solution

### Exercise 2

puts "Enter your annual salary (for example, type 50 for $50,000 USD): " annual_salary = gets.to_i * 1000 daily_salary = annual_salary / 365 puts "Your daily salary is ~$#{daily_salary}"


Sample output:

$ruby app.rb Enter your annual salary (for example, type 50 for$50,000 USD):
200
Your daily salary is ~$547  # 025 No need for a solution # 027 ### Exercise 1 price = 500_000 30.times do |n| puts "Year #{n}, left to pay: #{price - n * 16_666}" end  Output: Year 0, left to pay: 500000 Year 1, left to pay: 483334 Year 2, left to pay: 466668 Year 3, left to pay: 450002 Year 4, left to pay: 433336 Year 5, left to pay: 416670 Year 6, left to pay: 400004 Year 7, left to pay: 383338 Year 8, left to pay: 366672 Year 9, left to pay: 350006 Year 10, left to pay: 333340 Year 11, left to pay: 316674 Year 12, left to pay: 300008 Year 13, left to pay: 283342 Year 14, left to pay: 266676 Year 15, left to pay: 250010 Year 16, left to pay: 233344 Year 17, left to pay: 216678 Year 18, left to pay: 200012 Year 19, left to pay: 183346 Year 20, left to pay: 166680 Year 21, left to pay: 150014 Year 22, left to pay: 133348 Year 23, left to pay: 116682 Year 24, left to pay: 100016 Year 25, left to pay: 83350 Year 26, left to pay: 66684 Year 27, left to pay: 50018 Year 28, left to pay: 33352 Year 29, left to pay: 16686  ### Exercise 2 price = 500_000 interest = 0.04 annual_payment = 16_666 30.times do |n| remaining = price - n * annual_payment interest_payment = remaining * interest total = annual_payment + interest_payment puts "Year #{n}, left to pay: #{remaining}. You are paying #{annual_payment} plus #{interest_payment} of interest (total is #{total})" end  Output: Year 0, left to pay: 500000. You are paying 16666 plus 20000.0 of interest (total is 36666.0) Year 1, left to pay: 483334. You are paying 16666 plus 19333.36 of interest (total is 35999.36) Year 2, left to pay: 466668. You are paying 16666 plus 18666.72 of interest (total is 35332.72) Year 3, left to pay: 450002. You are paying 16666 plus 18000.08 of interest (total is 34666.08) Year 4, left to pay: 433336. You are paying 16666 plus 17333.44 of interest (total is 33999.44) Year 5, left to pay: 416670. You are paying 16666 plus 16666.8 of interest (total is 33332.8) Year 6, left to pay: 400004. You are paying 16666 plus 16000.16 of interest (total is 32666.16) Year 7, left to pay: 383338. You are paying 16666 plus 15333.52 of interest (total is 31999.52) Year 8, left to pay: 366672. You are paying 16666 plus 14666.880000000001 of interest (total is 31332.88) Year 9, left to pay: 350006. You are paying 16666 plus 14000.24 of interest (total is 30666.239999999998) Year 10, left to pay: 333340. You are paying 16666 plus 13333.6 of interest (total is 29999.6) Year 11, left to pay: 316674. You are paying 16666 plus 12666.960000000001 of interest (total is 29332.96) Year 12, left to pay: 300008. You are paying 16666 plus 12000.32 of interest (total is 28666.32) Year 13, left to pay: 283342. You are paying 16666 plus 11333.68 of interest (total is 27999.68) Year 14, left to pay: 266676. You are paying 16666 plus 10667.04 of interest (total is 27333.04) Year 15, left to pay: 250010. You are paying 16666 plus 10000.4 of interest (total is 26666.4) Year 16, left to pay: 233344. You are paying 16666 plus 9333.76 of interest (total is 25999.760000000002) Year 17, left to pay: 216678. You are paying 16666 plus 8667.12 of interest (total is 25333.120000000003) Year 18, left to pay: 200012. You are paying 16666 plus 8000.4800000000005 of interest (total is 24666.48) Year 19, left to pay: 183346. You are paying 16666 plus 7333.84 of interest (total is 23999.84) Year 20, left to pay: 166680. You are paying 16666 plus 6667.2 of interest (total is 23333.2) Year 21, left to pay: 150014. You are paying 16666 plus 6000.56 of interest (total is 22666.56) Year 22, left to pay: 133348. You are paying 16666 plus 5333.92 of interest (total is 21999.92) Year 23, left to pay: 116682. You are paying 16666 plus 4667.28 of interest (total is 21333.28) Year 24, left to pay: 100016. You are paying 16666 plus 4000.64 of interest (total is 20666.64) Year 25, left to pay: 83350. You are paying 16666 plus 3334.0 of interest (total is 20000.0) Year 26, left to pay: 66684. You are paying 16666 plus 2667.36 of interest (total is 19333.36) Year 27, left to pay: 50018. You are paying 16666 plus 2000.72 of interest (total is 18666.72) Year 28, left to pay: 33352. You are paying 16666 plus 1334.08 of interest (total is 18000.08) Year 29, left to pay: 16686. You are paying 16666 plus 667.44 of interest (total is 17333.44)  # 028 Skip for now # 029 Skip for now # 030 ### Exercise 1 Answers: 1) true 2) false 3) false ### Exercise 2 puts "Login:" login = gets.chomp puts "Password:" password = gets.chomp if login == "admin" && password == "12345" puts "Granted access to online banking" else puts "Access denied" end  ### Exercise 3 puts "Width (for example, type 5 for 5 meters):" width = gets.to_i puts "Length (for example, type 20 for 20 meters):" length = gets.to_i area = width * length puts "Area is #{area} square meters" price = 0 if area < 50 price = 1000 elsif area >= 50 && area < 100 price = 1500 else price = area * 25 end puts "Price for the land is$#{price}"


Output:

$ruby app.rb Width (for example, type 5 for 5 meters): 1000 Length (for example, type 20 for 20 meters): 1100 Area is 1100000 square meters Price for the land is$27500000


Skip for now

# 033

number = rand(1..1_000_000)
print 'Hi! I picked the number from 1 to 1 million, try to guess it: '

loop do
input = gets.to_i

if input == number
puts 'You guessed it!'
exit
else
if number > input
print 'Nope, the number is greater than that, try again: '
else
print 'Nope, the number is less than that, try again: '
end
end
end


Output:

$ruby app.rb ruby app.rb Hi! I picked the number from 1 to 1 million, try to guess it: 500000 Nope, the number is less than that, try again: 250000 Nope, the number is greater than that, try again: 350000 Nope, the number is greater than that, try again: 400000 Nope, the number is greater than that, try again: 450000 Nope, the number is greater than that, try again: 475000 Nope, the number is greater than that, try again: 490000 Nope, the number is greater than that, try again: 495000 Nope, the number is greater than that, try again: 499999 Nope, the number is less than that, try again: 498000 Nope, the number is less than that, try again: 497000 Nope, the number is greater than that, try again: 497500 Nope, the number is greater than that, try again: 497750 Nope, the number is greater than that, try again: 498000 Nope, the number is less than that, try again: 497900 Nope, the number is less than that, try again: 497800 Nope, the number is greater than that, try again: 497850 Nope, the number is greater than that, try again: 497875 Nope, the number is greater than that, try again: 497890 Nope, the number is greater than that, try again: 497899 Nope, the number is less than that, try again: 497895 Nope, the number is less than that, try again: 497893 Nope, the number is less than that, try again: 497891 You guessed it!  # 034 Skip for now # 035 loop do print "/\r" sleep 0.1 print "-\r" sleep 0.1 print "\\\r" sleep 0.1 print "|\r" sleep 0.1 end  # 037 Skip for now # 039 ### Exercise 1 def animated_rand value = rand(0..5) 0.upto(value) do |num| print "#{num}\r" sleep 0.3 end puts value end print "What's your age: " age = gets.to_i if age < 18 puts 'Sorry, but you should be at least 18 to play' exit end balance = 20 loop do puts 'Press Enter to pull the handle...' gets x = animated_rand y = animated_rand z = animated_rand puts "Result: #{x} #{y} #{z}" if x == 0 && y == 0 && z == 0 balance = 0 puts 'You lost your money' elsif x == 1 && y == 1 && z == 1 balance += 10 puts 'You won$10'
elsif x == 2 && y == 2 && z == 2
balance += 20
puts 'You won $20' else balance -= 0.5 puts 'You lost 50 cents' end puts "Your balance is #{balance} dollars" end  ### Exercise 2 No need for a solution (because of “use your imagination”) # 040 Skip for now # 044 $ irb
2.7.0 :001 > ['one', 'two', 'three'] # Standard
=> ["one", "two", "three"]

2.7.0 :002 > %w(one two three) # Shortcut
=> ["one", "two", "three"]

2.7.0 :003 > [:one, :two, :three] # Standard
=> [:one, :two, :three]

2.7.0 :004 > %i(one two three) # Shortcut
=> [:one, :two, :three]


Skip for now

# 046

### Exercise 1

No need for a solution (“try things out” type)

### Exercise 2

Array.new(5) { Array.new(4) { rand(1..5) } }


or

Array.new(5) do
Array.new(4) { rand(1..5) }
end


or

Array.new(5) do
Array.new(4) do
rand(1..5)
end
end


### Exercise 3

Array.new(4) { Array.new(5) { rand(1..5) } }


### Exercise 4

Array.new(5) { Array.new(4) { rand(0..9) } }


Skip for now

# 048

### Exercise 1

arr = [
['a', 'b', 'c'],
['d', 'e', 'f'],
['g', 'h', 'i']
]

print arr[0][0]
print arr[1][1]
print arr[2][2]

print arr[0][2]
print arr[1][1]
print arr[2][0]


### Exercise 2

arr = Array.new(3) { Array.new(3) { 'something' } }
puts arr.inspect

arr[1][1].upcase!
puts arr.inspect


### Exercise 3

# IMPORTANT: suboptimal solution that uses arrays only
# The hash data structure (see next chapters) is a better choice for the problem

def find_number_by_letter(letter)
arr = [
[],               # 0
[],               # 1
%w(A B C),        # 2
%w(D E F),        # 3
%w(G H I),        # 4
%w(J K L),        # 5
%w(M N O),        # 6
%w(P Q R S),      # 7
%w(T U V),        # 8
%w(W X Y Z)       # 9
]

arr.each_with_index do |subarray, i|
subarray.each do |letter_candidate|
return i if letter == letter_candidate
end
end

# Nothing found, just return the letter
letter
end

def phone_to_number(phone)
phone.each_char do |letter|
print find_number_by_letter(letter)
end
end

phone_to_number('555MATRESS') # should print 5556287377


Skip for now

# 052

[11, 22, 33, 44, 55].count(&:even?)


Skip for now

# 062

### Exercise 1

obj = {
soccer_ball: 410,
tennis_ball: 58,
golf_ball: 45
}

puts 'Golf ball weight on the Moon (grams):'
puts obj[:golf_ball] / 6

puts 'Soccer ball weight on the Moon (grams):'
puts obj[:soccer_ball] / 6

puts 'Tennis ball weight on the Moon (grams):'
puts obj[:tennis_ball] / 6


### Exercise 2

obj = {
soccer_ball: 410,
tennis_ball: 58,
golf_ball: 45
}

puts "***************"
puts "The Moon Store"
puts "***************"
puts

print 'How many golf balls are you willing to buy? '
golf_ball_cnt = gets.to_i

print 'How many soccer balls are you willing to buy? '
soccer_ball_cnt = gets.to_i

print 'How many tennis balls are you willing to buy? '
tennis_ball_cnt = gets.to_i

weight_on_earth = \
golf_ball_cnt   * obj[:golf_ball]   +
soccer_ball_cnt * obj[:soccer_ball] +
tennis_ball_cnt * obj[:tennis_ball]

puts "Total weight of all items on the Earth is #{(weight_on_earth.to_f / 1000)} kg or #{(weight_on_earth * 0.00220462)} lb"
puts "Total weight of all items on the Moon is #{(weight_on_earth.to_f / 1000 / 6)} kg or #{(weight_on_earth * 0.00220462 / 6)} lb"


Output:

$ruby ruby app.rb *************** The Moon Store *************** How many golf balls are you willing to buy? 1 How many soccer balls are you willing to buy? 2 How many tennis balls are you willing to buy? 3 Total weight of all items on the Earth is 1.039 kg or 2.29060018 lb Total weight of all items on the Moon is 0.17316666666666666 kg or 0.3817666966666667 lb  # 063 Skip for now # 064 ### Exercise 1 { client: "Mark Zuck", balance_usd: 123.45, show_deposits: true, transactions: [ { description: "McDonalds", type: :withdrawal, amount: 40 }, { description: "Selling ads", type: :deposit, amount: 1000 }, { description: "Selling user data", type: :deposit, amount: 300 }, { description: "Lawyer", type: :withdrawal, amount: 200 }, { description: "Lunch with friends", type: :withdrawal, amount: 100 }, ] }  ### Exercise 2 def show(info) puts "Name: #{info[:client]}" puts "Balance:$#{info[:balance_usd]}"
puts "Show deposits: #{info[:show_deposits]}"
puts

puts "Transactions:"

info[:transactions].each do |t|
next if !info[:show_deposits] && t[:type] == :deposit

puts "#{t[:description]}, #{t[:type]}, $#{t[:amount]}" end end show({ client: "Mark Zuck", balance_usd: 123.45, show_deposits: true, # Change to false to hide deposits transactions: [ { description: "McDonalds", type: :withdrawal, amount: 40 }, { description: "Selling ads", type: :deposit, amount: 1000 }, { description: "Selling user data", type: :deposit, amount: 300 }, { description: "Lawyer", type: :withdrawal, amount: 200 }, { description: "Lunch with friends", type: :withdrawal, amount: 100 }, ] })  Output 1: $ ruby app.rb
Name: Mark Zuck
Balance: $123.45 Show deposits: true Transactions: McDonalds, withdrawal,$40
Selling ads, deposit, $1000 Selling user data, deposit,$300
Lawyer, withdrawal, $200 Lunch with friends, withdrawal,$100


Output 2:

$ruby app.rb Name: Mark Zuck Balance:$123.45
Show deposits: false

Transactions:
McDonalds, withdrawal, $40 Lawyer, withdrawal,$200
Lunch with friends, withdrawal, $100  # 065 Skip for now # 068 One way to implement the program is to use default hash value of 0: def f(sentence) hash = Hash.new(0) sentence.each_char do |ch| hash[ch] += 1 end hash end puts f('quick brown fox jumps over the lazy dog').inspect  Another is to use Array#tally method (Ruby 2.7.0+) def f(sentence) sentence.split('').tally end puts f('quick brown fox jumps over the lazy dog').inspect  The output is the same: {"q"=>1, "u"=>2, "i"=>1, "c"=>1, "k"=>1, " "=>7, "b"=>1, "r"=>2, "o"=>4, "w"=>1, "n"=>1, "f"=>1, "x"=>1, "j"=>1, "m"=>1, "p"=>1, "s"=>1, "v"=>1, "e"=>2, "t"=>1, "h"=>1, "l"=>1, "a"=>1, "z"=>1, "y"=>1, "d"=>1, "g"=>1}  E.g. there are four “o” letters in the sentence above and 7 spaces. # 069 Skip for now # 070 ### Exercise 1 Here is how the program can be optimized. Look for “OPTIMIZATION” comment in 2 places below: # Import namespace below, because "set" # is not imported by default. require 'set' # The main that accepts a string (sentence). def f(str) # Create set instance set = Set.new # Iterate over each character in a string str.each_char do |c| # Only if character is greater than "a" and less than "z" # (ignore other characters) if c >= 'a' && c <= 'z' # Add to set set.add(c) end # OPTIMIZATION: return immediately, # no need to scan the rest of the string return true if set.size == 26 end false # OPTIMIZATION: we know that set size is not 26 end # prints true, because we use all letters of English # alphabet in the following sentence puts f('quick brown fox jumps over the lazy dog')  ### Exercise 2 No need for a solution (“try to implement yourself”). # 071 Skip for now # 073 The first block has books defined as array independent objects, like: books = [ { ... }, { ... }, { ... } ]  Since books are represented as array, the order is always guaranteed. However, if we want to find a book by its id, we’ll need to scan the entire array and compare ids one by one. Imagine we have 1 million books defined the following way: books = [ { isbn: '9783161484100', ... }, # 1st book { isbn: '8372684193990', ... }, # 2nd book ... { isbn: '0388819938812', ... } # 1.000.000th book ]  This data has sequential nature, and looking up an object takes linear time - in other words, the only way to go is to iterate and compare isbn to get the right one. However, when books are represented as hash (second block), ids are the hash key. There is no specific order (well, in Ruby language there is an order for convenience, but normally hash data structure has no order), but knowing how hashes work allows us to do quick search in constant time. We do not explain how exactly hashes achieve this performance, it’s not the purpose of this book. However, we highly encourage to read up on that, the algorithm is simple enough and quite interesting. # 075 Skip for now # 076 class Robot # Accessors, so we can access coordinates from the outside attr_accessor :x, :y # Constructor, accepts hash. If not specified, empty hash will be used. # In hash we expect two parameters: initial coordinates of the robot. # If not specified, will equal to zero by default. def initialize(options={}) @x = options[:x] || 0 @y = options[:y] || 0 @num = options[:num] || 0 end def right return if @num.even? self.x += 1 end def left return if @num.even? self.x -= 1 end def up return if @num.odd? self.y += 1 end def down return if @num.odd? self.y -= 1 end end # Commander is something that moves a robot. class Commander # Issue a command to move a robot. Accepts robot object # and sends it a random command. def move(who) m = [:right, :left, :up, :down].sample who.send(m) end end # Create commander object, we'll have only one commander # in this example. commander = Commander.new # Array of ten robots, each robot has its own number from 0 to 9. arr = [] 10.times do |num| arr << Robot.new(num: num) end # Infinite loop (hit ^C to stop the loop) loop do # Tricky way to clear the screen puts "\e[H\e[2J" # Draw the grid. It starts with -30 to 30 by X, # and from 12 to -12 by Y (12).downto(-12) do |y| (-30).upto(30) do |x| # Check if we have a robot with X and Y coordinates found = arr.any? { |robot| robot.x == x && robot.y == y } # Draw star if a robot was found. Dot otherwise. if found print '*' else print '.' end end # Move to the next line on the screen. puts end # Move each robot randomly. arr.each do |robot| commander.move(robot) end # Wait for half a second. sleep 0.5 end  # 077 ### Exercise 1 No need for a solution ### Exercise 2 class Robot # Accessors, so we can access coordinates from outside attr_accessor :x, :y # Constructor, accepts hash. If hash not specified, empty is used. # We expect two parameters in hash: initial robot coordinates; # if not specified, both will equal to zero. def initialize(options={}) @x = options[:x] || 0 @y = options[:y] || 0 end def right self.x += 1 end def left self.x -= 1 end def up self.y += 1 end def down self.y -= 1 end # New method, just a symbol we use for robots. def label '*' end end # Dog class has the similar interface, some methods are empty below. class Dog # Accessors, so we can access coordinates from outside attr_accessor :x, :y # Constructor, accepts hash. If hash not specified, empty is used. # We expect two parameters in hash: initial dog coordinates; # if not specified, both will equal to zero. def initialize(options={}) @x = options[:x] || 0 @y = options[:y] || 0 end def right self.x += 1 end # Empty method, but it exists. When called does nothing. We need it # to avoid "missing method" error. def left end # Another empty method. def up end def down self.y -= 1 end # New method, just a symbol we use for robots. def label '@' end end # Comander class sends commands and moves robots and dogs. # Note that THIS CLASS IS EXACTLY THE SAME AS IN PREVIOUS EXAMPLE. class Commander # Send command to move an object. Method accept object and sends # it a random command. def move(who) m = [:right, :left, :up, :down].sample # Polymorphism is happening here! We're sending command, # but we're unaware of receiver! who.send(m) end end # Create commander object. There is going to be only one commander. commander = Commander.new # Array of 10 robots and... arr = Array.new(10) { Robot.new } # ...one dog. Since dog implements the same interface, all objects # in array will be kinda same. arr.push(Dog.new(x: -12, y: 12)) arr.push(Dog.new(x: -12, y: 12)) # ADDING ONE MORE DOG arr.push(Dog.new(x: -12, y: 12)) # AND ONE MORE arr.push(Dog.new(x: -12, y: 12)) # AND ONE MORE # Infinite loop goes here (press ^C to stop) loop do # Tricky way to clear the screen puts "\e[H\e[2J" # Draw the grid. It goes from -12 to 12 by X, and 12 to -12 by Y. (12).downto(-12) do |y| (-12).upto(12) do |x| # Check if we have somebody with "x" and "y" coordinates. somebody = arr.find { |somebody| somebody.x == x && somebody.y == y } # Print label if somebody present. Print dot otherwise. if somebody # POLYMORPHISM GOES HERE. # We print "*" or "@", but we don't know what it is exactly, # and we don't have to know. print somebody.label else print '.' end end # Go to the next line. puts end # Hit check. If both objects have the same coordinates and their # labels aren't equal, then we assume that a robot caught the dog. game_over = arr.combination(2).any? do |a, b| a.x == b.x && \ a.y == b.y && \ a.label != b.label end if game_over puts 'Game over' exit end # Move each object in random order. arr.each do |somebody| # Call move method. Code is the same as in previous example. # Commander doesn't know about the object type. commander.move(somebody) end # Sleep for half a second. sleep 0.5 end  ### Exercise 3 The program below has 4 dogs and 2 robots on a battlefield. The game speed has been increased (see the last line), but it’s not necessary. class Robot # Accessors, so we can access coordinates from outside attr_accessor :x, :y # Constructor, accepts hash. If hash not specified, empty is used. # We expect two parameters in hash: initial robot coordinates; # if not specified, both will equal to zero. def initialize(options={}) @x = options[:x] || 0 @y = options[:y] || 0 end def right self.x += 1 end def left self.x -= 1 end def up self.y += 1 end def down self.y -= 1 end # New method, just a symbol we use for robots. def label '*' end end # Dog class has the similar interface, some methods are empty below. class Dog # Accessors, so we can access coordinates from outside attr_accessor :x, :y # Constructor, accepts hash. If hash not specified, empty is used. # We expect two parameters in hash: initial dog coordinates; # if not specified, both will equal to zero. def initialize(options={}) @x = options[:x] || 0 @y = options[:y] || 0 end def right self.x += 1 end # Empty method, but it exists. When called does nothing. We need it # to avoid "missing method" error. def left end # Another empty method. def up end def down self.y -= 1 end # New method, just a symbol we use for robots. def label '@' end end # Comander class sends commands and moves robots and dogs. # Note that THIS CLASS IS EXACTLY THE SAME AS IN PREVIOUS EXAMPLE. class Commander # Send command to move an object. Method accept object and sends # it a random command. def move(who) m = [:right, :left, :up, :down].sample # Polymorphism is happening here! We're sending command, # but we're unaware of receiver! who.send(m) end end # Create commander object. There is going to be only one commander. commander = Commander.new # Array of 10 robots and... arr = Array.new(2) { Robot.new } # ...one dog. Since dog implements the same interface, all objects # in array will be kinda same. arr.push(Dog.new(x: -12, y: 12)) arr.push(Dog.new(x: -12, y: 12)) # ADDING ONE MORE DOG arr.push(Dog.new(x: -12, y: 12)) # AND ONE MORE arr.push(Dog.new(x: -12, y: 12)) # AND ONE MORE # Infinite loop goes here (press ^C to stop) loop do # Tricky way to clear the screen puts "\e[H\e[2J" # Draw the grid. It goes from -12 to 12 by X, and 12 to -12 by Y. (12).downto(-12) do |y| (-12).upto(12) do |x| # Check if we have somebody with "x" and "y" coordinates. somebody = arr.find { |somebody| somebody.x == x && somebody.y == y } # Print label if somebody present. Print dot otherwise. if somebody # POLYMORPHISM GOES HERE. # We print "*" or "@", but we don't know what it is exactly, # and we don't have to know. print somebody.label else print '.' end end # Go to the next line. puts end # Hit check. If both objects have the same coordinates and their # labels aren't equal, then we assume that a robot caught the dog. game_over = arr.combination(2).any? do |a, b| a.x == b.x && \ a.y == b.y && \ a.label != b.label end # Check if all dogs reach the bottom right corner dogs_win = arr \ .select { |player| player.label == '@' } \ .all? { |dog| dog.x >= 12 || dog.y <= -12 } if game_over puts 'Game over' exit end if dogs_win puts 'Dogs win!' exit end # Move each object in random order. arr.each do |somebody| # Call move method. Code is the same as in previous example. # Commander doesn't know about the object type. commander.move(somebody) end # Sleep for a fraction of a second. sleep 0.01 end  # 080 No need for a solution (solution is inline). # 087 Skip for now # 091 ### Exercise 1 The answer to the question: test is going to fail ### Exercise 2 The ./spec/shipment_spec.rb can look like: require './lib/shipment' describe Shipment do it 'should calculate shipment with only one item' do expect(Shipment.total_weight(soccer_ball_count: 1)).to eq(439) expect(Shipment.total_weight(tennis_ball_count: 1)).to eq(87) expect(Shipment.total_weight(golf_ball_count: 1)).to eq(74) end it 'should calculate shipment with multiple items' do expect( Shipment.total_weight(soccer_ball_count: 3, tennis_ball_count: 2, golf_ball_count: 1) ).to eq(1420) end it 'should raise error when no options provided' do expect { Shipment.total_weight }.to raise_error("Can't calculate weight with empty options") end end  Output: $ rspec -f d

Shipment
should calculate shipment with only one item
should calculate shipment with multiple items
should raise error when no options provided

Finished in 0.00478 seconds (files took 0.13979 seconds to load)
3 examples, 0 failures