I am trying to make a battleship game in Ruby, but when I try to create an instance of the game board, I get "wrong number of arguments, 0 for 1". I dont see where I am going wrong as the initialize definition clearly accepts arguments.
class Board
attr_reader :grid, :default_grid
def intitalize(grid = self.class.default_grid, random = false)
#grid = grid
make_random_board if random
end
def self.default_grid
grid = Array.new(10){Array.new(10)}
end
def count
grid.flatten.count{|x| x == :s}
end
def self.random
self.new(self.default_grid, true)
end
def empty?(position = nil)
return true if position.nil?
else
false
end
def full?
grid.flatten.none?(&:nil?)
end
def place_random_ship
if full?
raise "error, board is full"
end
space = [rand(grid.length),rand(grid.length)]
until empty?(space)
space = [rand(grid.length),rand(grid.length)]
end
self[space] = :s
end
def make_random_board(count = 10)
count.times do
place_random_ship
end
end
end
emptygrid = Array.new(2){Array.new(2)}
myGame = Board.new(emptygrid)
You have a typo in your code. You should be using initialize instead of intitalize
And i believe the error you might have been getting would be ArgumentError: wrong number of arguments (1 for 0)
Which is because of your typo, the default class initialize method was used, which doesn't take in any arguments.
And something unrelated that i noticed in your code. You have defined a method named count and use variables named count. This is a code smell and i would suggest naming the method differently, because down the line, this might cause some bugs, that you might find hard to debug.
Related
My following class is like so:
attr_reader :player, :player_choice, :cpu_choice, :choices, :game, :result
def initialize(player)
#player = player
#player_choice = ""
#cpu_choice = ""
#choices = Choices.new
#result = ""
end
def get_result
#result
end
def show_cpu_choice
#cpu_choice
end
def set_player_choice(choice)
#player_choice = choice
set_cpu_choice
decide_winner
end
def set_cpu_choice
#cpu_choice = #choices.get_choices.sample
end
I have omitted any irrelevant methods but basically I want to hardcode my #cpu_choice to "Scissors" for example so this following test can work,since my cpu choice is randomly generated but it is not working whichever method i am trying in rspec.
My set_cpu_choice randomonises from an array from an instance variable in my Choices class btw.
let(:game) {Game.new("Johnny")}
describe 'Player wins' do
it 'Player selects Rock and CPU has picked Scissors' do
game.set_player_choice("Rock")
allow(game).to receive(:show_cpu_choice).and_return("Scissors")
expect(game.get_result).to eq("Johnny")
end
end
I have tried the above in my rspec and I have also tried to do it using instance_variable_set but my test still keeps randomising the cpu choice.
I have also looked at something called srand but that looks too complicated for me since I dont understand any of it.
So I'll assume that decide_winner calls show_cpu_choice and sets #result. The problem is that when set_player_choice is called game hasn't yet been set up to return "Scissors". The allow should be moved before the call to set_player_choice
I have a class. Let's call it SomeClass:
class SomeClass
end
Instead of defining instances of this class the normal way, I would like to define them all using a constant:
MyConstant = SomeClass.new
I want to be able to capture the name of the constant that some class was set too, in much the same way that standard ruby classes do with the .class method.
MyConstant.name #-> "MyConstant"
I want to be able to do this to render better error messages from all instances of some class, like so:
class SomeClass
def display_error_message
"Error, some class #{self.name} has a problem"
end
end
MyConstant.display_error_message
#-> "Error, some class MyConstant has a problem"
Any way to accomplish this?
EDIT
Here's an example to clarify what I'm shooting for.
(Enum is the name of the class I'm creating, which is meant to act similar to Swifts 'Enum' type. Basically it sets a predefined list of options (:pepperoni, :sausage, :mushroom) with a raw_value ("Pepperoni", "Sausage", "Mushroom".) Obviously in this example a hash or a simple algorithm for converting a symbol to an UpperCamel case string could work, but in reality the enum class will do a lot more, but this example shows the gist of it.
class Pizza
attr_reader :topping
Toppings = Enum.new do
option(:pepperoni).set("Pepperoni")
option(:sausage).set("Sausage")
option(:mushrooms).set("Mushrooms")
end
def set_topping(symbol)
#topping = Toppings[symbol]
end
end
pizza = Pizza.new
### Happy Case
pizza.set_topping(:pepperoni)
### Sad Case (Error message shown below is what I'm trying to figure out)
pizza.set_topping(:spinach)
#-> Error. enum Toppings has no option spinach
A variable is just a way of referring to an object, and the variable's name is irrelevant. If you say X = Y and Y happens to be a class, then the Y class already has the name "Y", so you can't change that.
As far as Ruby is concerned X and Y are indistinguishable.
If you want to alter the name you can make a subclass even if that subclass doesn't do anything different:
X = Class.new(Y)
X.name
# => "X"
Z = X
Z.name
# => "X"
That way preserves the name properly but only in the context of the initialization. I think Ruby does something sneaky and if a new class is being assigned to a constant it assigns a name, but for ordinary variables it does not:
x = Class.new(Y)
x.name
# => nil
So this is a special case.
The key here is that there's a huge difference between a subclass, which does impact the name, and a variable reference, which doesn't.
There's some other strange stuff going on here as it seems like the class somehow "knows" when it's being assigned to something and if that something is a constant it steals the constant's name for itself:
z = Class.new
z.name
# => nil
Z = z
z.name
# => "Z"
As they say in programming: "Wat?"
Your Enum class could look something like this:
class Enum
def initialize(name, &blk)
#defined_options = {}
#name = name.freeze
instance_eval(&blk)
#defined_options.freeze
end
def [](key)
if #defined_options.key? key
#defined_options[key].value
else
unfound_option = Option.new(#name, key)
raise "Option #{unfound_option} not found."
end
end
def to_s
"#{#name}"
end
def inspect
keys = #defined_options.keys.join(',')
"#<#{self}::{#{keys}}>"
end
class Option
attr_reader :value
def initialize(enum_name, key)
#value_initialized = false
#enum_name = enum_name
#key = key
end
def set(value)
if #value_initialized
raise "Value for #{self} can't be set to #{value} " +
"because it is already initialized to #{#value}"
else
#value_initialized = true
#value = value.freeze
end
end
def to_s
"#{#enum_name}::#{#key}"
end
def inspect
"#<#{self}>"
end
end
private
def option(sym)
unless #defined_options.key? sym
option = Option.new(#name, sym)
#defined_options[sym] = option
end
#defined_options[sym]
end
end
Now you can almost keep the syntax you have in your question, and do the following:
class Pizza
attr_reader :topping
# I had to add the name in the initializer for better error reporting.
Toppings = Enum.new('Toppings') do
option(:pepperoni).set("Pepperoni")
option(:sausage).set("Sausage")
option(:mushrooms).set("Mushrooms")
end
def set_topping(symbol)
#topping = Toppings[symbol]
end
end
pizza = Pizza.new
### Happy Case
pizza.set_topping(:pepperoni)
#=> "Pepperoni"
### Sad Case (Error message shown below is what I'm trying to figure out)
pizza.set_topping(:spinach)
#=> RuntimeError: Option Toppings::spinach not found.
This might give you an idea how to solve this issue. This is just a rough sketch of the class and could probably be tweaked to your needs.
In my Triangle class below, I have an initialize method that takes three arguments.
class Triangle
attr_accessor :side1, :side2, :side3
def initalize(one, two, three)
#side1 = one
#side2 = two
#side3 = three
end
end
When I create a new class and initialize it with three arguments,
one = Triangle.new(1,2,3)
puts one.side1
puts one.side2
puts one.side3
I get this error:
"lib/triangle.rb:21:in `initialize': wrong number of arguments (given 3, expected 0) (ArgumentError)".
What's up?
You have a typo:
def initalize # typo
def initialize # correct
BTW, a less manual way of handling the scenario in kind is:
def kind
:scalene if [#side1, #side2, #side3].uniq.length == 3
end
Ok so I just started learning ruby and I'm making a Yhatzee game, now this is where I'm currently at:
class Yhatzee
def dices
#dices.to_a= [
dice1=rand(1..6),
dice2=rand(1..6),
dice3=rand(1..6),
dice4=rand(1..6),
dice5=rand(1..6)
]
end
def roll_dice
#dices.to_a.each do |dice|
puts dice
end
end
end
x = Yhatzee.new
puts x.roll_dice
Now the reason i typed .to_a after the array is i kept getting a "uninitialized variable #dices" error, and that seemed to fix it, i have no idea why.
anyways on to my question, i currently don't get any errors but my program still won't print anything to the screen. I expected it to print out the value of each dice in the array... any idea what I'm doing wrong? It seems to work when i do it in a procedural style without using classes or methods so i assumed it might work if i made the 'dices' method public. But no luck.
There are a few issues here. Firstly #dices is nil because it is not set anywhere. Thus when you call #dices.to_a you will get []. Also the dices method will not work either because nil does not have a to_a= method and the local variables you are assigning in the array will be ignored.
It seems a little reading is in order but I would do something like the following: (Not the whole game just refactor of your code)
class Yhatzee
def dice
#dice = Array.new(5){rand(1..6)}
end
def roll_dice
puts dice
end
end
x = Yhatzee.new
puts x.roll_dice
There are alot of additional considerations that need to be made here but this should at least get you started. Small Example of how I would recommend expanding your logic: (I did not handle many scenarios here so don't copy paste. Just wanted to give you a more in depth look)
require 'forwardable'
module Yahtzee
module Display
def show_with_index(arr)
print arr.each_index.to_a
print "\n"
print arr
end
end
class Roll
include Display
extend Forwardable
def_delegator :#dice, :values_at
attr_reader :dice
def initialize(dice=5)
#dice = Array.new(dice){rand(1..6)}
end
def show
show_with_index(#dice)
end
end
class Turn
class << self
def start
t = Turn.new
t.show
t
end
end
attr_reader :rolls
include Display
def initialize
#roll = Roll.new
#rolls = 1
#kept = []
end
def show
#roll.show
end
def roll_again
if available_rolls_and_dice
#rolls += 1
#roll = Roll.new(5-#kept.count)
puts "Hand => #{#kept.inspect}"
show
else
puts "No Rolls left" if #rolls == 3
puts "Remove a Die to keep rolling" if #kept.count == 5
show_hand
end
end
def keep(*indices)
#kept += #roll.values_at(*indices)
end
def show_hand
show_with_index(#kept)
end
def remove(*indices)
indices.each do |idx|
#kept.delete_at(idx)
end
show_hand
end
private
def available_rolls_and_dice
#rolls < 3 && #kept.count < 5
end
end
end
The main problem with this code is that you are trying to use the #dices instance variable inside of the roll_dice method, however you are not defining the instance variable anywhere (anywhere that is being used). You have created the dices method but you are not actually instantiating it anywhere. I have outlined a fix below:
class Yhatzee
def initialize
create_dices
end
def roll_dice
#dices.each do |dice|
puts dice
end
end
private
def create_dices
#dices = Array.new(5){rand(1..6)}
end
end
x = Yhatzee.new
x.roll_dice
I have done some simple refactoring:
Created an initialize method, which creates the #dice instance variable on the class initialization.
Made the 'dices' method more descriptive and changed the method visibility to private so only the class itself is able to create the #dice.
Cleaned up the creation of the dices inside of the #dice instance variable
I have omitted the .to_a from the roll_dice method, now that we create the variable from within the class and we know that it is an array and it will be unless we explicitly redefine it.
UPDATE
Although I cleaned up the implementation of the class, it was kindly pointed out by #engineersmnky that I oversaw that the roll would return the same results each time I called the roll_dice function, I have therefore written two functions which will achieve this, one that defines an instance variable for later use and one that literally just returns the results.
class Yhatzee
def roll_dice
#dice = Array.new(5){rand(1..6)} # You will have access to this in other methods defined on the class
#dice.each {|dice| puts dice }
end
def roll_dice_two
Array.new(5){rand(1..6)}.each {|dice| puts dice } # This will return the results but will not be stored for later use
end
end
x = Yhatzee.new
x.roll_dice
x.roll_dice # Will now return a new result
Say I have the following class:
class Cashier
def purchase(amount)
(#purchases ||= []) << amount
end
def total_cash
(#purchases || []).inject(0) {|sum,amount| sum + amount}
end
end
This is for learning purposes only, please ignore how unrealistic this may be.
Now in general, the total_cash could be an expensive call to loop through all the items.
I want to know how I can call .inject ONLY if the #purchases variable is dirty i.e. there was something modified.
How would my class be modified to do this?
The simplest approach would be to maintain another variable to indicate whether or not #purchases is dirty. For example:
class Cashier
def initialize(*args)
# init #purchases and #total_cash
#is_purchases_dirty = false
end
def purchase(amount)
(#purchases ||= []) << amount
#is_purchases_dirty = true
end
def total_cash
return #total_cash unless #is_purchases_dirty
#is_purchases_dirty = false
#total_cash = (#purchases || []).inject(0) {|sum,amount| sum + amount}
return #total_cash
end
end
A cleaner/simpler approach may be to calculate #total_cash each time the setter is called for purchases. However, this means that you need to always use the setter, even within your class. It also means that you will be "hiding" an expensive operation inside of a setter method. You can decide which one you like better.
class Cashier
def purchase(amount)
(#purchases ||= []) << amount
#total_cash = (#purchases || []).inject(0) {|sum,amount| sum + amount}
end
def total_cash
#total_cash
end
end
I would also recommend against your naming scheme for an expensive operation. I would rename total_cash to something like calc_total_cash in order to tell users of your API that this is a relatively expensive call as opposed to a simple getter/setter.
You can take this a step further than the other answers if you wanted. Rather than changing your code to only recalculate when necessary, you can write the code that changes your code. Everybody loves a bit of metaprogramming.
Here's some code that takes the name of a method that performs a potentially long calculation, and a list of names of methods that when called invalidate any previous calculation, and writes the code to wrap the methods and only perform the calculation if necessary, returning the stored value if not.
module ExpensiveCalculation
def recalc_only_if_necessary(meth, *mutators)
aliased_method_name = "__#{meth.object_id}__"
value = "#__#{meth.object_id}_value__"
dirty_flag = "#__#{meth.object_id}_dirty__"
module_eval <<-EOE
alias_method :#{aliased_method_name}, :#{meth}
private :#{aliased_method_name}
def #{meth}(*args, &blk)
#{dirty_flag} = true unless defined? #{dirty_flag}
return #{value} unless #{dirty_flag}
#{value} = #{aliased_method_name}(*args, &blk)
#{dirty_flag} = false
#{value}
end
EOE
mutators.each do |mutator|
aliased_mutator = "__#{meth.object_id}_#{mutator.object_id}__"
module_eval <<-EOE
alias_method :#{aliased_mutator}, :#{mutator}
private :#{aliased_mutator}
def #{mutator}(*args, &blk)
#{dirty_flag} = true
#{aliased_mutator}(*args, &blk)
end
EOE
end
end
# this hook is used to make the new method
# private to the extended class.
def self.extend_object(obj)
super
obj.private_class_method :recalc_only_if_necessary
end
end
By making this available inside your class definition, you can wrap one or many methods easily without changing your existing code:
class Cashier
extend ExpensiveCalculation
def purchase(amount)
(#purchases ||= []) << amount
end
def total_cash
(#purchases || []).inject(0) {|sum,amount| sum + amount}
end
recalc_only_if_necessary :total_cash, :purchase
end
It might not make sense to do something like this if you just want to change one method, but if you have several that you want to change some way techniques like this can be pretty useful.
In the simplest case, you could define an instance variable for the thing you want to mark as dirty. Set it to true when the variable is modified (in your purchase method).
Check for the value in total_cash; if so, use a cached version of the total. Otherwise, compute the new value and store it in the cache.
class Cashier
def purchase(amount)
#purchases_dirty = true
(#purchases ||= []) << amount
end
def total_cash
#total_cash = (#purchases || []).inject(0) do |sum,amount|
sum + amount
end if (#purchases_dirty || #total_cash.nil?)
#purchases_dirty = false
#total_cash
end
end