TlačiťTlačiť Slovenčina English Hľadať RSS

© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 19. 4. 2024.

Stránka sa načítava, prosím čakajte…

Dátum: 4. 8. 2020, pred štyrmi rokmi

Balíček kvíz obsahuje základnú implementáciu kvízového rozhrania s otázkami s jednoduchou voľbou. V balíčku je definovaných päť tried: ChybaKvízu, Odpoveď, Otázka, VýberOtázkyKvíz. Z nich je z pohľadu programátora, ktorý balíček používa najdôležitejšia trieda Kvíz (ktorá prepája ostatné triedy a automaticky používa ich inštancie). Trieda VýberOtázky je dôležitá len ako údajový typ, do ktorého programátor číta obsahu otázok a odpovedí, ktoré zobrazuje riešiteľovi kvízu. Triedu ChybaKvízu môže programátor použiť v štruktúre try-catch na zachytenie výnimiek špecifických pre tento balíček.

Definícia kvízu (označovaná v tomto projekte aj ako konfigurácia kvízu) sa musí nachádzať v textovom súbore (dostupnom pre aplikáciu). Ústredná trieda balíčka Kvíz prijíma vo svojom konštruktore názov tohto súboru. Po konštrukcii kvízu musí programátor zabezpečiť obojsmernú komunikáciu s používateľom – riešiteľom: výpis alebo vykreslenie obsahu otázok a k nim prislúchajúcich možností, zobrazenie potrebných ovládacích prvkov na pohyb po kurze a vykonanie voľby z ponúkaných možností odpovedí a obslúženie reakcií na používateľské voľby týchto ovládacích prvkov.

Ukážku jedného z možných prístupov obsahuje HlavnáTrieda nižšie.

obrázok 
Ukážka rozhrania po spustení.

ikonaBalíček kviz.7z na prevzatie 19,70 kB (19,24 KiB), 5. 8. 2020

 

~

import kvíz.ChybaKvízu;
import kvíz.Kvíz;
import kvíz.VýberOtázky;

import knižnica.GRobot;
import knižnica.ObsluhaUdalostí;
import knižnica.Tlačidlo;

import static knižnica.Svet.*;

/**
 * Táto trieda je ukážkou použitia kvízového rozhrania. Dôležité časti ukážky
 * sú celý konštruktor (samozrejme, že i s ním bezprostredne súvisiace
 * deklarácie) a riadky:
 *
 *     kvíz = new Kvíz(názovSúboru, podrobnosti);
 *     new HlavnáTrieda();
 *
 * ktoré sú umiestnené v hlavnej metóde (main).
 *
 * (Ostatné riadky hlavnej metódy sú bonusom, ktorý iba zvyšuje možnosti
 * spôsobov spustenia a/alebo konfigurovania aplikácie.)
 *
 * Názov súboru a argument podrobnosti je rovnako možné zadať napevno.
 * Napríklad:
 *
 *     kvíz = new Kvíz("mojkviz.txt", false);
 */
public class HlavnáTrieda extends GRobot
{
	/** Inštancia kvízu. */
	private static Kvíz kvíz;

	/** Tlačidlo späť. */
	private final Tlačidlo späť;

	/** Tlačidlo ďalej. */
	private final Tlačidlo ďalej;

	/** Tlačidlo opakuj. */
	private final Tlačidlo opakuj;

	/** Tlačidlo koniec. */
	private final Tlačidlo koniec;

	/** Súkromný konštruktor. */
	private HlavnáTrieda()
	{
		skry();

		/**
		 * Najskôr sú vytvorené všetky inštancie tlačidiel:
		 */
		späť = new Tlačidlo("Späť");
		ďalej = new Tlačidlo("Ďalej");
		opakuj = new Tlačidlo("Opakuj");
		koniec = new Tlačidlo("Koniec");

		/**
		 * Vzápätí sú nastavené ich vlastnosti:
		 */
		späť.prilepDole();
		späť.prilepVľavo();
		späť.skočNa(10, 10);

		ďalej.prilepDole();
		ďalej.prilepVpravo();
		ďalej.skočNa(-10, 10);

		koniec.prilepDole();
		koniec.skočNa(0, 10);

		/**
		 * Potom je definovaná obsluha udalostí:
		 */
		new ObsluhaUdalostí()
		{
			/**
			 * V tejto reakcii sú obslúžené (takmer) všetky ovládacie prvky
			 * kvízu. Konkrétne všetky, ktoré sú realizované prostredníctvom
			 * tlačidiel. Voľba každého tlačidla má jednoducho za následok
			 * spustenie konkrétnej metódy kvízu, ktoré sú pripravené presne
			 * na tento účel. (V prípade potreby by programátor kvízu mohol
			 * tieto metódy volať pri iných príležitostiach. Napríklad ako
			 * reakciu na kliknutie do nejakého tvaru. Niektoré môžu byť
			 * spúšťané i automaticky – ako to je s metódou ďalšiaOtázka
			 * v tejto ukážke – pozri reakciu potvrdenieÚdajov.)
			 */
			@Override public void voľbaTlačidla()
			{
				// Keďže metódy kvízu môžu produkovať výnimky, sú všetky
				// volania uzavreté v štruktúre try-catch:
				try
				{
					// Reakcie na všetky tlačidlá sú relatívne logické,
					// v tejto ukáže ide predovšetkým o to ukázať, ktorá
					// metóda sa s čím spája:
					if (späť.zvolené())
					{
						kvíz.predchádzajúcaOtázka();
					}
					else if (ďalej.zvolené())
					{
						kvíz.ďalšiaOtázka();
					}
					else if (opakuj.zvolené())
					{
						kvíz.opakujKvíz();
					}
					else if (koniec.zvolené())
					{
						kvíz.koniec();
					}

					// Obnovenie obrazovky:
					obnovTlačidlá();
					kresliOtázku();
				}
				catch (ChybaKvízu ch)
				{
					// Reakciou na vznik výnimky je vypísanie hlásenia
					// riešiteľovi, ale v skutočnosti by výnimky mali
					// vznikať len v prípadoch, keď niečo zle premyslí
					// programátor alebo je niečo zle zadefinované
					// v definičnom súbore kvízu, takže teoreticky by
					// chybové hlásenia nemali zaujímať riešiteľa, ale
					// programátora a zadávateľa kvízu. Napriek tomu sú
					// hlásenia formulované tak, aby boli jednoduché
					// a informovali skôr riešiteľa než uvedené osoby,
					// pretože sa nedá zaručiť, že ich riešiteľ kvízu nikdy
					// neuvidí. Ide napríklad o správy typu „Ďalšia otázka
					// kvízu nejestvuje,“ „Voľbu nie je možné vykonať“
					// a podobne.
					správa(ch.getMessage());
				}
			}

			/**
			 * V tejto reakcii sa vyhodnotí odpoveď riešiteľa na základe
			 * písmena, ktoré zadal do vstupného riadka. Podstatný je riadok:
			 *
			 *     otázka.voľba(…);
			 *
			 * a všetky riadky, ktoré s ním nevyhnutne súvisia. Uvedený
			 * riadok pošle voľbu do aktuálnej otázky, voľba sa zaznamená
			 * a na základe toho môže byť neskôr vykonané vyhodnotenie kvízu.
			 *
			 * Rovnako dobre by mohla byť voľba odpovede vykonaná iným
			 * spôsobom, napríklad klikaním myšou na jednotlivé ponúknuté
			 * možnosti (zobrazené na plátne) alebo na iné oblasti vykreslené
			 * na plátne (odpoveď by mohla byť napríklad výber nejakého prvku
			 * na obrázku) a podobne. Závisí od preferencií (alebo požiadaviek
			 * na) programátora kvízu.
			 */
			@Override public void potvrdenieÚdajov()
			{
				/**
				 * Táto premenná je predvolene nastavená na logickú hodnotu
				 * lož (false). Logická hodnota pravda (true) sa do nej
				 * vloží ako dôsledok neplatnej voľby, čo môže byť buď
				 * odoslanie prázdneho riadka, alebo vznik výnimky v rámci
				 * niektorej z metód volaných v štruktúre try-catch.
				 *
				 * Cieľom zaznamenania a spracovania neplatnej voľby je
				 * zabezpečiť opätovné zobrazenie vstupného riadka. Bez tejto
				 * akcie by riešiteľ stratil kontakt s aplikáciou – nemal by
				 * možnosť komfortne doriešiť túto otázku (možnosť by dostal
				 * jedine v prípade, že bol kvíz v režime Ľ – ľubovoľne alebo
				 * O – s opakovaním, kedy by mal možnosť sa opätovne k otázke
				 * vrátiť).
				 */
				boolean neplatnáVoľba = false;

				// Pre štruktúru try-catch platia rovnaké informácie ako sú
				// uvedené v reakcii voľbaTlačidla.
				try
				{
					String voľba = prevezmiReťazec();
					if (voľba.isEmpty())
					{
						pípni();
						neplatnáVoľba = true;
					}
					else
					{
						// Prevezme aktuálnu otázku z kvízu:
						VýberOtázky otázka =
							kvíz.aktuálnaOtázka();

						// Pošle otázke voľbu vypočítanú z ASCII hodnoty
						// zadaného písmena (prevedeného na malé písmeno).
						// Písmeno a je nula, b je jednotka a tak ďalej:
						otázka.voľba(voľba.toLowerCase().
							charAt(0) - 'a');

						// Testovací výpis (všetky testovacie výpisy by mal
						// programátor v konečnej verzii svojho kvízu
						// vymazať; všetky tu bolo ponechané hlavne preto,
						// lebo je veľká šanca, že budú v priebehu vývoja
						// kvízu využité):
						System.out.println("\nVoľba: " +
							(voľba.toLowerCase().charAt(0) - 'a'));

						// Automatický prechod na nasledujúcu otázku
						// a obnovenie obrazovky:
						kvíz.ďalšiaOtázka();
						obnovTlačidlá();
						kresliOtázku();
					}
				}
				catch (ChybaKvízu ch)
				{
					// Vypísanie chybového hlásenia riešiteľovi – platí to
					// isté, čo je uvedené v reakcii voľbaTlačidla.
					pípni();
					správa(ch.getMessage());
					neplatnáVoľba = true;
				}

				if (neplatnáVoľba)
				{
					// Opätovné zobrazenie vstupného riadka:
					vykonaťNeskôr(() -> začniVstup());
				}
			}

			/**
			 * V tejto reakcii sa obnovia tlačidlá a prekreslí otázka. Je to
			 * bonusová funkcia, aby boli tlačidlá aj text otázky viditeľné
			 * v širšom spektre veľkostí okna.
			 *
			 * (Na zvýšenie tohto „spektra“ by bolo vhodné upraviť kreslenie
			 * otázky tak, aby sa automaticky zisťovala šírka textu, porovnala
			 * sa so šírkou okna a na základe toho sa otázka vykreslila na
			 * viacerých riadkoch.)
			 */
			@Override public void zmenaVeľkostiOkna()
			{
				obnovTlačidlá();
				kresliOtázku();
			}
		};

		/**
		 * Dokončenie inicializácie kvízu – generovanie výberu otázok,
		 * premiešanie otázok aj možností. Tieto akcie môžu byť presunuté do
		 * samostatnej metódy, ktorá by bola volaná ako reakcia na stlačenie
		 * nejakého tlačidla „Nový kvíz,“ prípadne iného prvku, prípadne by
		 * mohla byť volaná automaticky v určitej situácii.
		 */
		try
		{
			kvíz.generujVýberOtázok();
			kvíz.premiešajOtázky();
			kvíz.premiešajOdpovedeOtázok();
		}
		catch (ChybaKvízu ch)
		{
			správa(ch.getMessage());
		}

		/**
		 * Nasledujúca dvojica príkazov obnoví/prekreslí ovládacie prvky tohto
		 * kvízu a aktuálnu otázku. Je to rovnaká dvojica príkazov, ktorá sa
		 * nachádza v reakcii zmenaVeľkostiOkna.
		 */
		obnovTlačidlá();
		kresliOtázku();
	}


	// Metóda slúžiaca na „nakreslenie“ aktuálnej otázky, súhrnu pred
	// ukončením alebo vyhodnotenia kvízu.
	private void kresliOtázku()
	{
		podlaha.vymažGrafiku();
		VýberOtázky otázka = kvíz.aktuálnaOtázka();

		skočNa(0, hornýOkraj() - 30);

		if (null == otázka)
		{
			if (kvíz.jeKoniec())
			{
				domov();
				skoč(0, 50);
				text("Vyhodnotenie kvízu");

				skoč(0, -40);
				int body = kvíz.početBodov();
				int minimum = kvíz.minimumBodov();
				int maximum = kvíz.maximumBodov();
				text("Počet bodov: " + ("" + body).replace('-', '−') +
					" z " + maximum);

				skoč(0, -30);
				if (body >= 0)
					text("Úspešnosť: " + ((body * 100) / maximum) + " %");
				else
					text("(Ne)úspešnosť: −" + ((body * 100) / minimum) + " %");
			}
			else
			{
				text("Prešli ste všetky otázky.");
				skoč(0, -40);
				text("Teraz môžete kvíz ukončiť alebo si ešte raz " +
					"skontrolovať svoje odpovede.");

				// Testovacie výpisy:
				System.out.println();
				System.out.println("body: " + kvíz.početBodov());
				System.out.println("min.: " + kvíz.minimumBodov());
				System.out.println("max.: " + kvíz.maximumBodov());
			}
		}
		else
		{
			text(otázka.textOtázky());
			skoč(0, -30);

			int voľba = otázka.voľba();
			int počet = otázka.početOdpovedí();
			for (int i = 0; i < počet; ++i)
			{
				skoč(0, -30);
				text((i == voľba ? '•' : '○') + " " + // keď na tomto mieste
					// namiesto reťazca " " použijeme znak ' ', tak je celá
					// časť od odrážky (vrátane) po zátvorku (vynímajúc)
					// chybne sčítaná ako celé čísla a pripojená k zátvorke
					// a zvyšnému reťazcu, čiže napr.: 9804) …
					((char)('a' + i)) + ") " + otázka.textOdpovede(i));
			}

			// Testovacie výpisy:
			System.out.println();
			System.out.println("Voľba: " + otázka.voľba());
			System.out.println("body: " + otázka.body());
			System.out.println("min.: " + otázka.minimum());
			System.out.println("max.: " + otázka.maximum());
		}
	}

	// Toto je pomocná metóda na aktualizáciu stavov ovládacích prvkov kvízu,
	// predovšetkým tlačidiel, ale aj iných, ako vstupného riadka.
	private void obnovTlačidlá()
	{
		String navigácia = kvíz.odporúčanáNavigácia();

		// Testovací výpis:
		System.out.println();
		System.out.println("Navigácia: " + navigácia);

		if (-1 != navigácia.indexOf("<"))
			späť.zobraz();
		else
			späť.skry();

		if (-1 != navigácia.indexOf(">"))
			ďalej.zobraz();
		else
			ďalej.skry();

		if (-1 != navigácia.indexOf("O"))
			opakuj.zobraz();
		else
			opakuj.skry();

		if (-1 != navigácia.indexOf("0"))
			koniec.zobraz();
		else
			koniec.skry();

		if (-1 != navigácia.indexOf("E"))
			vykonaťNeskôr(() -> začniVstup());
		else
			zrušVstup();
	}

	/**
	 * Vstupný bod programu:
	 *
	 *     – prevezme názov súboru z prvého argumentu príkazového riadka,
	 *     – ak nebol zadaný žiadny názov súboru, načíta sa predvolený súbor
	 *       (definovaný v triede Kvíz).
	 *
	 * Voliteľné argumenty -podrobnosti a -vypíšKvíz spôsobia výpis
	 * podrobností o spracovaní súboru resp. výpis spracovaného kvízu.
	 */
	public static void main(String[] args)
	{
		/*#
		 * Predvolené hodnoty argumentov príkazového riadka:
		 */
		boolean podrobnosti = false;
		boolean vypíšKvíz = false;
		String názovSúboru = null;

		for (String arg : args)
		{
			// Testovací výpis:
			System.out.println("arg: " + arg);

			if (arg.equalsIgnoreCase("-podrobnosti") ||
				arg.equalsIgnoreCase("-podrobnosti=1") ||
				arg.equalsIgnoreCase("-podrobnosti=true"))
				podrobnosti = true;
			else if (
				arg.equalsIgnoreCase("-vypíš") ||
				arg.equalsIgnoreCase("-vypíš=1") ||
				arg.equalsIgnoreCase("-vypíš=true") ||
				arg.equalsIgnoreCase("-vypíšKvíz") ||
				arg.equalsIgnoreCase("-vypíšKvíz=1") ||
				arg.equalsIgnoreCase("-vypíšKvíz=true") ||
				arg.equalsIgnoreCase("-vypíš_kvíz") ||
				arg.equalsIgnoreCase("-vypíš_kvíz=1") ||
				arg.equalsIgnoreCase("-vypíš_kvíz=true"))
				vypíšKvíz = true;
			else
			{
				String argLo = arg.toLowerCase();

				if (argLo.startsWith("-súbor="))
					názovSúboru = arg.substring(7);
				else if (argLo.startsWith("-názovsúboru="))
					názovSúboru = arg.substring(13);
				else if (argLo.startsWith("-názov_súboru="))
					názovSúboru = arg.substring(14);
			}
		}

		// Testovací výpis:
		System.out.println("podrobnosti: " + podrobnosti);
		System.out.println("názovSúboru: " + názovSúboru);

		použiKonfiguráciu("kvíz.cfg");
		nekresli();

		try
		{
			kvíz = new Kvíz(názovSúboru, podrobnosti);
			if (vypíšKvíz)
			{
				farbaTextu(oranžová);
				vypíš("Názov súboru: ");
				farbaTextu(tyrkysová);
				if (null == názovSúboru)
				{
					vypíš(Kvíz.PREDVOLENÝ_NÁZOV_SÚBORU);
					farbaTextu(šedá);
					vypíšRiadok(" (predvolený)");
				}
				else
				{
					vypíšRiadok(názovSúboru);
				}

				kvíz.výpisKvízu();
			}
			else new HlavnáTrieda();
		}
		catch (Throwable t)
		{
			vypíšRiadok(t);
			t.printStackTrace();
		}

		kresli();
	}
}

~

package kvíz;

/**
 * Túto výnimku generuje trieda Kvíz v rôznych situáciách ako napríklad:
 * neúspech pri pokuse o vygenerovanie výberu náhodných otázok, neúspech pri
 * pokuse o posunutie na ďalšiu otázku a podobne.
 *
 * Výnimka by mala byť vždy zachytená programátorom kvízu a podľa typu chyby
 * by sa malo upraviť správanie aplikácie alebo prinajmenšom zobraziť
 * prislúchajúci oznam o zlyhaní riešiteľovi.
 */
@SuppressWarnings("serial")
public class ChybaKvízu extends Exception
{
	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že buď nie
	 * je definovaná žiadna otázka kvízu, alebo nebola spustená metóda
	 * generujVýberOtázok.
	 */
	public final static int NEDOSTATOK_OTÁZOK = 1;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že nejestvuje
	 * ďalšia otázka kvízu.
	 */
	public final static int NIE_JE_ĎALŠIA_OTÁZKA = 2;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že nejestvuje
	 * predchádzajúca otázka kvízu.
	 */
	public final static int NIE_JE_PREDCHÁDZAJÚCA_OTÁZKA = 3;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že voľbu
	 * odpovede práve nie je možné vykonať.
	 */
	public final static int VOĽBA_NIE_JE_MOŽNÁ = 4;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že zadaná voľba
	 * nie je platná.
	 */
	public final static int NEPLATNÁ_VOĽBA = 5;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že miešanie
	 * otázok alebo odpovedí bolo zamietnuté, pretože práve prebieha riešenie
	 * kvízu.
	 */
	public final static int MIEŠANIE_ZAMIETNUTÉ = 6;

	/**
	 * Číselná konštanta chybového stavu, ktorý signalizuje, že riešenie
	 * kvízu bolo ukončené a voľba odpovede už nie je možná.
	 */
	public final static int JE_KONIEC = 7;

	/** Texty chybových stavov podľa kódov: */
	private final static String[] textyChýb =
	{
		"Neznáma chyba.", // 0 – rezervované!
		"Do kvízu neboli vybrané žiadne otázky.", // 1
		"Ďalšia otázka kvízu nejestvuje.", // 2
		"Predchádzajúca otázka kvízu nejestvuje.", // 3
		"Voľbu nie je možné vykonať.", // 4
		"Zadaná voľba nie je platná.", // 5
		"Nie je možné miešať otázky alebo odpovede počas riešenia kvízu.", // 6
		"Voľba odpovede nie je možná, pretože neprebieha riešenie kvízu.", // 7
	};

	/**
	 * Toto je kód chyby kvízu. Kód je nastavený konštruktorom. Môže nadobúdať
	 * ľubovoľnú celočíselnú hodnotu, ale odporúčané je, aby mal hodnotu
	 * rovnú jednej z vyššie definovaných konštánt (kódov chýb:
	 * NEDOSTATOK_OTÁZOK až JE_KONIEC).
	 */
	public final int kód;

	/**
	 * Predvolený konštruktor, ktorý vytvorí inštanciu chyby kvízu podľa
	 * zadaného kódu chyby. Podľa zadaného kódu chyby je automaticky
	 * nastavený text chybovej správy, ktorý sa dá čítať metódou getMessage.
	 * Ak je kód chyby mimo povoleného rozsahu (v súčasnosti sú povolené
	 * hodnoty 1 až 7, čo sú kódy chýb definované ako konštatny tejto
	 * triedy), tak bude text chyby kvízu nastavený na „Neznáma chyba.“
	 *
	 * Programátor kvízu môže kódy chýb rozširovať podľa potreby, ale musí
	 * dať pozor na to, že kód 0 je rezervovaný na neznámu chybu.
	 *
	 * @param kód  kód chyby v rámci povoleného rozsahu
	 */
	public ChybaKvízu(int kód)
	{
		super(textyChýb[kód < 0 || kód >= textyChýb.length ? 0 : kód]);
		this.kód = kód;
	}

	/**
	 * Táto metóda vráti kód tejto chyby kvízu.
	 *
	 * @return kód tejto chyby
	 */
	public int kód()
	{
		return kód;
	}
}

~

package kvíz;

/**
 * Táto trieda uchováva údaje o jednej možnosti odpovede na otázku kvízu.
 */
public class Odpoveď
{
	/** Atribút uchovávajúci text odpovede. */
	private String textOdpovede;

	/**
	 * Atribút uchovávajúci hodnotu (počet bodov) získateľný za túto odpoveď.
	 */
	private int početBodov = 0;

	/**
	 * Konštruktor odpovede.
	 *
	 * @param textOdpovede  znenie tejto možnosti odpovede (platí to isté,
	 *                      čo pre znenie textu otázky)
	 */
	public Odpoveď(String textOdpovede)
	{
		this.textOdpovede = textOdpovede;
	}

	/**
	 * Metóda slúžiaca na zistenie počtu bodov za túto odpoveď.
	 *
	 * @return bodová hodnota tejto odpovede
	 */
	public int početBodov()
	{
		return početBodov;
	}

	/**
	 * Metóda slúžiaca na určenie počtu bodov za túto odpoveď.
	 *
	 * @param novýPočetBodov  nový počet bodov za túto odpoveď
	 */
	public void početBodov(int novýPočetBodov)
	{
		početBodov = novýPočetBodov;
	}

	/**
	 * Táto prekrytá metóda vracia text odpovede.
	 *
	 * Podrobnosti o tomto prekrytí sú zhodné s informáciami uvedenými
	 * v opise prekrytej metódy Otázka.toString.
	 *
	 * @return text odpovede
	 */
	@Override public String toString()
	{
		return textOdpovede;
	}
}

~

package kvíz;

import knižnica.Zoznam;

/**
 * Táto trieda uchováva všetky informácie o jednej otázke kvízu.
 * (A sprostredkúva prácu s nimi.)
 */
public class Otázka
{
	/** Atribút uchovávajúci text otázky. */
	private String textOtázky;

	/**
	 * Zoznam všetkých (správnych aj nesprávnych) kvízových odpovedí na
	 * túto otázku (čiže zoznam ponúkaných možností).
	 */
	private final Zoznam<Odpoveď> odpovede = new Zoznam<>();

	/**
	 * Tento atribút určuje bodovú sankciu, ktorá bude udelená študentovi,
	 * ak nezvolí ani jednu odpoveď (to jest, ak otázku preskočí). Predvolene
	 * nie je stanovená žiadna sankcia.
	 *
	 * V prípade, že je sankcia kladná, nemalo by jej udelenie za preskočenie
	 * otázky zmysel. V takom prípade je táto hodnota započítaná ako odmena
	 * za nepreskočenie tejto otázky. To sa dá využiť na udelenie paušálneho
	 * počtu bodov, ktoré riešiteľ získa vždy, keď sa aspoň pokúsi na otázku
	 * odpovedať (čiže aj v prípade nesprávnej voľby, čo by v inom prípade
	 * bolo realizovateľné udelením kladného počtu bodov v rôzneh výške za
	 * každú možnosť).
	 */
	private int sankcia = 0;

	/**
	 * Konštruktor otázky.
	 *
	 * @param textOtázky  znenie tejto otázky (predvolene sa predpokladá
	 *                    forma čistého textu, ale programátor kvízu si môže
	 *                    definovať vlastné kódy v texte, ktoré budú mať
	 *                    špecifický význam)
	 */
	public Otázka(String textOtázky)
	{
		this.textOtázky = textOtázky;
	}

	/**
	 * Táto metóda aktualizuje sankciu za preskočenie otázky, resp. odmenu
	 * za jej nepreskočenie. Ak je zadaná hodnota záporná, tak plní úlohu
	 * sankcie. Ak je kladná, tak ide o odmenu.
	 *
	 * @param nováSankcia  bodová výška sankcie, resp. odmey
	 */
	public void aktualizujSankciu(int nováSankcia)
	{
		sankcia = nováSankcia;
	}

	/**
	 * Táto metóda poskytne údaj o bodovej sankcii udelenej za preskočenie
	 * otázky, resp. o odmene za jej nepreskočenie. Záporná hodnota znamená
	 * sankciu, kladná odmenu.
	 *
	 * @return aktuálna výška sankcie, resp. odmey
	 */
	public int sankcia()
	{
		return sankcia;
	}

	/**
	 * Pridá novú odpoveď k tejto otázke.
	 *
	 * @param textOdpovede  text novej možnosti odpovede
	 */
	public void pridajOdpoveď(String textOdpovede)
	{
		odpovede.pridaj(new Odpoveď(textOdpovede));
	}

	/**
	 * Táto metóda poskytne počet definovaných odpovedí pre túto otázku.
	 *
	 * @return aktuálny počet možností odpovedí
	 */
	public int početOdpovedí()
	{
		return odpovede.dĺžka();
	}

	/**
	 * Táto metóda poskytne inštanciu naposledy definovanej možnosti odpovede
	 * na túto otázku. Ak nie je žiadna definovaná, tak metóda vráti hodnotu
	 * null.
	 *
	 * @return inštancia poslednej odpovede na túto otázku alebo null
	 */
	public Odpoveď poslednáOdpoveď()
	{
		if (odpovede.prázdny()) return null;
		return odpovede.posledný();
	}

	/**
	 * Táto metóda poskytne inštanciu možnosti odpovede na túto otázku podľa
	 * určeného poradového čísla. Ak taká odpoveď nejestvuje, tak metóda
	 * vráti hodnotu null.
	 *
	 * @param číslo  index odpovede, s ktorou chceme pracovať
	 * @return inštancia odpovede s indexom číslo alebo null
	 */
	public Odpoveď odpoveďČíslo(int číslo)
	{
		if (číslo >= odpovede.dĺžka()) return null;
		return odpovede.daj(číslo);
	}

	/**
	 * Táto metóda vykoná aktualizáciu bodového ohodnotenia poslednej
	 * možnosti odpovede (pridanej napríklad v priebehu čítania
	 * konfiguračného súboru). Ak nemá táto otázka definované žiadne možnosti
	 * odpovedí, tak táto metóda nevykoná žiadnu akciu.
	 *
	 * @param novýPočetBodov  nový počet bodov poslednej pridanej možnosti
	 *                        odpovede
	 */
	public void aktualizujBodyPoslednejOdpovede(int novýPočetBodov)
	{
		if (!odpovede.prázdny())
			odpovede.posledný().početBodov(novýPočetBodov);
	}

	/**
	 * Táto metóda vypočíta a vráti najnižší možný počet bodov, ktorý je
	 * možné získať odpovedaním na túto otázku, pričom metóda počíta aj
	 * s prípadnou sankciou za preskočenie otázky.
	 *
	 * @return najnižší možný počet bodov pridelený za odpovedanie, resp.
	 *         preskočenie tejto otázky
	 */
	public int vypočítajMinimum()
	{
		int min;
		if (odpovede.jePrázdny()) min = 0; else
		{
			min = odpovede.daj(0).početBodov();
			for (int i = 1; i < odpovede.dĺžka(); ++i)
			{
				int body = odpovede.daj(i).početBodov();
				if (body < min) min = body;
			}
		}

		if (sankcia < min) min = sankcia;
		if (0 < min) return 0;
		return min;
	}

	/**
	 * Táto metóda vypočíta a vráti najvyšší možný počet bodov, ktorý je
	 * možné získať odpovedaním na túto otázku. Metóda počíta aj s prípadnou
	 * odmenou za odpovedanie na otázku (za jej nepreskočenie).
	 *
	 * @return najvyšší možný počet bodov získaný za odpovedanie na túto
	 *         otázku (ak ju riešiteľ nepreskočí)
	 */
	public int vypočítajMaximum()
	{
		int max;

		if (odpovede.jePrázdny()) max = 0; else
		{
			max = odpovede.daj(0).početBodov();
			for (int i = 1; i < odpovede.dĺžka(); ++i)
			{
				int body = odpovede.daj(i).početBodov();
				if (body > max) max = body;
			}
		}

		if (sankcia > 0) max += sankcia;
		else if (max < 0) return 0;
		return max;
	}

	/**
	 * Táto prekrytá metóda vracia text otázky.
	 *
	 * Ide o prekrytie metódy, ktorú má definovaný každý objekt Javy.
	 * Predvolene ju Java používa na vyjadrenie objektu v textovej podobe
	 * (alebo na prevod do textovej podoby, ak chceme).
	 *
	 * To je využiteľné napríklad pri výpise informácie o objekte metódami
	 * System.out.println alebo Svet.vypíšRiadok.
	 *
	 * Technický detail: Ak metóda toString nie je prekrytá, tak predvolene
	 * vracia reťazec, ktorý obsahuje názov triedy, znak „at“ (@) a hešovací
	 * kód v šestnástkovej číselnej sústave. V mnohých prípadoch je táto
	 * metóda volaná úplne automaticky (vnútornými mechanizmami virtuálneho
	 * stroja Javy).
	 *
	 * @return text otázky
	 */
	@Override public String toString()
	{
		return textOtázky;
		// Pôvodne by sa zobrazilo:
		//     getClass().getName() + "@" + Integer.toHexString(hashCode());
	}
}

~

package kvíz;

import static knižnica.Svet.náhodnéCeléČíslo;

/**
 * Z definovanej množiny otázok sa má do kvízu vybrať podmnožina, ktorá bude
 * predložená riešiteľovi. Toto je trieda, ktorá slúži na uchovanie jednej
 * zo zvolených otázok. Séria inštancií tejto triedy bude tvoriť konkrétny
 * kvíz predložený riešiteľovi.
 */
public class VýberOtázky
{
	/**
	 * Toto je inštancia zvolenej otázky, čiže otázky, ktorú reprezentuje
	 * tento výber (táto inštancia výberu otázky). Inštancia je nastavená
	 * počas konštrukcie tohto objektu a je nemenná (finálna = konštantná).
	 */
	public final Otázka otázka;

	/**
	 * Tento príznak určuje, či bola táto otázka v rámci vypĺňania kvízu
	 * aspoň raz navštívená. (Ak boli navštívené všetky otázky, tak bude
	 * dovolené riešiteľovi kvíz odoslať.)
	 */
	private boolean navštívená = false;

	/**
	 * Tento atribút si bude pamätať riešiteľovu voľbu. Predvolená hodnota
	 * (-1) znamená, že riešiteľ neoznačil žiadnu možnosť (napríklad otázku
	 * preskočil, odložil na neskoršie riešenie, prípadne ešte nestihol
	 * navštíviť).
	 */
	private int voľba = -1;

	/**
	 * Toto pole slúži na vnútornú úpravu poradia odpovedí.
	 */
	private final int poradieOdpovedí[];

	/**
	 * Konštruktor, ktorý prijíma inštanciu otázky. Otázka je nemennou
	 * inštanciou tejto triedy, to znamená, že každá inštancia typu
	 * VýberOtázky musí mať vopred stanovenú inštanciu typu Otázka, ktorá
	 * sa počas celého bytia objektu typu VýberOtázky nebude meniť. Jeden
	 * VýberOtázky = jednoznačné priradenie k typu Otázka. (Pozor! To, že
	 * sa otázky nebudú v rámci kvízu opakovať nie je zabezpečené tu. To
	 * je zabezpečené o niečo komplikovanejším mechanizmom.)
	 *
	 * @param otázka  inštancia otázky, ktorú reprezentuje tento výber
	 */
	public VýberOtázky(Otázka otázka)
	{
		this.otázka = otázka;
		int počet = otázka.početOdpovedí();
		poradieOdpovedí = new int[počet];
		for (int i = 0; i < počet; ++i)
			poradieOdpovedí[i] = i;
	}

	/**
	 * Táto metóda premieša poradie odpovedí tohto výberu otázky.
	 */
	public void premiešajOdpovede()
	{
		int počet = otázka.početOdpovedí() - 1;
		for (int i = 0; i <= počet; ++i)
		{
			int j = (int)náhodnéCeléČíslo(0, počet);
			int k = (int)náhodnéCeléČíslo(0, počet);
			int l = poradieOdpovedí[j];
			poradieOdpovedí[j] = poradieOdpovedí[k];
			poradieOdpovedí[k] = l;
		}
	}

	/**
	 * Táto metóda poskytne počet odpovedí tohto výberu otázky.
	 *
	 * @return počet možností odpovede na otázku, ktorú reprezentuje tento
	 *     výberu
	 */
	public int početOdpovedí()
	{
		return poradieOdpovedí.length;
	}

	/**
	 * Táto metóda vráti text otázky tohto výberu.
	 *
	 * @return znenie otázky, ktorú reprezentuje tento výber
	 */
	public String textOtázky()
	{
		return otázka.toString();
	}

	/**
	 * Táto metóda vráti text odpovede určenej poradovým číslom. Metóda berie
	 * do úvahy prípadné vnútorné premiešanie odpovedí tohto výberu.
	 *
	 * @param číslo  poradové číslo (index) možnosti odpovede tohto výberu
	 *               (to jest index platný pre výber, ktorý môže možnosti
	 *               pôvodnej otázky premiešať)
	 * @return text zadanej možnosti odpovede tohto výberu otázky
	 */
	public String textOdpovede(int číslo)
	{
		return otázka.odpoveďČíslo(poradieOdpovedí[číslo]).toString();
	}

	/**
	 * Táto metóda vráti body odpovede určenej poradovým číslom. Metóda berie
	 * do úvahy prípadné vnútorné premiešanie odpovedí tohto výberu.
	 *
	 * @param číslo  poradové číslo (index) možnosti odpovede tohto výberu
	 *               (to jest index platný pre výber, ktorý môže možnosti
	 *               pôvodnej otázky premiešať)
	 * @return bodové ohodnotenie zadanej možnosti odpovede tohto výberu
	 *     otázky
	 */
	public int bodyOdpovede(int číslo)
	{
		return otázka.odpoveďČíslo(poradieOdpovedí[číslo]).početBodov();
	}

	/**
	 * Táto metóda označí túto otázku (v zmysle výberu otázky) za navštívenú.
	 * (Čo môže mať vplyv na priebeh kvízu.)
	 */
	public void navštívená()
	{
		navštívená = true;
	}

	/**
	 * Táto metóda overí, či riešiteľ túto otázku navštívil, alebo nie.
	 *
	 * @return ak bol tento výber otázky navštívený, tak je návratovou
	 *     hodnotou true, inak false
	 */
	public boolean bolaNavštívená()
	{
		return navštívená;
	}

	/**
	 * Táto metóda slúži na nastavenie voľby podľa poradového čísla odpovede
	 * (možnosti). Ide o voľbu zadanú riešiteľom, čiže o zvolenie konkrétnej
	 * možnosti odpovede.
	 *
	 * @param voľba  poradové číslo možnosti, ktorú zadal (zvolil) riešiteľ
	 */
	public void voľba(int voľba) throws ChybaKvízu
	{
		if (voľba > poradieOdpovedí.length)
			throw new ChybaKvízu(ChybaKvízu.NEPLATNÁ_VOĽBA);
		this.voľba = voľba;
	}

	/**
	 * Táto metóda slúži na overenie hodnoty riešiteľom zvolenej možnosti
	 * odpovede. Záporná hodnota znamená, že riešiteľ ešte nezvolil žiadnu
	 * možnosť.
	 *
	 * @return poradové číslo možnosti, ktorú zvolil riešiteľ alebo −1, ak
	 *     zatiaľ nebola vykonaná žiadna voľba
	 */
	public int voľba()
	{
		return voľba;
	}

	/**
	 * Táto metóda slúži na vrátenie počtu bodov, ktoré riešiteľ získal
	 * za tento výber otázky (podľa toho, či otázku (ne)preskočil a ktorú
	 * odpoveď zvolil).
	 *
	 * @return počet bodov pridelený za túto otázku
	 */
	public int body()
	{
		int body = 0;

		if (voľba < 0)
		{
			if (otázka.sankcia() < 0)
				return otázka.sankcia();
			return 0;
		}
		else if (otázka.sankcia() > 0)
			body = otázka.sankcia();

		body += otázka.odpoveďČíslo(poradieOdpovedí[voľba]).početBodov();
		return body;
	}

	/**
	 * Táto metóda slúži na vrátenie minimálnej hodnoty počtu bodov, ktoré
	 * je možné získať za otázku v tomto výbere.
	 *
	 * @return minimálny počet bodov za túto otázku
	 */
	public int minimum()
	{
		return otázka.vypočítajMinimum();
	}

	/**
	 * Táto metóda slúži na vrátenie maximálnej hodnoty počtu bodov, ktoré
	 * je možné získať za otázku v tomto výbere.
	 *
	 * @return maximálny počet bodov za túto otázku
	 */
	public int maximum()
	{
		return otázka.vypočítajMaximum();
	}

	/**
	 * Prekrytie tejto metódy zabezpečí korekciu toho, aby sa pri porovnávaní
	 * inštancie tohto výberu otázky s inštanciou otázky porovnávala priamo
	 * zhoda so zadanou otázkou (t. j. s atribútom otázka, a nie s touto
	 * inštanciou ako celkom).
	 *
	 * To je výhodné, keď budeme chcieť overiť, či je tento výber otázky
	 * zhodný s niektorou otázkou. Je logické, že keď budeme také porovnanie
	 * potrebovať vykonať, nebudeme chcieť overiť zhodu medzi inštanciou
	 * výberu otázky a inštanciou otázky, pretože tieto dve inštancie by sa
	 * (technicky) nikdy nemohli zhodovať. V skutočnosti budeme chcieť
	 * zistiť, či sa tento výber otázky vzťahuje k zadanej otázke. Keďže
	 * takéto porovnanie by sa neudialo automaticky, je definované toto
	 * prekrytie.
	 *
	 * V osatných prípadoch (napríklad pri porovnaní s inou inštanciou
	 * výberu) je zavolaná metóda nadradenej triedy (java.lang.Object).
	 *
	 * @param obj  referenčný objekt na porovnanie
	 * @return ak je referenčný objekt zhodný s týmto objektom (v súlade
	 *         s opisom), tak metóda vráti true, inak false
	 *
	 * @see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-
	 */
	@Override public boolean equals(Object obj)
	{
		if (obj instanceof Otázka)
			// Rýchlejšie by bolo toto porovnanie:
			//     return otázka == obj;

			// Ale korektnejšie je toto porovnanie:
			return otázka.equals(obj);

		// V ostatných prípadoch sa postupuje štandardne:
		return super.equals(obj);
	}

	/**
	 * Prekrytie tejto metódy je vyžadované novšími verziami prekladačov
	 * Javy v prípade, že prekrývame metódu equals (pozri vyššie). Keďže
	 * v skutočnosti nemáme k dispozícii lepšiu verziu vypočítania
	 * hašovacieho kódu, len voláme nadradenú metódu (t. j. metódu nadradenej
	 * triedy „super,“ ktorou je v tomto prípade java.lang.Object).
	 *
	 * @return hodnota hašovacieho kódu pre tento výber otázky
	 *
	 * @see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--
	 */
	@Override public int hashCode()
	{
		return super.hashCode();
	}
}

~

package kvíz;

import knižnica.GRobot;
import knižnica.Zoznam;

import static knižnica.Svet.*;

import java.io.IOException;

/**
 * Toto je ústredná trieda balíčka kvíz. Konštruktor tejto triedy prečíta
 * konfiguráciu zo zadaného textového súboru a v prípade, že nenastala chyba
 * kvíz „spustí.“ Osoba, ktorá bude absolvovať kvíz je v tejto dokumentácii
 * označovaná termínom „riešiteľ.“
 *
 * (Rozhranie interagujúce s riešiteľom zabezpečuje programátor, ktorý tento
 * balíček používa. Tento balíček sa stará len o vnútornú konzistenciu
 * a koordináciu kvízu.)
 */
public class Kvíz extends GRobot
{
	/**
	 * Zoznam všetkých kvízových otázok. (Prečítaných z konfiguračného
	 * súboru.)
	 */
	private final Zoznam<Otázka> otázky = new Zoznam<>();

	/**
	 * Zoznam s výberom otázok, ktoré boli vybrané do aktuálneho kvízu,
	 * to jest kvízu, ktorý bol vygenerovaný pre konkrétneho riešiteľa.
	 */
	private final Zoznam<VýberOtázky> výber = new Zoznam<>();

	/**
	 * Tento atribút je ukazovateľ aktuálnej otázky kvízu, to jest otázky vo
	 * výbere, na ktorej sa práve nachádza riešiteľ.
	 */
	private int otázkaVýberu = 0;

	/**
	 * Tento atribút si pamätá, koľko ráz bolo riešenie aktuálneho kvízu
	 * zopakované riešiteľom (prostredníctvom prislúchajúceho ovládacieho
	 * prvku, napríklad tlačidla s popisom „Opakuj“).
	 */
	private int početOpakovaní = 0;

	/**
	 * Pravdivá hodnota tohto atribútu (true) znamená, že kvíz bol ukončený
	 * a má sa zobraziť jeho vyhodnotenie. (Riešiteľ sa už k riešeniu nemôže
	 * vrátiť.)
	 */
	private boolean koniec = true;

	/**
	 * Tento špeciálny druh vymenovacej triedy (enum) slúži na uchovanie
	 * aktuálneho stavu spracovania konfiguračného súboru. Má výstižne
	 * pomenované entity (prvky), takže program je s jej použitím dostatočne
	 * dobre čitateľný.
	 */
	public enum StavSpracovania
	{
		/** Stav signalizujúci chybu. */
		CHYBA,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávaný počet otázok, ktorý má byť
		 * vygenerovaný riešiteľovi z celej množiny definovaných otázok.
		 * Nula znamená všetky otázky.
		 *
		 * (Celkový počet otázok kvízu nie je explicitne zadaný. Je zistený
		 * automaticky podľa počtu definícií otázok zadaných v konfiguračnom
		 * súbore. Ak súbor neobsahuje dostatočný počet definícií otázok
		 * (čiže ak je celkový počet otázok menší ako hodnota na riadku
		 * POČET_OTÁZOK), tak budú riešiteľovi vygenerované všetky dostupné
		 * otázky v náhodnom poradí.)
		 */
		POČET_OTÁZOK,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávané konfiguračné písmeno (P,
		 * O alebo L – v súlade s dokumentáciou nižšie).
		 */
		KONFIGURAČNÉ_PÍSMENO,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávané znenie otázky.
		 */
		ZNENIE_OTÁZKY,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávaná hodnota sankcie za preskočenie
		 * otázky, resp. odmena za jej nepreskočenie.
		 */
		SANKCIA_OTÁZKY,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávané znenie možnosti odpovede
		 * na otázku.
		 */
		ZNENIE_ODPOVEDE,

		/**
		 * Stav signalizujúci, že na najbližšom neprázdnom riadku
		 * konfiguračného súboru je očakávaná hodnota bodového zisku
		 * (prípadne straty) za zvolenie možnosti odpovede (vždy sa to
		 * vzťahuje na naposledy definovanú možnosť odpovede na otázku).
		 */
		BODY_ODPOVEDE,
	}


	/**
	 * Toto je inštancia vyššie definovanej vymenovacej triedy. (Tento atribút
	 * je používaný na koordináciu čítania konfiguračného súboru kvízu.)
	 */
	private StavSpracovania stavSpracovania = StavSpracovania.CHYBA;

	/**
	 * Toto je konštanta s predvoleným názvom súboru definície kvízu. Tento
	 * názov bude použitý, ak používateľ nezadá žiadny názov súboru.
	 */
	public final static String PREDVOLENÝ_NÁZOV_SÚBORU = "kvíz.txt";

	/**
	 * Nastavenie výpisu podrobností počas čítania. Nastavenie hodnoty tohto
	 * atribútu na true (parametrom konštruktora, pretože čítanie vykonáva
	 * priamo konštruktor) bude znamenať, že počas čítania a analýzy
	 * konfiguračného súboru budú na vnútornú konzolu sveta programovacieho
	 * rámca GRobot vypisované informácie o každom spracovanom riadku. Ide
	 * v podstate o režim ladenia kvízu, ktorý pomáha odhaliť chyby
	 * v definícii konfiguračného súboru. Prevolená je hodnota false. Vtedy
	 * bude konfigurácia prečítaná v „tichom režime“ – priebežné informácie
	 * o dianí sa nebudú zobrazovať.
	 */
	private boolean podrobnosti = false;

	/**
	 * Tento súkromný atribút slúži na uloženie skutočného názvu
	 * konfiguračného súboru, ktorý bol prečítaný. Je to vnútorný atribút
	 * slúžiaci najmä na výpis rôznych hlásení o chybách spracovania.
	 */
	private String názovSúboru;

	/**
	 * Počet otázok, ktorý má byť vygenerovaný riešiteľovi. (Predvolená
	 * hodnota 0 znamená všetky otázky. Prípadný výskyt zápornej hodnoty
	 * má rovnaký efekt ako 0.)
	 */
	private int početOtázok = 0;

	/**
	 * Konfiguračné písmeno určujúce správanie sa kvízu (počas riešenia):
	 *
	 *     P – priamočiaro. Otázky sa budú respondentovi zobrazovať postupne.
	 *         Respondent sa nebude môcť vrátiť k predchádzajúcej otázke.
	 *     O – s opakovaním. Otázky budú kladené priamočiaro, ale po prejdení
	 *         všetkých otázok dostane riešiteľ možnosť prejsť si kvíz ešte
	 *         raz (aby mohol prípadne upraviť svoje odpovede).
	 *  L alebo Ľ – ľubovoľne. Respondent sa môže počas vypĺňania ľubovoľne
	 *         pohybovať medzi otázkami vpred a vzad, no kvíz môže potvrdiť
	 *         až keď každú otázku navštívi aspoň raz.
	 *
	 * Predvolené je písmeno L.
	 *
	 * Podstatné je povedať to, že toto „správanie sa“ je len spôsob
	 * vnútornej koordinácie kvízu. Veľká časť implementácie musí byť
	 * vykonaná programátorom, ktorý tento balíček používa (programátorom
	 * rozhrania kvízu). Kvíz komunikuje prostredníctvom rôznych metód:
	 * aktuálnaOtázka, odporúčanáNavigácia, početBodov, jeKoniec atď., ale
	 * ich výsledky musí spracovať programátor rozhrania.
	 */
	private char konfiguračnéPísmeno = 'L';

	/**
	 * Tento atribút bude obsahovať inštanciu aktuálnej (teda práve
	 * definovanej) otázky.
	 */
	private Otázka aktuálnaOtázka;

	/**
	 * Tento atribút uchováva číslo riadka, ktorý bol práve prečítaný
	 * z konfiguračného súboru kvízu. Poznať číslo riadka je užitočné pri
	 * hlásení chybových stavov (pri ladení definície/konfigurácie nového
	 * kvízu).
	 */
	private int čísloRiadka;

	/**
	 * Tento atribút uchováva aktuálny riadok textu prečítaný
	 * z konfiguračného súboru kvízu. Atribút zabezpečuje komunikáciu medzi
	 * konštruktorom a súkromnými metódami inicializujúcimi inštanciu kvízu
	 * (čítajÚdaje, čísloNaRiadku, spracujÚdaje, prípadne ďalších).
	 */
	private String riadok;

	/**
	 * Základný konštruktor kvízu. Pracuje rovnako ako nasledujúca verzia
	 * konštruktora (Kvíz(názovSúboru, podrobnosti)), ibaže výpis podrobností
	 * (ladiacich informácií) je predvolene vypnutý.
	 *
	 * @param názovSúboru  názov konfiguračného súboru s kvízom
	 */
	public Kvíz(String názovSúboru)
	{
		this(názovSúboru, false);
	}

	/**
	 * Konštruktor kvízu umožňujúci zapnúť ladenie. Vytvorí nový kvíz podľa
	 * definície uloženej v súbore so zadaným názvom. Parameter podrobnosti
	 * umožňuje zapnúť režim ladenia, v ktorom sú na vnútornú konzolu sveta
	 * programovacieho rámca GRobot vypísané všetky podrobnosti o analýze
	 * súboru.
	 *
	 * @param názovSúboru  názov konfiguračného súboru s kvízom
	 * @param podrobnosti  atribút určujúci, či majú byť počas čítania
	 *                     vypisované podrobnosti o spracovaní na vnútornú
	 *                     konzolu sveta programovacieho rámca GRobot
	 */
	public Kvíz(String názovSúboru, boolean podrobnosti)
	{
		// Robot inštancie kvízu je predvolene skrytý.
		skry();

		/*#
		 * Hodnota atribútu podrobnosti nastavená na true zapne podrobný výpis
		 * priebehu spracovania konfiguračného súboru.
		 */
		this.podrobnosti = podrobnosti;

		/*#
		 * Overenie názvu súboru. Ak nie je zadaný žiadny názov, tak táto
		 * inštancia prečíta (resp. sa o to pokúsi) súbor s názvom zadaným
		 * v konštante PREDVOLENÝ_NÁZOV_SÚBORU (v súčasnosti je to kvíz.txt).
		 */
		if (null == názovSúboru || názovSúboru.isEmpty())
			názovSúboru = PREDVOLENÝ_NÁZOV_SÚBORU;

		/*#
		 * Najprv uložíme meno súboru do vnútorného atribútu a inicializujeme
		 * ďalšie atribúty.
		 */
		this.názovSúboru = názovSúboru;
		aktuálnaOtázka = null;
		čísloRiadka = 0;
		riadok = null;

		/*#
		 * Hneď potom prejdeme na spracovanie textového súboru:
		 */
		spracujSúbor();
	}

	/**
	 * Táto súkromná metóda slúži na otvorenie a zavretie konfiguračného
	 * súboru kvízu, pričom ona sama volá ďalšiu súkromnú metódu slúžiacu
	 * na sekvenčné čítanie údajov zo súboru.
	 */
	private void spracujSúbor()
	{
		/*#
		 * Toto je prvá úroveň zachytávania výnimiek. Na tejto úrovni sú
		 * zachytené výnimky, ktoré vznikli pri otváraní alebo zatváraní
		 * súboru. Operácia zavretia súboru je uložená v samostatnej
		 * (vnorenej) vrstve, aby mohla byť prípadná ňou spôsobená chyba
		 * (vrhnutá výnimka) jednoznačne odlíšená.
		 */
		try
		{
			súbor.otvorNaČítanie(názovSúboru);

			if (podrobnosti)
			{
				farbaTextu(purpurová);
				vypíšRiadok("Čítam konfiguráciu:");
			}

			/*# Prvým stavom bude „počet otázok.“ */
			stavSpracovania = StavSpracovania.POČET_OTÁZOK;

			/*#
			 * Nasledujúca metóda prečíta všetky údaje z konfiguračného
			 * súboru kvízu, počas čoho bude volať ďalšie súkromné metódy
			 * slúžiace na ich spracovanie.
			 */
			čítajÚdaje();

			try
			{
				/*# Zavretie konfiguračného súboru. */
				súbor.zavri();
			}
			catch (IOException e)
			{
				// Vstupno-výstupná chyba vzniknutá počas zatvárania súboru
				// bude vypísaná (červenou farbou) bez ohľadu na hodnotu
				// atribútu „podrobnosti“ (pozri konštruktory).
				farbaTextu(červená);
				vypíšRiadok("Chyba počas zavretia súboru v stave čítania " +
					"na riadku ", čísloRiadka, ".");
			}
		}
		catch (java.io.FileNotFoundException e)
		{
			// Ak súbor nejestvuje, tak bude vzniknutá chyba vypísaná
			// (červenou farbou) bez ohľadu na hodnotu atribútu „podrobnosti“
			// (pozri konštruktory).
			farbaTextu(červená);
			vypíšRiadok("Chyba počas otvorenia súboru. Súbor „", názovSúboru,
				"“ nejestvuje.");
		}
		catch (IOException e)
		{
			// Iná vstupno-výstupná chyba vzniknutá počas otvárania súboru
			// bude vypísaná rovnakým spôsobom ako predchádzajúca (červenou
			// farbou a bez ohľadu na hodnotu atribútu „podrobnosti“), ale
			// vo výpise budú uvedené: trieda chyby a text chyby
			// (v angličtine).
			farbaTextu(červená);
			vypíšRiadok("Neznáma chyba počas otvorenia súboru „", názovSúboru,
				"“: ", e.getClass().getName(), ": ", e.getMessage(), ".");
		}
		catch (Exception e)
		{
			// Všetky „iné chyby“ budú vypísané vo všeobecnom tvare:
			// „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“
			// (Pričom toto je úroveň 1. Tiež bez ohľadu na hodnotu
			// atribútu „podrobnosti.“)
			farbaTextu(červená);
			vypíšRiadok("Úroveň 1: ", e.getClass().getName(), ": ",
				e.getMessage(), " na riadku ", čísloRiadka, ".");
		}
	}

	/**
	 * Táto súkromná metóda slúži na sekvenčné čítanie údajov
	 * z konfiguračného súboru kvízu, pričom ona sama volá ďalšie súkromné
	 * metódy, ktoré slúžia na zmenu stavu spracovania a/alebo spracovanie
	 * prečítaných údajov.
	 */
	private void čítajÚdaje()
	{
		/*#
		 * Toto je druhá úroveň zachytávania výnimiek. Na tejto úrovni sú
		 * zachytené výnimky, ktoré vznikli pri čítaní súboru.
		 */
		try
		{
			while (null != (riadok = súbor.čítajRiadok()))
			{
				++čísloRiadka;

				if (podrobnosti)
				{
					vypíšRiadok();

					farbaTextu(zelená);
					vypíšRiadok("Stav: ", stavSpracovania);
				}

				if (riadok.isEmpty())
					/*#
					 * Volanie tejto metódy vykoná spracovanie prázdneho
					 * riadka, čo v podstate zahŕňa len správnu zmenu stavu
					 * spracovania.
					 */
					spracujPrázdnyRiadok();
				else
					/*#
					 * Volanie tejto metódy vykoná spracovanie údajov
					 * z neprázdnych riadkov.
					 */
					spracujÚdaje();
			}
		}
		catch (IOException e)
		{
			// Chyba vzniknutá počas čítania bude vypísaná (červenou farbou;
			// priebežne počas spracovania) a to bez ohľadu na hodnotu
			// atribútu „podrobnosti“ (pozri konštruktory).
			farbaTextu(červená);
			vypíšRiadok("Chyba počas čítania údajov na riadku ",
				čísloRiadka, ".");
		}
		catch (Exception e)
		{
			// Všetky „iné chyby“ budú vypísané vo všeobecnom tvare:
			// „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“
			// (Pričom toto je úroveň 2. Tiež bez ohľadu na hodnotu
			// atribútu „podrobnosti.“)
			farbaTextu(červená);
			vypíšRiadok("Úroveň 2: ", e.getClass().getName(), ": ",
				e.getMessage(), " na riadku ", čísloRiadka, ".");
		}

		if (podrobnosti)
		{
			vypíšRiadok();

			farbaTextu(oranžová);
			vypíšRiadok("Koniec súboru.");
		}
	}

	/**
	 * Táto súkromná metóda slúži na spracovanie prázdneho riadka prečítaného
	 * z konfiguračného súboru kvízu. Jej jedinou úlohou je zabezpečenie
	 * správnej zmeny stavu spracovania v závislosti od aktuálneho stavu
	 * spracovania.
	 *
	 * To v krátkosti znamená, že prázdne riadky slúžia na zmenu stavu
	 * spracovania.
	 *
	 * Prázdny riadok znamená „nezadaný údaj“ alebo „oddeľovač.“ Vo väčšine
	 * prípadov ide o nezadaný údaj, ktorému bude ponechaná predvolená
	 * hodnota. Bez ohľadu na to je nevyhnutné vykonanť správnu zmenu stavu
	 * spracovania súboru, inak by spracovanie nemohlo korektne pokračovať.
	 *
	 * (Ak by sme nezmenili stav spracovania, algoritmus spracúvajúci súbor
	 * by naďalej očakával ten istý druh údajov a najbližší neprázdny riadok
	 * by bol spracovaný nesprávne.)
	 *
	 * Poznámka: Metóda prednostne spracúva tie stavy, ktorých
	 * pravdepodobnosť výskytu je vyššia.
	 */
	private void spracujPrázdnyRiadok()
	{
		if (podrobnosti)
		{
			farbaTextu(tyrkysová);
			vypíšRiadok("Prázdny riadok ", čísloRiadka, ".");
		}

		/*#
		 * Prázdne riadky budú ovplyvňovať stav podľa nasledujúcich pravidiel:
		 */
		switch (stavSpracovania)
		{
		/*#
		 * Ak je aktuálny stav spracovania „sankcia otázky,“ tak prázdny
		 * riadok znamená, že sankcia nie je (nechce byť) definovaná (rovnaký
		 * efekt má zadanie nuly do tohto riadka). Ďalším stavom spracovania
		 * je „znenie odpovede.“
		 */
		case SANKCIA_OTÁZKY:
			stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE;
			break;

		/*#
		 * Ak je aktuálny stav spracovania „znenie odpovede,“ tak prázdny
		 * riadok ukončí definíciu otázky, ale len v prípade, ak má otázka
		 * definované aspoň dve odpovede. (Na toto obmedzenie nesmie zabúdať
		 * zadávateľ kvízu, inak bude ním vytvorený konfiguračný súbor
		 * nesprávne spracovaný.)
		 */
		case ZNENIE_ODPOVEDE:
			if (aktuálnaOtázka.početOdpovedí() >= 2)
			{
				stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY;
				prekresli();
			}
			break;

		/*#
		 * Ak je aktuálny stav spracovania „body odpovede,“ tak prázdny
		 * riadok bude znamenať počet bodov rovný nule a spracovanie prejde
		 * na čítanie ďalšej odpovede – to znamená, že na ukončenie definície
		 * otázky budú potrebné dva prázdne riadky.
		 */
		case BODY_ODPOVEDE:
			stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE;
			break;

		/*#
		 * Zo stavu spracovania „počet otázok“ sa prejde na stav
		 * „konfiguračné písmeno.“ (Toto je úplne prvý stav a vyskytuje
		 * sa len raz.)
		 */
		case POČET_OTÁZOK:
			stavSpracovania = StavSpracovania.KONFIGURAČNÉ_PÍSMENO;
			break;

		/*#
		 * Zo stavu spracovania „konfiguračné písmeno“ sa prejde na stav
		 * „znenie otázky.“ (Toto je druhý stav v poradí spracovania.
		 * Vyskytuje sa len raz.)
		 */
		case KONFIGURAČNÉ_PÍSMENO:
			stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY;
			prekresli();
			break;
		}
	}

	/**
	 * Toto je pomocná súkromná metóda, ktorá slúži na prevedenie prvého
	 * nájdeného číselného reťazca na riadku na celočíselnú hodnotu (typu
	 * int).
	 *
	 * @return celočíselná hodnota (ak sa obsah riadka nedá previesť, tak je
	 *     vrhnutá výnimka NumberFormatException)
	 *
	 * @throws NumberFormatException  ak riadok neobsahuje údaj v takom
	 *     tvare, ktorý sa dá previesť na celé číslo
	 */
	private int čísloNaRiadku()
	{
		// Nájdem na riadku medzeru:
		int indexOf = riadok.indexOf(' ');

		// Ak sa medzera našla, prevedie sa na číslo len časť reťazca po
		// prvú medzeru:
		if (-1 != indexOf)
			return Integer.parseInt(riadok.substring(0, indexOf));

		// Inak sa prevedie celý reťazec:
		return Integer.parseInt(riadok);
	}

	/**
	 * Táto súkromná metóda slúži na spracovanie údajov v neprázdnych
	 * riadkoch prečítaných z konfiguračného súboru kvízu. Údaje sú
	 * spracúvané podľa aktuálneho stavu spracovania.
	 */
	private void spracujÚdaje()
	{
		if (podrobnosti)
		{
			farbaTextu(modrá);
			vypíšRiadok("Riadok ", čísloRiadka, ": ", riadok);
		}

		/*#
		 * Toto je tretia úroveň zachytávania výnimiek. Na tejto úrovni sú
		 * zachytené výnimky, ktoré vznikli pri spracovaní údajov.
		 */
		try
		{
			/*#
			 * Podľa stavu sa vykoná prislúchajúca činnosť, ktorá zahŕňa
			 * zmenu stavu:
			 */
			switch (stavSpracovania)
			{
			/*#
			 * V stave spracovania „počet otázok“ je riadok prevedený
			 * na číselnú hodnotu určujúcu počet otázok kvízu, ktoré budú
			 * predložené riešiteľovi.
			 *
			 * (Zmysluplné sú nezáporné hodnoty menšie alebo rovné celkovému
			 * počtu otázok, ktorý je zistený automaticky. Väčšie hodnoty sú
			 * interpretované tak, že riešiteľovi majú byť predložené všetky
			 * otázky kvízu, pretože počet otázok na vyradenie bude záporný.
			 * V opise atribútu početOtázok je uvedené, že nulová alebo
			 * záporné hodnoty tohto atribútu sú iterpretované rovnako.
			 * Vďaka tomu chybná hodnota tohto atribútu nespôsobí chybu
			 * spracovania.)
			 *
			 * Stav spracovania sa zmení na „konfiguračné písmeno.“
			 */
			case POČET_OTÁZOK:
				stavSpracovania = StavSpracovania.KONFIGURAČNÉ_PÍSMENO;
				početOtázok = čísloNaRiadku();

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Počet otázok: ", početOtázok);
				}
				break;

			/*#
			 * V stave spracovania „konfiguračné písmeno“ je brané do úvahy
			 * len prvé písmeno (znak) na riadku. Zvyšok riadka je
			 * ignorovaný. Prvé písmeno (znak) je porovnané so znakmi
			 * z povolenej množiny konfiguračných znakov (POL). Ak nepatrí
			 * do množiny, tak bude konfigurácia automaticky nastavená na
			 * predvolenú hodnotu L. (Preto by bolo zbytočné zaradiť znak Ľ
			 * do povolenej množiny znakov.)
			 *
			 * Stav spracovania sa zmení na „znenie otázky.“
			 */
			case KONFIGURAČNÉ_PÍSMENO:
				stavSpracovania = StavSpracovania.ZNENIE_OTÁZKY;
				konfiguračnéPísmeno = riadok.charAt(0);

				switch (konfiguračnéPísmeno)
				{
				case 'P': case 'O': case 'L': break;
				case 'p': konfiguračnéPísmeno = 'P'; break;
				case 'o': konfiguračnéPísmeno = 'O'; break;
				default: konfiguračnéPísmeno = 'L';
				}

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Konfiguračné písmeno: ", konfiguračnéPísmeno);
				}

				prekresli();
				break;

			/*#
			 * V stave spracovania „znenie otázky“ je riadok priamo poslaný
			 * do novej inštancie otázky ako jej znenie.
			 *
			 * Stav spracovania sa zmení na „sankcia otázky.“
			 */
			case ZNENIE_OTÁZKY:
				stavSpracovania = StavSpracovania.SANKCIA_OTÁZKY;
				aktuálnaOtázka = new Otázka(riadok);
				otázky.pridaj(aktuálnaOtázka);

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Nová otázka: ", aktuálnaOtázka);
				}
				break;

			/*#
			 * V stave spracovania „sankcia otázky“ je riadok prevedený na
			 * číselnú hodnotu, ktorá je poslaná do otázky metódou
			 * aktualizujSankciu.
			 *
			 * (Poznámka: Záporná sankcie určuje počet trestných bodov
			 * udelených za preskočenie otázky a kladná hodnota odmenu
			 * udelenú za nepreskočenie otázky.)
			 *
			 * Stav spracovania sa zmení na „znenie odpovede.“ (Poznámka: Do
			 * tohto stavu sa bude spracovanie cyklicky vracať, kým nebude
			 * nájdený potrebný počet prázdnych riadkov.)
			 */
			case SANKCIA_OTÁZKY:
				stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE;
				aktuálnaOtázka.aktualizujSankciu(čísloNaRiadku());

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Sancia otázky: ", aktuálnaOtázka.sankcia());
				}
				break;

			/*#
			 * V stave spracovania „znenie odpovede“ je riadok poslaný priamo
			 * do textu novej odpovede patriacej aktuálne spracúvanej otázke.
			 *
			 * Stav spracovania sa zmení na „body odpovede.“
			 */
			case ZNENIE_ODPOVEDE:
				stavSpracovania = StavSpracovania.BODY_ODPOVEDE;
				aktuálnaOtázka.pridajOdpoveď(riadok);

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Nová odpoveď: ",
						aktuálnaOtázka.poslednáOdpoveď());
				}
				break;

			/*#
			 * V stave spracovania „body odpovede“ je riadok prevedený
			 * na číselnú hodnotu, ktorá je poslaná do otázky metódou
			 * aktualizujBodyPoslednejOdpovede. (Tá aktualizuje bodové
			 * ohodnotenie poslednej definovanej odpovede.)
			 *
			 * Stav spracovania sa zmení na „znenie odpovede,“ čím sa
			 * uzatvára cyklus zadávania znenia odpovedí a ich bodového
			 * ohodnotenia. Tieto stavy sa opakujú, kým nie je
			 * v konfiguračnom súbore nájdený potrebný počet prázdnych
			 * riadkov. (Pozri aj spracovanie stavu SANKCIA_OTÁZKY vyššie.)
			 */
			case BODY_ODPOVEDE:
				stavSpracovania = StavSpracovania.ZNENIE_ODPOVEDE;
				aktuálnaOtázka.aktualizujBodyPoslednejOdpovede(čísloNaRiadku());

				if (podrobnosti)
				{
					farbaTextu(hnedá);
					vypíšRiadok("Počet bodov odpovede: ",
						aktuálnaOtázka.poslednáOdpoveď().početBodov());
				}
				break;
			}
		}
		catch (NumberFormatException e)
		{
			// Ak vznikne chyba pri spracovaní číselného údaju, bude to
			// oznámené používateľovi (červenou farbou, priebežne počas
			// spracovania a bez ohľadu na hodnotu atribútu „podrobnosti“).
			farbaTextu(červená);
			vypíšRiadok("Nesprávny číselný formát na riadku ",
				čísloRiadka, ".");
		}
		catch (Exception e)
		{
			// Všetky „iné chyby“ budú vypísané vo všeobecnom tvare:
			// „Úroveň #: «trieda chyby»: «text chyby» na riadku #.“
			// (Pričom toto je úroveň 3. Tiež bez ohľadu na hodnotu
			// atribútu „podrobnosti.“)
			farbaTextu(červená);
			vypíšRiadok("Úroveň 3: ", e.getClass().getName(), ": ",
				e.getMessage(), " na riadku ", čísloRiadka, ".");
		}
	}


	/*# Nasledujú definície súkromných pomocných metód ladiacich výpisov. #*/


	/**
	 * Toto je pomocná súkromná metóda pre metódu výpisKvízu. Má za úlohu
	 * vypísať nenulovú bodovú hodnotu s prípadným prefixom podľa znamienka
	 * a s možnosťou uzavretia výpisu do zátvoriek.
	 *
	 * @param hodnota        bodová hodnota na vypísanie
	 * @param kladnýPrefix   prefix, ktorý má byť vypísaný pred kladnou
	 *                       hodnotou; hodnota null znamená „žiadny prefix“
	 * @param zápornýPrefix  prefix, ktorý má byť vypísaný pred zápornou
	 *                       hodnotou (tým nie je myslený výpis záporného
	 *                       znamienka, ktoré je vypísané v každom prípade);
	 *                       hodnota null znamená „žiadny prefix“
	 * @param zátvorky       ak je true, tak bude výpis uzavretý do zátvoriek
	 */
	private void vypíšBodovúHodnotu(int hodnota,
		String kladnýPrefix, String zápornýPrefix,
		boolean zátvorky)
	{
		if (hodnota < 0)
		{
			if (zátvorky)
			{
				farbaTextu(šedá);
				vypíš(" (");
			}

			if (null != zápornýPrefix)
			{
				farbaTextu(červená);
				vypíš(zápornýPrefix, ": ");
			}
			farbaTextu(svetločervená);
			vypíš(hodnota);

			if (zátvorky)
			{
				farbaTextu(šedá);
				vypíš(")");
			}
		}
		else if (hodnota > 0)
		{
			if (zátvorky)
			{
				farbaTextu(šedá);
				vypíš(" (");
			}

			if (null != kladnýPrefix)
			{
				farbaTextu(zelená);
				vypíš(kladnýPrefix, ": ");
			}
			farbaTextu(svetlozelená);
			vypíš(hodnota);

			if (zátvorky)
			{
				farbaTextu(šedá);
				vypíš(")");
			}
		}
	}

	/**
	 * Táto verzia súkromnej metódy nevyžaduje zadanie argumentu zátvorky –
	 * vypíše ich automaticky.
	 *
	 * @param hodnota        bodová hodnota na vypísanie
	 * @param kladnýPrefix   prefix, ktorý má byť vypísaný pred kladnou
	 *                       hodnotou; hodnota null znamená „žiadny prefix“
	 * @param zápornýPrefix  prefix, ktorý má byť vypísaný pred zápornou
	 *                       hodnotou (tým nie je myslený výpis záporného
	 *                       znamienka, ktoré je vypísané v každom prípade);
	 *                       hodnota null znamená „žiadny prefix“
	 */
	private void vypíšBodovúHodnotu(int hodnota,
		String kladnýPrefix, String zápornýPrefix)
	{
		vypíšBodovúHodnotu(hodnota, kladnýPrefix, zápornýPrefix, true);
	}

	/**
	 * Táto verzia súkromnej metódy nevyžaduje zadanie argumentov kladného
	 * a záporného prefixu – nevypíše žiadne.
	 *
	 * @param hodnota   bodová hodnota na vypísanie
	 * @param zátvorky  ak je true, tak bude výpis uzavretý do zátvoriek
	 */
	private void vypíšBodovúHodnotu(int hodnota, boolean zátvorky)
	{
		vypíšBodovúHodnotu(hodnota, null, null, zátvorky);
	}

	/**
	 * Táto verzia súkromnej metódy nevyžaduje zadanie argumentov kladného
	 * a záporného prefixu, ani zátvoriek – prefixy nevypíše, zátvorky áno.
	 *
	 * @param hodnota   bodová hodnota na vypísanie
	 */
	private void vypíšBodovúHodnotu(int hodnota)
	{
		vypíšBodovúHodnotu(hodnota, null, null, true);
	}

	/**
	 * Táto metóda vypíše úplné znenie všetkých otázok a odpovedí kvízu tak
	 * ako boli definované. Výpis slúži na kontrolu pre zadávateľa, či je
	 * jeho definícia kvízu správna.
	 */
	public void výpisKvízu()
	{
		int najhlbšíPrepadVKvíze = 0;
		int bodyZaKvíz = 0;

		for (int ot = 0; ot < otázky.dĺžka(); ++ot)
		{
			Otázka otázka = otázky.daj(ot);
			farbaTextu(purpurová);
			vypíšRiadok();

			if (ot < 9) vypíš(" ");
			vypíš(1 + ot, ". ");

			strop.zmrazOdsadenie(true);
			farbaTextu(modrá);
			vypíš(otázka);

			int sankcia = otázka.sankcia();
			if (0 != sankcia)
				vypíšBodovúHodnotu(sankcia, "odmena", "sankcia");
			vypíšRiadok();

			for (int od = 0; od < otázka.početOdpovedí(); ++od)
			{
				Odpoveď odpoveď = otázka.odpoveďČíslo(od);
				farbaTextu(hnedá);
				vypíš("    ", (char)('a' + od), ". ");
				strop.zmrazOdsadenie(true);

				int body = odpoveď.početBodov();

				if (body >= -1 && body <= 1)
				{
					if (body < 0)
						farbaTextu(červená);
					else if (body > 0)
						farbaTextu(zelená);
					else
						farbaTextu(šedá);
					vypíšRiadok(odpoveď);
				}
				else
				{
					if (body < 0)
						farbaTextu(červená);
					else
						farbaTextu(zelená);

					vypíš(odpoveď);

					vypíšBodovúHodnotu(body);
					vypíšRiadok();
				}
			}

			int minimum = otázka.vypočítajMinimum();
			int maximum = otázka.vypočítajMaximum();

			if (0 != minimum && 0 != maximum)
			{
				if (-1 != minimum || 1 != maximum)
				{
					farbaTextu(šedá);
					vypíš("Minimum: ");
					vypíšBodovúHodnotu(minimum, false);
					farbaTextu(šedá);
					vypíš("; maximum: ");
					vypíšBodovúHodnotu(maximum, false);
					vypíšRiadok();
				}

				if (0 > maximum)
				{
					farbaTextu(červená);
					vypíš("Za túto otázku nie je možné získať body!");
					vypíšRiadok();
				}
			}
			else if (0 != maximum)
			{
				if (1 != maximum)
				{
					farbaTextu(šedá);
					vypíš("Maximum: ");
					vypíšBodovúHodnotu(maximum, false);
					vypíšRiadok();
				}
			}
			else if (0 != minimum)
			{
				farbaTextu(šedá);
				vypíš("Minimum: ");
				vypíšBodovúHodnotu(minimum, false);
				vypíšRiadok();

				if (0 > minimum)
				{
					farbaTextu(červená);
					vypíš("Za túto otázku nie je možné získať body!");
					vypíšRiadok();
				}
			}
			else
			{
				farbaTextu(červená);
				vypíš("Za túto otázku nie je možné získať ani stratiť body!");
				vypíšRiadok();
			}

			najhlbšíPrepadVKvíze += minimum;
			bodyZaKvíz += maximum;
		}


		vypíšRiadok();
		if (0 == bodyZaKvíz)
		{
			farbaTextu(červená);
			vypíš("V kvíze nie je možné dosiahnuť kladný počet bodov!");
			vypíšBodovúHodnotu(bodyZaKvíz, false);
		}
		else
		{
			farbaTextu(modrá);
			vypíš("Maximálny počet bodov za kvíz: ");
			vypíšBodovúHodnotu(bodyZaKvíz, false);
		}
		vypíšRiadok();

		if (najhlbšíPrepadVKvíze < 0)
		{
			farbaTextu(šedá);
			vypíš("(");
			farbaTextu(modrá);
			vypíš("Najhlbší prepad v kvíze: ");
			vypíšBodovúHodnotu(najhlbšíPrepadVKvíze, false);
			farbaTextu(šedá);
			vypíšRiadok(")");
		}
		else if (najhlbšíPrepadVKvíze > 0)
		{
			// Toto asi nikdy nenastane, ale pre istotu je tu takéto
			// upozornenie implementované…
			farbaTextu(červená);
			vypíš("Pozor!");
			farbaTextu(modrá);
			vypíš(" V kvíze nie je možné získať menej než: ");
			vypíšBodovúHodnotu(najhlbšíPrepadVKvíze, false);
			farbaTextu(modrá);
			switch (najhlbšíPrepadVKvíze)
			{
			case 1: vypíš(" bod"); break;
			case 2: case 3: case 4: vypíš(" body"); break;
			default: vypíš(" bodov");
			}
			vypíšRiadok();
		}
	}


	/**
	 * Táto metóda zvolí všetky otázky kvízu v tom poradí, v ktorom boli
	 * definované a spustí nový kvíz. V tejto fáze smú byť otázky (a ich
	 * možnosti) ešte premiešané.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void vyberVšetkyOtázky() throws ChybaKvízu
	{
		if (0 == otázky.dĺžka()) throw new ChybaKvízu(
			ChybaKvízu.NEDOSTATOK_OTÁZOK);

		výber.vymaž();
		for (Otázka otázka : otázky)
			výber.pridaj(new VýberOtázky(otázka));

		koniec = false;
		otázkaVýberu = 0;
		početOpakovaní = 0;
	}

	/**
	 * Táto metóda vygeneruje nový výber otázok kvízu v tom poradí ako boli
	 * definované, s počtom zadaným v konfiguračnom súbore kvízu a spustí
	 * nový kvíz. V tejto fáze smú byť otázky (a ich možnosti) ešte
	 * premiešané.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void generujVýberOtázok() throws ChybaKvízu
	{
		// Metóda generovania výberu otázok je jednoduchá:
		//     – najskôr sa vyberú všetky definované otázky,
		//     – potom sa zistí, koľko otázok je potrebné vyradiť
		//       a zabezpečí sa ich vyradenie.

		vyberVšetkyOtázky();

		int trebaVyradiť = početOtázok <= 0 ? 0 :
			(otázky.dĺžka() - početOtázok);

		for (int i = 0; i < trebaVyradiť; ++i)
			výber.vymaž((int)náhodnéCeléČíslo(0, výber.dĺžka() - 1));
	}

	/**
	 * Táto metóda dodatočne premieša poradie zvolených otázok. Nesmie byť
	 * spustená počas riešenia kvízu. Ide o doplnok metód vyberVšetkyOtázky
	 * a generujVýberOtázok.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void premiešajOtázky() throws ChybaKvízu
	{
		if (0 == výber.dĺžka()) throw new ChybaKvízu(
			ChybaKvízu.NEDOSTATOK_OTÁZOK);

		for (VýberOtázky otázka : výber)
			if (otázka.bolaNavštívená())
				throw new ChybaKvízu(ChybaKvízu.MIEŠANIE_ZAMIETNUTÉ);

		int počet = výber.dĺžka() - 1;
		for (int i = 0; i <= počet; ++i)
			výber.vymeň((int)náhodnéCeléČíslo(0, počet),
				(int)náhodnéCeléČíslo(0, počet));
	}

	/**
	 * Táto metóda je nepovinným doplnkom k metódam vyberVšetkyOtázky
	 * a generujVýberOtázok a slúži na premiešanie poradia odpovedí všetkých
	 * otázok. Nesmie byť spustená počas riešenia kvízu.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void premiešajOdpovedeOtázok() throws ChybaKvízu
	{
		if (0 == výber.dĺžka()) throw new ChybaKvízu(
			ChybaKvízu.NEDOSTATOK_OTÁZOK);

		for (VýberOtázky otázka : výber)
			if (otázka.bolaNavštívená())
				throw new ChybaKvízu(ChybaKvízu.MIEŠANIE_ZAMIETNUTÉ);

		int počet = výber.dĺžka();
		for (int i = 0; i < počet; ++i)
			výber.daj(i).premiešajOdpovede();
	}

	/**
	 * Táto metóda vráti objekt typu VýberOtázky, s ktorým je možné ďalej
	 * pracovať. (Pozri opis tried VýberOtázky a Otázka a opisy ich metód.)
	 * Základom je zobrazenie textu otázky a jej možností v určenom poradí.
	 *
	 * Ak táto metóda vráti hodnotu null, znamená to, že kvíz sa skončil
	 * a môže byť vykonané hodnotenie riešiteľa, prípadne mu môže byť
	 * ponúknutá možnosť opätovného prejdenia otázok kvízu (na kontrolu),
	 * ak to dovoľuje odporúčaná navigácia.
	 *
	 * Množinu odporúčaných ovládacích prvkov vracia metóda
	 * odporúčanáNavigácia.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia
	 *         je aktuálnou otázkou kvízu
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public VýberOtázky aktuálnaOtázka()
	{
		System.out.println("A");
		if (koniec) return null;

		System.out.println("B: " + otázkaVýberu);
		if (otázkaVýberu < 0 || otázkaVýberu >= výber.dĺžka()) return null;

		System.out.println("C");
		VýberOtázky aktuálna = výber.get(otázkaVýberu);
		if (null != aktuálna) aktuálna.navštívená();
		return aktuálna;
	}

	/**
	 * Táto metóda posunie kvíz na nasledujúcu otázku. Metóda aktuálnaOtázka
	 * slúži na získanie objektu VýberOtázky na ďalšie spracovanie. (Pozri aj
	 * metódu ďalšiaOtázka.)
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void choďVpred() throws ChybaKvízu
	{
		if (otázkaVýberu >= výber.dĺžka())
			throw new ChybaKvízu(ChybaKvízu.NIE_JE_ĎALŠIA_OTÁZKA);

		++otázkaVýberu;
	}

	/**
	 * Táto metóda posunie kvíz na predchádzajúcu otázku. Metóda
	 * aktuálnaOtázka slúži na získanie objektu VýberOtázky na ďalšie
	 * spracovanie. (Pozri aj metódu predchádzajúcaOtázka.)
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void choďVzad() throws ChybaKvízu
	{
		if (otázkaVýberu < 0)
			throw new ChybaKvízu(ChybaKvízu.NIE_JE_PREDCHÁDZAJÚCA_OTÁZKA);

		--otázkaVýberu;
	}

	/**
	 * Táto metóda posunie kvíz na nasledujúcu otázku a vráti jej inštanciu,
	 * ak je to možné. Volá pri tom metódu aktuálnaOtázka.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia
	 *         je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public VýberOtázky ďalšiaOtázka() throws ChybaKvízu
	{
		choďVpred();
		return aktuálnaOtázka();
	}

	/**
	 * Táto metóda posunie kvíz na predchádzajúcu otázku a vráti jej
	 * inštanciu, ak je to možné. Volá pri tom metódu aktuálnaOtázka.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia
	 *         je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public VýberOtázky predchádzajúcaOtázka() throws ChybaKvízu
	{
		choďVzad();
		return aktuálnaOtázka();
	}

	/**
	 * Táto metóda posunie kvíz na začiatok – zopakuje cyklus riešenia.
	 * Spustenie tejto metódy by malo byť reakciou na voľbu ovládacieho
	 * prvku (napr. tlačidla) určeného na opakovanie riešenia kvízu.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return inštancia triedy VýberOtázky alebo null; vrátená inštancia
	 *         je aktuálnou otázkou kvízu; pozri aj metódu aktuálnaOtázka
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public VýberOtázky opakujKvíz()
	{
		otázkaVýberu = 0;
		++početOpakovaní;
		return aktuálnaOtázka();
	}

	/**
	 * Táto metóda slúži na získanie množiny odporúčaných navigačných prvkov,
	 * ktoré by mala aplikácia zobraziť riešiteľovi v súvislosti s aktuálnym
	 * stavom kvízu.
	 *
	 * Napríklad v prípade, že definícia kvízu poskytuje možnosť ľubovoľnej
	 * navigácie medzi otázkami počas kvízu to budú nasledujúce ovládacie
	 * prvky (predpokladajme, že používame tlačidlá) v nasledujúcich
	 * situáciách:
	 *
	 *     — tlačidlo „ďalej“ pri prvej otázke kvízu,
	 *     — tlačidlo „späť“ pri poslednej otázke kvízu,
	 *     — obidve spomenuté tlačidlá pre všetky ostatné otázky kvízu
	 *     — a tlačidlo „koniec kvízu“ po prejdení všetkých otázok kvízu.
	 *
	 * Množina prvkov je vrátená vo forme reťazca, v ktorom každý má znak
	 * význam jedného navigačného prvku (napr. tlačidla).
	 *
	 * <!-- Poznámka: Znakové entity &lt; a &gt; označujú znaky znamienok
	 * menší než a väčší než. Sú tu použité preto, lebo v rámci dokumentácie
	 * by tieto znaky nemali byť používané, pretože slúžia na vloženie HTML
	 * značiek. -->
	 *
	 * Môže ísť o nasledujúce znaky:
	 *
	 *  &lt; – ovládací prvok (napr. tlačidlo) na posun späť,
	 *  &gt; – ovládací prvok (napr. tlačidlo) na posun ďalej,
	 *     O – ovládací prvok (napr. tlačidlo) na spustenie opätovného
	 *         prejdenia otázok (rekapitulácie kvízu pred ukončením),
	 *     0 – ovládací prvok (napr. tlačidlo) na ukončenie kvízu
	 *         a zobrazenie vyhodnotenia,
	 *     E – ovládací prvok alebo prvky (napr. vstupný riadok, sústava
	 *         tlačidiel a pod.) slúžiace na voľbu odpovede,
	 *
	 *     * – ovládací prvok na spustenie nového kvízu.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return reťazec pozostávajúci z povolených znakov, z ktorých každý
	 *         označuje jeden odporúčaný ovládací prvok, ktorý by mal byť
	 *         zobrazený riešiteľovi (pozri opis vyššie)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public String odporúčanáNavigácia()
	{
		if (koniec) return "*";

		String zoznam = "";

		if ('L' == konfiguračnéPísmeno && otázkaVýberu > 0)
			zoznam += "<";

		if (otázkaVýberu < výber.dĺžka())
			zoznam += ">";
		else if ('L' == konfiguračnéPísmeno ||
			('O' == konfiguračnéPísmeno && 0 == početOpakovaní))
			zoznam += "O";

		boolean môžeSkončiť = true;

		for (VýberOtázky otázka : výber)
			if (!otázka.bolaNavštívená())
			{
				môžeSkončiť = false;
				break;
			}

		if (môžeSkončiť && (0 != početOpakovaní ||
			otázkaVýberu >= výber.dĺžka()))
			zoznam += "0";

		if (otázkaVýberu >= 0 && otázkaVýberu < výber.dĺžka())
			zoznam += "E";

		return zoznam;
	}


	/**
	 * Táto metóda pošle do jadra kvízu číslo odpovede (možnosti), ktorú
	 * zvolil riešiteľ.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @param možnosť  číslo odpovede (možnosti), ktorú zvolil riešiteľ
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public void voľbaOdpovede(int možnosť) throws ChybaKvízu
	{
		if (koniec) throw new ChybaKvízu(ChybaKvízu.JE_KONIEC);

		VýberOtázky aktuálna = aktuálnaOtázka();
		if (null == aktuálna)
			throw new ChybaKvízu(ChybaKvízu.VOĽBA_NIE_JE_MOŽNÁ);

		aktuálna.voľba(možnosť);
	}


	/**
	 * Táto metóda ukončí riešenie kvízu. Spustenie tejto metódy by malo byť
	 * reakciou na voľbu ovládacieho prvku (napr. tlačidla) určeného na
	 * ukončenie riešenia kvízu.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return null, pretože po ukončení kvízu vracia metóda aktuálnaOtázka
	 *         hodnotu null (pozri aj metódu aktuálnaOtázka)
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public VýberOtázky koniec()
	{
		koniec = true;
		return aktuálnaOtázka();
	}


	/**
	 * Táto metóda overí, či sa riešenie kvízu už skončilo.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return true ak sa kvíz už skončil
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public boolean jeKoniec()
	{
		return koniec;
	}


	/**
	 * Táto metóda zráta a vráti počet bodov, ktoré získal riešiteľ
	 * na základe svoji odpovedí.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return počet bodov získaných riešiteľom
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public int početBodov()
	{
		int body = 0;
		for (VýberOtázky otázka : výber)
			body += otázka.body();
		return body;
	}

	/**
	 * Táto metóda vypočíta a vráti najnižší počet bodov, ktoré bolo možné
	 * získať v tomto kvíze (aby bolo možné posúdiť úspešnosť riešiteľa).
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return najnižší počet bodov tohto kvízu
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public int minimumBodov()
	{
		int body = 0;
		for (VýberOtázky otázka : výber)
			body += otázka.minimum();
		return body;
	}

	/**
	 * Táto metóda vypočíta a vráti najvyšší počet bodov, ktoré bolo možné
	 * získať v tomto kvíze, aby bolo možné posúdiť úspešnosť riešiteľa.
	 *
	 * (Poznámka: Táto metóda patrí do množiny metód tvoriacich hlavnú časť
	 * komunikačného rozhrania balíčka kvízu. Toto sú metódy, ktoré
	 * sprostredkúvajú komunikáciu medzi inštanciou kvízu a rozhraním
	 * v iných projektoch.)
	 *
	 * @return najvyšší počet bodov tohto kvízu
	 *
	 * @see #vyberVšetkyOtázky()
	 * @see #generujVýberOtázok()
	 * @see #premiešajOtázky()
	 * @see #premiešajOdpovedeOtázok()
	 *
	 * @see #aktuálnaOtázka()
	 * @see #choďVpred()
	 * @see #choďVzad()
	 * @see #ďalšiaOtázka()
	 * @see #predchádzajúcaOtázka()
	 * @see #opakujKvíz()
	 * @see #odporúčanáNavigácia()
	 *
	 * @see #voľbaOdpovede(int)
	 * @see #koniec()
	 * @see #jeKoniec()
	 *
	 * @see #početBodov()
	 * @see #minimumBodov()
	 * @see #maximumBodov()
	 */
	public int maximumBodov()
	{
		int body = 0;
		for (VýberOtázky otázka : výber)
			body += otázka.maximum();
		return body;
	}
}

Prosím, zvoľte triedu balíčka kvíz.

Poznámka: Ak by ste sa rozhodli ukladať triedy balíčka ručne, nezabudnite, že musia byť uložené v (pod)priečinku s názvom kvíz.

2 otázky
Ľpriamočiaro – platí iba prvý znak, čiže v skutočnosti je nastavenie „ľubovoľne“ (zvyšok riadka je ignorovaný)

Otázka s dvomi možnosťami a odmenou 1.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 1.
-2
jeden
1
dva
2
nič
0

Otázka s dvomi možnosťami a odmenou 2.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 2.
-2
jeden
1
dva
2
nič
0

Otázka s dvomi možnosťami a odmenou 3.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 3.
-2
jeden
1
dva
2
nič
0

Otázka s dvomi možnosťami a odmenou 4.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 4.
-2
jeden
1
dva
2
nič
0

Otázka s dvomi možnosťami a odmenou 5.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 5.
-2
jeden
1
dva
2
nič
0

Otázka s dvomi možnosťami a odmenou 6.
2
áno
1
nie
-2

Otázka s tromi možnosťami a sankciou 6.
-2
jeden
1
dva
2
nič
0