Przejdź do głównej treści
INF.04-01-26.01-SGStyczeń 2026 · 26 kroków · ok. 83 min · 35 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja gry w kości (C# + .NET MAUI)

Rozwiązanie dotyczy arkusza z grą w kości w wersji C# + .NET MAUI: klasa Kosc z polem statycznym, dwoma konstruktorami i trzema metodami, program konsolowy, który tworzy dwie kości (jedną z rzutu losowego, drugą z liczby wpisanej z klawiatury), aplikacja mobilna .NET MAUI z pięcioma kośćmi, przyciskiem RZUT, sumą oczek i blokowaniem kości dotknięciem oraz dwa testy jednostkowe w MSTest.

To drugie rozwiązanie tego arkusza - pierwsze, w zakładce obok, robi to samo w Javie i Android Studio. Arkusz pokazuje oba środowiska (obraz 3 to .NET MAUI), a klucz ocenia je tymi samymi kryteriami. W C# klasa Kosc z konsoli przechodzi do aplikacji mobilnej bez zmian.

Kroki idą zdanie po zdaniu za treścią arkusza, a kod od początku jest taki, jak w porządnym projekcie. Na końcu dwa kroki porównują to rozwiązanie z wariantem w Javie.

Co widać w podglądzie. Wyniki programu konsolowego i testów pochodzą z prawdziwego uruchomienia kodu z danego kroku (.NET 10, MSTest). Ekran telefonu to symulacja w przeglądarce zbudowana z pliku XAML danego kroku, z obrazami kosc0.png - kosc6.png z archiwum zad1.7z; jej zachowanie odpowiada kodowi C# z kroku - klikaj „RZUT” i kości. Każdy stan aplikacji zbudowano też naprawdę (dotnet build) i porównano z emulatorem Pixel 8 (Android 16).

Jak liczą się punkty. Klucz ma 35 kryteriów (R.1.1 - R.4.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Przygotowanie

    Krok 1 · po tym etapie masz 0 z 35 kryteriów (pkt)

    Krok 1: Folder zdającego, podfoldery i obrazy z zad1.7z

    ok. 3 min
    Polecenie z arkusza

    Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsolowa, mobilna, testy. Do wykonania aplikacji mobilnej wykorzystaj umieszczone na pulpicie konta Egzamin obrazy z archiwum zad1.7z z hasłem: @Kosc6

    Na pulpicie zakładasz folder o nazwie z numerem PESEL, a w nim konsolowa, mobilna i testy. Archiwum zad1.7z rozpakowujesz hasłem z arkusza - w środku siedem obrazów kosc0.png (pusta ścianka na stan początkowy) - kosc6.png.

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-10 · po tym etapie masz 17 z 35 kryteriów (pkt)

    Krok 2: Projekt konsola w C#

    ok. 3 min
    Polecenie z arkusza

    Zaprogramuj klasę o nazwie Kosc implementującą logikę działania pojedynczej kości, wykorzystywanej później w grze w kości. Zastosowany obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python.

    Wybierasz C# - tę samą klasę Kosc wykorzystasz w aplikacji .NET MAUI. W Visual Studio: Utwórz nowy projekt → Aplikacja konsolowa, nazwa konsola (dotnet new console -n konsola). Program główny piszesz w Program.cs jako instrukcje najwyższego poziomu, klasę Kosc - w osobnym pliku Kosc.cs (Dodaj → Klasa...).

    Nazwę klasy narzuca arkusz; pola i metody nazywasz po angielsku, jak w wariancie w Javie - arkusz dopuszcza oba języki nazw. Konwencja C#: pola publiczne i metody wielką literą.

    konsola/Program.cs C# nowy plik
    Console.WriteLine("Hello, World!");
    Wynik programu po tym kroku
    > konsola.exe
    Hello, World!
  3. Krok 3: Pola klasy Kosc: licznik statyczny, nazwy plików, oczka, obraz, dostępność

    Kryteria: , , ,

    R.2.1 Zdefiniowana klasa Kosc z polami publicznymi: dwa typu całkowitego, jedno logiczne oraz polem publicznym statycznym typu całkowitego

    R.2.2 Nazwy obrazów od kosc0.png do kosc6.png zostały przypisane jako elementy tablicy lub innej kolekcji

    R.1.4 Polskie lub angielskie nazewnictwo pól i zmiennych. Nazewnictwo jest znaczące. Wyjątkami od reguły są zmienne bufor, tmp, iteratory pętli. Kryterium nie jest spełnione, gdy nazwy zmiennych nic nie znaczą, np. x, tab, tablica, foo

    R.1.5 Typy zmiennych pasują do problemu, np. tablica przechowuje napisy, liczba oczek oraz identyfiaktor pliku graficznego są typu całkowitego, zmienna przechowująca czy kość jest dostępna jest typu logicznego (w przypadku języka Python typ wynika z przypisanych danych)

    ok. 4 min
    Polecenie z arkusza

    Klasa Kosc powinna zawierać: pole ogólnodostępne, statyczne, przechowujące liczbę instancji klasy Kosc. Pola ogólnodostępne, przechowujące: nazwy plików przechowujących obrazy (kosc0.png, kosc1.png, kosc2.png, kosc3.png, kosc4.png, kosc5.png, kosc6.png) umieszczone w tablicy lub innej kolekcji typu napisowego, liczba oczek wyrzucona kością, typu liczbowego całkowitego (3 dla obrazu 1), identyfikator pliku graficznego odpowiadającego wyrzuconej liczbie oczek, typu całkowitego (3 dla obrazu 1, jest to indeks tablicy wskazujący na nazwę kosc3.png), informacja czy kość jest dostępna, typu logicznego.

    Wszystkie pola są public (ogólnodostępne) (R.2.1):

    • static int InstanceCount - jedno pole wspólne dla całej klasy, nie dla każdego obiektu; będzie liczyć utworzone kości. Odwołujesz się do niego przez nazwę klasy: Kosc.InstanceCount,
    • string[] ImageFiles - siedem nazw plików w tablicy (R.2.2); [ ... ] to wyrażenie kolekcji z C# 12. Indeks w tablicy równa się liczbie oczek: ImageFiles[3] to "kosc3.png",
    • int Pips - liczba oczek, int ImageId - indeks pliku w tablicy, bool Available - czy kość jest dostępna (R.1.5).

    Nazwy mówią, co przechowują (R.1.4): pips to po angielsku oczka na kości.

    konsola/Kosc.cs C# nowy plik
    namespace konsola;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    }

    Wynik programu bez zmian - taki jak po kroku 2.

  4. Krok 4: Konstruktor z argumentem: zakres, przypisanie, dostępność, licznik

    Kryteria: , , ,

    R.2.3 Konstruktor bezparametrowy losuje liczbę i przypisuje ją do pola wartości liczby oczek i pola identyfikatora pliku. Konstruktor z jednym argumentem przypisuje liczbie oczek i identyfikatorowi pliku: - wartość argumentu, gdy argument jest z zakresu <1, 6> - 0, w przeciwnym wypadku (w języku Python jeden konstruktor z domyślną wartością argumentu)

    R.2.4 W przynajmniej jednym konstruktorze jest inkrementowane pole statyczne oraz polu logicznemu przypisywana jest wartość true

    R.1.1 Kod źródłowy zapisany w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej

    R.1.2 Kod zapisany z wcięciami dla zagnieżdżeń bloków

    ok. 4 min
    Polecenie z arkusza

    Konstruktory klasy: jednoargumentowy, którego argument jest wartością wyrzuconej kości. W przypadku, gdy wartość argumentu jest inna niż 1, 2, 3, 4, 5 lub 6, ustawia wartość na 0. Przypisuje liczbie oczek i identyfikatorowi pliku wartość argumentu. Przypisuje wartość polu logicznemu: kość jest dostępna. Inkrementuje zmienną statyczną zliczającą instancje klasy.

    Najpierw pilnujesz zakresu: liczba spoza 1-6 zamienia się w 0 - indeks obrazu kosc0.png (R.2.3). Potem ta sama wartość trafia do Pips i ImageId, kość staje się dostępna (Available = true), a licznik instancji rośnie o 1 (R.2.4).

    Konstruktor ma nazwę klasy i nie ma typu zwracanego. Parametr value nadpisujesz wewnątrz konstruktora - to zwykła zmienna lokalna, zmiana nie wychodzi poza konstruktor.

    Typowe błędy

    Warunek value < 1 && value > 6 nigdy nie jest prawdziwy - liczba 9 przechodzi i ImageFiles[9] kończy program wyjątkiem IndexOutOfRangeException.

    konsola/Kosc.cs C# zmienione: 12 linii
    namespace konsola;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    
        public Kosc(int value)
        {
            if (value < 1 || value > 6)
            {
                value = 0;
            }
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    }

    Wynik programu bez zmian - taki jak po kroku 2.

  5. Krok 5: Konstruktor bez argumentu: losowanie 1-6

    Kryteria: ,

    R.2.3 Konstruktor bezparametrowy losuje liczbę i przypisuje ją do pola wartości liczby oczek i pola identyfikatora pliku. Konstruktor z jednym argumentem przypisuje liczbie oczek i identyfikatorowi pliku: - wartość argumentu, gdy argument jest z zakresu <1, 6> - 0, w przeciwnym wypadku (w języku Python jeden konstruktor z domyślną wartością argumentu)

    R.2.4 W przynajmniej jednym konstruktorze jest inkrementowane pole statyczne oraz polu logicznemu przypisywana jest wartość true

    ok. 3 min
    Polecenie z arkusza

    Bezargumentowy: losuje liczbę pseudolosową z zakresu od 1 do 6, przypisuje liczbie oczek i identyfikatorowi pliku wylosowaną liczbę, przypisuje wartość polu logicznemu: kość jest dostępna, inkrementuje zmienną statyczną zliczającą instancje klasy.

    Drugi konstruktor - przeciążenie o pustej liście parametrów. Random.Shared.Next(1, 7) losuje od 1 do 6: dolna granica jest włączona, górna wyłączona. Random.Shared to wspólny generator .NET - nie trzeba tworzyć własnego obiektu Random w każdej kości (kości tworzone tuż po sobie z osobnymi generatorami mogłyby w starszych wersjach .NET wylosować to samo). Dalej to samo co w pierwszym konstruktorze (R.2.3, R.2.4).

    Typowe błędy

    Random.Shared.Next(6) losuje 0-5: kość z zerem oczek, a szóstka nigdy nie wypada.

    konsola/Kosc.cs C# zmienione: 9 linii
    namespace konsola;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    
        public Kosc(int value)
        {
            if (value < 1 || value > 6)
            {
                value = 0;
            }
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public Kosc()
        {
            int value = Random.Shared.Next(1, 7);
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    }

    Wynik programu bez zmian - taki jak po kroku 2.

  6. Krok 6: Metody Roll i Block

    Kryteria: , ,

    R.2.5 Metoda realizująca rzut kością losuje wartość z zakresu <1,6> i przypisuje ją do pola wartości liczby oczek i pola identyfikatora pliku tylko, gdy pole dostępności ma wartość true

    R.2.6 Metoda blokująca kość jest bezparametrowa, nie zwraca wartości i ustawia pole logiczne na false

    R.1.3 Polskie lub angielskie nazewnictwo metod. Nazewnictwo jest znaczące

    ok. 3 min
    Polecenie z arkusza

    Metoda ogólnodostępna, bezparametrowa, niezwracająca wartości, która realizuje rzut kością tylko, gdy kość jest dostępna: losuje wartość z zakresu od 1 do 6, przypisuje wylosowaną wartość do pola liczby oczek i identyfikatora pliku graficznego. Metoda ogólnodostępna, bezparametrowa, niezwracająca wartości, która blokuje kość: ustawiana jest informacja, że kość jest niedostępna.

    Obie metody są public void bez parametrów (R.1.3):

    • Roll losuje i przypisuje wartość tylko, gdy Available jest true - zablokowana kość zachowuje swoje oczka (R.2.5),
    • Block ustawia Available = false (R.2.6).
    konsola/Kosc.cs C# zmienione: 15 linii
    namespace konsola;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    
        public Kosc(int value)
        {
            if (value < 1 || value > 6)
            {
                value = 0;
            }
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public Kosc()
        {
            int value = Random.Shared.Next(1, 7);
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public void Roll()
        {
            if (Available)
            {
                int value = Random.Shared.Next(1, 7);
                Pips = value;
                ImageId = value;
            }
        }
    
        public void Block()
        {
            Available = false;
        }
    }

    Wynik programu bez zmian - taki jak po kroku 2.

  7. Krok 7: Metoda ToWords: wartość słownie

    Kryteria:

    R.2.7 Istnieje metoda zwracająca wartość wyrzuconą na kości w postaci słownej np. "jeden" (w przypadku wartości spoza zakresu <1, 6> - dowolnie)

    ok. 3 min
    Polecenie z arkusza

    Metoda ogólnodostępna, bezparametrowa, zwracająca wartość wyrzuconą na kości w postaci tekstu (np. gdy wartość jest równa 3, zwracane jest „trzy”).

    Metoda zwraca string (R.2.7). Zamiast sześciu case z return używasz wyrażenia switch: każde ramię wartość => wynik daje napis, a _ łapie wszystko inne - tu jedyną inną możliwą wartość, 0 z konstruktora z liczbą spoza zakresu. Kompilator sprawdza, że wyrażenie zawsze coś zwraca.

    konsola/Kosc.cs C# zmienione: 14 linii
    namespace konsola;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    
        public Kosc(int value)
        {
            if (value < 1 || value > 6)
            {
                value = 0;
            }
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public Kosc()
        {
            int value = Random.Shared.Next(1, 7);
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public void Roll()
        {
            if (Available)
            {
                int value = Random.Shared.Next(1, 7);
                Pips = value;
                ImageId = value;
            }
        }
    
        public void Block()
        {
            Available = false;
        }
    
        public string ToWords()
        {
            return Pips switch
            {
                1 => "jeden",
                2 => "dwa",
                3 => "trzy",
                4 => "cztery",
                5 => "pięć",
                6 => "sześć",
                _ => "zero"
            };
        }
    }

    Wynik programu bez zmian - taki jak po kroku 2.

  8. Krok 8: Program główny: pierwsza kość i jej opis

    Kryteria: ,

    R.2.9 Uruchomiony program inicjuje jeden obiekt konstruktorem bezargumentowym, drugi jednoargumentowym. Po każdej inicjacji wyświetlana jest liczba instancji. Dla każdego obiektu wyświetlana jest wartość wyrzucona kością w postaci liczbowej i napisowej oraz odpowiadająca jej nazwa pliku (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.1.7 Program nawiązuje zrozumiałą komunikację z użytkownikiem, wyświetlane komunikaty są znaczące (jeżeli kod nie uruchamia się z powodu błędów kompilacji - sprawdzić w kodzie aplikacji)

    ok. 4 min
    Polecenie z arkusza

    Sprawdź działanie klasy w programie głównym: należy utworzyć dwa obiekty klasy Kosc, każdy za pomocą innego konstruktora. Po utworzeniu każdego obiektu należy wyświetlić: liczbę utworzonych instancji klasy, informację o liczbie oczek wyrzuconych kością (w postaci liczbowej i napisowej), nazwę pliku odpowiadającego wyrzuconej liczbie oczek. Informacja powinna być zrozumiała dla użytkownika.

    using konsola; udostępnia klasę z przestrzeni nazw projektu. Pierwszą kość tworzysz konstruktorem bez argumentu (R.2.9). Trzy informacje o kości wypiszesz po każdej z dwóch kości, więc od razu zamykasz je w funkcji lokalnej PrintDie - bez powtarzania tych samych trzech linii (R.1.7):

    • liczba instancji - pole statyczne czytasz przez nazwę klasy: Kosc.InstanceCount,
    • oczka liczbowo i słownie: die.Pips i die.ToWords(),
    • nazwa pliku: element tablicy ImageFiles o indeksie ImageId.

    Podgląd to prawdziwe uruchomienie.

    konsola/Program.cs C# zmienione: 12 linii
    using konsola;
    
    Kosc firstDie = new Kosc();
    Console.WriteLine("Pierwsza kość (rzut losowy, konstruktor bez argumentu)");
    PrintDie(firstDie);
    
    static void PrintDie(Kosc die)
    {
        Console.WriteLine($"Liczba utworzonych kości: {Kosc.InstanceCount}");
        Console.WriteLine($"Wyrzucono: {die.Pips} ({die.ToWords()})");
        Console.WriteLine($"Plik obrazu: {die.ImageFiles[die.ImageId]}");
    }
    Wynik programu po tym kroku
    > konsola.exe
    Pierwsza kość (rzut losowy, konstruktor bez argumentu)
    Liczba utworzonych kości: 1
    Wyrzucono: 3 (trzy)
    Plik obrazu: kosc3.png
  9. Krok 9: Druga kość z liczbą z klawiatury

    Kryteria: , ,

    R.2.9 Uruchomiony program inicjuje jeden obiekt konstruktorem bezargumentowym, drugi jednoargumentowym. Po każdej inicjacji wyświetlana jest liczba instancji. Dla każdego obiektu wyświetlana jest wartość wyrzucona kością w postaci liczbowej i napisowej oraz odpowiadająca jej nazwa pliku (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.1.7 Program nawiązuje zrozumiałą komunikację z użytkownikiem, wyświetlane komunikaty są znaczące (jeżeli kod nie uruchamia się z powodu błędów kompilacji - sprawdzić w kodzie aplikacji)

    R.2.8 Program uruchamia się w konsoli, co jest widoczne na zrzucie ekranu

    ok. 3 min
    Polecenie z arkusza

    W przypadku konstruktora jednoargumentowego liczba przekazana jako argument ma być pobrana z klawiatury.

    Liczbę czytasz przez int.TryParse(Console.ReadLine(), out int value): gdy wpisany tekst nie jest liczbą, value dostaje 0, a program się nie wywraca - konstruktor i tak zamienia wartości spoza 1-6 na 0. Drugą kość tworzysz konstruktorem z argumentem i wypisujesz tą samą funkcją (R.2.9, R.1.7).

    Podgląd to trzy prawdziwe uruchomienia - wszystkie interakcje, jakie arkusz każe udokumentować zrzutami: liczba z zakresu (4), liczba spoza zakresu (9) i tekst („trzy”). Licznik instancji rośnie z 1 na 2.

    konsola/Program.cs C# zmienione: 7 linii
    using konsola;
    
    Kosc firstDie = new Kosc();
    Console.WriteLine("Pierwsza kość (rzut losowy, konstruktor bez argumentu)");
    PrintDie(firstDie);
    Console.WriteLine();
    
    Console.Write("Podaj liczbę oczek drugiej kości (1-6): ");
    int.TryParse(Console.ReadLine(), out int value);
    Kosc secondDie = new Kosc(value);
    Console.WriteLine("Druga kość (wartość z klawiatury, konstruktor z argumentem)");
    PrintDie(secondDie);
    
    static void PrintDie(Kosc die)
    {
        Console.WriteLine($"Liczba utworzonych kości: {Kosc.InstanceCount}");
        Console.WriteLine($"Wyrzucono: {die.Pips} ({die.ToWords()})");
        Console.WriteLine($"Plik obrazu: {die.ImageFiles[die.ImageId]}");
    }
    Wynik programu po tym kroku
    > konsola.exe
    Pierwsza kość (rzut losowy, konstruktor bez argumentu)
    Liczba utworzonych kości: 1
    Wyrzucono: 3 (trzy)
    Plik obrazu: kosc3.png
    
    Podaj liczbę oczek drugiej kości (1-6): 4
    Druga kość (wartość z klawiatury, konstruktor z argumentem)
    Liczba utworzonych kości: 2
    Wyrzucono: 4 (cztery)
    Plik obrazu: kosc4.png
    
    > konsola.exe
    Pierwsza kość (rzut losowy, konstruktor bez argumentu)
    Liczba utworzonych kości: 1
    Wyrzucono: 3 (trzy)
    Plik obrazu: kosc3.png
    
    Podaj liczbę oczek drugiej kości (1-6): 9
    Druga kość (wartość z klawiatury, konstruktor z argumentem)
    Liczba utworzonych kości: 2
    Wyrzucono: 0 (zero)
    Plik obrazu: kosc0.png
    
    > konsola.exe
    Pierwsza kość (rzut losowy, konstruktor bez argumentu)
    Liczba utworzonych kości: 1
    Wyrzucono: 5 (pięć)
    Plik obrazu: kosc5.png
    
    Podaj liczbę oczek drugiej kości (1-6): trzy
    Druga kość (wartość z klawiatury, konstruktor z argumentem)
    Liczba utworzonych kości: 2
    Wyrzucono: 0 (zero)
    Plik obrazu: kosc0.png
  10. Krok 10: Zrzuty konsola1.png i konsola2.png, folder konsolowa

    Kryteria: , ,

    R.1.6 Podjęta próba uruchomienia kodu, udokumentowana zrzutem przedstawiającym uruchomiony program lub jego kompilację

    R.2.8 Program uruchamia się w konsoli, co jest widoczne na zrzucie ekranu

    R.4.6 Istnieje przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji konsolowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 3 min
    Polecenie z arkusza

    Wykonaj zrzuty ekranu dokumentujące wykonanie programu w konsoli. Zrzuty ekranu powinny obejmować cały obszar ekranu, z widocznym paskiem zadań oraz środowiskiem programistycznym. Liczba zrzutów ekranu powinna odpowiadać wszystkim możliwym interakcjom użytkownika z programem. Zrzuty ekranu zapisz w folderze konsolowa pod nazwami konsola1.png, konsola2.png, itd. W folderze konsolowa powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, skopiowany z projektu plik z kodem źródłowym programu, plik wykonywalny, jeżeli istnieje oraz zrzuty ekranu.

    „Wszystkie możliwe interakcje” to tu trzy przypadki z poprzedniego kroku: liczba 1-6, liczba spoza zakresu i tekst. Każdy uruchamiasz w Visual Studio (Ctrl+F5) i robisz zrzut całego ekranu - konsola1.png, konsola2.png, konsola3.png (R.1.6, R.2.8, R.4.6).

    W folderze konsolowa: konsola.zip, Kosc.cs, Program.cs, plik wykonywalny konsola.exe z bin\Debug\net8.0 razem z konsola.dll i zrzuty (R.4.9).

    Wynik programu bez zmian - taki jak po kroku 9.

  11. Etap 3: Aplikacja mobilna

    Kroki 11-17 · po tym etapie masz 28 z 35 kryteriów (pkt)

    Krok 11: Nowy projekt Kosci (.NET MAUI), obrazy i klasa Kosc

    ok. 4 min
    Polecenie z arkusza

    Za pomocą środowiska programistycznego dostępnego na stanowisku egzaminacyjnym wykonaj aplikację mobilną do gry w kości oraz uruchom ją w dostępnym emulatorze systemu mobilnego. W programie można wykorzystać klasę Kosc z aplikacji konsolowej.

    W Visual Studio: Utwórz nowy projekt → Aplikacja .NET MAUI, nazwa Kosci (dotnet new maui -n Kosci). Strona MainPage jest osadzona w Shell - stąd pasek „Home”, jak na obrazie 3 arkusza.

    • obrazy kosc0.png - kosc6.png przeciągasz do Resources/Images - w XAML i w kodzie wskazujesz je po nazwie pliku,
    • plik Kosc.cs z konsoli dodajesz do projektu (Dodaj → Istniejący element...) i zmieniasz w nim przestrzeń nazw na Kosci. Reszta klasy zostaje bez zmian - to ten sam język.
    Kosci/Kosc.cs C# nowy plik
    namespace Kosci;
    
    public class Kosc
    {
        public static int InstanceCount = 0;
    
        public string[] ImageFiles = ["kosc0.png", "kosc1.png", "kosc2.png", "kosc3.png",
                                      "kosc4.png", "kosc5.png", "kosc6.png"];
        public int Pips;
        public int ImageId;
        public bool Available;
    
        public Kosc(int value)
        {
            if (value < 1 || value > 6)
            {
                value = 0;
            }
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public Kosc()
        {
            int value = Random.Shared.Next(1, 7);
            Pips = value;
            ImageId = value;
            Available = true;
            InstanceCount++;
        }
    
        public void Roll()
        {
            if (Available)
            {
                int value = Random.Shared.Next(1, 7);
                Pips = value;
                ImageId = value;
            }
        }
    
        public void Block()
        {
            Available = false;
        }
    
        public string ToWords()
        {
            return Pips switch
            {
                1 => "jeden",
                2 => "dwa",
                3 => "trzy",
                4 => "cztery",
                5 => "pięć",
                6 => "sześć",
                _ => "zero"
            };
        }
    }
    Kosci/MainPage.xaml XAML nowy plik
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage">
    
        <ScrollView>
            <VerticalStackLayout
                Padding="30,0"
                Spacing="25">
                <Image
                    Source="dotnet_bot.png"
                    HeightRequest="185"
                    Aspect="AspectFit"
                    SemanticProperties.Description="dot net bot in a submarine number ten" />
    
                <Label
                    Text="Hello, World!"
                    Style="{StaticResource Headline}"
                    SemanticProperties.HeadingLevel="Level1" />
    
                <Label
                    Text="Welcome to &#10;.NET Multi-platform App UI"
                    Style="{StaticResource SubHeadline}"
                    SemanticProperties.HeadingLevel="Level2"
                    SemanticProperties.Description="Welcome to dot net Multi platform App U I" />
    
                <Button
                    x:Name="CounterBtn"
                    Text="Click me" 
                    SemanticProperties.Hint="Counts the number of times you click"
                    Clicked="OnCounterClicked"
                    HorizontalOptions="Fill" />
            </VerticalStackLayout>
        </ScrollView>
    
    </ContentPage>
    Kosci/MainPage.xaml.cs C# nowy plik
    namespace Kosci;
    
    public partial class MainPage : ContentPage
    {
    	int count = 0;
    
    	public MainPage()
    	{
    		InitializeComponent();
    	}
    
    	private void OnCounterClicked(object? sender, EventArgs e)
    	{
    		count++;
    
    		if (count == 1)
    			CounterBtn.Text = $"Clicked {count} time";
    		else
    			CounterBtn.Text = $"Clicked {count} times";
    
    		SemanticScreenReader.Announce(CounterBtn.Text);
    	}
    }
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  12. Krok 12: Pięć obrazów kosc0.png w dwóch rzędach

    Kryteria: , ,

    R.3.1 Zastosowany język znaczników XML/XAML lub inny do opisu interfejsu użytkownika oraz zdefiniowano przynajmniej jedną kontrolkę

    R.3.3 Zastosowane marginesy zewnętrzne dla obrazów i przycisku 10, wyśrodkowano wszystkie elementy widoku

    R.3.7 W stanie początkowym aplikacji wszystkie obrazy wyświetlają grafikę kosc0.png bez przezroczystości, przycisk jest opisany „RZUT” oraz pole tekstowe zawiera napis 0 (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    ok. 4 min
    Polecenie z arkusza

    Interfejs użytkownika zapisany za pomocą języka znaczników wspieranego w danym środowisku (np. XAML, XML). Zastosowany dowolny układ pozwalający na rozmieszczenie wszystkich elementów tak jak na obrazie 2 lub 3. Pięć obrazów kosc0.png bez przezroczystości (wszystkie kości są dostępne). Wielkość obrazów wyświetlających kości dopasowana do widoku z obrazu 2 lub 3. Marginesy zewnętrzne dla obrazów i przycisku 10. Wszystkie elementy widoku są wyśrodkowane.

    Usuwasz przykładową treść szablonu (i licznik z MainPage.xaml.cs). Układ z obrazu 3 to dwa rzędy kości jeden pod drugim, więc (R.3.1):

    • VerticalStackLayout z VerticalOptions="Center" - cała zawartość stoi na środku ekranu w pionie,
    • w nim dwa HorizontalStackLayout - rząd z dwiema i rząd z trzema kośćmi; HorizontalOptions="Center" stawia każdy rząd na środku w poziomie (R.3.3),
    • pięć kontrolek Image z obrazem kosc0.png, rozmiar 80 × 80 i margines zewnętrzny Margin="10" (R.3.3, R.3.7). Każda ma nazwę Die1 ... Die5 - pod nią kod dostanie kontrolkę.
    Kosci/MainPage.xaml XAML zmienione: 11 linii
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage">
    
        <VerticalStackLayout VerticalOptions="Center">
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die1" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die2" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die3" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die4" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die5" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
        </VerticalStackLayout>
    
    </ContentPage>
    Kosci/MainPage.xaml.cs C# zmienione: 4 linie
    namespace Kosci;
    
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  13. Krok 13: Przycisk RZUT i pole tekstowe z wynikiem 0

    Kryteria: ,

    R.3.7 W stanie początkowym aplikacji wszystkie obrazy wyświetlają grafikę kosc0.png bez przezroczystości, przycisk jest opisany „RZUT” oraz pole tekstowe zawiera napis 0 (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.3.3 Zastosowane marginesy zewnętrzne dla obrazów i przycisku 10, wyśrodkowano wszystkie elementy widoku

    ok. 2 min
    Polecenie z arkusza

    Przycisk z napisem „RZUT”. Pole tekstowe z wynikiem rzutu z wartością 0, zawierające sumę oczek pięciu kości. Rozmieszczenie elementów zgodne z obrazami 2 lub 3.

    Pod kośćmi przycisk „RZUT” z marginesem 10 i napis ResultLabel z wartością startową 0 (R.3.7). Oba wyśrodkowane HorizontalOptions="Center" - w VerticalStackLayout bez tego rozciągnęłyby się na całą szerokość (R.3.3). Układ jest już taki jak na obrazie 3 (R.3.6).

    Kosci/MainPage.xaml XAML zmienione: 8 linii
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage">
    
        <VerticalStackLayout VerticalOptions="Center">
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die1" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die2" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die3" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die4" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die5" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <Button Text="RZUT"
                    HorizontalOptions="Center"
                    Margin="10" />
    
            <Label x:Name="ResultLabel"
                   Text="0"
                   HorizontalOptions="Center" />
        </VerticalStackLayout>
    
    </ContentPage>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  14. Krok 14: Tło strony, tło przycisku i czcionka 40

    Kryteria:

    R.3.2 Zastosowane kolory tła: okna lub rozkładu: #ED27C121, przycisku: #ED275021, rozmiar czcionki pola tekstowego ustawiono na 40

    ok. 2 min
    Polecenie z arkusza

    Tło okna / rozkładu: #ED27C121, tło przycisku: #ED275021. Rozmiar czcionki pola tekstowego 40.

    • tło strony BackgroundColor="#ED27C121" i przycisku #ED275021 (R.3.2). Kolor ma osiem cyfr szesnastkowych: w MAUI, tak jak w Androidzie, pierwsze dwie (ED) to przezroczystość (237 z 255, czyli ok. 93% krycia), a dopiero dalej czerwony, zielony i niebieski. W CSS byłoby odwrotnie - przezroczystość na końcu,
    • FontSize="40" dla wyniku (R.3.2).
    Kosci/MainPage.xaml XAML zmienione: 4 linie
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage"
                 BackgroundColor="#ED27C121">
    
        <VerticalStackLayout VerticalOptions="Center">
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die1" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die2" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die3" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die4" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die5" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <Button Text="RZUT"
                    BackgroundColor="#ED275021"
                    HorizontalOptions="Center"
                    Margin="10" />
    
            <Label x:Name="ResultLabel"
                   Text="0"
                   FontSize="40"
                   HorizontalOptions="Center" />
        </VerticalStackLayout>
    
    </ContentPage>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  15. Krok 15: RZUT: losowanie dostępnych kości, obrazy i suma

    Kryteria: , , ,

    R.3.4 Dla przycisku oraz dla przynajmniej jednego obrazu zdefiniowana metoda kliknięcia, która jest powiązana z kontrolką

    R.3.5 Program odwołuje się do przynajmniej jednej kontrolki w sposób zgodny z danym środowiskiem programistycznym

    R.3.8 Kliknięcie przycisku powoduje, że są losowane wartości tylko dla kostek dostępnych, odpowiadające wartościom obrazy są wyświetlane (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.3.9 Kliknięcie przycisku powoduje wyświetlenie pod nim sumy wszystkich oczek na wyświetlanych obrazach (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    ok. 4 min
    Polecenie z arkusza

    Działanie aplikacji po wciśnięciu przycisku RZUT: dla dostępnych kości losowana jest liczba oczek każdej z nich. W kontrolkach obrazu wyświetlana jest grafika odpowiadająca liczbie oczek na każdej kości. Liczona jest suma oczek z wszystkich kości i wyświetlana w polu tekstowym.

    Stan gry trzymasz w polach strony: tablica pięciu obiektów Kosc i tablica pięciu kontrolek obrazu. Do kontrolek odwołujesz się po nazwach z x:Name (Die1 ... Die5, ResultLabel) - kompilator tworzy z nich pola klasy (R.3.5). W konstruktorze, po InitializeComponent(), tworzysz kości new Kosc(0) - wartość 0 to obraz kosc0.png, kość dostępna, jak w stanie początkowym (R.3.7).

    Kliknięcie wiążesz w XAML: Clicked="OnRollClicked" (R.3.4). W metodzie dla każdej kości wywołujesz Roll() - losuje tylko dostępne kości, bo tak została napisana w konsoli (R.3.8) - wstawiasz obraz o nazwie z tablicy ImageFiles i dodajesz oczka do sumy (R.3.9). Kliknij „RZUT” w podglądzie.

    Kosci/MainPage.xaml XAML zmienione: 1 linia
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage"
                 BackgroundColor="#ED27C121">
    
        <VerticalStackLayout VerticalOptions="Center">
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die1" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die2" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die3" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die4" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
                <Image x:Name="Die5" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10" />
            </HorizontalStackLayout>
    
            <Button Text="RZUT"
                    BackgroundColor="#ED275021"
                    Clicked="OnRollClicked"
                    HorizontalOptions="Center"
                    Margin="10" />
    
            <Label x:Name="ResultLabel"
                   Text="0"
                   FontSize="40"
                   HorizontalOptions="Center" />
        </VerticalStackLayout>
    
    </ContentPage>
    Kosci/MainPage.xaml.cs C# zmienione: 20 linii
    namespace Kosci;
    
    public partial class MainPage : ContentPage
    {
        private readonly Kosc[] dice = new Kosc[5];
        private readonly Image[] diceImages;
    
        public MainPage()
        {
            InitializeComponent();
            diceImages = [Die1, Die2, Die3, Die4, Die5];
            for (int i = 0; i < dice.Length; i++)
            {
                dice[i] = new Kosc(0);
            }
        }
    
        private void OnRollClicked(object? sender, EventArgs e)
        {
            int sum = 0;
            for (int i = 0; i < dice.Length; i++)
            {
                dice[i].Roll();
                diceImages[i].Source = dice[i].ImageFiles[dice[i].ImageId];
                sum += dice[i].Pips;
            }
            ResultLabel.Text = sum.ToString();
        }
    }
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  16. Krok 16: Dotknięcie kości: blokada z przezroczystością i odblokowanie

    Kryteria: ,

    R.3.4 Dla przycisku oraz dla przynajmniej jednego obrazu zdefiniowana metoda kliknięcia, która jest powiązana z kontrolką

    R.3.10 Dla wszystkich kości: gdy dana kość jest wyświetlana bez przezroczystości, po kliknięciu widoczna jest przezroczystość. Po kolejnym kliknięciu, kość jest wyświetlana bez przezroczystości (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    ok. 4 min
    Polecenie z arkusza

    Działanie aplikacji po kliknięciu na dowolny obraz kości: jeżeli kość jest dostępna: jest ustawiana na niedostępną i przezroczystość obrazu jest ustawiana na 50%. Jeżeli kość jest niedostępna: jest ustawiana na dostępną i obraz jest wyświetlany bez przezroczystości.

    Kontrolka Image nie ma zdarzenia kliknięcia - dotknięcie rozpoznaje TapGestureRecognizer dodany do obrazu w Image.GestureRecognizers. Wszystkie pięć obrazów wskazuje tę samą metodę OnDieTapped (R.3.4).

    Który obraz dotknięto? Parametr sender to kontrolka, która wywołała zdarzenie; Array.IndexOf(diceImages, sender) daje jej pozycję w tablicy, a tym samym kość o tym samym indeksie. Dalej (R.3.10):

    • dostępną kość blokujesz metodą Block() i ustawiasz Opacity = 0.5 - 50% przezroczystości,
    • zablokowaną odblokowujesz (Available = true - pole jest publiczne) i wracasz do Opacity = 1.

    W podglądzie dotknij dwóch kości i kliknij „RZUT” - zablokowane zachowują oczka, jak na obrazie 3 (stan 3).

    Typowe błędy

    Ustawienie samego Opacity bez Block() - kość wygląda na zablokowaną, ale Roll() dalej ją losuje (R.3.8).

    Kosci/MainPage.xaml XAML zmienione: 25 linii
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="Kosci.MainPage"
                 BackgroundColor="#ED27C121">
    
        <VerticalStackLayout VerticalOptions="Center">
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die1" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnDieTapped" />
                    </Image.GestureRecognizers>
                </Image>
                <Image x:Name="Die2" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnDieTapped" />
                    </Image.GestureRecognizers>
                </Image>
            </HorizontalStackLayout>
    
            <HorizontalStackLayout HorizontalOptions="Center">
                <Image x:Name="Die3" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnDieTapped" />
                    </Image.GestureRecognizers>
                </Image>
                <Image x:Name="Die4" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnDieTapped" />
                    </Image.GestureRecognizers>
                </Image>
                <Image x:Name="Die5" Source="kosc0.png" WidthRequest="80" HeightRequest="80" Margin="10">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnDieTapped" />
                    </Image.GestureRecognizers>
                </Image>
            </HorizontalStackLayout>
    
            <Button Text="RZUT"
                    BackgroundColor="#ED275021"
                    Clicked="OnRollClicked"
                    HorizontalOptions="Center"
                    Margin="10" />
    
            <Label x:Name="ResultLabel"
                   Text="0"
                   FontSize="40"
                   HorizontalOptions="Center" />
        </VerticalStackLayout>
    
    </ContentPage>
    Kosci/MainPage.xaml.cs C# zmienione: 16 linii
    namespace Kosci;
    
    public partial class MainPage : ContentPage
    {
        private readonly Kosc[] dice = new Kosc[5];
        private readonly Image[] diceImages;
    
        public MainPage()
        {
            InitializeComponent();
            diceImages = [Die1, Die2, Die3, Die4, Die5];
            for (int i = 0; i < dice.Length; i++)
            {
                dice[i] = new Kosc(0);
            }
        }
    
        private void OnRollClicked(object? sender, EventArgs e)
        {
            int sum = 0;
            for (int i = 0; i < dice.Length; i++)
            {
                dice[i].Roll();
                diceImages[i].Source = dice[i].ImageFiles[dice[i].ImageId];
                sum += dice[i].Pips;
            }
            ResultLabel.Text = sum.ToString();
        }
    
        private void OnDieTapped(object? sender, TappedEventArgs e)
        {
            int index = Array.IndexOf(diceImages, sender);
            Kosc die = dice[index];
            if (die.Available)
            {
                die.Block();
                diceImages[index].Opacity = 0.5;
            }
            else
            {
                die.Available = true;
                diceImages[index].Opacity = 1;
            }
        }
    }
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  17. Krok 17: Uruchomienie w emulatorze, zrzuty i folder mobilna

    Kryteria: ,

    R.3.6 Aplikacja kompiluje się i uruchamia w emulatorze, co jest udokumentowane zrzutem ekranu, układ wszystkich kontrolek jest zgodny z obrazem 2 lub 3 w arkuszu egzaminacyjnym (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.4.7 Istnieje przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji mobilnej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 4 min
    Polecenie z arkusza

    Podejmij próbę kompilacji i emulacji aplikacji. Wykonaj zrzuty ekranowe dokumentujące wszystkie stany aplikacji. Zrzuty zapisz w folderze mobilna pod nazwami mobilna1.png, mobilna2.png, itd. Wszystkie zrzuty muszą zawierać cały obszar ekranu z paskiem zadań i środowiskiem programistycznym. W folderze mobilna powinno znaleźć się archiwum całego folderu projektu oraz skopiowane pliki z kodem źródłowym, które były modyfikowane oraz pliki z zrzutami.

    Uruchamiasz aplikację (F5) na emulatorze Androida. Zrzuty całego ekranu z Visual Studio: mobilna1 - stan początkowy, mobilna2 - po rzucie, mobilna3 - po zablokowaniu kilku kości i kolejnym rzucie (R.3.6, R.4.7).

    W folderze mobilna: archiwum projektu (bez folderów bin i obj - to wyniki budowania), MainPage.xaml, MainPage.xaml.cs, Kosc.cs i zrzuty (R.4.9).

    Ekran telefonu bez zmian - taki jak po kroku 16.

  18. Etap 4: Testy jednostkowe

    Kroki 18-22 · po tym etapie masz 34 z 35 kryteriów (pkt)

    Krok 18: Projekt testów MSTest z odwołaniem do konsoli

    Kryteria:

    R.4.1 Co najmniej w jednej metodzie testującej została wykorzystana asercja, nazwy metod odzwierciedlają cel testu, zastosowane narzędzie testujące odpowiednie dla języka

    ok. 3 min
    Polecenie z arkusza

    Wykonaj testy jednostkowe aplikacji konsolowej, metody realizującej rzut kością. Do napisania testów wykorzystaj bibliotekę do testów jednostkowych dostępną na stanowisku egzaminacyjnym dla języka programowania, w którym została napisana klasa Kosc.

    Dla C# w Visual Studio jest gotowy szablon Projekt testowy MSTest (dotnet new mstest -n testy); dodajesz go do rozwiązania obok projektu konsola. Testy muszą widzieć klasę Kosc, więc w projekcie testów dodajesz odwołanie do projektu konsola (Dodaj → Odwołanie do projektu...) - w pliku testy.csproj pojawia się ProjectReference.

    Przykładowy Test1.cs szablonu zastępujesz plikiem KoscTests.cs: klasa testów z atrybutem [TestClass], a using konsola; udostępnia klasę Kosc (R.4.1).

    testy/KoscTests.cs C# nowy plik
    using konsola;
    
    namespace testy;
    
    [TestClass]
    public sealed class KoscTests
    {
    }
    testy/testy.csproj Projekt C# nowy plik
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net10.0</TargetFramework>
        <LangVersion>latest</LangVersion>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="MSTest" Version="4.0.1" />
      </ItemGroup>
    
      <ItemGroup>
        <ProjectReference Include="..\konsola\konsola.csproj" />
      </ItemGroup>
    
      <ItemGroup>
        <Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
      </ItemGroup>
    
    </Project>
    Wynik programu po tym kroku
    > dotnet new mstest -n testy
    Pomyślnie utworzono szablon "MSTest — projekt testowy".
  19. Krok 19: Test 1: wynik rzutu z zakresu 1-6

    Kryteria: ,

    R.4.1 Co najmniej w jednej metodzie testującej została wykorzystana asercja, nazwy metod odzwierciedlają cel testu, zastosowane narzędzie testujące odpowiednie dla języka

    R.4.2 W teście metody realizującej rzut kością sprawdzone czy wartość jest z zakresu <1,6>

    ok. 3 min
    Polecenie z arkusza

    W testach jednostkowych należy rozważyć następujące przypadki testowe: czy wyrzucona wartość jest w zakresie od 1 do 6. W testach nazwy metod testujących należy dobrać tak, aby wyrażały cel danego testu. Nazwy metod należy zapisać zgodnie z konwencją nazewnictwa w danym języku programowania. Każdy przypadek testowy powinien być sprawdzany oddzielną metodą.

    Metoda testowa ma atrybut [TestMethod], jest public void i bez parametrów. Nazwa w konwencji C# - wielką literą, z podkreśleniem między testowaną metodą a oczekiwanym zachowaniem: Roll_ReturnsValueFromOneToSix (R.4.1).

    Jeden rzut niczego nie dowodzi - wynik jest losowy. Rzucasz 1000 razy i po każdym rzucie sprawdzasz asercją Assert.IsTrue, że oczka mieszczą się w 1-6 (R.4.2). Komunikat z liczbą oczek pokaże się, gdyby test się nie powiódł.

    testy/KoscTests.cs C# zmienione: 10 linii
    using konsola;
    
    namespace testy;
    
    [TestClass]
    public sealed class KoscTests
    {
        [TestMethod]
        public void Roll_ReturnsValueFromOneToSix()
        {
            Kosc die = new Kosc();
            for (int i = 0; i < 1000; i++)
            {
                die.Roll();
                Assert.IsTrue(die.Pips >= 1 && die.Pips <= 6, $"Wyrzucono {die.Pips}");
            }
        }
    }

    Wynik programu bez zmian - taki jak po kroku 18.

  20. Krok 20: Test 2: zablokowana kość się nie zmienia

    Kryteria:

    R.4.3 W teście metody realizującej rzut kością sprawdzone czy wartość się nie zmienia jeżeli pole dostępność jest ustawione na false

    ok. 3 min
    Polecenie z arkusza

    Czy wartość na kości pozostaje bez zmian jeżeli kość nie jest dostępna.

    Kość tworzysz z ustaloną wartością 4 (konstruktor z argumentem) - wtedy wiesz, czego oczekiwać. Blokujesz ją i rzucasz 100 razy; Assert.AreEqual(4, die.Pips) porównuje wartość oczekiwaną (pierwszy argument) z rzeczywistą (R.4.3). Gdyby Roll losował mimo blokady, test padłby niemal na pewno już w pierwszych rzutach.

    testy/KoscTests.cs C# zmienione: 12 linii
    using konsola;
    
    namespace testy;
    
    [TestClass]
    public sealed class KoscTests
    {
        [TestMethod]
        public void Roll_ReturnsValueFromOneToSix()
        {
            Kosc die = new Kosc();
            for (int i = 0; i < 1000; i++)
            {
                die.Roll();
                Assert.IsTrue(die.Pips >= 1 && die.Pips <= 6, $"Wyrzucono {die.Pips}");
            }
        }
    
        [TestMethod]
        public void Roll_DoesNotChangeValue_WhenDieIsBlocked()
        {
            Kosc die = new Kosc(4);
            die.Block();
            for (int i = 0; i < 100; i++)
            {
                die.Roll();
                Assert.AreEqual(4, die.Pips);
            }
        }
    }

    Wynik programu bez zmian - taki jak po kroku 18.

  21. Krok 21: Uruchomienie wszystkich testów i zrzuty test1.png

    Kryteria: ,

    R.4.4 Uruchomiona przynajmniej jedna metoda testująca udokumentowana zrzutem ekranu

    R.4.5 Wynik uruchomionego testu z R.4.2 lub R.4.3 wynika z danych testowych co jest udokumentowane zrzutem ekranu

    ok. 3 min
    Polecenie z arkusza

    Po napisaniu metod testujących uruchom wszystkie metody. Wykonaj zrzuty ekranu dokumentujące uruchomienie testów, zrzuty zapisz w katalogu testy pod nazwami test1.png, test2.png, itd. Na zrzutach powinny być widoczne wyniki działania metod testujących. Zrzuty ekranu powinny obejmować cały obszar ekranu, z widocznym paskiem zadań oraz środowiskiem programistycznym.

    W Visual Studio: Test → Eksplorator testów → Uruchom wszystkie testy (w terminalu: dotnet test w folderze testy). Podgląd to prawdziwy wynik: oba testy przechodzą, a wynik wynika z danych testowych - tysiąca rzutów i wartości 4 (R.4.4, R.4.5). Zrzut całego ekranu z Eksploratorem testów i zielonymi znacznikami - test1.png w folderze testy.

    Wynik programu po tym kroku
    > dotnet test
      Powodzenie Roll_ReturnsValueFromOneToSix [10 ms]
      Powodzenie Roll_DoesNotChangeValue_WhenDieIsBlocked [< 1 ms]
    Przebieg testu zakończony powodzeniem.
    Łączna liczba testów: 2
         Zakończone pomyślnie: 2
     Łączny czas: 0,9613 Sekundy
  22. Krok 22: Folder testy: projekt, plik testów, zrzut

    Kryteria:

    R.4.9 Przygotowana jest dokumentacja w postaci płyty: oba projekty są spakowane oraz zmodyfikowane pliki są na zewnątrz archiwów

    ok. 2 min
    Polecenie z arkusza

    Projekt testów przygotuj do nagrania na płytę. W folderze testy powinno znaleźć się archiwum całego folderu projektu oraz skopiowane pliki z kodem źródłowym, które były modyfikowane oraz zrzuty ekranu.

    Folder projektu testy pakujesz do archiwum (bez bin, obj i TestResults), obok kopiujesz KoscTests.cs i zrzut test1.png (R.4.9).

    Wynik programu bez zmian - taki jak po kroku 21.

  23. Etap 5: Oddanie pracy

    Kroki 23-24 · po tym etapie masz 35 z 35 kryteriów (pkt)

    Krok 23: Plik egzamin z dokumentacją narzędzi

    Kryteria:

    R.4.8 Dokumentacja zawiera: nazwę systemu operacyjnego, nazwy środowisk programistycznych, nazwy języków programowania, nazwy emulatora dla aplikacji mobilnej

    ok. 2 min
    Polecenie z arkusza

    Utwórz plik tekstowy z dokumentacją i nazwij go egzamin. Dokument powinien zawierać informacje o narzędziach wykorzystanych na egzaminie: nazwę systemu operacyjnego, nazwy środowisk programistycznych, nazwę emulatora dla aplikacji mobilnej, nazwy języków programowania. Dokument egzamin zapisz w folderze z numerem zdającego.

    Plik tekstowy egzamin.txt bezpośrednio w folderze z numerem zdającego (nie w podfolderze) - system, środowisko, emulator i języki (R.4.8).

    egzamin.txt Tekst nowy plik
    System operacyjny: Windows 11
    Środowisko programistyczne: Visual Studio (aplikacja konsolowa, testy MSTest, aplikacja .NET MAUI)
    Emulator: Android Emulator - Pixel 8 (Android 16, API 36)
    Języki programowania: C# (aplikacja konsolowa, testy i aplikacja mobilna), XAML (wygląd aplikacji mobilnej)

    Wynik programu bez zmian - taki jak po kroku 21.

  24. Krok 24: Nagranie płyty

    Kryteria:

    R.4.9 Przygotowana jest dokumentacja w postaci płyty: oba projekty są spakowane oraz zmodyfikowane pliki są na zewnątrz archiwów

    ok. 5 min
    Polecenie z arkusza

    Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować foldery: konsolowa, mobilna, testy oraz plik egzamin.

    Sprawdzasz zawartość przed nagraniem (R.4.9):

    • konsolowa: konsola.zip, Kosc.cs, Program.cs, konsola.exe z konsola.dll, konsola1.png - konsola3.png,
    • mobilna: archiwum projektu, MainPage.xaml, MainPage.xaml.cs, Kosc.cs, mobilna1.png - mobilna3.png,
    • testy: archiwum projektu, KoscTests.cs, test1.png,
    • plik egzamin.txt.

    Nagrywasz, sprawdzasz odczyt płyty, opisujesz ją numerem zdającego.

    Wynik programu bez zmian - taki jak po kroku 21.

  25. Etap 6: Porównanie z wariantem Java + Android

    Kroki 25-26 · po tym etapie masz 35 z 35 kryteriów (pkt)

    Krok 25: Te same kryteria, inny zapis

    ok. 3 min
    Polecenie z arkusza

    Zastosowany obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python.

    wymaganieC# + .NET MAUIJava + Android
    losowanie 1-6Random.Shared.Next(1, 7)random.nextInt(6) + 1
    liczba słowniewyrażenie switchinstrukcja switch z return
    układVerticalStackLayout + dwa HorizontalStackLayoutLinearLayout pionowy + dwa poziome
    kolor #ED27C121przezroczystość na początku (ARGB)tak samo (ARGB)
    kliknięcie przyciskuClicked="OnRollClicked"setOnClickListener / android:onClick
    dotknięcie obrazuTapGestureRecognizersetOnClickListener na ImageView
    przezroczystośćOpacity = 0.5setAlpha(0.5f)
    testyMSTest: [TestMethod], Assert.AreEqualJUnit 5: @Test, assertEquals

    Klasa Kosc jest w obu wariantach ta sama w konsoli i w aplikacji - w C# i w Javie przenosi się ją do projektu mobilnego, zmieniając tylko przestrzeń nazw albo pakiet.

    Wynik programu bez zmian - taki jak po kroku 21.

  26. Krok 26: Co wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Za pomocą środowiska programistycznego dostępnego na stanowisku egzaminacyjnym wykonaj aplikację mobilną do gry w kości oraz uruchom ją w dostępnym emulatorze systemu mobilnego.

    Klucz ocenia oba środowiska tymi samymi kryteriami. C# + .NET MAUI - jedno środowisko (Visual Studio) na konsolę, testy i aplikację mobilną, a błąd w nazwie metody zdarzenia wychodzi już przy kompilacji. Pierwsze budowanie projektu MAUI na Androida trwa kilka minut, więc emulator i pusty projekt warto uruchomić na samym początku egzaminu. Java + Android Studio - najczęstsze środowisko mobilne na stanowiskach; konsola i testy JUnit w IntelliJ.

    W MAUI łatwo przeoczyć, że Image nie reaguje na kliknięcia bez TapGestureRecognizer - bez niego kryterium R.3.10 nie jest spełnione.

    Wynik programu bez zmian - taki jak po kroku 21.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.