Black Jack

Black Jack

Ejercicio de aprendizaje

Introducción

Comparaciones

Python admite los siguientes operadores de comparación básicos:

Operador Operación Descripción
> «mayor que» a > b es True si a es estrictamente mayor en valor que b
< «menor que» a < b es True si a es estrictamente menor en valor que b
== «igual a» a == b es True si a es estrictamente igual a b en valor
>= «mayor o igual que» a >= b es True si a > b O a == b en valor
<= «menor o igual que» a <= b es True si a < b o a == b en valor
!= «distinto de» a != b es True si a == b es False
is «identidad» a is b es True si y solo si a y b son el mismo objeto
is not «identidad negada» a is not b es True si a y b no son el mismo objeto
in «prueba de pertenencia» a in b es True si a es miembro, subconjunto o elemento de b
not in «prueba de pertenencia negada» a not in b es True si a no es miembro, subconjunto o elemento de b

Todos tienen la misma prioridad (que es mayor que la de las operaciones booleanas, pero menor que la de las operaciones aritméticas o de bits).

Comparación entre distintos tipos de datos

Los objetos de tipos distintos (excepto los tipos numéricos) nunca se comparan como iguales de forma predeterminada. Las instancias no idénticas de una class tampoco se compararán como iguales, a menos que la class defina métodos especiales de comparación enriquecida que personalicen el comportamiento de comparación predeterminado de object. La personalización mediante rich comparisons se tratará en un ejercicio posterior. Para obtener (mucho) más detalle sobre este tema, consulta comparaciones de valores en la documentación de Python.

Los tipos numéricos son (en su mayoría) una excepción a esta regla de coincidencia de tipos. Un integer puede considerarse igual a un float (o un octal igual a un hexadecimal), siempre que los tipos se puedan convertir implícitamente para la comparación.

Para los demás tipos numéricos de la biblioteca estándar de Python (complex, decimal, fractions), los operadores de comparación se definen donde «tienen sentido» (donde la conversión implícita no cambia el resultado), pero lanzan un TypeError si los objetos subyacentes no se pueden convertir con precisión para la comparación. Para más información sobre las reglas que Python usa para la conversión numérica, consulta conversiones aritméticas en la documentación de Python.

>>> import fractions

# A string cannot be converted to an int.
>>> 17 == '17'
False

# An int can be converted to float for comparison.
>>> 17 == 17.0
True

# The fraction 6/3 can be converted to the int 2
# The int 2 can be converted to 0b10 in binary.
>>> 6/3 == 0b10
True

# An int can be converted to a complex number with a 0 imaginary part.
>>> 17 == complex(17)
True

# The fraction 2/5 can be converted to the float 0.4
>>> 0.4 == 2/5
True

>>> complex(2/5, 1/2) == complex(0.4, 0.5)
True

Cualquier comparación ordenada de un número con un tipo NaN (not a number) es False. Un efecto secundario confuso de la definición de NaN de Python es que NaN nunca se compara como igual a NaN.

>>> x = float('NaN')

>>> 3 < x
False

>>> x < 3
False

# NaN never compares equal to NaN
>>> x == x
False

Comparación de strings

A diferencia de los números, los strings (str) se comparan lexicográficamente, usando sus puntos de código Unicode individuales (el resultado de pasar cada punto de código del str a la función integrada ord(), que devuelve un int). Si todos los puntos de código de ambos strings coinciden y están en el mismo orden, los dos strings se consideran iguales. Esta comparación se hace por pares: el primero con el primero, el segundo con el segundo, etc. En Python 3.x, str y bytes no se pueden convertir ni comparar directamente.

>>> 'Python' > 'Rust'
False

>>> 'Python' > 'JavaScript'
True

# Examples with Mandarin.
# hello < goodbye
>>> '你好' < '再见'
True

# ord() of first characters
>>> ord('你'), ord('再')
(20320, 20877)

# ord() of second characters
>>> ord('好'), ord('见')
(22909, 35265)

# And with Korean words.
# Pretty < beautiful.
>>> '예쁜' < '아름다운'
False

>>> ord('예'), ord('아')
(50696, 50500)

Encadenamiento de comparaciones

Los operadores de comparación se pueden encadenar de forma arbitraria, lo que significa que se pueden usar en cualquier combinación y de cualquier longitud. Ten en cuenta que la evaluación de una expresión se realiza de left a right.

Por ejemplo, x < y <= z es equivalente a x < y and y <= z, salvo que y se evalúa solo una vez. En ambos casos, z no se evalúa en absoluto cuando se determina que x < y es False. A esto se le suele llamar short-circuit evaluation: la evaluación se detiene si el valor de verdad de la expresión ya se ha determinado.

El Short circuiting es compatible con varios operadores booleanos, funciones y también con el encadenamiento de comparaciones en Python. A diferencia de muchos otros lenguajes de programación, incluidos C, C++, C# y Java, las expresiones encadenadas como a < b < c en Python tienen una interpretación matemática y una precedencia convencionales.

>>> x = 2
>>> y = 5
>>> z = 10

>>> x < y < z
True

>>> x < y > z
False

>>> x > y < z
False

Comparación de la identidad de objetos

Los operadores is y is not comprueban la identidad de un objeto, en lugar de su valor. La identidad de un objeto nunca cambia después de crearlo y se puede obtener usando la función id().

<apple> is <orange> se evalúa como True si y solo si id(<apple>) == id(<orange>). <apple> is not <orange> da el resultado inverso.

Debido a su condición de singleton, None y NotImplemented siempre se deben comparar con otros elementos usando is y is not. Consulta la documentación de referencia de Python sobre comparaciones de valores y PEP8 para más detalles sobre esta convención.

>>> my_fav_numbers = [1, 2, 3]

>>> your_fav_numbers = my_fav_numbers

>>> my_fav_numbers is your_fav_numbers
True

# The returned id will differ by system and python version.
>>> id(my_fav_numbers)
4517478208

# your_fav_numbers is only an alias pointing to the original my_fav_numbers object.
# Assigning a new name does not create a new object.
>>> id(your_fav_numbers)
4517478208


>>> my_fav_numbers is not your_fav_numbers
False

>>> my_fav_numbers is not None
True

>>> my_fav_numbers is NotImplemented
False

Comparaciones de pertenencia

Los operadores in y not in comprueban la pertenencia. <fish> in <soup> se evalúa como True si <fish> es un miembro de <soup> (si <fish> es un subconjunto de <soup> o está contenido en <soup>), y se evalúa como False en caso contrario. <fish> not in <soup> devuelve la negación, o el opuesto de <fish> in <soup>.

Para los tipos string y bytes, <name> in <fullname> es True si y solo si <name> es una subcadena de <fullname>.

# A set of lucky numbers.
>>> lucky_numbers = {11, 22, 33}
>>> 22 in lucky_numbers
True

>>> 44 in lucky_numbers
False

# A dictionary of employee information.
>>> employee = {'name': 'John Doe', 
                'id': 67826, 'age': 33, 
                'title': 'ceo'}

# Checking for the membership of certain keys.
>>> 'age' in employee
True

>>> 33 in employee
False

>>> 'lastname' not in employee
True

# Checking for substring membership
>>> name = 'Super Batman'
>>> 'Bat' in name
True

>>> 'Batwoman' in name
False

Instrucciones

En este ejercicio vas a implementar algunas reglas del Blackjack, como la forma en que se juega y se puntúa.

Nota: En este ejercicio, A significa as, J significa jota, Q significa reina y K significa rey. Los comodines se descartan. Se asume una baraja estándar de 52 cartas con palos franceses, pero en la mayoría de las versiones se mezclan varias barajas juntas para jugar.

1. Calcula el valor de una carta

En el Blackjack, depende de cada jugador si un as vale 1 u 11 puntos (hablaremos más de eso después). Las figuras (J, Q, K) valen 10 puntos y cualquier otra carta vale su valor «pip» (numérico).

Define la función value_of_card(<card>) con el parámetro card. La función debe devolver el valor numérico del string de carta que se le pasa. Como un as puede tomar varios valores (1 o 11), por ahora esta función debe fijar el valor de una carta de as en 1. Más adelante implementarás una función para determinar el valor de una carta de as, dada una mano existente.

>>> value_of_card('K')
10

>>> value_of_card('4')
4

>>> value_of_card('A')
1

2. Determina qué carta tiene un valor más alto

Define la función higher_card(<card_one>, <card_two>) con los parámetros card_one y card_two. Para fines de puntuación, el valor de J, Q o K es 10. La función debe devolver cuál carta tiene el valor más alto para la puntuación. Si ambas cartas tienen el mismo valor, devuelve las dos. Puedes devolver ambas cartas usando una coma en la sentencia return:

# Using a comma in a return creates a Tuple.  Tuples will be covered in a later exercise.
>>> def returning_two_values(value_one, value_two):
        return value_one, value_two

>>> returning_two_values('K', '3')
('K', '3')

Un as puede tomar varios valores, así que para esta tarea fijaremos las cartas A en un valor de 1.

>>> higher_card('K', '10')
('K', '10')

>>> higher_card('4', '6')
'6'

>>> higher_card('K', 'A')
'K'

3. Calcula el valor de un as

Como se mencionó antes, un as puede valer ya sea 1 o 11 puntos. Los jugadores intentan acercarse lo más posible a una puntuación de 21, sin pasarse de 21 (lo que se conoce como «bust»).

Define la función value_of_ace(<card_one>, <card_two>) con los parámetros card_one y card_two, que son un par de cartas que ya están en la mano antes de recibir una carta de as. Tu función tendrá que decidir si el próximo as valdrá 1 o 11, y devolver ese valor. Recuerda: el valor de la mano con el as debe ser lo más alto posible sin pasarse de 21.

Pista: si ya tenemos un as en la mano, el valor del próximo as sería 1.

>>> value_of_ace('6', 'K')
1

>>> value_of_ace('7', '3')
11

4. Determina una mano «natural» o «blackjack»

Si a un jugador le reparten un as (A) y una carta de diez (10, K, Q o J) como sus dos primeras cartas, entonces el jugador tiene una puntuación de 21. Esto se conoce como una mano de blackjack.

Define la función is_blackjack(<card_one>, <card_two>) con los parámetros card_one y card_two, que son un par de cartas. Determina si la mano de dos cartas es un blackjack y devuelve el valor booleano True si lo es, o False en caso contrario.

Nota: El cálculo de la puntuación se puede hacer de muchas maneras. Pero si es posible, nos gustaría que comprobaras si hay un as y una carta de diez en la mano (o en una posición determinada), en lugar de sumar los valores de la mano.

>>> is_blackjack('A', 'K')
True

>>> is_blackjack('10', '9')
False

5. Dividir pares

Si las dos primeras cartas de una mano tienen el mismo valor (por ejemplo, dos seises o una Q y una K), un jugador puede elegir tratarlas como dos manos separadas. Esto se conoce como «dividir pares».

Define la función can_split_pairs(<card_one>, <card_two>) con los parámetros card_one y card_two, que son un par de cartas. Determina si esta mano de dos cartas se puede dividir en dos pares. Si la mano se puede dividir, devuelve el valor booleano True; en caso contrario, devuelve False

>>> can_split_pairs('Q', 'K')
True

>>> can_split_pairs('10', 'A')
False

6. Doblar la apuesta

Cuando las dos cartas originales repartidas suman 9, 10 u 11 puntos, un jugador puede hacer una apuesta adicional igual a su apuesta original. Esto se conoce como «doblar la apuesta».

Define la función can_double_down(<card_one>, <card_two>) con los parámetros card_one y card_two, que son un par de cartas. Determina si en esta mano de dos cartas se puede «doblar la apuesta» y devuelve el valor booleano True si se puede, o False en caso contrario.

>>> can_double_down('A', '9')
True

>>> can_double_down('10', '2')
False
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Python Exercism

¿Todo listo para empezar Black Jack?

Regístrate en Exercism para aprender y dominar Python con 17 conceptos146 ejercicios y mentoría humana real, todo gratis.