Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@
],
"status": "wip"
},
{
"slug": "blackjack",
"name": "Blackjack",
"uuid": "f76b5124-6627-47a2-b39b-acec27c0a67f",
"concepts": [
"switch"
],
"prerequisites": [
"strings"
],
"status": "wip"
},
{
"slug": "mixed-juices",
"name": "Mixed Juices",
Expand Down
15 changes: 15 additions & 0 deletions exercises/concept/blackjack/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Hints

## 1. Calculate the value of any given card

- An old-style [`switch`][ref-switch] works well here, as it only needs to handle a single card on each call.
- Using [`case_match`][ref-case_match] is another option, but quotes around strings cannot be omitted in this case.

## 2. Implement the decision logic for the first turn

- [`case_when`][ref-case_when] is helpful in this task.
- The task can be solved in base R using a series of conditionals, but less elegantly.

[ref-switch]: https://stat.ethz.ch/R-manual/R-devel/library/base/html/switch.html
[ref-case_match]: https://dplyr.tidyverse.org/reference/case_match.html
[ref-case_when]: https://dplyr.tidyverse.org/reference/case_when.html
57 changes: 57 additions & 0 deletions exercises/concept/blackjack/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Instructions

In this exercise we will simulate the first turn of a [Blackjack](https:#en.wikipedia.org/wiki/Blackjack) game.

You will receive two cards and will be able to see the face up card of the dealer. All cards are represented using a string such as "ace", "king", "three", "two", etc. The values of each card are:

| card | value | card | value |
| :---: | :---: | :-----: | :---: |
| ace | 11 | eight | 8 |
| two | 2 | nine | 9 |
| three | 3 | ten | 10 |
| four | 4 | jack | 10 |
| five | 5 | queen | 10 |
| six | 6 | king | 10 |
| seven | 7 | *other* | 0 |

**Note**: Commonly, aces can take the value of 1 or 11 but for simplicity we will assume that they can only take the value of 11.

Depending on your two cards and the card of the dealer, there is a strategy for the first turn of the game, in which you have the following options:

- Stand (S)
- Hit (H)
- Split (P)
- Automatically win (W)

Although not optimal yet, you will follow the strategy your friend Alex has been developing, which is as follows:

- If you have a pair of aces you must always split them.
- If you have a Blackjack (two cards that sum up to a value of 21), and the dealer does not have an ace, a figure or a ten then you automatically win. If the dealer does have any of those cards then you'll have to stand and wait for the reveal of the other card.
- If your cards sum up to a value within the range [17, 20] you should always stand.
- If your cards sum up to a value within the range [12, 16] you should always stand unless the dealer has a 7 or higher, in which case you should always hit.
- If your cards sum up to 11 or lower you should always hit.

## 1. Calculate the value of any given card.

Implement a function `parse_card` to calculate the numerical value of a card:

```R
parse_card("ace")
# => 11
```

## 2. Implement the decision logic for the first turn.

Write a function `first_turn` that implements the decision logic as described above:

```R
first_turn(card1, card2, dealer_card)
```

Here are some examples for the expected outcomes:

```R
first_turn("ace", "ace", "jack") == "P"
first_turn("ace", "king", "ace") == "S"
first_turn("five", "queen", "ace") == "H"
```
83 changes: 83 additions & 0 deletions exercises/concept/blackjack/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Introduction

The `switch()` function can be a concise replacement for a long series of `if` ... `else if` tests.
The variable being switched on is most commonly a string, and if so the quotes can be omitted from the selector.

```R
star <- function(type) {
switch(type,
M = , # will "fall through" if no value given
K = "red dwarf",
G = "Earth-like",
"bigger star" # only correct for O,B,A,F
)
}

> star("M")
[1] "red dwarf"
```

Note that options will only fall through if the value is left blank, as with `M` in the example above.
There is no need to include `break` statements as with some other languages.

When switching on `character` types, the final value is a default, as here.
With integer types, no default can be given.

## Functions in `dplyr`

The `switch` in base R is quite limited: it will only do an exact match to a single input.
This seemed reasonable when R was first released in 1993, but needs improvement for modern usage.

~~~~exercism/note
As mentioned in the [`Conditionals`][conditionals] Concept, the `tidyverse` collection of packages is designed to supplement (and sometimes replace) base R functionality without impacting backwards compatibility.

The `dplyr` package can be brought into scope by adding either `library(dplyr)` (for the single package) or `library(tidyverse)` (for the whole collection) to the top of your code.

[concept-conditionals]: https://exercism.org/tracks/r/concepts/conditionals
~~~~

The `dplyr` library provides two extra functions related to `switch`.

### The `case_match` function

`case_match` is essentially a vectorized version of `switch`, with some extra options.

Matching is still exact, but:

- The options on the left can be vectors, and the input matches if any element matches.
- Options on the left and results on the right are linked with a tilde `~` instead of `=`.
- A default can be specified in all cases.

```R
library(dplyr)

x <- c("a", "b", "a", "d", "b", "c", "e")
case_match(
x,
"a" ~ 1,
"b" ~ 2,
"c" ~ 3,
c("d", "e") ~ 4, # either "d' or "e" will match
.default = 100 # note the different syntax for the default
)
#> [1] 1 2 1 4 2 3 4
```

### The `case_when` function

`case_when` takes `case_match` syntax a stage further, by allowing any logical expression on the left of the `~`.

The input vector (`x` in the example below) is not supplied as an argument, it just needs to be already defined.

```R
x <- 1:10
case_when(
x < 3 ~ "low",
between(x, 3, 5) ~ "mid",
between(x, 6, 8) ~ "high",
.default = "what?"
)
#> [1] "low" "low" "mid" "mid" "mid" "high" "high" "high" "what?" "what?"
```

The `between()` function is also part of `dplyr`.
20 changes: 20 additions & 0 deletions exercises/concept/blackjack/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"colinleach"
],
"files": {
"solution": [
"blackjack.R"
],
"test": [
"test_blackjack.R"
],
"exemplar": [
".meta/exemplar.R"
]
},
"forked_from": [
"go/blackjack"
],
"blurb": "Learn about switch and conditionals by playing blackjack"
}
27 changes: 27 additions & 0 deletions exercises/concept/blackjack/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Design

## Goal

The goal of this exercise is to teach the student the use of `switch()` in base R, and the `dplyr` functions `case_match()` and `case_when()`.

## Learning objectives

- Know the syntax of a `switch()`.
- Know that this is most often used with string values, where the quotes can be omitted.
- Know that a default is possible when switching on strings, but this is always `NULL` when switching on integers.
- Know that `case_match()` is a vectorized form of switch, with somewhat different syntax.
- Know that `case_when()` allows much more versatile pattern matching.

## Out of scope

Use of `case_when()` with string functions and regular expressions is introduced briefly in the About, but is not part of the exercise (perhaps unfortunately).

## Concepts

The Concepts this exercise unlocks are:

- `loops`

## Prerequisites

- `strings`
65 changes: 65 additions & 0 deletions exercises/concept/blackjack/.meta/exemplar.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
library(dplyr)

# Old version

# parse_card <- function(card) {
# switch(card,
# ace = 11,
# two = 2,
# three = 3,
# four = 4,
# five = 5,
# six = 6,
# seven = 7,
# eight = 8,
# nine = 9,
# ten = ,
# jack = ,
# queen = ,
# king = 10,
# 0
# )
# }

# first_turn <- function(card1, card2, dealer_card) {
# hand_score <- parse_card(card1) + parse_card(card2)
# dealer_score <- parse_card((dealer_card))
# if (hand_score == 22) {
# return("P")
# }
# if (hand_score == 21) {
# return(ifelse(dealer_score < 10, "W", "S"))
# }
# if (hand_score >= 17 || (hand_score >= 12 && dealer_score < 7)) {
# return("S")
# }
# "H"
# }

parse_card <- function(card) {
case_match(
card,
"ace" ~ 11,
"two" ~ 2,
"three" ~ 3,
"four" ~ 4,
"five" ~ 5,
"six" ~ 6,
"seven" ~ 7,
"eight" ~ 8,
"nine" ~ 9,
c("ten", "jack", "queen", "king") ~ 10,
.default = 0
)
}

first_turn <- function(card1, card2, dealer_card) {
hand_score <- parse_card(card1) + parse_card(card2)
dealer_score <- parse_card((dealer_card))
case_when(
hand_score == 22 ~ "P",
hand_score == 21 ~ if_else(dealer_score < 10, "W", "S"),
hand_score >= 17 || (hand_score >= 12 && dealer_score < 7) ~ "S",
.default = "H"
)
}
5 changes: 5 additions & 0 deletions exercises/concept/blackjack/blackjack.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
parse_card <- function(card) {
}

first_turn <- function(card1, card2, dealer_card) {
}
Loading