Logo Czechitas

Domovská stránka

Intenzivní kurz Programuju: Java

10 týdenní kurz Javy pro začátečnice

Vloženo: 14. 7. 2016

Úkoly na léto

Pexeso

Jsme tu s úkolem na léto. Cílem je dokončit pexeso, které jsme začali na posledních dvou lekcích Javy. Pexeso je trošku složitější aplikace, budeme ji tedy vyvíjet v několika krocích.

  1. Oživení toho, co jsme už udělali
    1. Naprogramovali jsme rozdání 8x8 kartiček na hlavní okno aplikace.
    2. Naprogramovali jsme zamíchání kartiček.
    3. Vytvořili jsme třídu Karticka, která sdružuje všechny údaje o jedné kartičce.
  2. Další postup

    Nejprve uděláme menší úklid v kódu (odborně se mluví o refactoringu).

    1. Funkcionalitu rozdání kartiček, kterou pravděpodobně máte jako obsluhu události priOtevreniOkna(...), přesuneme do samostatné metody a z obsluhy ji zavoláme:

      Původně:

      private void priOtevreniOkna(WindowEvent e) {
          // prikazy
      }
      

      Změníme na:

      public void rozdejKarticky() {
          // prikazy
      }
      
      private void priOtevreniOkna(WindowEvent e) {
          rozdejKarticky();
      }
      
    2. Podobně funcionalitu zamíchání kartiček máte v programu pravděpodobně jako obsluhu události priStiskuBtnZamichat(...). I tu separujeme do samostatné metody a tuto metodu zavoláme z obsluhy události.

      Původně:

      private void priStiskuBtnZamichat(ActionEvent e) {
          // prikazy
      }
      

      Změníme na:

      public void zamichejKarticky() {
          // puvodni prikazy
      }
      
      private void priStiskuBtnZamichat(ActionEvent e) {
          zamichejKarticky();
      }
      
    3. Podíváme se na třídu Karticka. Trošku ji zjednodušíme.

      Původně:

      public class Karticka {
      
          Integer cisloKarty;     // 0..63
          Integer cisloObrazku;   // 0..31
          Integer poziceX;        // 0..7
          Integer poziceY;        // 0..7
          Boolean jeLicemNahoru;
          ImageIcon obrazekLice;
          ImageIcon obrazekRubu;
          JButton btnKarticka;
      
      }
      

      Změníme na:

      public class Karticka {
      
          Integer cisloObrazku; // 0..31
          Boolean jeLicemNahoru;
          ImageIcon obrazekLice;
          ImageIcon obrazekRubu;
          JButton vizualniKomponenta;
      
      }
      

    Základní pravidlo úklidu v kódu (refactoringu) je, že po změnách funguje program pořád stejně. Nepřidáváme ani neodebíráme žádnou funkcionalitu. Zkuste tedy aplikaci spustit a všechno musí pořád fungovat. Každá z vás má trochu jiný program, proto se mohou objevit drobné odchylky a z nich pramenící chyby. Je nutné je opravit, než budete pokračovat dál.

    Pokud by to byl i přesto nepřekonatelný problém (a nešlo by to vyřešit naší radou například přes Facebook), můžete vyjít z mého projektu 30-Trida_Karticka.
    Najdete ho zde: Ukol_na_leto-Pexeso-zadani-heslo_czechitas.7z

  3. Přidáme funkcionalitu při kliknutí na kartičku

    Budeme chtít, aby se kartička otočila, když se na ni klikne. Jednou rubem nahoru, podruhé lícem nahoru. Pro začátek není důležité, že to není podle pravidel pexesa.

    1. Připravme si tedy následující metody (a naprogramujme jejich těla):
      public void otocKartickuLicemNahoru(Karticka karta) {
          // prikazy
      }
      
      public void otocKartickuRubemNahoru(Karticka karta) {
          // prikazy
      }
      
    2. Předchozí metody jsou ale zatím mrtvé. Nikdo je nevolá.
      Potřebujeme, aby se tyto metody volaly při kliknutí na kartičku. Součástí objektu typu Karticka je vizuální komponenta. Je typu JButton. [Najděte si její deklaraci ve tříde Kartička] Tato vizuální komponenta má událost "při kliknutí". My této události zaregistrujeme obsluhu (addActionListener(...)). Provedeme to na stejném místě, kde nastavujeme ostatní vlastnosti vizuální komponenty v metodě rozdejKarticky():
      public void rozdejKarticky() {
          // ... prikazy ...
      
          for (Integer y = 0; y < 8; y++) {
              for (Integer x = 0; x < 8; x++) {
                  Karticka novaKarta = new Karticka();
                  // ... prikazy ...
      
                  novaKarta.vizualniKomponenta.addActionListener(
                          evt -> priKliknutiNaKarticku(novaKarta));
                  // ... prikazy ...
              }
          }
      }
      
      private void priKliknutiNaKarticku(Karticka karta) {
          if (karta.jeLicemNahoru) {
              otocKartickuRubemNahoru(karta);
          } else {
              otocKartickuLicemNahoru(karta);
          }
      }
      

    Nyní by se měla každá kartička při kliknutí otáčet rubem nebo lícem nahoru.

  4. Úplné zmizení kartičky

    Bude se nám hodit i metoda na úplné zmizení kartičky z okna aplikace.

    public void nechejZmizetKarticku(Karticka karta) {
        karta.vizualniKomponenta.setVisible(false);
        contentPane.remove(karta.vizualniKomponenta);
    }
    

    Je tam použita technika contentPane.remove(...), což jsme neprobírali. Proto jsem zde uvedl celý výpis metody včetně těla. Metodu samozřejmě vyzkoušejte. Například tak, že změníte priKliknutiNaKarticku(...) tak, aby místo otáčení karty lícem nahoru, se karta úplně odstranila.

  5. Hromadné metody

    Dále budeme potřebovat hromadnou metodu, která otáčí všechny kartičky, a druhou metodu, která nechá všechny kartičky zmizet.

    public void otocVsechnyKartickyRubemNahoru() {
        // prikazy
    }
    
    public void nechejZmizetVsechnyKarticky() {
        // prikazy
    }
    

    Po naprogramování je opět vyzkoušejte. Například zavoláním z metody priStiskuBtnZamichat(...).

  6. Test na zbývající kartičky

    Budeme potřebovat metodu, která vyhodnotí, zda je ještě vidět alespoň jedna kartička.

    public Boolean zbyvajiNejakeKarticky() {
        // prikazy
    }
    

    Metoda vrací true, pokud je alespoň jedna kartička vidět. Jinak řečeno, vizualniKomponenta z alespoň jedné kartičky má vlastnost visible nastavenu na true. Pokud žádná kartička není vidět (všechny byly nechány zmizet), vrací false.

  7. Vyhodnocení dvou kartiček

    Poslední přípravná metoda, kterou budeme potřebovat, je vyhodnocení, zda dvě kartičky patří k sobě (zda jde o dvojici se stejným obrázkem).
    Pokud ne, obě kartičky se pouze otočí rubem nahoru.
    Pokud ano, obě kartičky zmizí, a pokud už nezbývá žádná kartička, pogratulujeme hráči k vítězství.

    public void vyhodnotUhodnotiDvojice(Karticka karta1, Karticka karta2) {
        // prikazy
    }
    

    Gratulaci lze napsat například vyvoláním dialogového okna:

    JOptionPane.showMessageDialog(
            this,
            "Gratulujeme! Vyhráli jste!",
            "Výhra",
            JOptionPane.INFORMATION_MESSAGE);
    
  8. Dokončení pexesa

    Poslední krok je opravdové dokončení pexesa. Budeme potřebovat zabudovat pravidla ohledně postupného otáčení kartiček a vyhodnocování dvojic.

    1. Protože bude nutno nejprve kliknout na jednu kartičku a teprve pak na druhou, budeme si potřebovat uchovávat v proměnných obě zvolené kartičky.
      public class HlavniOkno extends JFrame {
          // ostatni promenne
      
          Karticka zvolenaKarta1;
          Karticka zvolenaKarta2;
      
          // metody
      }
      
    2. Nyní už jen změníme chování obsluhy události priKliknutiNaKarticku(...)

      Metoda musí:

      1. Pokud je stisknutá kartička rubem nahoru, otočit kartičku lícem nahoru. Dále uložit otočenou kartičku buď do proměnné zvolenaKarta1 nebo zvolenaKarta2, podle toho, která z těchto proměnných je ještě null. Na začátku jsou obě proměnné null. Po prvním stisknutím bude zvolenaKarta1 nastavena a zvolenaKarta2 zůstane null. Po druhém stisknutí bude nastavena i zvolenaKarta2.
      2. Pokud jsou již obě kartičky otočeny, je třeba vyhodnotit, zda jde o dvojici a buď je nechat zmizet nebo jen otočit rubem nahoru (tuto funkcionalitu už máme hotovou z minula v metodě vyhodnotUhodnotiDvojice(...)).
        private void priKliknutiNaKarticku(Karticka karta) {
            // prikazy
        }
        
  9. Vylepšení

    Asi jste si všimly, že po vybrání druhé kartičky se obě karty buď ihned zase otočí rubem nahoru nebo zmizí. Je to způsobeno tím, že se druhá kartička sice otočí, ale ihned se vyhodnotí výsledek a rovnou se otočí zpět.

    1. Můžeme zkusit proces zpomalit čekáním:
    2. private void priKliknutiNaKarticku(Karticka karta) {
          // ... prikazy ...
      
          ((JComponent)contentPane).paintImmediately(
              0, 0, contentPane.getWidth(), contentPane.getHeight());
          ThreadUtils.sleep(1000);
      
          // Vyhodnot karticky
      }
      
    3. Já ale navrhuji, abychom místo pevného čekání vyhodnotili kartičky až při třetím kliknutí na libovolnou kartičku.

      Zařídíme to snadno: Prohodíme podúkol i) a ii) z minulého bodu. Tím se dosáhne toho, že se budou vyhodnocovat kartičky až při kliknutí na třetí kartičku. Mně to přijde přirozenější. Můžete zkusit obě varianty a rozhodnout se samy.




Celé řešení je po jednotlivých krocích k dispozici zde:
Ukol_na_leto-Pexeso-heslo_czechitas.7z
Snažte se ho ale nepoužít.