Blackjack

Blackjack

Exercício de aprendizagem

Introdução

O case (referido frequentemente como switch noutras linguagens) é uma forma de expressão de controlo, como o if-else. O case permite encadear várias instruções if-else-if e pode ser mais legível, mantendo ainda o controlo do fluxo.

Um case é definido pela palavra-chave case, seguida de uma expressão opcional. Depois, para cada case, usa-se a palavra-chave when, seguida de uma expressão que é comparada com a expressão do case. A palavra-chave when não deve estar indentada em relação à palavra-chave case. A seguir à palavra-chave when vem o código que deve ser executado se a expressão do case corresponder à expressão do when. O case permite uma instrução else opcional, que é executada se nenhum outro case corresponder.

A expressão do case é avaliada e depois comparada com cada expressão do when. A comparação é feita com o operador de igualdade de 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

Operador de igualdade de case (===)

O operador de igualdade de case (===) é um pouco diferente do operador de igualdade (==). O operador verifica se o lado direito é membro do conjunto descrito pelo lado esquerdo. Isto significa que a posição de cada operando é importante. O funcionamento depende do tipo do lado esquerdo: por exemplo, um Range verifica se o lado direito está no intervalo, e um Object verifica se o lado direito é uma instância do Object.

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

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

Case com várias expressões

Os cases permitem corresponder a várias expressões num único case, com cada valor possível separado por uma vírgula. O código é executado se alguma das expressões corresponder. Isto pode ser útil quando queres que um único case tenha vários valores possíveis.

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

Case com intervalos

Os cases também podem verificar se um valor está num intervalo. Para isso, usa-se um intervalo como expressão do when.

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

Case sem expressão

Quando não é precisa uma expressão do case, é possível omiti-la. Assim, cada expressão do case é avaliada quanto à sua veracidade. E faz com que se comportem como instruções if-else-if.

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

When numa só linha

O Ruby permite instruções case numa só linha. Isto pode ser usado quando tens uma instrução simples de uma linha. A instrução when de uma linha escreve-se como when <expression> then <statement>. E, quando usada na instrução else, escreve-se como else <statement>.

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

Case com tipos

O case permite corresponder a tipos. Isto é útil quando queres comportamentos diferentes consoante o tipo de uma variável.

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

Instruções

Neste exercício, vamos simular o primeiro turno de um jogo de Blackjack.

1. Calcula o valor de qualquer carta.

Vais receber duas cartas e vais poder ver a carta virada para cima do crupiê. Todas as cartas são representadas por uma string como "ace", "king", "three", "two", etc. Os valores de cada carta são:

carta valor carta valor
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

Normalmente, os ases podem valer 1 ou 11, mas, para simplificar, vamos assumir que só podem valer 11.

Implementa o método Blackjack.parse_card, que recebe uma carta como string enquanto argumento. O método deve devolver o valor da carta como um número inteiro.

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

2. Dá nome a intervalos de valores.

A pontuação do jogador tem de ser categorizada em intervalos de valores. A pontuação do jogador calcula-se somando os valores das duas cartas do jogador. Os intervalos usados são:

intervalo valor
low [4, 11]
mid [12, 16]
high [17, 20]
blackjack [21]

Implementa o método Blackjack.card_range, que recebe como argumentos duas cartas sob a forma de strings. O método deve devolver o nome do intervalo de valores em que as duas cartas se inserem.

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

3. Implementa a lógica de decisão para o primeiro turno.

Consoante as tuas duas cartas e a carta do crupiê, existe uma estratégia para o primeiro turno do jogo, em que tens as seguintes opções:

  • Ficar (S)
  • Pedir carta (H)
  • Dividir (P)
  • Ganhar automaticamente (W)

Ainda que não seja ótima, vais seguir a estratégia que o teu amigo Alex tem vindo a desenvolver, que é a seguinte:

  • Se tiveres um par de ases, tens sempre de os dividir.
  • Se tiveres um Blackjack (duas cartas que somam um valor de 21), e o crupiê não tiver um ás, uma figura ou um dez, ganhas automaticamente. Se o crupiê tiver alguma dessas cartas, tens de ficar e esperar que a outra carta seja revelada.
  • Se as tuas cartas somarem um valor dentro do intervalo [17, 20], deves ficar sempre.
  • Se as tuas cartas somarem um valor dentro do intervalo [12, 16], deves ficar sempre, a não ser que o crupiê tenha um 7 ou mais, caso em que deves pedir carta sempre.
  • Se as tuas cartas somarem 11 ou menos, deves pedir carta sempre.

Implementa o método Blackjack.first_turn, que recebe como argumentos três cartas sob a forma de strings. O método deve devolver a decisão que deves tomar, sob a forma de uma string.

Blackjack.first_turn("ace", "ace", "two")
# => "P"
Editar via GitHub A ligação abre numa nova janela ou separador
Ruby Exercism

Estás pronto para começar Blackjack?

Inscreve-te no Exercism para aprenderes e dominares Ruby com 21 conceitos121 exercícios, e mentoria humana real, tudo grátis.