W ramach tych warsztatów stworzysz aplikację na Androida do rzucania kostką. Gdy użytkownik „rzuci kostką”, zostanie wygenerowany losowy wynik. Wynik uwzględnia liczbę ścianek kostki. Na przykład na 6-ściennej kostce mogą wypaść tylko wartości od 1 do 6.
Tak będzie wyglądać gotowa aplikacja.
Aby pomóc Ci skupić się na nowych koncepcjach programowania w tej aplikacji, użyjesz narzędzia do programowania w Kotlinie w przeglądarce, aby utworzyć podstawowe funkcje aplikacji. Program wyświetli wyniki w konsoli. Później zaimplementujesz interfejs użytkownika w Android Studio.
W tym pierwszym ćwiczeniu z programowania utworzysz program w języku Kotlin, który symuluje rzut kostką i wyświetla losową liczbę, tak jak w przypadku prawdziwej kostki.
Wymagania wstępne
- Jak otwierać, edytować i uruchamiać kod na stronie https://try.kotlinlang.org/
- Utwórz i uruchom program w Kotlinie, który używa zmiennych i funkcji oraz wyświetla wynik w konsoli.
- Formatuj liczby w tekście za pomocą szablonu ciągu znaków z notacją
${variable}
.
Czego się nauczysz
- Jak programowo generować liczby losowe, aby symulować rzuty kostką.
- Jak uporządkować kod, tworząc klasę
Dice
ze zmienną i metodą. - Jak utworzyć instancję obiektu klasy, zmodyfikować jej zmienne i wywołać jej metody.
Co utworzysz
- Program w języku Kotlin w narzędziu do programowania w Kotlinie w przeglądarce, który może wykonać losowy rzut kostką.
Wymagania
- komputer z połączeniem internetowym,
Gry często zawierają element losowy. Możesz zdobyć losową nagrodę lub przejść losową liczbę pól na planszy. W życiu codziennym możesz używać losowych cyfr i liter, aby generować bezpieczniejsze hasła.
Zamiast rzucać prawdziwymi kostkami, możesz napisać program, który będzie symulował rzucanie kostkami. Za każdym razem, gdy rzucasz kostką, wynik może być dowolną liczbą z zakresu możliwych wartości. Na szczęście nie musisz tworzyć własnego generatora liczb losowych na potrzeby takiego programu. Większość języków programowania, w tym Kotlin, ma wbudowany sposób generowania liczb losowych. W tym zadaniu użyjesz kodu w języku Kotlin do wygenerowania liczby losowej.
Konfigurowanie kodu startowego
- W przeglądarce otwórz stronę https://try.kotlinlang.org/.
- Usuń cały istniejący kod w edytorze kodu i zastąp go kodem poniżej. Jest to funkcja
main()
, z którą pracowaliśmy w poprzednich ćwiczeniach (patrz ćwiczenie Napisz pierwszy program w Kotlinie).
fun main() {
}
Korzystanie z funkcji losowej
Aby rzucić kostką, musisz mieć sposób na przedstawienie wszystkich prawidłowych wartości. W przypadku zwykłej 6-ściennej kostki dopuszczalne wyniki rzutu to: 1, 2, 3, 4, 5 i 6.
Wcześniej dowiedzieliśmy się, że istnieją typy danych, takie jak Int
dla liczb całkowitych i String
dla tekstu. IntRange
to kolejny typ danych, który reprezentuje zakres liczb całkowitych od punktu początkowego do końcowego. IntRange
to odpowiedni typ danych do reprezentowania możliwych wartości, jakie można uzyskać podczas rzutu kostką.
- W funkcji
main()
zdefiniuj zmienną jakoval
o nazwiediceRange
. Przypisz ją do liczbyIntRange
od 1 do 6, która reprezentuje zakres liczb całkowitych, jakie można uzyskać na 6-ściennej kostce do gry.
val diceRange = 1..6
Zakres w języku Kotlin jest oznaczony symbolem 1..6
, ponieważ zawiera liczbę początkową, dwie kropki i liczbę końcową (bez spacji między nimi). Inne przykłady zakresów liczb całkowitych to 2..5
dla liczb od 2 do 5 i 100..200
dla liczb od 100 do 200.
Podobnie jak wywołanie funkcji println()
powoduje wydrukowanie podanego tekstu, możesz użyć funkcji random()
, aby wygenerować i zwrócić losową liczbę z określonego zakresu. Podobnie jak wcześniej możesz zapisać wynik w zmiennej.
- W sekcji
main()
zdefiniuj zmienną jakoval
o nazwierandomNumber
. - Ustaw wartość
randomNumber
na wynik wywołania funkcjirandom()
w zakresiediceRange
, jak pokazano poniżej.
val randomNumber = diceRange.random()
Zwróć uwagę, że wywołujesz funkcję random()
na zmiennej diceRange
, używając kropki między zmienną a wywołaniem funkcji. Możesz to odczytać jako „generowanie liczby losowej z diceRange
”. Wynik jest następnie przechowywany w zmiennej randomNumber
.
- Aby wyświetlić losowo wygenerowaną liczbę, użyj notacji formatowania ciągu znaków (nazywanej też „szablonem ciągu znaków”)
${randomNumber}
, jak pokazano poniżej.
println("Random number: ${randomNumber}")
Gotowy kod powinien wyglądać tak.
fun main() {
val diceRange = 1..6
val randomNumber = diceRange.random()
println("Random number: ${randomNumber}")
}
- Uruchom kod kilka razy. Za każdym razem powinny się wyświetlać dane wyjściowe podobne do tych poniżej, ale z innymi liczbami losowymi.
Random number: 4
Gdy rzucasz kostkami, są one prawdziwymi obiektami w Twoich rękach. Napisany przez Ciebie kod działa bez zarzutu, ale trudno sobie wyobrazić, że chodzi w nim o prawdziwe kostki. Uporządkowanie programu tak, aby przypominał rzeczy, które reprezentuje, ułatwia jego zrozumienie. Fajnie byłoby mieć programowe kostki, którymi można rzucać.
Wszystkie kostki działają w zasadzie tak samo. Mają te same właściwości, np. boki, i zachowują się w ten sam sposób, np. można je rolować. W Kotlinie możesz utworzyć programistyczny schemat kostki, który określa, że kostka ma ścianki i może wylosować liczbę. Ten schemat nazywa się klasą.
Na podstawie tej klasy możesz utworzyć rzeczywiste obiekty kostek do gry, zwane instancjami obiektów. Możesz na przykład utworzyć 12-ścienną lub 4-ścienną kostkę do gry.
Definiowanie klasy Dice
W kolejnych krokach zdefiniujesz nową klasę o nazwie Dice
, która będzie reprezentować kostkę do gry.
- Aby zacząć od nowa, usuń kod w funkcji
main()
, tak aby wyglądał jak poniżej.
fun main() {
}
- Pod funkcją
main()
dodaj pusty wiersz, a potem dodaj kod, aby utworzyć klasęDice
. Jak pokazano poniżej, zacznij od słowa kluczowegoclass
, po którym wpisz nazwę klasy, a następnie nawiasy klamrowe otwierający i zamykający. Pozostaw miejsce między nawiasami klamrowymi, aby umieścić kod klasy.
class Dice {
}
W definicji klasy możesz określić co najmniej jedną właściwość klasy za pomocą zmiennych. Prawdziwe kostki mogą mieć różną liczbę ścianek, kolor lub wagę. W tym zadaniu skupisz się na właściwości liczby ścianek kostki.
- W klasie
Dice
dodajvar
o nazwiesides
, która będzie określać liczbę ścianek kostki. Ustaw wartośćsides
na 6.
class Dice {
var sides = 6
}
To wszystko. Masz teraz bardzo prostą klasę reprezentującą kostki.
Tworzenie instancji klasy Dice
Klasa Dice
to schemat kostki. Aby mieć w programie rzeczywistą kostkę, musisz utworzyć instancję obiektu Dice
. (Jeśli potrzebujesz 3 kostek, utwórz 3 instancje obiektu).
- Aby utworzyć instancję obiektu
Dice
, w funkcjimain()
utwórz zmiennąval
o nazwiemyFirstDice
i zainicjuj ją jako instancję klasyDice
. Zwróć uwagę na nawiasy po nazwie klasy, które oznaczają, że tworzysz nową instancję obiektu z tej klasy.
fun main() {
val myFirstDice = Dice()
}
Teraz, gdy masz obiekt myFirstDice
, czyli rzecz wykonaną na podstawie planu, możesz uzyskać dostęp do jego właściwości. Jedyną właściwością elementu Dice
jest jego sides
. Do właściwości uzyskujesz dostęp za pomocą „notacji kropkowej”. Aby uzyskać dostęp do właściwości sides
obiektu myFirstDice
, wywołujesz myFirstDice.sides
, co wymawia się „myFirstDice
kropka sides
”.
- Pod deklaracją
myFirstDice
dodaj instrukcjęprintln()
, aby wyświetlić liczbęsides
wmyFirstDice.
.
println(myFirstDice.sides)
Kod powinien wyglądać tak:
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
}
class Dice {
var sides = 6
}
- Uruchom program. Powinien on wyświetlić liczbę
sides
zdefiniowaną w klasieDice
.
6
Masz teraz klasę Dice
i prawdziwą kostkę myFirstDice
z 6 sides
.
Rzućmy kostką!
Rzut kostką
Wcześniej używasz funkcji do drukowania warstw ciasta. Rzucanie kostkami to również działanie, które można zaimplementować jako funkcję. Ponieważ wszystkie kostki można rzucać, możesz dodać do klasy Dice
funkcję rzutu. Funkcja zdefiniowana w klasie jest też nazywana metodą.
- W klasie
Dice
poniżej zmiennejsides
wstaw pusty wiersz, a następnie utwórz nową funkcję rzutu kostką. Zacznij od słowa kluczowego Kotlinfun
, po którym wpisz nazwę metody, a następnie nawiasy()
i nawiasy klamrowe{}
. Możesz zostawić pusty wiersz między nawiasami klamrowymi, aby zrobić miejsce na więcej kodu, jak pokazano poniżej. Klasa powinna wyglądać tak.
class Dice {
var sides = 6
fun roll() {
}
}
Gdy rzucisz sześcienną kostką do gry, otrzymasz losową liczbę z zakresu od 1 do 6.
- W metodzie
roll()
utwórzval randomNumber
. Przypisz mu losową liczbę z zakresu1..6
. Użyj notacji kropkowej, aby wywołaćrandom()
w zakresie.
val randomNumber = (1..6).random()
- Po wygenerowaniu losowej liczby wyświetl ją w konsoli. Ukończona metoda
roll()
powinna wyglądać tak, jak pokazano poniżej.
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
- Aby faktycznie wywołać
myFirstDice
wmain()
, wywołaj metodęroll()
na obiekciemyFirstDice
. Metodę wywołuje się za pomocą „notacji kropkowej”. Aby wywołać metodęroll()
obiektumyFirstDice
, wpiszmyFirstDice.roll()
, co wymawia się „myFirstDice
kropkaroll()
”.
myFirstDice.roll()
Gotowy kod powinien wyglądać tak.
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
myFirstDice.roll()
}
class Dice {
var sides = 6
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
}
- Uruchom kod. Pod liczbą ścianek powinny się wyświetlić wyniki losowego rzutu kostką. Uruchom kod kilka razy i zwróć uwagę, że liczba ścianek pozostaje taka sama, a wartość rzutu kostką się zmienia.
6 4
Gratulacje! Masz zdefiniowaną klasę Dice
ze zmienną sides
i funkcją roll()
. W funkcji main()
utworzono nową instancję obiektu Dice
, a następnie wywołano na niej metodę roll()
, aby wygenerować liczbę losową.
Obecnie wyświetlasz wartość zmiennej randomNumber
w funkcji roll()
i działa to świetnie. Czasami jednak bardziej przydatne jest zwrócenie wyniku funkcji do miejsca, z którego została wywołana. Możesz na przykład przypisać wynik działania metody roll()
do zmiennej, a potem przesunąć gracza o tę wartość. Zobaczmy, jak to zrobić.
- W
main()
zmień wiersz z napisemmyFirstDice.roll()
. Utwórzval
o nazwiediceRoll
. Ustaw ją na wartość zwróconą przez metodęroll()
.
val diceRoll = myFirstDice.roll()
Nie spowoduje to jeszcze żadnych zmian, ponieważ funkcja roll()
nie zwraca jeszcze żadnych wartości. Aby ten kod działał zgodnie z oczekiwaniami, funkcja roll()
musi zwracać jakąś wartość.
W poprzednich ćwiczeniach z programowania dowiedzieliśmy się, że w przypadku argumentów wejściowych funkcji trzeba określić typ danych. Podobnie musisz określić typ danych zwracanych przez funkcję.
- Zmień funkcję
roll()
, aby określić typ danych, które mają być zwracane. W tym przypadku liczba losowa toInt
, więc typ zwracany toInt
. Składnia określania typu zwracanego jest następująca: po nazwie funkcji, po nawiasach, dodaj dwukropek, spację, a następnie słowo kluczoweInt
dla typu zwracanego funkcji. Definicja funkcji powinna wyglądać tak, jak pokazano poniżej.
fun roll(): Int {
- Uruchom ten kod. W widoku problemów pojawi się błąd. Brzmi on:
A ‘return' expression is required in a function with a block body.
Zmieniono definicję funkcji, aby zwracała wartość Int
, ale system zgłasza, że
kod nie zwraca w rzeczywistości wartości Int
. „Blok kodu” lub „treść funkcji” to kod między nawiasami klamrowymi funkcji. Aby naprawić ten błąd, zwróć wartość z funkcji za pomocą instrukcji return
na końcu treści funkcji.
- W
roll()
usuń instrukcjęprintln()
i zastąp ją instrukcjąreturn
dlarandomNumber
. Funkcjaroll()
powinna wyglądać tak, jak pokazano poniżej.
fun roll(): Int {
val randomNumber = (1..6).random()
return randomNumber
}
- W
main()
usuń instrukcję drukowania dla ścianek kostki. - Dodaj instrukcję, która wyświetli wartości zmiennych
sides
idiceRoll
w zdaniu informacyjnym. Gotowa funkcjamain()
powinna wyglądać podobnie do kodu poniżej.
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
- Uruchom kod. Dane wyjściowe powinny wyglądać tak:
Your 6 sided dice rolled 4!
Oto cały Twój dotychczasowy kod.
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
}
}
Nie wszystkie kostki mają 6 ścianek. Kostki do gry mają różne kształty i rozmiary: 4, 8, a nawet 120 ścianek.
- W
Dice
klasie wroll()
metodzie zmień zakodowaną na stałe wartość1..6
nasides
, aby zakres, a tym samym wylosowana liczba losowa, zawsze odpowiadał liczbie ścianek.
val randomNumber = (1..sides).random()
- W funkcji
main()
, poniżej i po wydrukowaniu wyniku rzutu kostką, zmień wartośćsides
of myFirstDice
na 20.
myFirstDice.sides = 20
- Skopiuj i wklej poniższy istniejący już komunikat o drukowaniu w miejscu, w którym zmieniono liczbę stron.
- Zastąp drukowanie
diceRoll
drukowaniem wyniku wywołania metodyroll()
na obiekciemyFirstDice
.
println("Your ${myFirstDice.sides} sided dice has rolled a ${myFirstDice.roll()}!")
Program powinien wyglądać tak.
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
}
}
- Uruchom program. Powinna się wyświetlić wiadomość dotycząca 6-ściennej kostki do gry i druga wiadomość dotycząca 20-ściennej kostki do gry.
Your 6 sided dice rolled 3! Your 20 sided dice rolled 15!
Klasa reprezentuje rzecz, często fizyczną, w świecie rzeczywistym. W tym przypadku klasa Dice
reprezentuje fizyczną kostkę. W rzeczywistości kostki nie mogą zmieniać liczby ścianek. Jeśli chcesz użyć kostki o innej liczbie ścianek, musisz kupić inną kostkę. Z punktu widzenia programowania oznacza to, że zamiast zmieniać właściwość sides istniejącej instancji obiektu Dice
, należy utworzyć nową instancję obiektu dice z potrzebną liczbą ścian.
W tym zadaniu zmodyfikujesz klasę Dice
, aby podczas tworzenia nowej instancji można było określić liczbę boków. Zmień definicję klasy Dice
, aby akceptowała argument określający liczbę boków. Działa to podobnie do funkcji, która może przyjmować argumenty wejściowe.
- Zmodyfikuj definicję klasy
Dice
, aby akceptowała argument całkowity o nazwienumSides
. Kod w klasie nie ulegnie zmianie.
class Dice(val numSides: Int) {
// Code inside does not change.
}
- W klasie
Dice
usuń zmiennąsides
, ponieważ możesz teraz używać zmiennejnumSides
. - Ustaw też zakres na
numSides
.
Klasa Dice
powinna wyglądać tak.
class Dice (val numSides: Int) {
fun roll(): Int {
val randomNumber = (1..numSides).random()
return randomNumber
}
}
Jeśli uruchomisz ten kod, zobaczysz wiele błędów, ponieważ musisz zaktualizować tag main()
, aby działał ze zmianami w klasie Dice
.
- W
main()
, aby utworzyćmyFirstDice
o 6 bokach, musisz teraz przekazać liczbę boków jako argument do klasyDice
, jak pokazano poniżej.
val myFirstDice = Dice(6)
- W instrukcji print zmień
sides
nanumSides
. - Następnie usuń kod, który zmienia wartość
sides
na 20, ponieważ ta zmienna już nie istnieje. - Usuń też stwierdzenie
println
znajdujące się pod nim.
Funkcja main()
powinna wyglądać jak poniższy kod. Jeśli ją uruchomisz, nie powinny wystąpić żadne błędy.
fun main() {
val myFirstDice = Dice(6)
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
- Po wydrukowaniu pierwszego rzutu kostką dodaj kod, który utworzy i wydrukuje drugi obiekt
Dice
o nazwiemySecondDice
z 20 ścianami.
val mySecondDice = Dice(20)
- Dodaj instrukcję drukowania, która przetwarza i drukuje zwróconą wartość.
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
- Gotowa funkcja
main()
powinna wyglądać tak.
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
}
}
- Uruchom gotowy program. Dane wyjściowe powinny wyglądać tak:
Your 6 sided dice rolled 5! Your 20 sided dice rolled 7!
Podczas pisania kodu lepiej jest zachować zwięzłość. Możesz pozbyć się zmiennej randomNumber
i zwrócić bezpośrednio liczbę losową.
- Zmień instrukcję
return
, aby bezpośrednio zwracała liczbę losową.
fun roll(): Int {
return (1..numSides).random()
}
W drugim poleceniu drukowania umieszczasz wywołanie funkcji pobierania liczby losowej w szablonie ciągu. Możesz pozbyć się zmiennej diceRoll
, wykonując tę samą czynność w pierwszej instrukcji drukowania.
- Wywołaj
myFirstDice.roll()
w szablonie ciągu znaków i usuń zmiennądiceRoll
. Pierwsze 2 wiersze kodu funkcji main() wyglądają teraz tak:
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
- Uruchom kod. Wynik powinien być taki sam.
To ostateczny kod po refaktoryzacji .
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()
}
}
- Wywołaj funkcję
random()
na obiekcieIntRange
, aby wygenerować liczbę losową:(1..6).random()
- Klasy są jak plan obiektu. Mogą mieć właściwości i zachowania zaimplementowane jako zmienne i funkcje.
- Instancja klasy reprezentuje obiekt, często fizyczny, np. kostkę do gry. Możesz wywoływać działania na obiekcie i zmieniać jego atrybuty.
- Podczas tworzenia instancji możesz przekazać dane wejściowe do klasy, określając argument definicji klasy. Na przykład:
class Dice(val numSides: Int)
, a następnie utwórz instancję za pomocąDice(6)
. - Funkcje mogą zwracać wartości. W definicji funkcji określ typ danych, który ma być zwracany, i użyj instrukcji
return
w treści funkcji, aby coś zwrócić. Na przykład:fun example(): Int { return 5 }
Wykonaj te czynności:
- Nadaj klasie
Dice
kolejny atrybut koloru i utwórz wiele instancji kostek o różnej liczbie ścianek i kolorach. - Utwórz klasę
Coin
, nadaj jej możliwość odwracania, utwórz instancję klasy i odwróć kilka monet. Jak użyć funkcji random() z zakresem, aby przeprowadzić rzut monetą?