Nos codelabs deste caminho de aprendizado, você criará o Dice Roller, um app Android. Quando o usuário "jogar o dado", um resultado aleatório será gerado. O resultado considera o número de lados do dado. Por exemplo, somente valores de 1 a 6 podem ser tirados em um dado de seis lados.
O app final ficará assim:
Para que você possa se concentrar nos novos conceitos de programação do app, você usará a ferramenta de programação em Kotlin no navegador para criar a funcionalidade principal do app. O programa enviará os resultados ao console. Depois, você implementará a interface do usuário no Android Studio.
Neste primeiro codelab, você criará um programa em Kotlin que simula um jogo de dado e gera um número aleatório, assim como um dado faria.
Prerequisites
- Saber abrir, editar e executar código em https://try.kotlinlang.org/ (link em inglês).
- Criar e executar um programa em Kotlin que usa variáveis e funções e exibe um resultado no console.
- Formatar números em um texto usando um modelo de string com a notação
${variable}
.
O que você aprenderá
- Como gerar números aleatórios de forma programática para simular jogos de dado
- Como estruturar o código criando uma classe
Dice
com uma variável e um método. - Como criar uma instância de objeto de uma classe, modificar as variáveis e chamar os métodos.
O que você criará
- Um programa Kotlin na ferramenta de programação em Kotlin no navegador que pode jogar dados de forma aleatória.
O que é necessário
- Um computador com conexão de Internet
Os jogos geralmente contêm um elemento aleatório. Você pode ganhar um prêmio aleatório ou avançar por um número aleatório de casas em um jogo de tabuleiro. No dia a dia, você pode usar números e letras aleatórios para definir senhas mais seguras.
Em vez de realmente jogar os dados, é possível criar um programa que simule isso para você. Cada vez que o dado é jogado, o resultado pode ser qualquer número do intervalo de valores possíveis. Felizmente, você não precisará criar seu próprio gerador de números aleatórios para esse programa. A maioria das linguagens de programação, incluindo Kotlin, tem uma forma integrada de gerar números aleatórios. Nesta tarefa, você usará o código em Kotlin para gerar um número aleatório.
Configurar o código inicial
- No navegador, abra o site https://try.kotlinlang.org/.
- Exclua todos os códigos no editor e substitua pelo código abaixo. Essa é a função
main()
com que você trabalhou em codelabs anteriores. Consulte Criar seu primeiro programa em Kotlin.
fun main() {
}
Usar a função aleatória
Para jogar um dado, você precisa de uma maneira de representar todos os valores válidos dele. Para um dado comum de seis lados, os resultados aceitáveis são: 1, 2, 3, 4, 5 e 6.
Anteriormente, você aprendeu que existem diferentes tipos de dados, como Int
para números inteiros e String
para textos. IntRange
é outro tipo de dados e representa um intervalo de números inteiros, de um ponto de partida até um ponto final. IntRange
é o tipo de dados indicado para representar os valores possíveis de um jogo de dado.
- Dentro da função
main()
, defina uma variável comoval
, chamadadiceRange
. Atribua essa variável a umIntRange
de 1 a 6, que representa o intervalo de números inteiros que podem ser gerados por um dado de seis lados.
val diceRange = 1..6
É possível dizer que 1..6
é um intervalo em Kotlin porque ele tem um número inicial, dois pontos e um número final (sem espaços no meio). Outros exemplos de intervalos inteiros são 2..5
, para os números de 2 a 5, e 100..200
, para os números de 100 a 200.
Da mesma forma que chamar println()
instrui o sistema a exibir o texto fornecido, é possível usar uma função chamada random()
para gerar e retornar um número aleatório para determinado intervalo. Assim como antes, é possível armazenar o resultado em uma variável.
- Em
main()
, defina uma variável comoval
chamadarandomNumber
. - Faça com que
randomNumber
tenha o valor do resultado da chamada pararandom()
no intervalodiceRange
, conforme mostrado abaixo.
val randomNumber = diceRange.random()
Você chama random()
em diceRange
usando um ponto entre a variável e a chamada de função. Isso pode ser lido como "gerar um número aleatório a partir de diceRange
". O resultado é armazenado na variável randomNumber
.
- Para ver o número gerado aleatoriamente, use a notação de formatação de string (também chamada de "modelo de string")
${randomNumber}
para exibi-lo, conforme mostrado abaixo.
println("Random number: ${randomNumber}")
O código finalizado ficará assim:
fun main() {
val diceRange = 1..6
val randomNumber = diceRange.random()
println("Random number: ${randomNumber}")
}
- Execute o código várias vezes. Você verá o resultado conforme o exemplo abaixo, cada vez com um número aleatório diferente.
Random number: 4
Ao jogar dados, você tem objetos reais nas suas mãos. Embora o código que você acabou de criar funcione perfeitamente, é difícil imaginar que ele realmente representa um dado. Organizar um programa para que ele seja mais parecido com o que ele representa facilita a compreensão. Seria legal ter um dado programático que pudesse ser jogado.
Todos os dados funcionam basicamente da mesma forma. Eles têm as mesmas propriedades, como os lados, e o mesmo comportamento, já que precisam ser jogados. Em Kotlin, é possível criar um modelo programático que informa que o dado tem lados e pode gerar um número aleatório como resultado. Esse modelo é chamado de classe.
Com essa classe, é possível criar objetos de dados reais, chamados instâncias de objetos. Por exemplo, você pode criar um dado de 12 ou 4 lados.
Definir uma classe Dice
Nas etapas a seguir, você definirá uma nova classe chamada Dice
para representar um dado.
- Para recomeçar, limpe o código na função
main()
, para ficar com o código mostrado abaixo.
fun main() {
}
- Abaixo dessa função
main()
, adicione uma linha em branco e acrescente um código para criar a classeDice
. Conforme mostrado abaixo, comece com a palavra-chaveclass
, seguida do nome da classe e de chaves. Deixe um espaço entre as chaves para colocar o código referente à classe.
class Dice {
}
Dentro da definição de classe, é possível especificar uma ou mais propriedades para a classe usando variáveis. Os dados reais podem ter diferentes lados, cores ou pesos. Nesta tarefa, você se concentrará na propriedade do número de lados do dado.
- Dentro da classe
Dice
, adicione umavar
chamadasides
, referente ao número de lados que o dado terá. Definasides
como 6.
class Dice {
var sides = 6
}
É isso. Agora você tem uma classe muito simples, representando um dado.
Criar uma instância da classe Dice
Com a classe Dice
, você tem um modelo de dado. Para ter um dado real no programa, é necessário criar uma instância de objeto Dice
. Caso você precisasse ter três dados, seriam criadas três instâncias de objeto.
- Para criar uma instância de objeto
Dice
, na funçãomain()
, crie umaval
chamadamyFirstDice
e inicialize-a como uma instância da classeDice
. Há parênteses após o nome da classe, o que indica que você está criando uma nova instância de objeto a partir da classe.
fun main() {
val myFirstDice = Dice()
}
Agora que você tem um objeto myFirstDice
, criado a partir do modelo, é possível acessar as propriedades dele. A única propriedade de Dice
é sides
. Para acessar uma propriedade, use a "notação de ponto". Portanto, para acessar a propriedade sides
de myFirstDice
, chame myFirstDice.sides
, digitando "myFirstDice
ponto sides
".
- Abaixo da declaração
myFirstDice
, adicione uma instruçãoprintln()
para gerar o número desides
demyFirstDice.
println(myFirstDice.sides)
O código ficará assim:
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
}
class Dice {
var sides = 6
}
- Execute o programa. Ele gerará o número de
sides
definido na classeDice
.
6
Agora você tem uma classe Dice
e um dado myFirstDice
com seis sides
.
Chegou a hora de jogar o dado.
Jogar o dado
Anteriormente, você usou uma função para executar a ação de exibir camadas de bolos. Jogar o dado também é uma ação que pode ser implementada como uma função. Já que todos os dados podem ser jogados, é possível adicionar uma função dentro da classe Dice
. Uma função definida dentro de uma classe também é chamada de método.
- Na classe
Dice
, abaixo da variávelsides
, insira uma linha em branco e crie uma nova função para jogar o dado. Comece com a palavra-chave em Kotlinfun
, seguida do nome do método, seguido de parênteses()
, seguidos de chaves{}
. Você pode deixar uma linha em branco entre as chaves para haver espaço para inserir mais códigos, conforme mostrado abaixo. A classe ficará assim:
class Dice {
var sides = 6
fun roll() {
}
}
Ao jogar um dado de seis lados, ele gera um número aleatório entre 1 e 6.
- No método
roll()
, crie umaval randomNumber
. Atribua um número aleatório no intervalo de1..6
. Use a notação de ponto para chamarrandom()
no intervalo.
val randomNumber = (1..6).random()
- Depois de gerar o número aleatório, exiba-o no console. O método
roll()
final ficará como o código abaixo.
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
- Para jogar
myFirstDice
, emmain()
, chame o métodoroll()
emmyFirstDice
. Para chamar um método, use a "notação de ponto". Dessa forma, para chamar o métodoroll()
demyFirstDice
, insiramyFirstDice.roll()
, digitando "myFirstDice
pontoroll()
".
myFirstDice.roll()
O código final ficará assim:
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
myFirstDice.roll()
}
class Dice {
var sides = 6
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
}
- Execute o código. Você verá o resultado de uma jogada aleatória abaixo do número de lados. Execute o código várias vezes. Observe que o número de lados permanece igual, mas o valor do resultado muda.
6 4
Parabéns! Você definiu uma classe Dice
com uma variável sides
e uma função roll()
. Na função main()
, você criou uma nova instância de objeto Dice
e chamou o método roll()
para gerar um número aleatório.
Por enquanto, você está exibindo o valor de randomNumber
na função roll()
, e isso funciona muito bem. Mas, às vezes, é mais útil retornar o resultado de uma função para o que a tiver chamado. Por exemplo, é possível atribuir o resultado do método roll()
a uma variável e, em seguida, mover o jogador de acordo com o valor. Vejamos como isso é feito.
- Em
main()
, modifique a linhamyFirstDice.roll()
. Crie umval
chamadodiceRoll
. Defina um valor igual ao retornado pelo métodoroll()
.
val diceRoll = myFirstDice.roll()
Ele ainda não faz nada, porque roll()
ainda não retorna nada. Para que esse código funcione como deveria, roll()
precisa retornar algo.
Nos codelabs anteriores, você aprendeu que é necessário especificar um tipo de dados para inserir argumentos nas funções. Da mesma forma, é necessário especificar um tipo de dados para o que é retornado de uma função.
- Mude a função
roll()
para especificar o tipo de dados que será retornado. Neste caso, o número aleatório é umInt
. Portanto, o tipo de retorno éInt
. A sintaxe para especificar o tipo de retorno é: depois do nome da função, depois dos parênteses, adicione dois pontos, espaço e a palavra-chaveInt
referente ao tipo de retorno da função. A definição da função ficará como o código abaixo.
fun roll(): Int {
- Execute o código. Você verá um erro em Problems View. Ele informa:
A ‘return' expression is required in a function with a block body.
Você alterou a definição da função para retornar um Int
, mas o sistema informa que
o código não está retornando um Int
. "Corpo do bloco" ou "corpo da função" refere-se ao código entre as chaves de uma função. Para corrigir esse erro, retorne o valor de uma função usando uma instrução return
no final do corpo da função.
- Em
roll()
, remova a instruçãoprintln()
e substitua por uma instruçãoreturn
pararandomNumber
. A funçãoroll()
ficará como o exemplo abaixo.
fun roll(): Int {
val randomNumber = (1..6).random()
return randomNumber
}
- Em
main()
, remova a instrução de exibição referente aos lados do dado. - Adicione uma instrução para imprimir o valor de
sides
ediceRoll
em uma frase informativa. A funçãomain()
final precisa ficar como o código abaixo.
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
- Execute o código. O resultado ficará assim:
Your 6 sided dice rolled 4!
Veja todo o código até agora.
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
}
}
Nem todos os dados têm seis lados. Existem diversos formatos e tamanhos de dados: 4 lados, 8 lados, até 120 lados.
- Na classe
Dice
, no métodoroll()
, mude o1..6
codificado para usarsides
, de modo que o intervalo e, assim, o número aleatório, esteja sempre de acordo com o número de lados.
val randomNumber = (1..sides).random()
- Na função
main()
, abaixo e depois de exibir o resultado do dado, mudesides
deFirstDice
para ser definido como 20.
myFirstDice.sides = 20
- Copie e cole a instrução de exibição abaixo no local em que você mudou o número de lados.
- Substitua a exibição de
diceRoll
pela exibição do resultado da chamada do métodoroll()
emmyFirstDice
.
println("Your ${myFirstDice.sides} sided dice has rolled a ${myFirstDice.roll()}!")
O programa ficará assim.
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
}
}
- Execute o programa. Você verá uma mensagem para o dado de seis lados e outra para o dado de 20 lados.
Your 6 sided dice rolled 3! Your 20 sided dice rolled 15!
A ideia de uma classe é representar um elemento, muitas vezes algo físico no mundo real. Neste caso, uma classe Dice
representa um dado. No mundo real, o número de lados de um dado não muda. Se você quiser outro número de lados, precisará de um dado diferente. Em programação, isso significa que, em vez de mudar a propriedade de lados de uma instância de objeto Dice
, você criará uma nova instância de objeto de dado, com o número de lados necessários.
Nesta tarefa, você modificará a classe Dice
para especificar o número de lados ao criar uma nova instância. Mude a definição da classe Dice
para aceitar um argumento para o número de lados. Isso é parecido com a forma como uma função pode aceitar argumentos como resultado.
- Modifique a definição da classe
Dice
para aceitar um argumento inteiro com o nomenumSides
. O código dentro da classe não mudará.
class Dice(val numSides: Int) {
// Code inside does not change.
}
- Dentro da classe
Dice
, exclua a variávelsides
, já que agora é possível usarnumSides
. - Além disso, corrija o intervalo para usar
numSides
.
A classe Dice
ficará assim:
class Dice (val numSides: Int) {
fun roll(): Int {
val randomNumber = (1..numSides).random()
return randomNumber
}
}
Ao executar esse código você verá muitos erros, já que é necessário atualizar main()
para trabalhar com as mudanças na classe Dice
.
- Em
main()
, para criarmyFirstDice
com seis lados, é necessário transmitir o número de lados como um argumento para a classeDice
, conforme mostrado abaixo.
val myFirstDice = Dice(6)
- Na instrução de exibição, mude
sides
paranumSides
. - Abaixo disso, exclua o código que muda
sides
para 20, já que essa variável não existe mais. - Exclua também a instrução
println
abaixo dela.
A função main()
ficará parecida com o código abaixo e, ao executá-la, não haverá erros.
fun main() {
val myFirstDice = Dice(6)
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
- Depois de exibir o primeiro dado, adicione código para criar e exibir um segundo objeto
Dice
, chamadomySecondDice
, com 20 lados.
val mySecondDice = Dice(20)
- Adicione uma instrução que jogue o dado e exiba o valor retornado.
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
- A função
main()
concluída ficará parecida com esta:
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
}
}
- Execute o programa finalizado. O resultado ficará assim:
Your 6 sided dice rolled 5! Your 20 sided dice rolled 7!
Ao programar códigos, o melhor é ser conciso. Você pode eliminar a variável randomNumber
e retornar o número aleatório diretamente.
- Mude a instrução
return
para retornar o número aleatório diretamente.
fun roll(): Int {
return (1..numSides).random()
}
Na segunda instrução de exibição, insira a chamada para receber o número aleatório no modelo de string. É possível eliminar a variável diceRoll
fazendo o mesmo na primeira instrução de exibição.
- Chame
myFirstDice.roll()
no modelo de string e exclua a variáveldiceRoll
. As duas primeiras linhas do código main() ficarão assim.
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
- Execute o código. Você verá que não há diferença no resultado.
Este é o código final após a refatoração.
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()
}
}
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 {
return (1..numSides).random()
}
}
- Chame a função
random()
em umIntRange
para gerar um número aleatório:(1..6).random()
- As classes são como o modelo de um objeto. Elas podem ter propriedades e comportamentos, implementados como variáveis e funções.
- A instância de uma classe representa um objeto, geralmente um objeto físico, como um dado. É possível chamar as ações no objeto e mudar os atributos.
- É possível passar entradas para uma classe ao criar uma instância ao especificar um argumento para a definição da classe. Por exemplo:
class Dice(val numSides: Int)
e, então, criar uma instância comDice(6)
. - As funções podem retornar algo. Especifique o tipo de dados a ser retornado na definição da função e use uma instrução
return
no corpo da função para retornar algo. Por exemplo:fun example(): Int { return 5 }
- Geração de números aleatórios (Wikipédia) (link em inglês)
- O complicado desafio de projetar um dado de 120 lados (link em inglês)
- Classes em Kotlin (link em inglês)
- Declarações de função em Kotlin (link em inglês)
- Retornar um valor de uma função (link em inglês)
Faça o seguinte:
- Forneça outro atributo de cor à classe
Dice
e crie várias instâncias de dados, com diferentes números de lados e cores. - Crie uma classe
Coin
, conceda a capacidade de virar, crie uma instância de classe e jogue moedas. Como você usaria a função aleatória() com um intervalo para conseguir jogar uma moeda?