Alekx
Alekx

Reputation: 901

dynamic variable values

I'm looking for some advice on dynamic variables. Particularly if I'm making the best use of instance_variable_set or if there is a better way I should be looking at. Thanks in advance for any advice. Now for the code.

I'm creating a Blackjack game. And I have two value_creator methods that look like this:

class Blackjack

  def dealers_card_value_creator
    @card_value = @hit_dealer
      case
      when @card_value.match(/\d+/)
        @card_value = @card_value.match(/\d+/)[0].to_i
      when @card_value.match(/Ace/)
        if @dealers_hand >= 11
          @card_value = 1
        else
          @card_value = 11
        end
      when @card_value.match(/Jack|Queen|King/)
        @card_value = 10
      else
        puts "there was an error. please try again."
      end
    @hit_dealer = @card_value
    @dealers_hand = @dealers_hand + @hit_dealer
      if @dealers_hand > 21 then puts "Dealer went over 21. You won $#{@bet}. Nice!" 
        deal end
  end

def players_card_value_creator
    @card_value = @hit_player
    case
    when @card_value.match(/\d+/)
      @card_value = @card_value.match(/\d+/)[0].to_i
    when @card_value.match(/Ace/)
      if @players_hand >= 11
        @card_value = 1
      else
        @card_value = 11
      end
    when @card_value.match(/Jack|Queen|King/)
      @card_value = 10
    else
      puts "there was an error. please try again."
    end
    @hit_player = @card_value
    @players_hand = @players_hand + @hit_player
      if @players_hand > 21 then puts "You went over 21. You lost $#{@bet}. Try again." 
        deal end
  end
end

as you can see, they are basically the same. So I created this method to be used by both Dealers and Players.

def value_creator(string)
    hit = "@#{string}" #returns either @player or @dealer
    instance_variable_set(hit, "#{@hit_card}")
    @card_value = hit
    case
    when @card_value.match(/\d+/)
      @card_value = @card_value.match(/\d+/)[0].to_i
    when @card_value.match(/Ace/)
      if "@#{string}"_hand >= 11
        @card_value = 1
      else
        @card_value = 11
      end
    when @card_value.match(/Jack/)
      @card_value = 10
    when @card_value.match(/Queen/)
      @card_value = 10
    when @card_value.match(/King/)
      @card_value = 10
    else
      puts "there was an error. please try again."
    end
    "@#{string}" = @card_value
    "@#{string}"_hand = "@#{string}"_hand + "@#{string}"
  end

  def hit_dealer
    @hit_card = @shuffled_deck.pop
    value_creator("dealer")
  end

  def hit_player
    @hit_card = @shuffled_deck.pop
    value_creator("player")
  end

But when I try to call the value of "@#{string}" with hit_player it returns @player, not the value it was assigned.

Lots of code, but I hope it helps to give a better understanding of what I'm trying to do and will help better direct advice. Again, thanks in advance.

Upvotes: 1

Views: 187

Answers (1)

knut
knut

Reputation: 27845

The comment in

hit = "@#{string}" #returns either @player or @dealer

is not correct. The result is not @player or @dealer, it is the String '@player' or '@dealer'.

You may do something like:

def value_creator( variant )
  case variant 
    when :player
      hit = @player
    when :dealer
      hit = @dealer
    else
      raise ArgumentError
    end
    #....
end
def hit_dealer
  @hit_card = @shuffled_deck.pop
  value_creator(:dealer)
end

def hit_player
  @hit_card = @shuffled_deck.pop
  value_creator(:player)
end

Alternative: Use instance_variable_get to get the value.

  hit = instance_variable_get("@#{string}")

Two other possibilities:

  hit = eval("@#{string}")

(I don't like it - eval is evil).

And with send.

You have to define two accessors first:

attr_reader :player
attr_reader :dealer

Then you can use:

def value_creator( string )
  hit = send( string )

Upvotes: 1

Related Questions