轨道
/
Ruby
Ruby
/
练习
/
二十一点
二十一点

二十一点

学习练习

简介

Case(在其他语言中常称为 switch)是一种类似 if-else 的控制表达式。 Case 能串联多个 if-else-if 语句,在保持流程控制的同时往往更易读。

一个 case 由关键字 case 定义,后面可选地跟一个表达式。 接下来,每个分支用关键字 when,后面跟一个表达式,它会与 case 表达式进行比较。 when 关键字不应相对于 case 关键字缩进。 when 关键字之后,是当 case 表达式与 when 表达式匹配时要执行的代码。 Case 允许带一个可选的 else 语句,当没有其他分支匹配时执行。

先计算 case 表达式,然后与每个 when 表达式比较。 比较使用的是 case 相等运算符(===)。

value = 1
case value
when 1
  "One"
when 2
  "Two"
else
  "Other"
end

# This is the same as:
value = 1
if 1 === value
  "One"
elsif 2 === value
  "Two"
else
  "Other"
end

case 相等运算符(===)

case 相等运算符(===)与相等运算符(==)略有不同。 它会检查右侧是否为左侧所描述的集合的成员。 也就是说,每个操作数放在哪一侧是有影响的。 具体如何工作取决于左侧的类型,例如 Range 会检查右侧是否在范围内,而 Object 会检查右侧是否为 Object 的实例。

(1..3) == 1  # => false
(1..3) === 1 # => true

String == "foo"  # => false
String === "foo" # => true

带多个表达式的 case

case 允许在同一个分支里匹配多个表达式,各个可能的值用逗号分隔。 只要其中任意一个表达式匹配,就会执行对应的代码。 当你希望同一个分支有多个可能的值时,这很有用。

case var
when 1, 2
  "One or two"
else
  "Other"
end

使用范围的 case

case 也可以检查某个值是否在一个范围内。 只需把范围作为 when 表达式即可。

case var
when 1..3
  puts "One to three"
else
  puts "Other"
end

没有 case 表达式的情况

如果不需要 case 表达式,可以把它省略。 这样一来,每个分支的表达式都会按真假值来判断。 使其行为与 if-else-if 语句相同。

case
when 1 == 1
  "One is equal to one"
when 1 > 2
  "One is greater than two"
else
  "Other"
end

单行 when

Ruby 允许使用单行 case 语句。 当语句很简单、只有一行时,就可以这样做。 单行 when 语句写作 when <expression> then <statement>。 用在 else 中时,写作 else <statement>。

case var
when 1 then "One"
when 2 then "Two"
else "Other"
end

使用类型的 case

case 可以按类型进行匹配。 当你想根据变量的类型做出不同行为时,这很有用。

case var
when Integer
  "Integer"
when String
  "String"
else
  "Other"
end

说明

在这个练习中,我们将模拟二十一点游戏的第一个回合。

1. 计算任意一张牌的点数

你会拿到两张牌,并且能看到庄家亮出的那张明牌。 所有牌都用字符串表示,例如 “ace”、“king”、“three”、“two” 等。每张牌的点数如下:

牌 点数 牌 点数
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 其他 0
Note

通常,A 既可以算作 1,也可以算作 11,但为了简单起见,我们假设它只能算作 11。

实现 Blackjack.parse_card 方法,它接收一个字符串形式的牌名作为实参。 该方法应以整数形式返回这张牌的点数。

Blackjack.parse_card("ace")
# => 11

2. 为点数范围命名

玩家的得分需要划分到不同的点数范围内。 玩家得分由两张手牌的点数相加得到。 使用的范围如下:

范围 点数
low [4, 11]
mid [12, 16]
high [17, 20]
blackjack [21]

实现 Blackjack.card_range 方法,它接收两个字符串形式的牌名作为实参。 该方法应返回这两张牌所属点数范围的名称。

Blackjack.card_range("ten", "king")
# => "high"

3. 实现第一回合的决策逻辑

根据你的两张牌和庄家的明牌,游戏的第一回合有一套策略,你可以做出以下几种选择:

  • 停牌(S)
  • 要牌(H)
  • 分牌(P)
  • 自动获胜(W)

虽然这套策略还不是最优的,但你会采用你的朋友 Alex 一直在完善的策略,具体如下:

  • 如果你拿到一对 A,就必须分牌。
  • 如果你拿到二十一点(两张牌的点数之和为 21),而庄家没有 A、花牌或 10,你就自动获胜。 如果庄家确实有这些牌中的任意一张,你就只能停牌,等待庄家亮出另一张牌。
  • 如果你的两张牌之和在 [17, 20] 范围内,就应该总是停牌。
  • 如果你的两张牌之和在 [12, 16] 范围内,就应该总是停牌,除非庄家的牌是 7 或更大,这时就应该要牌。
  • 如果你的两张牌之和为 11 或更低,就应该要牌。

实现 Blackjack.first_turn 方法,它接收三个字符串形式的牌名作为实参。 该方法应以字符串形式返回你应该采取的决策。

Blackjack.first_turn("ace", "ace", "two")
# => "P"
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Ruby Exercism

准备好开始 二十一点 了吗?

注册 Exercism,借助 21 个概念121 个练习 和真人导师指导,学习并掌握 Ruby,全部免费。