1. Antes de comenzar
En los codelabs de esta ruta de aprendizaje, compilarás una app de Dice Roller para Android. Cuando el usuario "lance el dado", se generará un resultado aleatorio. Ese resultado tendrá en cuenta la cantidad de caras que tiene el dado. Por ejemplo, para un dado de 6 caras, solo saldrán valores del 1 al 6.
Así se verá la app final:
Para que puedas concentrarte en los nuevos conceptos de programación relacionados con esta app, usarás la herramienta de programación de Kotlin basada en el navegador para crear la funcionalidad principal de la app. El programa enviará los resultados a la consola. Más adelante, implementarás la interfaz de usuario en Android Studio.
En este primer codelab, crearás un programa en Kotlin que simule un lanzamiento de dados y muestre un número al azar, como ocurriría con un dado real.
Requisitos previos
- Conocimientos para abrir, editar y ejecutar código en https://developer.android.com/training/kotlinplayground
- Habilidad para crear y ejecutar un programa en Kotlin que use variables y funciones, y que imprima un resultado en la consola
- Capacidad de dar formato a números dentro del texto mediante una plantilla de cadenas con la notación
${variable}
Qué aprenderás
- Cómo generar números al azar de manera programática para simular los lanzamientos de dados
- Cómo estructurar tu código mediante la creación de una clase
Dice
con una variable y un método - Cómo crear una instancia de objeto de una clase, modificar sus variables y llamar a sus métodos
Qué compilarás
- Compilarás un programa en la herramienta de programación de Kotlin basada en el navegador que pueda realizar un lanzamiento de dados aleatorio.
Requisitos
- Una computadora con conexión a Internet
2. Lanza números al azar
A menudo, los juegos cuentan con un elemento azaroso. Puedes ganar un premio aleatorio o avanzar una cantidad de posiciones al azar en el tablero de juego. En tu vida cotidiana, puedes usar números y letras al azar para generar contraseñas más seguras.
En lugar de usar dados reales, puedes escribir un programa que simule un lanzamiento de dados para ti. Cada vez que lances el dado, el resultado puede ser cualquier número dentro del rango de valores posibles. Afortunadamente, no tienes que compilar tu propio generador de números al azar para tener un programa de este tipo. La mayoría de los lenguajes de programación, incluido Kotlin, tienen una manera integrada de modo que puedas generar números al azar. En esta tarea, usarás el código de Kotlin para lograrlo.
Configura tu código de partida
- En tu navegador, abre el sitio web https://developer.android.com/training/kotlinplayground.
- En el editor de código, borra todo el código existente y reemplázalo con el que se muestra a continuación. Esta es la función
main()
con la que trabajaste en codelabs anteriores.
fun main() {
}
Usa la función de aleatorización
A los efectos de lanzar un dado, necesitas una forma de representar todos los resultados válidos posibles. Para un dado común de 6 caras, los valores aceptables son 1, 2, 3, 4, 5 y 6.
Con anterioridad, aprendiste que existen tipos de datos, como Int
para números enteros y String
para texto. IntRange
es otro tipo de datos y representa un rango de números enteros desde un valor de partida hasta el último. IntRange
es un tipo de datos adecuado si quieres representar los valores posibles que puede arrojar un lanzamiento de dados.
- Dentro de la función
main()
, define una variable como unval
llamadodiceRange
. Asígnala a unIntRange
del 1 al 6, que represente el rango de números enteros que puede obtenerse cuando lanzas un dado de 6 caras.
val diceRange = 1..6
Podrás ver que 1..6
es un rango en Kotlin porque tiene un número de inicio, dos puntos y un número final (sin espacios entre ellos). Otros ejemplos de rangos de números enteros son 2..5
para los números 2 a 5 y 100..200
para los números 100 a 200.
De manera similar a como la llamada a println()
le indica al sistema que imprima el texto proporcionado, puedes usar una función llamada random()
para generar y mostrar un número al azar perteneciente a un rango determinado. Como antes, puedes almacenar el resultado en una variable.
- Dentro de
main()
, define una variable como unval
llamadorandomNumber
. - Haz que
randomNumber
tenga el valor del resultado de la llamada arandom()
en el rangodiceRange
, como se muestra a continuación.
val randomNumber = diceRange.random()
Observa que llamas a random()
en diceRange
mediante un punto entre la variable y la llamada a función. Puedes leer esto como "generando un número al azar del diceRange
". El resultado se almacenará en la variable randomNumber
.
- Si quieres ver el número generado de manera aleatoria, usa la notación de formato de cadenas (también llamada "plantilla de cadenas")
${randomNumber}
para imprimirlo, como se muestra a continuación.
println("Random number: ${randomNumber}")
El código finalizado debería verse de la siguiente manera:
fun main() {
val diceRange = 1..6
val randomNumber = diceRange.random()
println("Random number: ${randomNumber}")
}
- Ejecuta el código varias veces. Deberías obtener un resultado como el siguiente, con diferentes números al azar cada vez.
Random number: 4
3. Crear una clase Dice
Cuando lanzas dados, estos son objetos reales en tus manos. Si bien el código que acabas de escribir funciona a la perfección, es difícil imaginar que se trata de un dado de verdad. Cuando organizas un programa de modo que se asemeje más a las cosas que representa, resulta más fácil entenderlo. Por eso, sería genial tener dados programáticos que puedas lanzar.
En esencia, todos los dados funcionan de la misma manera. Tienen las mismas propiedades, como tener caras, y tienen el mismo comportamiento, como el hecho de que se pueden lanzar. En Kotlin, puedes crear un plano programático de un dado que establezca que este tiene caras y que, cuando lo lances, generará un número al azar. Este plano se denomina clase.
A partir de esa clase, puedes crear objetos de dados llamados instancias de objeto. Por ejemplo, puedes crear un dado de 12 caras o uno de 4.
Cómo definir una clase Dice
En los siguientes pasos, definirás una nueva clase llamada Dice
para representar un dado que se puede lanzar.
- Para comenzar de cero, borra el código de la función
main()
de modo que tengas el código tal como se muestra a continuación.
fun main() {
}
- Debajo de esta función
main()
, agrega una línea en blanco y, luego, agrega el código para crear la claseDice
. Como se muestra más abajo, comienza con la palabra claveclass
, seguida del nombre de la clase y una llave de apertura y cierre. Si quieres incluir el código correspondiente a la clase, deja espacio entre las llaves.
class Dice {
}
Dentro de una definición de clase, puedes usar variables para especificar una propiedad de la clase o más. Los dados reales tienen una cantidad de caras, un color o un peso. En esta tarea, te concentrarás en la propiedad asociada con la cantidad de caras.
- Dentro de la clase
Dice
, agrega unavar
llamadasides
para la cantidad de caras que tendrá tu dado. Establecesides
en 6.
class Dice {
var sides = 6
}
Se ve así. Ahora tienes una clase muy simple que representa el dado.
Crea una instancia de la clase Dice
Con esta clase Dice
, tienes un plano del dado. Para tener un dado real en tu programa, deberás crear una instancia de objeto Dice
. (Y, si necesitaras tener tres dados, crearías tres instancias de objeto).
- Para crear una instancia de objeto de
Dice
, en la funciónmain()
, crea unval
llamadomyFirstDice
e inicialízalo como una instancia de la claseDice
. Observa los paréntesis después del nombre de la clase, lo que indica que estás creando una instancia de objeto nueva desde la clase.
fun main() {
val myFirstDice = Dice()
}
Ahora que tienes un objeto myFirstDice
, algo creado a partir del plano, puedes acceder a sus propiedades. La única propiedad de Dice
es su sides
. Puedes acceder a una propiedad mediante la "notación de puntos". Por lo tanto, para acceder a la propiedad sides
de myFirstDice
, llama a myFirstDice.sides
, que se pronuncia "myFirstDice
punto sides
".
- Debajo de la declaración de
myFirstDice
, agrega una sentenciaprintln()
para obtener el número desides
demyFirstDice.
println(myFirstDice.sides)
Tu código debería verse de la siguiente manera:
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
}
class Dice {
var sides = 6
}
- Ejecuta el programa. Debería mostrar el número de
sides
definido en la claseDice
.
6
Ahora tienes una clase Dice
y un dado real myFirstDice
con 6 sides
.
¡Lancemos el dado!
Lanza el dado
Anteriormente, usaste una función para realizar la acción de imprimir capas de pastel. Lanzar un dado también es una acción que se puede implementar como una función. Y, como todos los dados se pueden lanzar, puedes agregar una función para ello dentro de la clase Dice
. Una función que se define dentro de una clase también se denomina método.
- En la clase
Dice
, debajo de la variablesides
, inserta una línea en blanco y, luego, crea una nueva función para lanzar el dado. Comienza con la palabra clavefun
en Kotlin, seguida del nombre del método, de los paréntesis()
y, por último, de las llaves de apertura y cierre{}
. Puedes dejar una línea en blanco entre llaves para escribir más código, como se muestra más abajo. Tu clase debería verse así:
class Dice {
var sides = 6
fun roll() {
}
}
Cuando lanzas un dado de seis caras, genera un número al azar entre 1 y 6.
- Dentro del método
roll()
, crea unval randomNumber
. Asígnale un número al azar que pertenezca al rango1..6
. Usa la notación de puntos para llamar arandom()
en el rango.
val randomNumber = (1..6).random()
- Después de generar el número al azar, imprímelo en la consola. El método
roll()
terminado debería tener el siguiente aspecto:
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
- Para implementar
myFirstDice
, enmain()
, llama al métodoroll()
enmyFirstDice
. La forma de llamar a un método es mediante la "notación de puntos". Por lo tanto, para llamar al métodoroll()
demyFirstDice
, escribemyFirstDice.roll()
, que se pronuncia "myFirstDice
puntoroll()
".
myFirstDice.roll()
El código terminado debería verse de la siguiente manera:
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
myFirstDice.roll()
}
class Dice {
var sides = 6
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
}
- Ejecuta tu código. Deberías ver el resultado de un lanzamiento aleatorio del dado debajo del número de caras. Ejecuta el código varias veces y observa que la cantidad de caras siga siendo la misma y que el valor del dado cambie.
6 4
¡Felicitaciones! Definiste una clase Dice
con una variable sides
y una función roll()
. En la función main()
, creaste una nueva instancia de objeto Dice
y, luego, llamaste al método roll()
en ella para producir un número al azar.
4. Muestra el resultado del lanzamiento
Por el momento, imprimes el valor de randomNumber
en tu función roll()
y eso funciona muy bien. Pero, a veces, resulta más útil devolver el resultado de una función a lo que sea que la haya llamado. Por ejemplo, podrías asignar el resultado del método roll()
a una variable y, luego, mover un jugador esa cantidad de posiciones. Veamos cómo hacerlo.
- En
main()
, modifica la línea que dicemyFirstDice.roll()
. Crea unval
llamadodiceRoll
. Establécelo en el valor que muestra el métodoroll()
.
val diceRoll = myFirstDice.roll()
De momento no hará nada, porque roll()
aún no muestra nada. Para que este código funcione según lo previsto, roll()
debe mostrar algo.
En los codelabs anteriores, aprendiste que debes especificar un tipo de datos para los argumentos de entrada de las funciones. De la misma manera, debes especificar un tipo de datos para los datos que muestra una función.
- Cambia la función
roll()
para que especifique el tipo de datos que se mostrará. En este caso, el número al azar es unInt
, por lo que el tipo de datos que se muestra esInt
. La sintaxis a los efectos de especificar dicho tipo de datos es: después del nombre de la función, después de los paréntesis, agrega dos puntos, espacio y la palabra claveInt
correspondiente al tipo de datos de la función. La definición de la función debería ser similar al siguiente código:
fun roll(): Int {
- Ejecuta este código. Verás un error en Problems View. Dice lo siguiente:
A ‘return' expression required in a function with a block body (‘{...}')
Cambiaste la definición de la función para que muestre un objeto Int
, pero el sistema indica que el código no muestra un Int
. "Block body" (cuerpo del bloque) o "function body" (cuerpo de la función) hacen referencia al código que se encuentra entre las llaves de una función. Para corregir este error, puedes mostrar un valor de una función con una sentencia return
al final del cuerpo de la función.
- En
roll()
, quita la sentenciaprintln()
y reemplázala por una sentenciareturn
pararandomNumber
. Tu funciónroll()
debería ser similar al siguiente código:
fun roll(): Int {
val randomNumber = (1..6).random()
return randomNumber
}
- En
main()
, quita la sentencia de impresión de las caras del dado. - Agrega una sentencia para imprimir el valor de
sides
ydiceRoll
en una oración informativa. La funciónmain()
terminada debería tener el aspecto del código que se indica a continuación:
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
- Ejecuta el código. El resultado debería ser similar al siguiente:
Your 6 sided dice rolled 4!
Este es el código que escribiste hasta ahora.
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
class Dice {
var sides = 6
fun roll(): Int {
val randomNumber = (1..6).random()
return randomNumber
}
}
5. Cambia la cantidad de caras de tu dado
No todos los dados tienen 6 caras. Los dados pueden ser de diversas formas y tamaños: 4 caras, 8 caras y hasta 120 caras.
- En tu clase
Dice
, dentro del métodoroll()
, cambia el1..6
hard-coded y, en su lugar, usasides
, de modo que el rango y, por lo tanto, el número obtenido al azar, siempre sean los adecuados para la cantidad de caras.
val randomNumber = (1..sides).random()
- En la función
main()
que aparece a continuación, después de imprimir el resultado del lanzamiento, cambiasides
demyFirstDice
y establécelo en 20.
myFirstDice.sides = 20
- Copia y pega la sentencia de impresión existente que aparece a continuación debajo del lugar donde cambiaste la cantidad de caras.
- Reemplaza la impresión de
diceRoll
por la correspondiente al resultado de llamar al métodoroll()
enmyFirstDice
.
println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
El programa debería verse así:
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
myFirstDice.sides = 20
println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
}
class Dice {
var sides = 6
fun roll(): Int {
val randomNumber = (1..sides).random()
return randomNumber
}
}
- Ejecuta el programa. Deberías ver un mensaje para el dado de 6 caras y un segundo mensaje para el dado de 20 caras.
Your 6 sided dice rolled 3! Your 20 sided dice rolled 15!
6. Personaliza tu dado
La idea de una clase es representar algo, a menudo algo físico en el mundo real. En este caso, una clase Dice
representa un dado físico. En el mundo real, los dados no pueden cambiar su cantidad de caras. Si quieres una cantidad diferente de caras, deberás conseguir un dado diferente. De manera programática, esto significa que, en lugar de cambiar la propiedad de caras de una instancia de objeto Dice
existente, deberás crear una instancia de objeto para el dado nuevo con la cantidad de caras que necesites.
En esta tarea, modificarás la clase Dice
de modo que puedas especificar el número de caras cuando crees una instancia nueva. Cambia la definición de la clase Dice
para proporcionar el número de caras. Esto es similar a la forma en que una función puede aceptar argumentos de entrada.
- Modifica la definición de la clase
Dice
para que acepte un número entero llamadonumSides
. El código de la clase no cambiará.
class Dice(val numSides: Int) {
// Code inside does not change.
}
- Dentro de la clase
Dice
, borra la variablesides
, ya que ahora puedes usarnumSides
. - Además, corrige el rango para usar
numSides
.
Tu clase Dice
debería verse así:
class Dice (val numSides: Int) {
fun roll(): Int {
val randomNumber = (1..numSides).random()
return randomNumber
}
}
Si ejecutas este código, verás muchos errores, porque deberás actualizar main()
de modo que funcione con los cambios en la clase Dice
.
- En
main()
, para crearmyFirstDice
con 6 caras, deberás proporcionar la cantidad de caras como un argumento de la claseDice
, como se muestra a continuación.
val myFirstDice = Dice(6)
- En la sentencia de impresión, cambia
sides
anumSides
. - Debajo de eso, borra el código que cambia
sides
a 20, porque esa variable ya no existe. - Borra también la sentencia
println
que se encuentra debajo.
La función main()
debería ser similar al siguiente código, y si la ejecutas, no debería arrojar errores.
fun main() {
val myFirstDice = Dice(6)
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
- Después de imprimir el primer resultado del lanzamiento de dados, agrega código para crear e imprimir un segundo objeto
Dice
llamadomySecondDice
con 20 caras.
val mySecondDice = Dice(20)
- Agrega una sentencia de impresión que imprima el valor que resulte del lanzamiento.
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
- La función
main()
terminada debería verse de la siguiente manera:
fun main() {
val myFirstDice = Dice(6)
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
val mySecondDice = Dice(20)
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}
class Dice (val numSides: Int) {
fun roll(): Int {
val randomNumber = (1..numSides).random()
return randomNumber
}
}
- Ejecuta tu programa terminado. El resultado debería verse así:
Your 6 sided dice rolled 5! Your 20 sided dice rolled 7!
7. Implementa las prácticas recomendadas de codificación
Cuando escribes código, es aconsejable la brevedad. Puedes deshacerte de la variable randomNumber
y mostrar el número al azar directamente.
- Cambia la sentencia
return
de modo que muestre el número al azar de forma directa.
fun roll(): Int {
return (1..numSides).random()
}
En la segunda sentencia de impresión, coloca la llamada para obtener el número al azar en la plantilla de cadenas. Puedes deshacerte de la variable diceRoll
haciendo lo mismo en la primera sentencia de impresión.
- Llama a
myFirstDice.roll()
en la plantilla de cadenas y borra la variablediceRoll
. Las primeras dos líneas de tu códigomain()
ahora deberían tener este aspecto:
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
- Ejecuta tu código. No debería haber diferencias en el resultado.
Este es el código final luego de refactorizarlo.
fun main() {
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
val mySecondDice = Dice(20)
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}
class Dice (val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
8. Código de solución
fun main() {
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
val mySecondDice = Dice(20)
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}
class Dice (val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
9. Resumen
- Llama a la función
random()
en unIntRange
de modo que generes un número al azar:(1..6).random()
. - Las clases son como el plano de un objeto. Pueden tener propiedades y comportamientos, implementados como variables y funciones.
- Una instancia de una clase representa un objeto, a menudo un objeto físico, como un dado. Puedes llamar a las acciones en el objeto y cambiar sus atributos.
- Puedes suministrar valores a una clase cuando creas una instancia. Por ejemplo:
class Dice(val numSides: Int)
y, luego, crear una instancia conDice(6)
. - Las funciones pueden mostrar un resultado. Especifica en la definición de la función el tipo de datos que se mostrará y usa una sentencia
return
en el cuerpo de la función para que muestre un resultado. Por ejemplo:fun example(): Int { return 5 }
.
10. Más información
11. Practica por tu cuenta
Haz lo siguiente:
- Dale a tu clase
Dice
otro atributo de color y crea varias instancias de dados de diferentes colores y cantidades de caras. - Crea una clase
Coin
, dale la capacidad de girar, crea una instancia de la clase y haz girar algunas monedas. ¿Cómo usarías la funciónrandom()
mediante un rango a los efectos de lograr el giro de una moneda?