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

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

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

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

Zadanie

Naprogramujte jednoduchú zábavnú aplikáciu, ktorá bude umožňovať pridávanie bublín, ktoré bude môcť používateľ praskať vystreľujúcimi ihlami.

Požiadavky:

  • Nech má používateľ možnosť pridávať na plochu náhodne generované ihly a bubliny, pričom pri bublinách bude náhodne generovaná ich poloha a pri ihlách orientácia (smer).
  • Nech sa vytvorené bubliny, ktoré kolidujú vzájomne odpudzujú, kým nenájdu prázdny priestor.
  • Nech ihla, ktorá narazí do bubliny zmizne a spôsobí bubline určité poškodenie, ktoré bude používateľovi indikované zmenou farby.
  • Nech po prekročení určitej úrovne poškodenia bublina praskne.
  • Nech bubliny, ktoré prasknú vygenerujú nové ihly. (Čím môže vzniknúť kaskáda praskania.)

Bonusové požiadavky:

  • Pridajte do aplikácie možnosť voľby počtu naraz pridávaných bublín alebo ihiel.
  • Pridajte do aplikácie počítadlo bublín.
  • Počítadlo môže náhodne putovať po obrazovke.
  • Počítadlo môže hodnotu počtu zobrazovať formou rímskej číslice.
  • Pridajte možnosť rovnomerného rozloženia pridávaných ihiel. To znamená, že napríklad tri ihly by sa od spoločnej polohy vzďaľovali so vzájomnou (rovnomernou) odchýlkou 120˚, päť ihiel 72˚ a podobne.
  • Spestrite správanie bublín podľa poškodenia tak, aby sa úroveň poškodenia nemenila priamo úmerne, ale náhodne, pričom každý typ poškodenia by mal iný dôsledok. Príklad:
  • Ak by všetky bubliny mali pri vytvorení rovnakú farbu (f1), táto farba by znamenala „plné zdravie“ a pri náraze ihly do takejto bubliny by sa farba vždy zmenila na inú stanovenú (f2), ktorá by znamenala „slabé poškodenie.“
  • Po náraze ihly s farbou slabého poškodenia (f2) by sa farba bubliny zmenila náhodne (čiže bublina by mala šancu vrátiť sa aj do stavu plného zdravia).
  • Ďalšia farba, f3, odlišná od f1 a f2 by znamenala „silné poškodenie.“ Keby ihla narazila do takejto bubliny, tá by ihneď praskla.
  • Keď ihla narazí do bubliny, ktorá má ľubovoľnú farbu, okrem f1, f2 a f3, tak bublina zmení svoju farbu na f3. (Čím stratí šancu na „uzdravenie.“)

Návrhy farieb:

  • Plné zdravie (f1): akvamarínová.
  • Slabé poškodenie (f2): žltá.
  • Silné poškodenie (f3): červená.

Riešenie

Toto riešenie má implementované všetky hlavné aj bonusové požiadavky. Je rozdelené do troch tried, pričom dve z nich (IhlaBublina) sú roboty s minimálne rozšírenou funkčnosťou.

Táto trieda v sebe obsahuje takmer kompletné riešenie projektu. Ďalšie dve triedy môžeme vnímať ako pomocné, riešiace predovšetkým vzhľadovú stránku projektu.

V tejto triede sú definované obidva zoznamy: ihiel a bublín a je tu implementovaná práca s nimi, ktorá zahŕňa napĺňanie zoznamov (tvorbu alebo obnovu ihiel a bublín), ovládanie bublín, kontrolu kolízií a podobne.

Tiež sú tu definované prvky (položky ponuky) slúžiace na požadovanú konfiguráciu: zmenu počtu naraz generovaných bublín a ihiel, prepínanie medzi rovnomerným a nerovnomerným rozložením generovaných ihiel.

Kód by nemal byť náročný na porozumenie.

~

import knižnica.*;

public class IhlyABubliny extends GRobot
{
	private final Zoznam<Ihla> ihly = new Zoznam<>();
	private final Zoznam<Bublina> bubliny = new Zoznam<>();

	private boolean rovnomerne = true;
	private int početIhiel = 7;
	private int pridajNaraz = 5;

	private int viditeľnýchBublín = 100;

	private final Obrázok ikonaOznačeného = new Obrázok(16, 16);

	private PoložkaPonuky položkaPočet;
	private PoložkaPonuky položkaPridaj;
	private PoložkaPonuky položkaRovnomerne;

	private IhlyABubliny()
	{
		for (int i = 0; i < viditeľnýchBublín; ++i)
			bubliny.pridaj(new Bublina());

		for (int i = 0; i < viditeľnýchBublín * 6; ++i)
		{
			Bublina bublina = new Bublina();
			bublina.skry();
			bubliny.pridaj(bublina);
		}

		for (int i = 0; i < viditeľnýchBublín * 49; ++i)
			ihly.pridaj(new Ihla());

		kresliDoObrázka(ikonaOznačeného);
		kruh(4);

		položkaPočet = Svet.pridajPoložkuPonuky(
			"Počet ihiel", Kláves.VK_H, Kláves.VK_N);
		položkaPridaj = Svet.pridajPoložkuPonuky(
			"Prírastok bublín", Kláves.VK_B, Kláves.VK_B);
		položkaRovnomerne = Svet.pridajPoložkuPonuky(
			"Rovnomerné rozloženie", Kláves.VK_R, Kláves.VK_M);
		položkaRovnomerne.ikona(ikonaOznačeného);

		// (Táto trieda je zároveň počítadlom bublín.)
		farba(šedá);
		písmo("Cambria", 44);
		zrýchlenie(0.1, false);
		gyroskop(90.0);
		zdvihniPero();
		náhodnáPoloha();
		náhodnýCieľ();
		aktivuj();
	}

	@Override public void voľbaPoložkyPonuky()
	{
		if (položkaPočet.aktivovaná())
		{
			Long počet = Svet.upravCeléČíslo(početIhiel, "Počet ihiel");
			if (null != počet)
			{
				if (počet <= 0) početIhiel = 1;
				else if (počet > 14) početIhiel = 14;
				else početIhiel = počet.intValue();
			}
		}
		else if (položkaPridaj.aktivovaná())
		{
			Long počet = Svet.upravCeléČíslo(pridajNaraz, "Prírastok bublín");
			if (null != počet)
			{
				if (počet <= 0) pridajNaraz = 1;
				else if (počet > 100) pridajNaraz = 100;
				else pridajNaraz = počet.intValue();
			}
		}
		else if (položkaRovnomerne.aktivovaná())
		{
			rovnomerne = !rovnomerne;
			if (rovnomerne)
				položkaRovnomerne.ikona(ikonaOznačeného);
			else
				položkaRovnomerne.ikona((Obrázok)null);
			Svet.pípni();
		}
	}

	public void náhodnýCieľ()
	{
		// (Robot počítadla sa pohybuje náhodne.)
		cieľ(
			Svet.náhodnéCeléČíslo(
				(long)Svet.najmenšieX(),
				(long)Svet.najväčšieX()),
			Svet.náhodnéCeléČíslo(
				(long)Svet.najmenšieY(),
				(long)Svet.najväčšieY()));
	}

	private void pridajBubliny()
	{
		// Poznámka: Menovka „ďalší“ umožňuje prerušenie viacerých úrovní
		// riadiacich štruktúr (cyklov). Bez menovky by sme museli používať
		// pomocnú premennú alebo rozdeliť algoritmus do dvoch metód ako to
		// je pri aktivácii ihiel.
		ďalší: for (int i = 0; i < pridajNaraz; ++i)
		{
			for (Bublina bublina : bubliny)
				if (bublina.skrytý())
				{
					// (Farba bubliny určuje mieru/typ poškodenia.)
					bublina.farba(akvamarínová);
					bublina.náhodnáPoloha();
					bublina.zobraz();
					continue ďalší;
				}

			bubliny.pridaj(new Bublina());
		}
	}

	private void aktivujIhlu(Poloha p, Smer s)
	{
		for (Ihla ihla : ihly)
			if (ihla.neaktívny())
			{
				ihla.poloha(p);
				ihla.smer(s);
				ihla.aktivuj();
				return;
			}

		Ihla ihla = new Ihla();
		ihly.pridaj(ihla);
		ihla.poloha(p);
		ihla.smer(s);
		ihla.aktivuj();
	}

	private void aktivujIhly(Poloha p)
	{
		double pôvodnýUhol = 360.0 / početIhiel;
		double pootočenie = Svet.náhodnéReálneČíslo(pôvodnýUhol);
		if (rovnomerne)
		{
			for (int i = 0; i < početIhiel; ++i)
				aktivujIhlu(p, new Uhol(pôvodnýUhol * i + pootočenie));
		}
		else
		{
			// Ani pri vypnutej voľbe rovnomerného rozloženia nie je
			// orientácia ihiel úplne náhodná. Pohybuje sa v škále násobkov
			// uhlov rovnomerného rozloženia. (To znamená, že niektoré ihly
			// sa budú prekrývať.)
			for (int j = 0; j < početIhiel; ++j)
			{
				int i = (int)Svet.náhodnéCeléČíslo(početIhiel);
				aktivujIhlu(p, new Uhol(pôvodnýUhol * i + pootočenie));
			}
		}
	}

	@Override public void kresliTvar()
	{
		// (Táto trieda je zároveň počítadlom bublín.)
		text(Svet.celéNaRímske(viditeľnýchBublín));
	}

	@Override public void zastavenie()
	{
		// (Robot počítadla sa pohybuje náhodne.)
		náhodnýCieľ();
	}

	@Override public void tik()
	{
		viditeľnýchBublín = 0;
		for (Bublina bublina : bubliny)
		{
			if (bublina.viditeľný())
			{
				++viditeľnýchBublín;
				for (Ihla ihla : ihly)
					if (ihla.aktívny() && ihla.koliduje(bublina))
					{
						ihla.deaktivuj();

						// (Farba bubliny určuje mieru/typ poškodenia.)
						if (akvamarínová == bublina.farba())
						{
							bublina.farba(žltá);
						}
						else if (žltá == bublina.farba())
						{
							bublina.náhodnáFarba();
						}
						else if (červená == bublina.farba())
						{
							--viditeľnýchBublín;
							bublina.skry();
							aktivujIhly(bublina);
						}
						else
						{
							bublina.farba(červená);
						}
						break;
					}
			}

			if (bublina.viditeľný() && bublina.neaktívny())
			{
				for (Bublina bublina2 : bubliny)
				{
					if (bublina != bublina2 && bublina2.viditeľný() &&
						bublina2.neaktívny() && bublina2.koliduje(bublina))
					{
						// (Odpudzovanie bublín.)
						Svet.nekresli();
						Poloha p; double posun;
						bublina2.otočNa(bublina);
						bublina2.vpravo(180);
						posun = 4 + bublina.veľkosť() / 2;
						bublina2.skoč(posun);
						p = bublina2.poloha();
						bublina2.odskoč(posun);
						bublina2.cieľ(p);
						bublina.otočNa(bublina2);
						bublina.vpravo(180);
						posun = 4 + bublina2.veľkosť() / 2;
						bublina.skoč(posun);
						p = bublina.poloha();
						bublina.odskoč(posun);
						bublina.cieľ(p);
						Svet.kresli();
						break;
					}
				}
			}
		}
	}

	@Override public void klik()
	{
		if (ÚdajeUdalostí.tlačidloMyši(PRAVÉ)) pridajBubliny();
		else aktivujIhly(ÚdajeUdalostí.polohaMyši());
	}

	public static void main(String[] args)
	{
		Svet.skry();
		Svet.nekresli();
		Svet.použiKonfiguráciu("IhlyABubliny.cfg");
		new IhlyABubliny();
		Svet.zbaľ();
		Svet.kresli();
		Svet.zobraz();
	}
}
  Bublina »

Táto trieda je veľmi jednoduchá. Jej úlohou je iba kresliť svoj tvar – tvar bubliny (ktorý má tvar kružnice so štvrťoblúkom vo vnútri).

~

import knižnica.*;

public class Bublina extends GRobot
{
	public Bublina()
	{
		ohranič(PLOT);
		veľkosť(20);
		farba(akvamarínová);
		hrúbkaČiary(1);
		náhodnáPoloha();
		zrýchlenie(0.1, false);
		maximálnaRýchlosť(2);
		gyroskop(90.0);
		zdvihniPero();
	}

	@Override public void kresliTvar()
	{
		krúžok();
		preskočVľavo(veľkosť() * 0.66);
		choďPoOblúku(90, veľkosť() * -0.66);
	}
}
« Ihly a bubliny Ihla »

Úlohou tejto triedy je kresliť svoj tvar – tvar ihly (ktorý má formu skokovo sa stenčujúcej čiary), deaktivovať sa po opustení plátna a automaticky sa zobrazovať alebo skrývať podľa stavu aktivity.

~

import knižnica.*;

public class Ihla extends GRobot
{
	public Ihla()
	{
		ohranič();
		veľkosť(20);
		farba(tmavotyrkysová);
		zdvihniPero();
		rýchlosť(15, false);
		skry();
	}

	@Override public void kresliTvar()
	{
		double hrúbka = veľkosť() / 5;
		double dĺžka = veľkosť() / 2;
		odskoč();
		for (int i = 0; i < 4; ++i)
		{
			if (hrúbka > 0) hrúbkaČiary(hrúbka);
			else hrúbkaČiary(0.5);
			hrúbka -= 1.0;
			vpred(dĺžka);
		}
	}

	@Override public void mimoHraníc()
	{
		deaktivuj();
	}

	@Override public void aktivácia()
	{
		zobraz();
	}

	@Override public void deaktivácia()
	{
		skry();
	}
}
« Bublina  

 

Výsledok

  
obrázok 
Ukážka vzhľadu aplikácie. 
 

Návrhy na zlepšenia

  • Vzájomné vzďaľovanie sa kolidujúcich bublín by mohlo byť spresnené tak, aby bubliny skončili v tesnej vzdialenosti od seba (takmer sa dotýkajúc – tak, aby ich vzájomná poloha nebola viac vyhodnocovaná ako kolízia).
  • Aktuálny spôsob kreslenia tvaru ihly je najjednoduchším rýchlym riešením. Dal by sa vyriešiť aj iným spôsobom, napríklad vyplneným dlhým tenkým trojuholníkom s oblúkom namiesto najkratšej strany.