Data structures for a Blackjack game - ruby

I'm trying to learn how to use Ruby and as my first application I'd like to build a console based Blackjack game.
I'm not too familiar with the Ruby constructs and the only way I'll learn and feel at home is build things and learn from my mistakes.
I'm thinking of creating a Card class, and has a Stack class has a collection of Cards.
However I don't know exactly what built in type I need to use to hold these Card objects.
Here is my card class:
class Card
attr_accessor :number, :suit
def initialize(number, suit)
#number = number
#suit = suit
end
def to_s
"#{#number} of #{#suit}"
end
end
Since I'm coming from C#, I thought of using something like List Cards {get;set;} - does something like this exists in Ruby or maybe there is a better more rubyesque way to do this.

Ruby is a dynamic language, so it usually doesn't rely on strict compile-time type checking, thus making constructions, such as templatized List<Card> fairly useless. Ruby generally has one universal data type of ordered collections - Array, which closely mimics ArrayList concept of Java/C#.
So, unless you want Stack to be something special, you can just use Array as is:
#stack = []
#stack << Card.new(6, :spades)
#stack << Card.new(10, :hearts)
or extend Array in some way:
class Stack < Array
# some ultra-cool new methods
end
#stack = Stack.new
#stack << Card.new(...)

Ruby's builtin Array type has methods for acting like a stack:
a = [1, 2, 3] # arrays can be initialized with []
a.push(4) # a is now [1, 2, 3, 4]
a.pop # returns 4, a is now [1, 2, 3]

Two Ruby Quiz episodes were dedicated to blackjack. The solutions to the second one might give you some ideas: http://www.rubyquiz.com/quiz151.html

Related

Overriding the << method for instance variables

Let's suppose I have this class:
class Example
attr_accessor :numbers
def initialize(numbers = [])
#numbers = numbers
end
private
def validate!(number)
number >= 0 || raise(ArgumentError)
end
end
I would like to run the #validate! on any new number before pushing it into the numbers:
example = Example.new([1, 2, 3])
example.numbers # [1, 2, 3]
example.numbers << 4
example.numbers # [1, 2, 3, 4]
example.numbers << -1 # raise ArgumentError
Below is the best I can do but I'm really not sure about it.
Plus it works only on <<, not on push. I could add it but there is risk of infinite loop...).
Is there a more "regular" way to do it? I couldn't find any official process for that.
class Example
attr_accessor :numbers
def initialize(numbers = [])
#numbers = numbers
bind = self # so the instance is usable inside the singleton block
#numbers.singleton_class.send(:define_method, :<<) do |value|
# here, self refers to the #numbers array, so use bind to refer to the instance
bind.send(:validate!, value)
push(value)
end
end
private
def validate!(number)
number >= 0 || raise(ArgumentError)
end
end
Programming is a lot like real life: it is not a good idea to just run around and let strangers touch your private parts.
You are solving the wrong problem. You are trying to regulate what strangers can do when they play with your private parts, but instead you simply shouldn't let them touch your privates in the first place.
class Example
def initialize(numbers = [])
#numbers = numbers.clone
end
def numbers
#numbers.clone.freeze
end
def <<(number)
validate(number)
#numbers << number
self
end
private
def validate(number)
raise ArgumentError, "number must be non-negative, but is #{number}" unless number >= 0
end
end
example = Example.new([1, 2, 3])
example.numbers # [1, 2, 3]
example << 4
example.numbers # [1, 2, 3, 4]
example << -1 # raise ArgumentError
Let's look at all the changes I made one-by-one.
cloneing the initializer argument
You are taking a mutable object (an array) from an untrusted source (the caller). You should make sure that the caller cannot do anything "sneaky". In your first code, I can do this:
ary = [1, 2, 3]
example = Example.new(ary)
ary << -1
Since you simply took my array I handed you, I can still do to the array anything I want!
And even in the hardened version, I can do this:
ary = [1, 2, 3]
example = Example.new(ary)
class << ary
remove_method :<<
end
ary << -1
Or, I can freeze the array before I hand it to you, which makes it impossible to add a singleton method to it.
Even without the safety aspects, you should still do this, because you violate another real-life rule: Don't play with other people's toys! I am handing you my array, and then you mutate it. In the real world, that would be considered rude. In programming, it is surprising, and surprises breed bugs.
cloneing in the getter
This goes to the heart of the matter: the #numbers array is my private internal state. I should never hand that to strangers. If you don't hand the #numbers array out, then none of the problems you are protecting against can even occur.
You are trying to protect against strangers mutating your internal state, and the solution to that is simple: don't give strangers your internal state!
The freeze is technically not necessary, but I like it to make clear to the caller that this is just a view into the state of the example object, and they are only allowed to view what I want them to.
And again, even without the safety aspects, this would still be a bad idea: by exposing your internal implementation to clients, you can no longer change the internal implementation without breaking clients. If you change the array to a linked list, your clients are going to break, because they are used to getting an array that you can randomly index, but you can't randomly index a linked list, you always have to traverse it from the front.
The example is unfortunately too small and simple to judge that, but I would even question why you are handing out arrays in the first place. What do the clients want to do with those numbers? Maybe it is enough for them to just iterate over them, in which case you don't need to give them a whole array, just an iterator:
class Example
def each(...)
return enum_for(__callee__) unless block_given?
#numbers.each(...)
self
end
end
If the caller wants an array, they can still easily get one by calling to_a on the Enumerator.
Note that I return self. This has two reasons:
It is simply the contract of each. Every other object in Ruby that implements each returns self. If this were Java, this would be part of the Iterable interface.
I would actually accidentally leak the internal state that I work so hard to protect! As I just wrote: every implementation of each returns self, so what does #numbers.each return? It returns #numbers, which means my whole Example#each method returns #numbers which is exactly the thing I am trying to hide!
Implement << myself
Instead of handing out my internal state and have the caller append to it, I control what happens with my internal state. I implement my own version of << in which I can check for whatever I want and make sure no invariants of my object are violated.
Note that I return self. This has two reasons:
It is simply the contract of <<. Every other object in Ruby that implements << returns self. If this were Java, this would be part of the Appendable interface.
I would actually accidentally leak the internal state that I work so hard to protect! As I just wrote: every implementation of << returns self, so what does #numbers << number return? It returns #numbers, which means my whole Example#<< method returns #numbers which is exactly the thing I am trying to hide!
Drop the bang
In Ruby, method names that end with a bang mean "This method is more surprising than its non-bang counterpart". In your case, there is no non-bang counterpart, so the method shouldn't have a bang.
Don't abuse boolean operators for control flow
… or at least if you do, use the keyword versions (and / or) instead of the symbolic ones (&& / ||).
But really, you should void it altogether. do or die is idiomatic in Perl, but not in Ruby.
Technically, I have changed the return value of your method: it used to return true for a valid value, now it returns nil. But you ignore its return value anyway, so it doesn't matter.
validate is probably not a good name for the method, though. I would expect a method named validate to return a boolean result, not raise an exception.
An exceptional message
You should add messages to your exceptions that tell the programmer what went wrong. Another possibility is to create more specific exceptions, e.g.
class NegativeNumberError < ArgumentError; end
But that would be overkill in this case. In general, if you expect code to "read" your exception, create a new class, if you expect humans to read your exception, then a message is enough.
Encapsulation, Data Abstraction, Information Hiding
Those are three subtly different but related concepts, and they are among the most important concepts in programming. We always want hide our internal state and encapsulate it behind methods that we control.
Encapsulation to the max
Some people (including myself) don't particularly like even the object itself playing with its internal state. Personally, I even encapsulate private instance variables that are never exposed behind getters and setters. The reason is that this makes the class easier to subclass: you can override and specialize methods, but not instance variables. So, if I use the instance variable directly, a subclass cannot "hook" into those accesses.
Whereas if I use getter and setter methods, the subclass can override those (or only one of those).
Note: the example is too small and simple, so I had some real trouble coming up with a good name (there is not enough in the example to understand how the variable is used and what it means), so eventually, I just gave up, but you will see what I mean about using getters and setters:
class Example
class NegativeNumberError < ArgumentError; end
def initialize(numbers = [])
self.numbers_backing = numbers.clone
end
def each(...)
return enum_for(__callee__) unless block_given?
numbers_backing.each(...)
self
end
def <<(number)
validate(number)
numbers_backing << number
self
end
private
attr_accessor :numbers_backing
def validate(number)
raise NegativeNumberError unless number >= 0
end
end
example = Example.new([1, 2, 3])
example.each.to_a # [1, 2, 3]
example << 4
example.each.to_a # [1, 2, 3, 4]
example << -1 # raise NegativeNumberError

How to use value of a string to refer to variables?

Some programmer made a method that gets lots of arguements like this:
def age_entry(age_1, age_2, age_3, age_4, age_5, age_6, age_7, age_8)
end
They could pass an array but simply they didn't. I love automation and hate to repeatedly add these variables to and array like this
ages = [age_1, age_2, age_3 ,..., age_8]
I would like to use metaprogramming or other ways to loop with a for or each methods to add them variables to an array like this:
(1..8).each do |index| do
ages << "age_" + index #value of age_[index] get saved to ages
end
P.S. I know I can use copy and paste but this is only for doing automation stuff with Ruby.
"Some programmer" should remember that you can pass in arrays. This sort of method signature is really obnoxious to work with for a multitude of reasons, some of them you've already discovered.
One way to refactor this method and preserve functionality is to just take in varargs:
def age_entry(*ages)
end
Now those values are put in an array for you but you can call the method the same way as before. As a plus you can specify more or fewer entries.
Variables with names like x1, x2 and so on are violations of the Zero, One or Infinity Rule and are a sign you need to think about the problem differently.
You don’t need any metaprogramming here. Just splat them:
ages = [age_1, age_2, age_3 ,..., age_8]
# ⇓ HERE
age_entry(*ages)
If you want to collect age_(1..8) into the array, assuming all local vars are defined, use Kernel#binding:
b = binding
ages = (1..8).map { |i| b.local_variable_get("age_#{i}") }
Suppose the method is as follows.
def oldest(age_bill, age_barb, age_trixie)
puts "Barb is #{age_barb} years old"
[age_bill, age_barb, age_trixie].max
end
oldest(35, 97, 29)
#=> 97
As well as the calculation in the penultimate line (which the OP wishes to avoid), this method requires knowledge of an individual method argument (age_barb). The following is one way to accomplishing both requirements.
def oldest(age_bill, age_barb, age_trixie)
puts "Barb is #{age_barb} years old"
b = binding
args = method(__method__).parameters.map { |arg| b.local_variable_get(arg[1]) }
args.max
end
#=> 97
Barb is 97 years old
Here
args
#=> [35, 97, 29]

Are these objects? Why do they look like that when I print them?

this is my first post and I'm quite new to programming/this site, so I apologise in advance if I'm doing something wrong/annoying.
I wanted to find a way to define objects without having to do so for each object. I came up with this
class Number
def initialize(name)
#name = name
end
def description
puts "I'm #{#name} "
end
end
a = ["zero", "one","two", "three", "four"]
for i in (0..5) do
a[i] = Number.new(a[i])
end
a[3].description
I'm hoping someone can tell me what kind of Frankensteins monster I've created?
It seems to work, a[3].description returns "I'm three" but does that mean three/a[3] exists as its own object and not an element of an array?
Furthermore if I try to do:
puts a[3]
I get:
<Context::Number:0x000000009b7fd0 #name="three">, #
To clarify I just want to know whether I have actually managed to create objects here, and why on earth when I try and access elements of my array I get that weird feedback (kind of seems like its accessing memory or something, but that is a little beyond me)
My thanks in advance for anyone who replies to this.
All objects stand on their own, regardless of whether they are contained by/in other objects such as Array instances.
Regarding this:
<Context::Number:0x000000009b7fd0 #name="three">, #
...did you mean you get that when you puts a[3] and not puts a?
Every instance of Object and its subclasses has a to_s method that returns a string representation of the object. Since you did not override that in your Number class, it used the default implementation defined in class Object. It is showing you:
1) the class name (I presume you defined Number in side a class or module named Context)
2) the object id (a unique id in the Ruby runtime)
3) the string representation of its instance variable(s)
Also, regarding this:
a = ["zero", "one","two", "three", "four"]
This is equivalent and easier to type (I use 2 spaces for better readability):
%w(zero one two three four)
Also, as Ilya pointed out, map will simplify your code. I'll go a little further and recommend this to do the array initialization:
a = %w(zero one two three four).map { |s| Number.new(s) }
Yes, you have created objects. It's just how Ruby represents a class as a string.
class MyClass
attr_accessor :one, :two
def initialize(one, two)
#one, #two = one, two
end
end
my_class = MyClass.new(1, 2)
my_class.to_s # #<MyClass:0x007fcacb8c7c68>
my_class.inspect # #<MyClass:0x007fcacb8c7c68 #one=1, #two=2>

How to extend a core class(Array) in a new user defined class

In ruby console when we type
a = String.new('cool')
'cool'
Likewise for array
b = Array.new([1,2,3,4])
[1,2,3,4]
I need to do something like the following
class NewArray < Array
def initialize(something)
# doing some action with something
# and construct and array like
[1,2,3,4,5]
end
...
end
Now when I call this from the console
a = NewArray.new(something)
[1,2,3,4,5]
Till now I am getting something like
a = NewArray.new(something)
#<NewArray:0x00000aab5fe>
a
#<NewArray:0x00000aab5fe>
UPDATE
changed NewArray(something) to NewArray.new(something)
It is recommended to never inherit from core classes.(unless you really know what you are are doing) A Reference but there are many more.
You can however get the functionality you want through the SimpleDelegator class like so
require 'delegate'
class NewArray < SimpleDelegator; end
NewArray.new([1,2,3])
#=> [1, 2, 3]
SimpleDelegator Docs
In order to be able to send an argument to a name such as NewArray, you will need to define it as a method.
def NewArray(something)
[1, 2, 3, 4 ,5]
end
puts NewArray('anything')
This is not that common to do, but it is done for methods like Integer and Array and a few others on Kernel module.

querying activerecord models for plain arrays (iterations,map,whatever...needing to learn some things)

So, I needed to get some values from associations on my a model:
def provide_list(x,y)
self.send(x.to_sym).map {|k,v| k[y] }
end
Ok, fine. In that model I can take an instance and do
instance.provide_list('resources', 'thefieldiwant')
to be more specific, I'll get an array of the values for the associated resource e.g
accountinstance.provide_list('users', 'numberlogins')
accountinstance.provide_list('users', 'loginproblems')
and get arrays like
[45,56,78,1,2,etc]
[5,5,7,1,2,etc]
and then zip them together, getting tuple arrays ([45,5],[56,6],...) for each user I can pass around and whatnot
I want to improve this, so I don't have to run each time manually when I want a specific array of fields for an association (and as a prelude to doing a method that can get and zip what I need with one call for each association instance):
def provide_listing(dd,*args)
args.each do |ee|
self.send(dd.to_sym).map {|k,v| k[ee] }
end
end
This doesn't work, I just get an array of the args, so something I don't know about is tripping this up. Basic question: how do I get the second to behave in tune with the first, except provide an array for each arg.
Open to suggestions about a better way, what I'm doing wrong, if this has been asked before etc. This seems a basic thing that I can't do right this minute, so I'm asking before putting it away for a while so someone with more expertise can enlighten me.
The solution is me learning more refined methods of manipulating ActiveRecord which I'm hardly expert, reviewing this is helping:
http://guides.rubyonrails.org/active_record_querying.html
the main problem with the second approach is that you are returning the value of #each, which is always just the object #each was called on, in this case args.
I like Pedro's solution, but i'd tweak it slightly:
def provide_list(association, *args)
self.send(association).select(args).map { |r| r.attributes.values }
end
Few things to note:
you don't need to use .to_sym, send and select will take strings or symbols.
i'd avoid using raw sql, sure it will be faster, but more prone to errors, portability issues and sql injection - never write raw sql unless you have a very good reason.
Hope that helps!
Hacky, but should work:
def provide_list association, *args
association = self.send(association.to_sym)
association.connection.select_all("select #{args.join(',')} from #{association.table_name}'").map(&:values)
end
You can try this in the console:
self.send(dd.to_sym)
returns an array of hashes. For example:
#arr = [{a:1, b:2, c:3}, {a:4, b:5, c:6}]
You probably want to return a 2d array like this:
def provide_listing( *args )
args.map do |arg|
#arr.map do |h|
h[arg]
end
end
end
provide_listing( :a, :b, :c )
=> [[1, 4], [2, 5], [3, 6]]

Resources