Ruby OOP correct concept? - ruby

The exercise questions are below with my answers.
#Create a Tree class with a rings attribute and getter method.
#Trees create a ring for every winter that passes
#It should have a bear_fruit? method which should return true if the
#has fruit that year. the tree produces fruit when it has
#more than 7 rings but less than 15, but false otherwise.
#The class should also have an winter_season method that increases #rings attr by 1.
Can anyone give me constructive criticism on this code?
class Tree
attr_accessor :winters, :rings, :bear_fruit?
def initialize(winters, rings)
#winters = winters
#rings = rings
end
def rings_created
#winters = 0
#rings = 0
while #winters == #rings do
#winters +=1
#rings +=1
break if #winters == 100
end
end
end
def bear_fruit
if #rings > 6 || < 16
#bear_fruit? = true
else
#bear_fruit? = false
end
end
def winter_season
#winters = 0
#rings = 0
while #winters < #rings do
#winters +=1
#rings +=2
break if #winters == 100
end
end
end
end

According to the exercise, you are supposed to create a class Tree with a single attribute rings and two methods, bear_fruit? and winter_season:
Create a Tree class with
a rings attribute and getter method
a bear_fruit? method which
returns true if the tree has more than 7 rings but less than 15
returns false otherwise
a winter_season method that
increases rings by 1
That's all. It doesn't say that a tree should track winters and it doesn't mention any loops.
Here's how I would implement it:
class Tree
attr_reader :rings
def initialize
#rings = 0
end
def bear_fruit?
#rings > 7 && #rings < 15
end
def winter_season
#rings += 1
end
end

First, does it work? I'm guessing not. Run it and see what the error is.
Ruby provides a number of ways of looping which you can look up in the ruby docs. I prefer not to use while loops if I can avoid it, partly because it can lead to less readable code with the use of break. Look up the times method and other enumerables.

Related

Should calculations be done within the insert method or the getter method within Ruby Class?

I am going through an Interview Cake question and they have the calculations done upon insertion of a new value. My instinct was to put that logic in the getter method and I was hoping for some insight on best practices.
Their Solution:
# Write a class TempTracker with these methods:
#
# insert()—records a new temperature
# getMax()—returns the highest temp we've seen so far
# getMin()—returns the lowest temp we've seen so far
# getMean()—returns the mean of all temps we've seen so far
# getMode()—returns a mode of all temps we've seen so far
class TempTracker
def initialize
# for mode
#occurrences = [0] * 111 # array of 0s at indices 0..110
#max_occurrences = 0
#mode = nil
# for mean
#total_numbers = 0
#total_sum = 0.0 # mean should be float
#mean = nil
# for min and max
#min_temp = nil
#max_temp = nil
end
def insert(temperature)
# for mode
#occurrences[temperature] += 1
if #occurrences[temperature] > #max_occurrences
#mode = temperature
#max_occurrences = #occurrences[temperature]
end
# for mean
#total_numbers += 1
#total_sum += temperature
#mean = #total_sum / #total_numbers
# for min and max
#max_temp = temperature if #max_temp.nil? || temperature > #max_temp
#min_temp = temperature if #min_temp.nil? || temperature < #min_temp
end
def get_max
#max_temp
end
def get_min
#min_temp
end
def get_mean
#mean
end
def get_mode
#mode
end
end
Insert and getter methods that I wrote initially:
// ...
def insert(temp)
#max = temp if #max.nil? || #max < temp
#min = temp if #min.nil? || #min > temp
#total_sum += temp
#total_numbers += 1
#occurrences[temp] += 1
end
// ...
def get_mean
#total_sum / #total_numbers
end
def get_mode
#occurrences.each_with_index.max[1]
end
end
Any help understanding if/ why having the calculation logic in the insert method would be hugely helpful!
Insert is a state-changing operation. It makes sense to reflect all the changes right there on the spot. Get, on the other hand, does not change state, it simply reads it.
Imagine that you do one insert and a hundred reads. It is not very efficient to repeat calculations on each get, as the result can't possibly change since the last time. Waste of resources, but can make for somewhat cleaner code.
I just noticed that I assumed a read-heavy workload. In a write-heavy workload, where you write more than you read, it's the other way around. Efficiency dictates that you put calculations in the getter. There's no one true answer, it's always a tradeoff, is what I'm trying to say.

Fixnum class method modification

This is a description of a code kata that I'm working on for code wars. The aim of the kata is to accomplish this:
The aim of this Kata is to modify the Fixnum class to give it the palindrome_below method. This method returns all numbers from and including 1 up to but not including itself that are palindromes for a given base.
For example in base 2 (binary)
1 = "1"
2 = "10"
3 = "11"
4 = "100"
Therefore 1 and 3 are palindromes in base two and the method should return the following.
5.palindrome_below(2)
=> [1, 3]
Here is the code that I wrote so far for this kata:
class Fixnum
def self.palindrome_below(binary)
palindrome_match = []
until self == 0
if to_s(binary) == to_s(binary).reverse
palindrome_match << self
self -= 1
end
end
palindrome_match
end
end
I tried to decrease self by 1. Sublime is telling me that I'm not able to decrease the value of self but I need to reduce self. Because this is a class method, I need to modify self.
This is what I tried as a work around:
class Fixnum
def self.palindrome_below(binary)
palindrome_match = []
self_placeholder = self
until self_placeholder == 0
if self_placeholder.to_s(binary) == self_placeholder.to_s(binary).reverse
palindrome_match << self_placeholder
self_placeholder -= 1
end
end
palindrome_match
end
end
This time, I placed self in a wrapper variable so I could modify it. When I try this, it says that there is an undefined method called palindrome_below. Doing this implementation should have monkey patched Fixnum. I'm not sure what I'm doing wrong. Can someone point me in the right direction?
A Working Solution (based your second attempt above):
class Fixnum
def palindrome_below(base)
palindrome_match = []
num = self - 1
until num == 0
if num.to_s(base) == num.to_s(base).reverse
palindrome_match << num
end
num -= 1
end
palindrome_match.reverse
end
end
What I changed:
You were right in adding a self_placeholder -- I named this variable num. In Ruby, Fixnums are immutable, so you can't change the value of the particular Fixnum itself.
I subtracted 1 from num right at the beginning, so as to avoid including the number itself in the result array
palindrome_below(base) needs to be an instance method. You very much care about the value of the specific instance of the Fixnum class (ie., the value of the number).
You need to subtract 1 from num outside your if statement.
I reversed the palindrome_match array so that it returns in the proper ascending order.
A Far Superior Solution (courtesy of #CarySwoveland's comment above).
class Fixnum
def palindrome_below(base)
1.upto(self-1).select { |num| num.to_s(base) == num.to_s(base).reverse }
end
end

Ruby - Finding primes under 100?

I'd like to write a code that prints out all primes under 100. Here is the code I have so far
class Numbers
def is_a_prime?(int)
x = 2
while x < int/2
if int % x == 0
return false
else
return true
end
end
end
def primes_under_100
x = 2
while x < 100
print x if is_a_prime?(x) # calling the method I defined above
x+= 1
end
end
end
Unfortunately when I call the method using primes_under_100 I get
undefined local variable or method 'primes_under_100' for main:Object
I'd like to know where I went wrong. None of my methods are private. Help is appreciated.
An other way to do this is extend Fixnum. Whit this you should be able to call it on int values.
this should be something like this
class Fixnum
def is_a_prime?
(2..(self/2)).each do |x|
if self % x == 0
return false
end
end
return true
end
end
In order for your code to work you will need to make the following modifications
class Numbers
def is_a_prime?(int)
x = 2
while x < int/2
if int % x == 0
return false
else
return true
end
end
end
def primes_under_100
x = 2
while x < 100
# Notice you're calling is_a_prime? on the instance of the Numbers object
# and sending x as an argument. Not calling is_a_prime? on the 'x'
print x if is_a_prime?(x)
x+= 1
end
end
end
Then call Numbers.new.primes_under_100
How are you calling it? They are public methods of the Number class, so in order to call them, you need to instantiate an object of the Number class:
number = Numbers.new
primes = number.primes_under_100
Also, as the comment from Leo Correa in my answer stated, the method is_a_prime? can't be called like that, you should use:
print x if is_a_prime?(x)
I don't know which version of Ruby include this method to Prime, but if you are using 2.2 and higher you can do it like this.
Add this to top of the file
require 'prime'
And method for showing primes under specific number.
Prime.each(100) do |prime|
p prime #=> 2, 3, 5, 7, 11, ...., 97
end
Here is the reference

Determining a poker hand with two arrays

I want to create a simple multiplayer poker program in ruby, but I feel like I'm reinventing the wheel:
table = [card1, card2, card3, card4, card5]
mike = [card6, card7]
john = [card8, card9]
card6.face = "A"
card6.suit = "spades"
I'm having a hard time writing an algorithm to decide which of the possible hands each player has.
For example, in order to determine if a player was dealt a flush I wrote something like this:
together = table + hand
# Populate hash with number of times each suit was found
$suits.each do |suit|
matched[suit] = together.select{|card| card.suit == suit}.size
end
matched.each do |k,v|
if v == 5
puts "#{k} were seen five times, looks like a flush."
end
end
This doesn't seem very comprehensive (No way to tell if it's an Ace-high or a 6-high flush) nor very ruby-like.
Is there a more obvious way to determine hands in poker?
It's probably far from perfect, but I wrote some methods to detect poker hands to solve a project euler problem. Maybe it can give you some ideas ; full code is here: https://github.com/aherve/Euler/blob/master/pb54.rb
In a nutshell, Hand is defined by an array of Card, that respond to Card.value and Card.type:
def royal_flush?
return #cards if straight_flush? and #cards.map(&:value).max == Poker::values.max
end
def straight_flush?
return #cards if straight? and #cards.map(&:type).uniq.size == 1
end
def four_of_a_kind?
x_of_a_kind?(4)
end
def full_house?
return #hand if three_of_a_kind? and Hand.new(#cards - three_of_a_kind?).one_pair?
return nil
end
def flush?
return #cards if #cards.map(&:type).uniq.size == 1
end
def straight?
return #cards if (vs = #cards.map(&:value).sort) == (vs.min..vs.max).to_a
end
def three_of_a_kind?
x_of_a_kind?(3)
end
def two_pairs?
if (first_pair = one_pair?) and (second = Hand.new(#cards - one_pair?).one_pair?)
return first_pair + second
else
return false
end
end
def one_pair?
x_of_a_kind?(2)
end
def high_card?
#cards.sort_by{|c| c.value}.last
end
private
def x_of_a_kind?(x)
Poker::values.each do |v|
if (ary = #cards.select{|c| c.value == v}).size == x
return ary
end
end
return false
end
end

Ruby RPG: undefined method for creature traits

Ok, so I've written a few simple RPGs before in other languages (namely python), so I decided to try writing one in ruby. So, this morning, I sat down and typed up the basics of combat mechanics, made a few creatures, and started on the story. However, for some reason, when I try to run the program I get this error:
herosquest.rb:67: undefined method 'traits' for Creature::Hero:Class (NoMethodError)
I looked back at the code (which I'll provide in full at the end) and found the definition in lines 26 to 55, exactly where it should be.
class Creature
# Creature attributes are used to determine the outcome of combat.
traits :life, :strength, :charisma, :weapon
attr_accessor :life, :strength, :charisma, :weapon
#New class methods for hp, str, char, wep
def self.life( val )
#traits ||= {}
#traits['life'] = val
end
def self.strength( val )
#traits ||= {}
#traits['strength'] = val
end
def self.charisma( val )
#traits ||= {}
#traits['charisma'] = val
end
def self.weapon( val )
#traits ||= {}
#traits['weapon'] = val
end
#initialize sets defaults for each attr
def initialize
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
Why isn't it working?
Here's the rest of the code, if that helps.
Note: When I copied my code, some of the indents got screwed up. It's much neater than it looks.
#CREATURE MECHANICS
#Create critters. Make them do stuff.
class Creature
def self.metaclass; class << self; self; end; end
def self.traits( *arr )
return #traits if arr.empty?
attr_accessor( *arr )
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
class_eval do
define_method( :initialize ) do
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
end
class Creature
# Creature attributes are used to determine the outcome of combat.
traits :life, :strength, :charisma, :weapon
attr_accessor :life, :strength, :charisma, :weapon
#New class methods for hp, str, char, wep
def self.life( val )
#traits ||= {}
#traits['life'] = val
end
def self.strength( val )
#traits ||= {}
#traits['strength'] = val
end
def self.charisma( val )
#traits ||= {}
#traits['charisma'] = val
end
def self.weapon( val )
#traits ||= {}
#traits['weapon'] = val
end
#initialize sets defaults for each attr
def initialize
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
#CREATURE TYPES
#Hero
#Our brave Hero! Chosen by the gods! Mighty slayer
#of beasts! Also, broke. Which is why he's hunting
#a dragon. Poor, foolish mortal...
class Hero < Creature
traits :bombs, :arrows, :money
life 10
strength 4
charisma 4
weapon 4
bombs rand(3..10)
arrows rand(15..100)
money rand(0..25) #Should be used with Traders for buying stuff
#Bow. Needs arrows. Duh. Hero should start with, say, 50?
def ^( enemy )
if #arrows.zero?
puts "[Twang. Your imaginary arrow, alas, fails to slay the creature. Or have any effect at all. Moron.]"
return
end
#arrows -= 1
fight( enemy, rand(0+((charisma+(0..4))*2))) #Zero means you missed
end
end
#The hero's sword, given to him by the gods.
#Unlimited uses. Also, shiny.
def /( enemy )
fight( enemy, rand(4+weapon+(strength*2)))
end
#Trading. Ah, commerce! Doesn't quite work yet.
#Need a way to make sure money stays positive,
#and some sort of way to make sure you can't
#cheat the poor trader out of his goods.
def trade( enemy )
if #money.zero?
puts "[ The trader ignores you. You need money.]"
return
else puts "[What do you want to buy?\n Arrows = 10 for 2 coins\n Bombs = 5 for 5 coins]" #Should add new weapons someday
purchase = gets.chomp
if purchase == "Arrows"
puts "[You buy 10 arrows for 2 coins]"
#arrows += 10
#money -= 2
elsif purchase == "Bombs"
puts "[You buy 5 bombs for 5 coins]"
#bombs += 5
#money -= 5
end
end
#Mmm, tasty lembas. Elvish waybread. One small
#bite is enough to fill the stomach of a grown man.
def %( enemy )
lembas = rand( charisma )
puts "[Tasty lembas gives you #{ lembas } life.]"
#life += lembas
fight( enemy, 0 )
end
#Bombs. They explode. Hopefully, far away from you.
def *( enemy )
if #bombs.zero?
puts "[You light your finger on fire instead of the fuze. Then, you realize you're out of bombs.]"
#life -= 1
return
end
#bombs -= 1
fight( enemy, rand(weapon+(2..25)))
end
end
#Man
#Doesn't do much. Stands around and says boring things. Well, someday, anyway...
class Man < Creature
traits :chat #Will eventually say random stuff about the weather or whatever.
life 4
strength 2
charisma 4
weapon 1
end
#Woman
#Like Man. Except she smells nicer.
#Oh, and she's got a frying pan...
#Looks fierce.
class Woman < Creature
life 4
strength 2
charisma 8
weapon 4
end
#Trader
#Hangs around taverns and pushes
#useless stuff on passers-by.
class Trader < Creature
traits :bombs, :arrows, :money
life 4
strength 4
charisma 16
weapon 10 #Armed and dangerous. Sort of...
bombs rand(0..100)
arrows rand(0..500)
money rand(100..2500)
end
#Monkey
#Pain in the butt. Travels in packs. Makes
#faces and throws bananas.
class Monkey < Creature
life 8
strength 5
charisma 11
weapon 2 #Beats you with its cute little fists.
end
#Cow
#Cows won't attack you unless you hit them, or try to ride them,
#or make fun of them for chewing cud.
#Also, they don't like it when people stare
#at their udders. It makes them uncomfortable.
class Cow < Creature
life 18
strength 15
charisma 2 #It's a cow, not a rocket scientist
weapon 3
end
#Deer
#The deer is not as timid as it appears. Serves you right
#for picking on a poor, "helpless" creature. Jerk.
class Deer < Creature
life 30
strength 22
charisma 5
weapon 15
end
#Biker
#Found on paths. Rides a bike.
#Not *super* tough, but should be
#strong enough to make you fear
#for your life.
class Biker < Creature
life 34
strength 10
charisma 4 #They aren't very bright...
weapon 8
end
#Angel
#Heavenly host. "Information: Kill"
#Seriously, don't screw around with these guys.
class Angel < Creature
life 85
strength 8
charisma 60 #Seriously smart, since it's
weapon 25 #secretly a robot.
end
#Tentacle
#Found in lakes by throwing things ala Merry and Pippin.
#NOTE: Should include this in the first scene with a lake:
#"I am afraid of the pool. Don't disturb it!"
#Should preferably be said by a smallish man, muttering about
#"My precious," Mordor, and someone called Sam.
class Tentacle < Creature
life 9
strength 3 #Not strong, but there's a lot of them.
charisma 4
weapon 8
end
#Watcher in the Water
#Found after killing a bunch of tentacles.
#Should be tough enough to scare you off,
#but weak enough that you won't die
#instantly.
class Watcher_in_the_Water < Creature
life 250
strength 10
charisma 35
weapon 4 #I don't want him to be too powerfull
end
#Dragon
#Will totally kill you to death. A lot.
#Final boss type thing. Surrounded
#by his hoard of glittering treasure.
#Oooh, shiny...
class Dragon < Creature
life 500 # tough scales
strength 95 # bristling veins
charisma 65 # toothy smile
weapon 157 # fire breath
end
#COMBAT MECHANICS
#Hitting in combat
def hit( damage )
p_up = rand( charisma )
if p_up % 9 == 7
#life += p_up / 4
puts "[#{ self.class } magic powers up #{ p_up }!]"
end
#life -= damage
puts "[#{ self.class } has died.]" if #life <= 0
end
#Turn in combat
def fight( enemy, weapon )
if life <= 0
puts "[#{ self.class } is dead, Jim. You can't fight!]"
return
end
#You attack
your_hit = rand( strength + weapon )
puts "[Your #{weapon} hit the #{enemy} for #{ your_hit } points of damage!]"
enemy.hit( your_hit )
#Enemy fights back
p enemy
if enemy.life > 0
enemy_hit = rand( enemy.strength + enemy.weapon )
puts "[The #{enemy} hit with #{ enemy_hit } points of damage!]"
self.hit( enemy_hit )
end
end
end
#PLACES, EVENTS, & STORY
#First, generate the Player
h = Hero.new
#Determine player name and gender
print "What is your name, adventurer?\n"
name=gets.chomp.to_s
print "#{name}, are you male or female?\n"
gender=gets
print "\n"
#player chooses his/her class
until heroclass = (1..3)
print"What class do you want?\n(1 for Scout, 2 for Fighter, or 3 for Ranger)\n"
heroclass = gets.to_i
if heroclass == 1
heroclass = "Scout"
Hero.charisma = charisma + 3
Hero.strength = strength - 1
Hero.weapon = weapon + 1
Hero.arrows = arrows + 50
print"Scouts are quick and intelligent, but they are a bit weak.\nTheir prefered weapon is the bow.\n"
break
elsif heroclass == 2
heroclass = "Fighter"
Hero.strength = strength + 3
Hero.charisma = charisma - 3
Hero.weapon = weapon + 3
Hero.life = life + 5
print"Fighters are skilled at close combat, and their endurance is unmached by lesser mortals.\n(They aren't very bright though...)\nTheir prefered weapon is the sword\n"
break
elsif heroclass == 3
heroclass = "Ranger"
Hero.strength = strength - 1
Hero.charisma = charisma + 2
Hero.life = life + 2
Hero.bombs = bombs + 5
Hero.weapon = weapon + 2
print"Rangers are highly skilled, but not as strong as fighters or as agile as scouts.\nThey fight well with all weapons, but they particularly enjoy blowing stuff up.\n"
break
else print "Please enter a valid class number."
end
print "Welcome, #{name}, the #{gender} #{heroclass}, Chosen Hero of the gods!\n"
print "The disembodied voice you are now hearing is your Spirit Guide"
end
When I tried running your code, I got the error
so_question.rb:28:in `<class:Creature>': undefined method `traits' for Creature::Creature:Class (NoMethodError)
from so_question.rb:26:in `<class:Creature>'
from so_question.rb:3:in `<main>'
because you have a class called Creature within the class Creature, and traits is a class method on Creature, not on Creature::Creature.

Resources