Title: Can I use comparison between two variables inside a Ruby case statement?
Tags: ruby
Question: I'm trying out this code but getting nil everytime (It's a simplyfied version of a Black Jack game):
Is it because I'm comparing two variables in a case statement?

```def end_game_message(player_score, bank_score)
  message = ""
  case player_score
  when player_score == 21
    message = "Black Jack!"
  when player_score &gt; bank_score
    message = "You win!"
  when player_score &gt; 21
    message = "You lose!"
  when player_score < bank_score
    message = "You lose"
  when player_score == bank_score
    message = "Push"
  end
  message
end

puts end_game_message(21, 15)
```

Thanks in advance for any help!

Comment: @NicolasPoletti : While the answer of Masafumi Okura is correct, it does not explain exactly why your approach failed: You `when` statements all evaluate to either `true` or `false`, and the expression after `case` is an integer value. Since an integer never matches either true or false, none of the `when` clauses succeed. Since you have provided no `else` clause, your `case` statement has no effect. In particular, it does not reassign `message`. Hence, since you return `message`, which is initially set to `""`, the function will return the null string, and not `nil`, as JörgWMittag said.

Comment: Where, exactly, are you getting `nil`? I don't see how this code can produce `nil`. (Well, the return value of `puts` is `nil`, but it is *always* `nil`, so that shouldn't be surprising.)

Here is the accepted answer: IMO, you should use ```if``` instead of ```case``` in this case. If you'd like to use ```case```, however, the code might look like this:

```def end_game_message(player_score, bank_score)
  case player_score
  when 21
    "Black Jack!"
  when -&gt; s { s &gt; bank_score }
    "You win!"
  when -&gt; s { s &gt; 21 }
    "You lose!"
  when -&gt; s { s < bank_score }
    "You lose"
  when -&gt; s { s == bank_score }
    "Push"
  end
end

puts end_game_message(21, 15)
```

The key point is to give ```Proc``` object to ```when``` clause. In this case, ```s``` is ```player_score``` (value given to ```case``` clause`).

(And minor improvement: ```case``` statement returns value so you don't have to assign a message to local variable)

Comment for this answer: Or use the `case\nwhen player_score == 21 ...` form (i.e. just drop the `player_score` from `case player_score`. Also worth mentioning that your proc-version works because the `case expr` form uses the `===` operator to "compare" `expr` with each of the `when`s and [evaluates the proc with `expr` as its argument](https://ruby-doc.org/core/Proc.html#method-i-3D-3D-3D).
Comment for this answer: This concludes the player loses if the player's score is no more than 21 and the dealer's score is greater than 21. Also,  `when -> s { s > 21 }` must precede `when -> s { s > bank_score }`.

Here is another answer: ```def end_game_message(player_score, bank_score)
  case player_score
  when 21
    bank_score == 21 ? "Push" : "Black Jack!"
  when (22..)
    "You lose!"
  when (..bank_score-1)
    bank_score <= 21 ? "You lose!" : "You win"
  when (bank_score+1..) 
    "You win!"
  else
    "Push"
  end
end
```



```end_game_message(21,18)  #=&gt; "Black Jack!" 
end_game_message(21,21)  #=&gt; "Push" 
end_game_message(22,22)  #=&gt; "You lose!" 
end_game_message(12,22)  #=&gt; "You win!" 
end_game_message(18,19)  #=&gt; "You lose!" 
end_game_message(19,18)  #=&gt; "You win!" 
end_game_message(18,18)  #=&gt; "Push" 
```

```(..bank_score-1)``` and ```(bank_score+1..)``` are beginless and endless ranges, introduced in Ruby v2.7. They are not essential, of course, as they could be replaced with ```(0..bank_score-1)``` and ```(bank_score+1..30)```. ```(...bank_score)``` could be used in place of ```(..bank_score-1)``` but my own style guide states that three-dot ranges are to be avoided except where the end of an infinite range is to be excluded. 


Here is another answer: There are actually several different questions in your question.

The first is in the title:

```
  Can I use comparison between two variables inside a Ruby case statement?
```

And the answer to that is "Yes, of course, why wouldn't you?" You can use any Ruby expression.

Okay, well, if we want to be really pedantic, the answer is "No", for two reasons:


You cannot compare variables, you can only compare objects, and variables aren't objects.
Ruby doesn't have a ```case``` statement, only a ```case``` expression. (In fact, Ruby doesn't have jaredmartinez@example.com.)


(This may sound overly pedantic, but understanding the difference between variables and objects is fundamental not only in Ruby, but in programming in general, as is the difference between expressions and statements.)

Your next question is, why you get ```nil```. The answer to that is that the last expression in your code is

```puts end_game_message(21, 15)
```

So, your code evaluates to the return value of ```Kernel#puts```, and ```Kernel#puts``` is defined to always return ```nil```.

The question that I think you are really asking but isn't actually in your question, is "Why does the ```case``` expression not work how I think it does?"

You simply need to remember how the ```case``` expression is evaluated. There are two different forms of the ```case``` expression. (See section 51.243.225.55.4 The ```case``` expression) of the ISO Ruby Language Specification, for example.

The simpler form is what the ISO Ruby Language Specification calls the ```case```-expression-without-expression, which looks like this:

```case               # Look ma, no expression
when bar then :bar
when baz then :baz
else          :qux
end
```

And is evaluated like this:

```if    bar then :bar
elsif baz then :baz
else           :qux
end
```

The other form is the ```case```-expression-with-expression

```case foo           # In this case, there is an expression here
when bar then :bar
when baz then :baz
else          :qux
end
```

Which is evaluated like this:

```if    bar === foo then :bar
elsif baz === foo then :baz
else                   :qux
end
```

In your case, you are using the ```case```-expression-with-expression, so your ```case``` expression is evaluated like this:

```if    (player_score == 21)         === player_score
  message = "Black Jack!"
elsif (player_score &gt; bank_score)  === player_score
  message = "You win!"
elsif (player_score &gt; 21)          === player_score
  message = "You lose!"
elsif (player_score < bank_score)  === player_score
  message = "You lose"
elsif (player_score == bank_score) === player_score
  message = "Push"
end
```

Since ```player_score == 21```, ```player_score &gt; bank_score```, ```player_score &gt; 21```, ```player_score < bank_score```, and ```player_score == bank_score``` all evaluate to either ```true``` or ```false``` and ```player_score``` is a number, all the branches in your ```case``` expression are actually checking something like this:

```some_boolean === some_number
```

Which is never true!

So, how do we fix this?

The simplest fix would be to use the ```case```-expression-without-expression instead, and to do that, the only thing we need to do, is delete the expression after the ```case```:

```case
when player_score == 21
  message = "Black Jack!"
when player_score &gt; bank_score
  message = "You win!"
when player_score &gt; 21
  message = "You lose!"
when player_score < bank_score
  message = "You lose"
when player_score == bank_score
  message = "Push"
end
```

This will be evaluated like this:

```if    player_score == 21
  message = "Black Jack!"
elsif player_score &gt; bank_score
  message = "You win!"
elsif player_score &gt; 21
  message = "You lose!"
elsif player_score < bank_score
  message = "You lose"
elsif player_score == bank_score
  message = "Push"
end
```

Which will do what you want.

If you want to keep the ```case```-expression-with-expression, you need to make sure that the ```when```-argument of each ```when```-clause is something that responds to ```===``` in a way that makes sense for your comparison.

We could, for example, use ```Range```s and ```Integer```s. The ```Range#===``` method checks whether the argument is inside the ```Range``` and the ```Integer#===``` method checks whether the argument is numerically equal, so we could re-write the ```case``` expression like this:

```case player_score
when 21
  message = "Black Jack!"
when ((bank_score + 1)..)
  message = "You win!"
when (22..)
  message = "You lose!"
when ...bank_score
  message = "You lose"
when bank_score
  message = "Push"
end
```

Since the cases are evaluated top to bottom and you can evaluate multiple conditions in the same case, we can re-shuffle these a bit to make them nicer to read:

```case player_score
when 21
  message = "Black Jack!"
when bank_score
  message = "Push"
when 21.., ...bank_score
  message = "You lose!"
when (bank_score..)
  message = "You win!"
end
```

Also, remember that the ```case``` expression is an expression, meaning that it evaluates to a value, namely it evaluates to the value of the branch that was evaluated. Therefore, we can "pull out" the assignment to ```message```:

```message = case player_score
when 21
  "Black Jack!"
when bank_score
  "Push"
when 21.., ...bank_score
  "You lose!"
when (bank_score..)
  "You win!"
end
```

Note that this has slightly different semantics than what we had before: Before, the assignment was only evaluated when one of the branches was evaluated, otherwise, the value of ```message``` stayed what it was before. Whereas in this form, the assignment is always evaluated, and if none of the branches gets evaluated, the ```case``` expression evaluates to ```nil```.

However, the way the ```case``` expression is written, all cases are covered, so there will always be a non-```nil``` value assigned.

Once we have made this transformation, we notice that at the beginning of the method, message is assigned the empty string ```""``` and then immediately gets assigned a different value. So, we can get rid of the first assignment, since its effects are immediately overwritten anyway.

And once we have done that, we see that we assign to the ```message``` variable and then immediately return it. So, we might just as well return the value of the ```case``` expression instead, like this:

```def end_game_message(player_score, bank_score)
  case player_score
  when 21
    "Black Jack!"
  when bank_score
    "Push"
  when 21.., ...bank_score
    "You lose!"
  when (bank_score..)
    "You win!"
  end
end
```

Comment for this answer: There’s another case you need to account for: the player’s score  21. Here the player wins but you have the player losing. Dealing with that is non-trivial as you cannot merely compare the two scores. Also, the “Push” case needs to follow the “You lose!” case because the player loses if the scores are equal and greater than 21.
Comment for this answer: Thanks. I was indeed too aggressive in refactoring / simplifying.
Comment for this answer: @CarySwoveland: If only questions came with test suites :-D

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 3, 'CommentCount': 2, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2020:00:04/17/20', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<ruby>', 'Title': 'Can I use comparison between two variables inside a Ruby case statement?', 'ViewCount': 423, 'id': 61263475, 'user_AboutMe': '', 'user_AccountId': 17726084.0, 'user_DisplayName': 'Nicolas Poletti', 'user_Reputation': 35.0, 'user_Views': 12.0, 'user_id': 12869764.0}}