Learn to Program Answer Key


This here is an answer key for the exercises in Chris Pine's wonderful Learn to Program book. The answers, while not the prettiest or most efficient code, have been tested to work as the problems were understood.

Please excuse some of the formatting, it may not make the code as readable as it should be.

If you have better solutions to any of the problems please feel free to pass them along.

PAGE 12 2.4 A Few Things to Try


1. Hours in a year

puts 365 * 24


2. Minutes in a decade

puts 10 * 365 * 24 * 60


3. Seconds I'm old, 8/8/76 to 5/7/07 (9:45pm)

puts (30 * 365 * 24 * 60 * 60) + (272 * 24 * 60 * 60) + (21 * 60 * 60) + (45 * 60)


4. 912,000,000 seconds, how old?

puts 912000000/60/60/24/365


PAGE 26 5.4 A Few Things to Try

1. Ask first, middle and last name. Greet with full name.

ask first, middle, last name. greet with full name.
puts 'What is your first name?'
nameF = gets.chomp
puts 'What is your middle name?'
nameM = gets.chomp
puts 'What is your last name?'
nameL = gets.chomp
puts ''
puts 'Hi ' + nameF + ' ' + nameM + ' ' + nameL



2. Ask for favorite number. Add one and suggest the result is bigger and better.

ask for favorite number. add 1 to the number and suggest the result is bigger and better than favorite number.
puts 'What is your favorite number?'
numFAV = gets.chomp
numBET = numFAV.to_i + 1
puts ''
puts 'Your favorite number ' + numFAV.to_s + ' is nice, however ' + numBET.to_s + ' is bigger and better.'

PAGE 32 Last Paragraph

Ask for first, middle, last name and add the lengths together.

puts 'What is your first name?'
fname = gets.chomp
puts 'What is your middle name?'
mname = gets.chomp
puts 'What is your last name?'
lname = gets.chomp
numletters = fname.length + mname.length + lname.length
puts 'Did you know, there are ' + numletters.to_s + ' letters in your full name?'

PAGE 35 6.2 A Few Things to Try

1. Angry boss program

puts 'What do you want!?'
want = gets.chomp
puts 'WHADDYA MEAN "I WANT ' + want.upcase + '" ?!? YOU\'RE FIRED'



2. Program to display a table of contents.

page_width = 60
puts ('Table of Contents'.center(page_width))
puts ''
puts ('Chapter 1: Getting Started'.ljust(page_width/2) + 'page 1'.rjust(page_width/2))
puts ('Chapter 2: Numbers'.ljust(page_width/2) + 'page 9'.rjust(page_width/2))
puts ('Chapter 3: Letters'.ljust(page_width/2) + 'page 13'.rjust(page_width/2))

PAGE 49 Sidebar

Change example 'bye' program to get rid of first space

input = ''
while input != 'bye'
input = gets.chomp
if input != 'bye'
puts input
end
end
puts 'Come again soon!'

PAGE 55 7.5 A Few Things to Try

1. 99 Bottles of Beer on the Wall...

bottles = 99
while bottles != 0
puts bottles.to_s + ' bottles of beer on the wall'
puts bottles.to_s + ' bottles of beer'
bottles = bottles - 1
puts 'take one down, pass it around'

if bottles == 1
puts bottles.to_s + ' bottle of beer on the wall'
else
puts bottles.to_s + ' bottles of beer on the wall'
end

puts ''

if bottles == 1
puts bottles.to_s + ' bottle of beer on the wall'
puts bottles.to_s + ' bottle of beer'
bottles = bottles - 1
puts 'take one down, pass it around'
puts bottles.to_s + ' bottles of beer on the wall'
end

end


2. A Deaf Grandma program.

say = 'hi grammy'
while say != 'BYE'
say = gets.chomp
while say != say.upcase
puts 'HUH? SPEAK UP SONNY!'
say = gets.chomp
end
year = rand(21) + 1930
puts 'NO, NOT SINCE ' + year.to_s
end


3. Extend your Deaf Grandma program.

say1 = ''
say2 = ''
say3 = ''

while say1 != 'BYE' || say2 != 'BYE' || say3 != 'BYE'


while say1 != 'BYE'

say1 = gets.chomp

if say1 == say1.upcase
year1 = rand(22) + 1929
puts 'NO, NOT SINCE ' + year1.to_s
else
puts 'HUH?! SPEAK UP, SONNY!'
end

if say1 != 'BYE'
say2 = ''
say3 = ''
end
end

while say2 != 'BYE'

say2 = gets.chomp

if say2 == say2.upcase
year2 = rand(22) + 1929
puts 'NO, NOT SINCE ' + year2.to_s
else
puts 'HUH?! SPEAK UP, SONNY!'
end

if say2 != 'BYE'
say1 = ''
say3 = ''
end

end

while say3 != 'BYE'

say3 = gets.chomp

if say3 == say3.upcase
year3 = rand(22) + 1929
puts 'NO, NOT SINCE ' + year3.to_s
else
puts 'HUH?! SPEAK UP, SONNY!'
end

if say3 != 'BYE'
say1 = ''
say2 = ''
end
end

end


4. Leap Years.

puts 'Input a starting year:'
start_year = gets.chomp
puts 'Input an ending year:'
end_year = gets.chomp
puts ''

while start_year.to_i <= end_year.to_i

if start_year.to_f%400 == 0
puts start_year
elsif start_year.to_f%100 == 0
elsif start_year.to_f%4 == 0
puts start_year
end

start_year = start_year.to_i + 1

end

PAGE 62 Last Paragraph

Puts an array containing other arrays.

array = ['one', 'two', ['alpha', 'beta']]
puts array

PAGE 63 8.3 A Few Things to Try

1. Type as many words as you want. When you press enter on an empty line, the typed words are printed in alphabetical order.

puts 'Type in as many words as you want. When finished press \'Enter\' on an empty line'
x = 0
word = 'word1'
array =[]
while word != ''
word = gets.chomp
array[x] = word
x = x + 1
end
puts ''
puts array.sort


A more straight-forward alternative, courtesy Adrian:


word = 'word'
words =[]

puts 'Please type as many words per line then press the Enter Key.'
puts 'When you are finished press the Enter Key without typing anything.'
while word != ''
word = gets.chomp
words = words.push word
end
puts ''
puts 'Your original values:'
puts words
puts ''
puts 'Your values sorted:'
puts words.sort
puts ''


. . . and another alternative, courtesy Stephen:

awords = []
word = "x"

puts "Type as many words as you want, or press \"enter\" to quit."
while word != ""
#get word from user
word = gets.chomp

#add to array
awords.push word
end

#user exited loop test for array before printing
if awords.empty?
puts "Now sorting what you typed.. thanks."
puts awords.sort
end


. . . one more from Joraaver:

puts "Type in as many words as you want. When you are done, press \'Enter'\ on the next line."
# array for the entries
words = []
entry = "0"
# taking responses and pushing them into the array
while entry != ""
entry = gets.chomp
words.push entry
end
# displaying data
puts "Thanks for your input. Sorting data ..."
puts words.sort



2. Rewrite table of contents program from p. 35 using arrays.

toc = ['Table of Contents', 'Chapter 1: Getting Started', 'page 1','Chapter 2: Numbers','page 9',
'Chapter 3: Letters','page 13']
page_width = 60
puts (toc[0].center(page_width))
puts ''
puts (toc[1].ljust(page_width/2) + toc[2].rjust(page_width/2))
puts (toc[3].ljust(page_width/2) + toc[4].rjust(page_width/2))
puts (toc[5].ljust(page_width/2) + toc[6].rjust(page_width/2))


Joraaver offers another solution:

toc = ["Table of Contents","Chapter 1: Getting Started", "page 1",
"Chapter 2: Numbers", "page 9", "Chapter 3: Letters", "page 13"]
line_width = 60
i = 0
puts(toc[0].center(line_width))
puts ""
# loop to define when to stop putting lines for the contents.
until i == 6
puts(toc[i += 1].ljust(line_width/2) + toc[i += 1].rjust(line_width/2))
end

PAGE 72 Top of Page

Find out what say_moo returns.

def say_moo number_of_moos
puts 'mooooooo....'*number_of_moos
end

puts say_moo(3)

PAGE 76 9.4 A Few Things to Try

1. Fix up the ask method.

def ask question

puts question
reply = gets.chomp.downcase

while reply != 'yes' && reply != 'no'
puts 'Please answer "yes" or "no".'
puts question
reply = gets.chomp.downcase
end

if reply == 'yes'
return true
else
return false
end

end

puts 'Hello, and thank you for ...'
puts

ask 'Do you like eating tacos?'
ask 'Do you like eating burritos?'
wets_bed = ask 'Do you wet the bed?'
ask 'Do you like eating chimichangas?'
ask 'Do you like eating sopapillas?'
puts 'Just a few more questions...'
ask 'Do you like drinking horchata?'
ask 'Do you like eating flautas?'

puts
puts 'DEBRIEFING'
puts 'Thank you for ...'
puts
puts wets_bed


Erez Ben offers an alternative:

def ask question
while question != ''
puts question
reply = gets.chomp.downcase
if reply == 'yes'
return true
elsif reply == 'no'
return false
end
puts ' Please answer "yes" or "no".'
end
end

puts ' Hello, and thank you for...'
puts
ask ' Do you like eating tacos?' # Ignore this return value
ask ' Do you like eating burritos?' # And this one
wets_bed = ask ' Do you wet the bed?' # Save this return value
ask ' Do you like eating chimichangas?'
ask ' Do you like eating sopapillas?'
puts ' Just a few more questions...'
ask ' Do you like drinking horchata?'
ask ' Do you like eating flautas?'
puts
puts ' DEBRIEFING:'
puts ' Thank you for...'
puts
puts wets_bed


Eelco also offers an alternative:

def ask question
while true
puts question
reply = gets.chomp.downcase
if reply == 'yes'
return true
else if reply == 'no'
return false
else
puts ' Please answer "yes" or "no".'
end
end
end
end

puts ' Hello, and thank you for...'
puts
ask ' Do you like eating tacos?' # Ignore this return value
ask ' Do you like eating burritos?' # And this one
wets_bed = ask ' Do you wet the bed?' # Save this return value
ask ' Do you like eating chimichangas?'
ask ' Do you like eating sopapillas?'
puts ' Just a few more questions...'
ask ' Do you like drinking horchata?'
ask ' Do you like eating flautas?'
puts
puts ' DEBRIEFING:'
puts ' Thank you for...'
puts
puts wets_bed




2. Old-school Roman numerals.

def roman number

m_length = 0
d_length = 0
c_length = 0
l_length = 0
x_length = 0
v_length = 0
i_length = 0

if number >= 1000
m_length = number/1000
number = number%1000
end

if number >= 500
d_length = number/500
number = number%500
end

if number >= 100
c_length = number/100
number = number%100
end

if number >= 50
l_length = number/50
number = number%50
end

if number >= 10
x_length = number/10
number = number%10
end

if number >= 5
v_length = number/5
number = number%5
end

if number < 5
i_length = number/1
number = number%10
end

puts 'M'*m_length + 'D'*d_length + 'C'*c_length + 'L'*l_length + 'X'*x_length +
'V'*v_length + 'I'*i_length

end

roman 2999


3. Modern Roman numerals.

def roman number

m_length = 0
n900_length = 0
d_length = 0
n400_length = 0
c_length = 0
n90_length = 0
l_length = 0
n40_length = 0
x_length = 0
n9_length = 0
v_length = 0
n4_length = 0
i_length = 0

if number >= 1000
m_length = number/1000
number = number%1000
end

if number >= 900
n900_length = number/900
number = number%900
end

if number >= 500
d_length = number/500
number = number%500
end

if number >= 400
n400_length = number/400
number = number%400
end

if number >= 100
c_length = number/100
number = number%100
end

if number >= 90
n90_length = number/90
number = number%90
end

if number >= 50
l_length = number/50
number = number%50
end

if number >= 40
n40_length = number/40
number = number%40
end

if number >= 10
x_length = number/10
number = number%10
end

if number >= 9
n9_length = number/9
number = number%9
end

if number >= 5
v_length = number/5
number = number%5
end

if number >= 4
n4_length = number/4
number = number%4
end

if number < 4
i_length = number/1
number = number%10
end

puts 'M'*m_length + "CM"*n900_length + 'D'*d_length + 'CD'*n400_length + 'C'*c_length +
'XC'*n90_length + 'L'*l_length + 'XL'*n40_length + 'X'*x_length + 'IX'*n9_length +
'V'*v_length + 'IV'*n4_length + 'I'*i_length

end

x = ''
while x != 'exit'
puts 'Enter a number, and the equivalent roman numeral will be returned.'
puts 'Type exit to quit.'
x = gets.chomp
roman x.to_i
end

PAGE 83 Bottom of the page.

Check to make sure don't fall off world

M = 'land'
o = 'water'

world = [
[o,o,o,o,o,M,o,o,o,o,o],
[o,o,o,o,M,M,o,o,o,o,o],
[o,o,o,o,o,M,o,o,M,M,o],
[o,o,o,M,o,M,o,o,o,M,o],
[o,o,o,o,o,M,M,o,o,o,o],
[o,o,o,o,M,M,M,M,o,o,o],
[M,M,M,M,M,M,M,M,M,M,M],
[o,o,o,M,M,o,M,M,M,o,o],
[o,o,o,o,o,o,M,M,o,o,o],
[o,M,o,o,o,M,M,o,o,o,o],
[o,o,o,o,o,M,o,o,o,o,o]]

def continent_size world, x ,y

if x < 0 or x > 10 or y < 0 or y > 10
return 0
end

if world[y][x] != 'land'
return 0
end



size = 1
world [y][x] = 'counted land'


size = size + continent_size(world, x-1, y-1)
size = size + continent_size(world, x , y-1)
size = size + continent_size(world, x+1, y-1)
size = size + continent_size(world, x-1, y )
size = size + continent_size(world, x+1, y )
size = size + continent_size(world, x-1, y+1)
size = size + continent_size(world, x , y+1)
size = size + continent_size(world, x+1, y+1)
size

end

puts continent_size(world, 5, 5)

PAGE 84 10.2 Sorting

Sort method.

# starting condition
list = [ ]


# ask the question
puts 'Enter a list of words, press \'enter\' to quit and they will be returned sorted.'
word = 'one'

# get the words in the first list
while word != ''
word = gets.chomp
list.push word
end

# sort definition wrapper method
def sort array
count = -2

# count number of words in first list
array.each do |word|
count = count + 1
end

recursive_sort(array, [ ], [ ], count)
end

# recursive sort method definition
def recursive_sort listA, listB, sorted, nums

# local variables
x = 0
y = 0
z = 0


# cycle I
while x <= nums

if listA[x] <= listA[y]
y = y + 1

if y == nums + 1
sorted.push listA[x]
x = x + 1
y = 0
z = z + 1
end

else
listB.push listA[x]
x = x + 1
y = 0
end

end

# cycle II
nums = nums - z
listA = [ ]
x = 0
y = 0
z = 0
if nums < 0
x = nums + 5
end

while x <= nums

if listB[x] <= listB[y]
y = y + 1

if y == nums + 1
sorted.push listB[x]
x = x + 1
y = 0
z = z + 1
end

else
listA.push listB[x]
x = x + 1
y = 0
end

end

nums = nums - z
x = 0
y = 0
z = 0

if nums < 0
puts 'sorted list: '
puts sorted

else recursive_sort(listA, [ ], sorted, nums)

end

end

sort list

PAGE 85 10.3 A Few Things to Try

1. Shuffle method.

# starting condition
list = [ ]

# as the question
puts 'Enter a list of words, press \'enter\' to quit and they will be returned randomly shuffled.'
word = 'one'

# get the words in the first list
while word != ''
word = gets.chomp
list.push word
end

# define shuffle method
def shuffle array

# starting conditions of local variables
randomized = [ ]
count = -2
x = 0
y = 0


array.each do |word|
count = count + 1
end

while y <= count

x = rand(count+1)

if array[x] != 'used'
randomized.push array[x]
array [x] = 'used'
y = y + 1
end

end

puts randomized

end

shuffle list


2. Dictionary sort method.

# starting condition
list = [ ]


# ask the question
puts 'Enter a list of words, press \'enter\' to quit and they will be returned sorted.'
word = 'one'

# get the words in the first list
while word != ''
word = gets.chomp
list.push word
end

# sort definition wrapper method
def sort array
count = -2

# count number of words in first list
array.each do |word|
count = count + 1
end

recursive_sort(array, [ ], [ ], count)
end

# recursive sort method definition
def recursive_sort listA, listB, sorted, nums

# local variables
x = 0
y = 0
z = 0


# cycle I
while x <= nums

if listA[x].downcase <= listA[y].downcase
y = y + 1

if y == nums + 1
sorted.push listA[x]
x = x + 1
y = 0
z = z + 1
end

else
listB.push listA[x]
x = x + 1
y = 0
end

end

# cycle II
nums = nums - z
listA = [ ]
x = 0
y = 0
z = 0
if nums < 0
x = nums + 5
end

while x <= nums

if listB[x].downcase <= listB[y].downcase
y = y + 1

if y == nums + 1
sorted.push listB[x]
x = x + 1
y = 0
z = z + 1
end

else
listA.push listB[x]
x = x + 1
y = 0
end

end

nums = nums - z
x = 0
y = 0
z = 0

if nums < 0
puts 'sorted list: '
puts sorted

else recursive_sort(listA, [ ], sorted, nums)

end

end

sort list

PAGE 92 10.5 A Few More Things To Try

1 & 2. Expand upon english_number with thousands, millions, billions, and trillions

def english_number number
if number < 0 # no neg numbers
return 'Please enter a number that isn\'t negative'
end
if number == 0
return 'zero'
end

# no more special cases, no more returns

num_string = '' # This is the string we will return, define it an start 'empty'

ones_place = [ 'one', 'two', 'three',
'four', 'five', 'six',
'seven', 'eight', 'nine']

tens_place = [ 'ten', 'twenty', 'thirty',
'forty', 'fifty', 'sixty',
'seventy', 'eighty', 'ninty']

teenagers = [ 'eleven', 'twelve', 'thirteen',
'fourteen', 'fifteen', 'sixteen',
'seventeen', 'eighteen', 'nineteen']


# "left" is how much of the number we still have to write
# "write" is which part we are writing out at the time

left = number

# trillions
write = left/1000000000000
left = left - write*1000000000000

if write > 0
trillions = english_number write
num_string = num_string + trillions + ' trillion'

if left > 0
num_string = num_string + ' '
end
end

# billions
write = left/1000000000
left = left - write*1000000000

if write > 0
billions = english_number write
num_string = num_string + billions + ' billion'

if left > 0
num_string = num_string + ' '
end
end

# millions
write = left/1000000
left = left - write*1000000

if write > 0
millions = english_number write
num_string = num_string + millions + ' million'

if left > 0
num_string = num_string + ' '
end
end

# thousands
write = left/1000
left = left - write*1000

if write > 0
thousands = english_number write
num_string = num_string + thousands + ' thousand'

if left > 0
num_string = num_string + ' '
end
end

write = left/100 # how many hundreds left
left = left - write*100 # subtract off those hundreds

if write > 0
# recursion
hundreds = english_number write
num_string = num_string + hundreds + ' hundred'

if left > 0
# adds a space
num_string = num_string + ' '
end
end

write = left/10 # how many tens left
left = left - write*10 # subtract off those tens

if write > 0
if ((write == 1) and (left > 0))
# exception for teenagers
num_string = num_string + teenagers[left-1]
# -1 because of the array ordering [0] is eleven etc.
# with teenagers the digit in the ones place is taken care of so nothing left
left = 0
else
num_string = num_string + tens_place[write-1]
end

if left > 0
# add a space
num_string = num_string + '-'
end
end

write = left # how many ones left
left = 0 # subtract off those ones

if write > 0
num_string = num_string + ones_place[write-1]
end

# now we just return the num_string

num_string
end

puts english_number( 0)
puts english_number( 9)
puts english_number( 10)
puts english_number( 11)
puts english_number( 17)
puts english_number( 32)
puts english_number( 88)
puts english_number( 99)
puts english_number(100)
puts english_number(101)
puts english_number(234)
puts english_number(3211)
puts english_number(999999)
puts english_number(1000000000000)
puts english_number(1000)
puts english_number(10000)
puts english_number(1000000)
puts english_number(10000000)
puts english_number(1000000000)
puts english_number(10000000000)


3. Wedding number format.

def english_number number
if number < 0 # no neg numbers
return 'Please enter a number that isn\'t negative'
end
if number == 0
return 'zero'
end

# no more special cases, no more returns

num_string = '' # This is the string we will return, define it an start 'empty'

ones_place = [ 'one', 'two', 'three',
'four', 'five', 'six',
'seven', 'eight', 'nine']

tens_place = [ 'ten', 'twenty', 'thirty',
'forty', 'fifty', 'sixty',
'seventy', 'eighty', 'ninty']

teenagers = [ 'eleven', 'twelve', 'thirteen',
'fourteen', 'fifteen', 'sixteen',
'seventeen', 'eighteen', 'nineteen']


# "left" is how much of the number we still have to write
# "write" is which part we are writing out at the time

left = number

# trillions
write = left/1000000000000
left = left - write*1000000000000

if write > 0
trillions = english_number write
num_string = num_string + trillions + ' trillion'

if left > 0
num_string = num_string + ' '
end
end

# billions
write = left/1000000000
left = left - write*1000000000

if write > 0
billions = english_number write
num_string = num_string + billions + ' billion'

if left > 0
num_string = num_string + ' and '
end
end

# millions
write = left/1000000
left = left - write*1000000

if write > 0
millions = english_number write
num_string = num_string + millions + ' million'

if left > 0
num_string = num_string + ' and '
end
end

# thousands
write = left/1000
left = left - write*1000

if write > 0
thousands = english_number write
num_string = num_string + thousands + ' thousand'

if left > 0
num_string = num_string + ' and '
end
end

write = left/100 # how many hundreds left
left = left - write*100 # subtract off those hundreds

if write > 0
# recursion
hundreds = english_number write
num_string = num_string + hundreds + ' hundred'

if left > 0
# adds a space
num_string = num_string + ' and '
end
end

write = left/10 # how many tens left
left = left - write*10 # subtract off those tens

if write > 0
if ((write == 1) and (left > 0))
# exception for teenagers
num_string = num_string + teenagers[left-1]
# -1 because of the array ordering [0] is eleven etc.
# with teenagers the digit in the ones place is taken care of so nothing left
left = 0
else
num_string = num_string + tens_place[write-1]
end

if left > 0
# add a space
num_string = num_string + ' and '
end
end

write = left # how many ones left
left = 0 # subtract off those ones

if write > 0
num_string = num_string + ones_place[write-1]
end

# now we just return the num_string

num_string
end

puts english_number( 0)
puts english_number( 9)
puts english_number( 10)
puts english_number( 11)
puts english_number( 17)
puts english_number( 32)
puts english_number( 88)
puts english_number( 99)
puts english_number(100)
puts english_number(101)
puts english_number(234)
puts english_number(3211)
puts english_number(999999)
puts english_number(1000000000000)
puts english_number(1000)
puts english_number(10000)
puts english_number(1000000)
puts english_number(10000000)
puts english_number(1000000000)
puts english_number(10000000000)
puts english_number(1976)


4. 'Ninety-nine Bottles of Beer" using english_number.

def english_number number
if number < 0 # no neg numbers
return 'Please enter a number that isn\'t negative'
end
if number == 0
return 'zero'
end

# no more special cases, no more returns

num_string = '' # This is the string we will return, define it an start 'empty'

ones_place = [ 'one', 'two', 'three',
'four', 'five', 'six',
'seven', 'eight', 'nine']

tens_place = [ 'ten', 'twenty', 'thirty',
'forty', 'fifty', 'sixty',
'seventy', 'eighty', 'ninty']

teenagers = [ 'eleven', 'twelve', 'thirteen',
'fourteen', 'fifteen', 'sixteen',
'seventeen', 'eighteen', 'nineteen']


# "left" is how much of the number we still have to write
# "write" is which part we are writing out at the time

left = number

# trillions
write = left/1000000000000
left = left - write*1000000000000

if write > 0
trillions = english_number write
num_string = num_string + trillions + ' trillion'

if left > 0
num_string = num_string + ' '
end
end

# billions
write = left/1000000000
left = left - write*1000000000

if write > 0
billions = english_number write
num_string = num_string + billions + ' billion'

if left > 0
num_string = num_string + ' '
end
end

# millions
write = left/1000000
left = left - write*1000000

if write > 0
millions = english_number write
num_string = num_string + millions + ' million'

if left > 0
num_string = num_string + ' '
end
end

# thousands
write = left/1000
left = left - write*1000

if write > 0
thousands = english_number write
num_string = num_string + thousands + ' thousand'

if left > 0
num_string = num_string + ' '
end
end

write = left/100 # how many hundreds left
left = left - write*100 # subtract off those hundreds

if write > 0
# recursion
hundreds = english_number write
num_string = num_string + hundreds + ' hundred'

if left > 0
# adds a space
num_string = num_string + ' '
end
end

write = left/10 # how many tens left
left = left - write*10 # subtract off those tens

if write > 0
if ((write == 1) and (left > 0))
# exception for teenagers
num_string = num_string + teenagers[left-1]
# -1 because of the array ordering [0] is eleven etc.
# with teenagers the digit in the ones place is taken care of so nothing left
left = 0
else
num_string = num_string + tens_place[write-1]
end

if left > 0
# add a space
num_string = num_string + '-'
end
end

write = left # how many ones left
left = 0 # subtract off those ones

if write > 0
num_string = num_string + ones_place[write-1]
end

# now we just return the num_string

num_string
end

bottles = 999
while bottles != 0
puts english_number(bottles).to_s + ' bottles of beer on the wall'
puts english_number(bottles).to_s + ' bottles of beer'
bottles = bottles - 1
puts 'take one down, pass it around'

if bottles == 1
puts english_number(bottles).to_s + ' bottle of beer on the wall'
else
puts english_number(bottles).to_s + ' bottles of beer on the wall'
end

puts ''

if bottles == 1
puts english_number(bottles).to_s + ' bottle of beer on the wall'
puts english_number(bottles).to_s + ' bottle of beer'
bottles = bottles - 1
puts 'take one down, pass it around'
puts english_number(bottles).to_s + ' bottles of beer on the wall'
end

end

PAGE 104 11.6 A Few Things To Try

1. Adapt picture-downloading/file-renaming program to your computer.

Picture-download/file-rename program
# Mac gives a 'cross-device link error' when using File.rename
# to move between Volumes. So assume all pics are moved from camera drive
# to a folder on the computer, 'camera', and then need to be renamed and moved
# to a pictures folder on the desktop.

folder_name = 'Desktop/pics/'

pic_names = Dir['camera/**/*.{JPG,jpg}']

puts 'what would you like to call this batch?'
batch_name = gets.chomp

puts
print 'Downloading '+ pic_names.length.to_s+' files: '

pic_number = 1

pic_names.each do |name|
print '.'

new_name = if pic_number < 10
folder_name.to_s + batch_name + '0' + pic_number.to_s + '.jpg'
else
folder_name.to_s + batch_name + pic_number.to_s + '.jpg'
end

if File.exist? new_name
puts
puts 'DANGER: file names already exist, exiting!'
puts
exit
else
File.rename name, new_name
end

pic_number = pic_number + 1
end

puts
puts 'Done'


2. Build your own playlists.

def shuffle array

x = 0
track_number = 0
number_songs = 0
playlist = [ ]

number_songs = array.length


while x < number_songs

track_number = rand(number_songs)

if array[track_number] != ''
playlist.push array[track_number]
array[track_number] = ''
x = x + 1
else
end
end


filename = 'Desktop/playlist.m3u'

File.open filename, 'w' do |f|
f.puts playlist
end

end

tracks = Dir['Desktop/music/**/*.{ogg,mp3,m4a}']

shuffle tracks


3. Mixed random better playlist.

# music_shuffle tries to minimize clumping at the beginning of playlist,
# does not generate a perfectly mixed up playlist
# just less clumpy


def music_shuffle array

x = 0
track_number = 0
number_songs = 0
playlist = [ ]
full_song_name = [ ]
album_on_deck = ''
previous_album = ''

number_songs = array.length

while x < number_songs

track_number = rand(number_songs)

full_song_name = array[track_number].split'/'

album_on_deck = full_song_name[2]

if album_on_deck != previous_album

if array[track_number] != ''
playlist.push array[track_number]
array[track_number] = ''
x = x + 1
else
end

previous_album = album_on_deck

else
end

end

filename = 'Desktop/playlist.m3u'

File.open filename, 'w' do |f|
f.puts playlist
end

end

tracks = Dir['Desktop/music/**/*.{ogg,mp3,m4a}']

music_shuffle tracks

PAGE 108 12.2 A Few Things To Try

1. one billion seconds . . .

birth_time = Time.mktime(1976, 10, 7, 11, 40, 54)
billion_seconds_old = birth_time + 1000000000
puts billion_seconds_old


2. Happy Birthday!

x = 0

puts 'What year were you born in?'
year = gets.chomp

while x != 1
puts 'What month were you born in?'
month = gets.chomp.downcase

if month == 'january'
month = 1
x = 1
elsif month == 'february'
month = 2
x = 1
elsif month == 'march'
month = 3
x = 1
elsif month == 'april'
month = 4
x = 1
elsif month == 'may'
month = 5
x = 1
elsif month == 'june'
month = 6
x = 1
elsif month == 'july'
month = 7
x = 1
elsif month == 'august'
month = 8
x = 1
elsif month == 'september'
month = 9
x = 1
elsif month == 'october'
month = 10
x = 1
elsif month == 'november'
month = 11
x = 1
elsif month == 'december'
month = 12
x = 1
else
puts 'Please type in a month'
x = 0
end
end

puts 'What day were you born on?'
day = gets.chomp

right_now = Time.new
birth_day = Time.mktime(year.to_i, month.to_i, day.to_i)

years_old = (right_now - birth_day)/(60*60*24*365)

puts ''

years_old.to_i.times do
puts 'spank'
end

PAGE 113 12.6 A Few Things To Try

1. Roman numeral to integer.

# Create new hash
roman_numeral = { }
status = 'invalid'

# Defining the roman numeral hash
roman_numeral['I'] = 1
roman_numeral['V'] = 5
roman_numeral['X'] = 10
roman_numeral['L'] = 50
roman_numeral['C'] = 100
roman_numeral['D'] = 500
roman_numeral['M'] = 1000

while status != 'valid'

# Request a Roman numeral, capitalize input and put in array
puts 'Please input a Roman Numeral'
input_numeral = gets.chomp.upcase
no_of_chars = input_numeral.length
input_array = [ ]
counter1 = 0
while counter1 < no_of_chars
input_array.push input_numeral[counter1]
counter1 = counter1 + 1
end

# Check each character in the array to see if its a Roman Numeral
counter2 = 0

input_array.each do |input_letter|

roman_numeral.each do |rom_letter,number|

if input_letter.chr == rom_letter
counter2 = counter2 + 1
else
end

end

end

if counter2 == input_array.length
status = 'valid'
else
status = 'invalid'
puts 'Error: Not all characters are valid roman numerals'
end

end

# Convert roman numeral letter array to array of numbers
number_array = [ ]

input_array.each do |letter|
letter = letter.chr
number = roman_numeral[letter]
number_array.push number
end

# Add/Subtract number array one at a time starting from right
counter3 = number_array.length - 1

# Starting value, first number
number = number_array[counter3]
counter3 = counter3 - 1


while counter3 > -1

# Sum from left to right
if number_array[counter3] < number_array[counter3+1]
number = number - number_array[counter3]
else
number = number + number_array[counter3]
end

counter3 = counter3 - 1

end

puts number


2. Birthday Helper

# Program needs .txt input file.

filename = 'birthdays.txt'
birth_dates = { }

File.open filename do |f|
f.each_line do |entry|
date = entry[-13..-1]
name = entry[0..-15]
birth_dates[name] = date
end

end


puts 'Enter a name and their next birthday will be returned'
name = gets.chomp

date = birth_dates[name]
year = date[-5..-1].to_i
day = date[-9,2].to_i

month_string = date[-13,3]
if month_string == 'Jan'
month = 1
elsif month_string == 'Feb'
month = 2
elsif month_string == 'Mar'
month = 3
elsif month_string == 'Apr'
month = 4
elsif month_string == 'May'
month = 5
elsif month_string == 'Jun'
month = 6
elsif month_string == 'Jul'
month = 7
elsif month_string == 'Aug'
month = 8
elsif month_string == 'Sep'
month = 9
elsif month_string == 'Oct'
month = 10
elsif month_string == 'Nov'
month = 11
elsif month_string == 'Dec'
month = 12
end

today = Time.new

birthdate = Time.mktime(year,month,day)
birthday = Time.mktime(2008,month,day)

if birthday < today
next_birthday = Time.mktime(2009,month,day)
age = (next_birthday - birthdate)/(60*60*24*365)
age = age.to_i
puts name + '\'s next birthday will be ' + next_birthday.to_s
puts 'and they will be ' + age.to_s + ' years old'
else
age = (birthday - birthdate)/(60*60*24*365)
age = age.to_i
puts name + '\'s next birthday will be ' + birthday.to_s
puts 'and they will be ' + age.to_s + ' years old'
end

PAGE 117 13.1 A Few Things to Try

1. Shuffle method as array method

# starting condition
list = [ ]

# ask the question
puts 'Enter a list of words, press \'enter\' to quit and they will be returned randomly shuffled.'
word = 'one'

# get the words in the first list
while word != ''
word = gets.chomp
list.push word
end


# defining the array method

class Array

# defining to_shuffle
def to_shuffle

# starting conditions of local variables
randomized = [ ]
count = -2
x = 0
y = 0


self.each do |word|
count = count + 1
end

while y <= count

x = rand(count+1)

if self[x] != 'used'
randomized.push self[x]
self[x] = 'used'
y = y + 1
end

end

puts randomized

end

end

# shuffling and array

list.to_shuffle


2. Factorial method as integer method.

puts 'Enter a number and the factorial of that number will be returned'
num = gets.chomp.to_i

class Integer

def factorial
if self < 0
return 'You can\'t take the factorial of a negative number!'
end

if self <= 1
1
else
self * (self-1).factorial
end
end
end

puts num.factorial


3. Roman numeral method as Integer method.

class Integer

def roman

m_length = 0
n900_length = 0
d_length = 0
n400_length = 0
c_length = 0
n90_length = 0
l_length = 0
n40_length = 0
x_length = 0
n9_length = 0
v_length = 0
n4_length = 0
i_length = 0

number = self

if number >= 1000
m_length = number/1000
number = number%1000
end

if number >= 900
n900_length = number/900
number = number%900
end

if number >= 500
d_length = number/500
number = number%500
end

if number >= 400
n400_length = number/400
number = number%400
end

if number >= 100
c_length = number/100
number = number%100
end

if number >= 90
n90_length = number/90
number = number%90
end

if number >= 50
l_length = number/50
number = number%50
end

if number >= 40
n40_length = number/40
number = number%40
end

if number >= 10
x_length = number/10
number = number%10
end

if number >= 9
n9_length = number/9
number = number%9
end

if number >= 5
v_length = number/5
number = number%5
end

if number >= 4
n4_length = number/4
number = number%4
end

if number < 4
i_length = number/1
number = number%10
end

puts 'M'*m_length + "CM"*n900_length + 'D'*d_length + 'CD'*n400_length + 'C'*c_length +
'XC'*n90_length + 'L'*l_length + 'XL'*n40_length + 'X'*x_length + 'IX'*n9_length +
'V'*v_length + 'IV'*n4_length + 'I'*i_length

end

end

x = ''
while x != 'exit'
puts 'Enter a number, and the equivalent roman numeral will be returned.'
puts 'Type exit to quit.'
x = gets.chomp
num = x.to_i
num.roman
end

PAGE 126 13.4 A Few More Things to Try

1. Make an OrangeTree Class.

class OrangeTree

def initialize
@age = 0
@tall = 0
@fruit = 0
puts 'A new Orange Tree is planted.'
end

def height
puts 'The tree is ' + @tall.to_s + ' feet tall.'
end

def count_the_oranges
puts 'The tree has ' + @fruit.to_s + ' oranges.'
end

def pick_an_orange
if @fruit < 1
puts 'Sorry, there are no oranges to pick this year.'
else
puts 'You pick an orange from the tree. It\'s very delicious.'
@fruit = @fruit - 1
end
end

def one_year_passes
puts 'One year passes'
@age = @age + 1
@tall = @tall + 1
@fruit = 0

if dead?
puts 'The Orange Tree dies'
exit
end

if @age > 2
@fruit = @age*10
else
@fruit = 0
end

end

private

def dead?
@age > 5
end

end

puts ''
tree = OrangeTree.new
tree.height
tree.count_the_oranges
tree.pick_an_orange
puts ''
tree.one_year_passes
tree.height
tree.count_the_oranges
tree.pick_an_orange
puts ''
tree.one_year_passes
tree.one_year_passes
tree.height
tree.count_the_oranges
tree.pick_an_orange
tree.pick_an_orange
tree.pick_an_orange
tree.pick_an_orange
tree.pick_an_orange
tree.pick_an_orange
tree.count_the_oranges
puts ''
tree.one_year_passes
tree.height
tree.count_the_oranges
tree.pick_an_orange
tree.pick_an_orange
tree.pick_an_orange
tree.count_the_oranges
tree.pick_an_orange
tree.count_the_oranges
puts ''
tree.one_year_passes
tree.height
tree.count_the_oranges
puts ''
tree.one_year_passes



2. Interact with your baby dragon.

class Dragon

def initialize name
@name = name
@asleep = false
@stuff_in_belly = 10 # he's full
@stuff_in_intestine = 0 # doesn't need to go

puts @name + ' is born.'
end

# Feed
def feed
puts 'You feed ' + @name + '.'
@stuff_in_belly = 10
passage_of_time
end

# Walk
def walk
puts 'You walk ' + @name + '.'
@stuff_in_intestine = 0
passage_of_time
end

# Put to bed
def put_to_bed
puts 'You put ' + @name + ' to bed.'
@asleep = true
3. times do
if @asleep
passage_of_time
end
if @asleep
puts @name + ' snores, filling the room with smoke.'
end
end
if @asleep
@asleep = false
puts @name + ' wakes up slowly.'
end
end

# Toss
def toss
puts 'You toss ' + @name + ' up into the air.'
puts 'He giggles, which singes your eyebrows.'
passage_of_time
end

# Rock
def rock
puts 'You rock ' + @name + ' gently.'
@asleep = true
puts 'He briefly dozes off...'
passage_of_time
if @asleep
@asleep = false
puts '...but wakes when you stop'
end
end

private

# following methods are internal

# hungry?
def hungry?
@stuff_in_belly <= 2
end

# poopy?
def poopy?
@stuff_in_intestine >= 8
end

# passage of time, this is when things happen
def passage_of_time
if @stuff_in_belly > 0
# move food from belly to intestine
@stuff_in_belly = @stuff_in_belly - 1
@stuff_in_intestine = @stuff_in_intestine + 1
else
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + ' is starving! In desperation, he ate YOU!'
exit
end

if @stuff_in_intestine >= 10
@stuff_in_intestine = 0
puts 'Whoops! ' + @name + ' had an accident...'
end

if hungry?
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + '\'s stomach grumbles...'
end

if poopy?
if @asleep
@asleep = false
puts 'He wakes up suddenly!'
end
puts @name + ' does the potty dance...'
end
end

end

pet = Dragon.new 'Norbert'

command = ''

while command != 'exit'
puts 'Enter a command to control your dragon:'
command = gets.chomp
if command == 'feed'
pet.feed
elsif command == 'walk'
pet.walk
elsif command == 'put_to_bed'
pet.put_to_bed
elsif command == 'toss'
pet.toss
elsif command == 'rock'
pet.rock
elsif command == 'exit'
exit
else
puts 'Can\'t recognize your command, please re-enter.'
end

end

PAGE 136 14.4 A Few Things to Try

1. Even better profiling.

#Set @profile_toggle 'on' to turn profiling on

@profile_toggle = 'off'

def profile block_description, &block
if @profile_toggle == 'on'
start_time = Time.now
block.call
duration = Time.now - start_time
puts block_description+' : '+duration.to_s+' seconds'
else
block.call
end
end

# 25000 doublings block
profile '25000 doublings' do
number = 1

25000.times do
number = number + number
end

puts number.to_s.length.to_s+' digits'
end

# count to a million
profile 'count to a million' do
number = 0
1000000.times do
number = number + 1
end
end


2. Grandfather Clock

def clock some_proc

#convert hours to 12 hour format
current_hour = Time.new.hour
if current_hour == 0
current_hour = current_hour + 12
elsif current_hour > 12
current_hour = current_hour - 12
end

#call the proc the number of hours passed
current_hour.to_i.times do
some_proc.call
end

end

# Dong proc
dong_proc = Proc.new do
puts 'DONG!'
end

clock dong_proc


3. Program logger.

def log block_description, &block
puts 'Beginning "'+block_description+'" . . .'
value_returned = block.call
puts '. . . "'+block_description+'" finished, returning:'
puts value_returned
end

log 'outer block' do

log 'some little block' do
5
end

log 'yet another block' do
'I like Thai food!'
end

false
end


4.Better logger.

$nesting_depth = 0
$space = ' '

def log block_description, &block
puts $space*$nesting_depth + 'Beginning "'+block_description+'" ...'
$nesting_depth = $nesting_depth + 1
value_returned = block.call
$nesting_depth = $nesting_depth - 1
puts $space*$nesting_depth + '... "'+block_description+'" finished, returning:'
puts $space*$nesting_depth + value_returned.to_s
end

log 'outer block' do

log 'some little block' do

log 'teeny-tiny block' do
'lots of love'
end

42
end

log 'yet another block' do
'I love Indian food!'
end

true
end