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

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

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

Dátum: 5. 4. 2020, pred štyrmi rokmi, aktualizované: 6. 4. 2020, pred štyrmi rokmi

Tento príklad sa zaoberá simuláciou šírenia sa nákazy (predpokladajme, že vírusovej) v určitej populácii (budeme predpokladať, že ľudskej – tomu bude zodpovedať aj určitý druh špeciálneho správania sa – pobyt populácie doma).

Ako všetky simulácie, aj táto je veľkým zjednodušením reality. Táto je založená na týchto predpokladoch:

  • centrom pozornosti je osoba, ktorá môže mať (v súvislosti so šírením vírusu) viaceré stavy, napríklad: zdravá, nakazená, chorá…
  • každá osoba má rôzne parametre ovplyvňujúce rýchlosť zmeny stavov, napríklad: imunitu, inkubáciu (aj keď toto je skôr vlastnosť patriaca vírusu; simulácia sa však nezaoberá jednotlivými čiastočkami vírusu, tak je vlastnosť prisúdená osobe), „odolnosť“ chorého (voči úmrtiu – starší a rizikoví pacienti ju majú nižšiu)…
  • ide o nový vírus (čomu by malo zodpovedať nastavenie parametrov imunity), takže nebude nič čudné na tom, keď si nakoniec štádiami nakazeného a chorého prejde každý a pôjde pri tom len o to, kedy a na ako dlho (o závažnosti priebehu ochorenia však simulácia nepovie nič)…

Komentár k implementácii

Keď sa podrobnejšie pozriete na princíp simulácie, nájdete tam atribút infekcia. Cieľom atribútu je simulovať „vírusovú nálož“ osoby… Funguje perfektne. Každá osoba „schytá“ nejakú úroveň, aj tí „neovplyvnení“ (modrí = zdraví – v legende je momentálne singulár; v súlade s vymenovacím typom v programe).

Nedá sa zabrániť tomu, aby sa určitej minimálnej náloži niekto pri takto infekčnej nákaze vyhol. No všimnite si, ako klesne počet „infikovaných“ (to jest tých, u ktorých táto nálož už prekročila takú hranicu, aby mohli začať nákazu šíriť ďalej, ale ešte nie sú chorí), keď zavedieme pravidlo „zostaň doma.“

Prvá verzia je veľmi jednoduchá a priamočiara – inicializuje osobu (podľa rozsahov atribútov určených prostedníctvom konštánt), vyrobí želaný počet osôb a spustí simuláciu. Hlavnými akčnými prvkami simulácie budú metódy starajúce sa o zmeny stavov osoby (aktivita v triede Osoba) a o zabezpečenie kontaktu osôb s prenosom nákazy (kontakt v triede Osoba volaný v metóde tik triedy Hlavná). Simulácia sa dá reštartovať kliknutím myšou.

ikonavirus­‑01.7z 4,28 kB (4,18 KiB), 2. 4. 2020

 

Trieda Osoba.java

~

import knižnica.*;

public class Osoba extends GRobot
{
	// Rozsahy pre atribúty simulácie (nižšie):
	public static int imunita1 = 10;
	public static int imunita2 = 20;
	public static int odolnosť1 = 150;
	public static int odolnosť2 = 300;
	public static int inkubácia1 = 300;
	public static int inkubácia2 = 600;
	public static int odchyt1 = 100;
	public static int odchyt2 = 200;

	// Konštanty stavov:
	public final static int ČISTÝ = 0;
	public final static int NAKAZENÝ = 1;
	public final static int CHORÝ = 2;
	public final static int KARANTÉNA = 3;
	public final static int VYLIEČENÝ = 4;
	public final static int ZOMREL = 5;

	public final static int MAX_STAV = ZOMREL + 1;

	// Hranica, po ktorej prekročení bude osoba považovaná
	// za nakazenú:
	private int imunita;

	// Energia, po ktorej vyčerpaní počas procesu liečenia
	// osoba zomiera:
	private int odolnosť;

	// Časový limit, po ktorého uplynutí sa stáva nakazená
	// osoba chorou:
	private int inkubácia;

	// Reakčný čas potrebný na to, aby chorú osobu odchytili
	// do karantény:
	private int odchyt;

	// Aktuálna úroveň nainfikovanosti osoby (jej hodnota
	// musí najprv prekročiť hodnotu imunity, aby osoba ochorela
	// potom hodnota klesá a po dosiahnutí nuly sa osoba stáva
	// vyliečenou = imúnnou):
	private int infekcia;

	// Aktuálny stav osoby:
	private int stav;


	// Konštruktor.
	public Osoba()
	{
		ohranič(PLOT);
		vypĺňajTvary();
		veľkosť(5);
		zdvihniPero();
		aktivuj(false);
		reštart();
	}


	// Metóta slúžiaca na reštartovanie osoby (pri spúšťaní novej simulácie).
	public void reštart()
	{
		imunita = (int)Svet.náhodnéCeléČíslo(imunita1, imunita2);
		odolnosť = (int)Svet.náhodnéCeléČíslo(odolnosť1, odolnosť2);
		inkubácia = (int)Svet.náhodnéCeléČíslo(inkubácia1, inkubácia2);
		odchyt = (int)Svet.náhodnéCeléČíslo(odchyt1, odchyt2);

		infekcia = 0;
		stav = ČISTÝ;

		náhodnáPoloha();
		náhodnýSmer();
		rýchlosť(5, false);
	}

	// Týmto sa nastaví osobe taká uroveň infekcie, aby bola pri najbližšom
	// vykonaní reakcie „aktivita“ označená za infikovanú.
	public void infikuj()
	{
		infekcia = imunita + 1;
	}

	// Metóda na zmenu stavu osoby.
	public void stav(int nový)
	{
		if (ZOMREL == stav) return;

		if (CHORÝ == nový) infekcia *= 2;
		if (KARANTÉNA == nový) odolnosť *= 2;

		stav = nový;

		if (KARANTÉNA == stav || ZOMREL == stav) rýchlosť(0, false);
		else rýchlosť(5, false);
	}

	// Metóda kontrolujúca a simulujúca kontakt dvoch osôb.
	public void kontakt(Osoba o)
	{
		// (Poznámka: Aj keby sme mali, nekontrolujeme, či osoba
		// „o“ náhodou nie je táto osoba, pretože v hlavnej triede
		// je zabezpečené, aby sa to nestalo.)

		if (KARANTÉNA == stav || KARANTÉNA == o.stav) return;

		if (vzdialenosťOd(o) < 10)
		{
			int i1 = 0, i2 = 0;
			if (NAKAZENÝ == o.stav && VYLIEČENÝ > stav) ++i1;
			if (NAKAZENÝ == stav && VYLIEČENÝ > o.stav) ++i2;
			infekcia += i1;
			o.infekcia += i2;
		}
	}

	@SuppressWarnings("fallthrough")
	@Override public void aktivita()
	{
		switch (stav)
		{
		case ČISTÝ:
			if (infekcia > imunita) stav(NAKAZENÝ);
			break;

		case NAKAZENÝ:
			if (--inkubácia <= 0) stav(CHORÝ);
			break;

		case CHORÝ:
			if (--odchyt <= 0) stav(KARANTÉNA);
			// nobreak

		case KARANTÉNA:
			if (--infekcia <= 0) stav(VYLIEČENÝ);
			if (--odolnosť <= 0) stav(ZOMREL);
			break;

		// case VYLIEČENÝ: break;
		// case ZOMREL: break;
		}
	}

	@Override public void mimoHraníc()
	{
		náhodnýSmer();
	}

	@Override public void kresliTvar()
	{
		switch (stav)
		{
		case ČISTÝ: farba(svetlomodrá); break;
		case NAKAZENÝ: farba(tmavožltá); break;
		case CHORÝ: farba(svetločervená); break;
		case KARANTÉNA: farba(svetlopurpurová); break;
		case VYLIEČENÝ: farba(tmavotyrkysová); break;
		case ZOMREL: farba(tmavošedá); break;
		}
		krúžok();
		if (KARANTÉNA == stav)
			kružnica(2 * veľkosť());
	}

	public static void main(String[] args) { Hlavná.main(args); }
}

Trieda Hlavná.java

~

import knižnica.*;

public class Hlavná extends GRobot
{
	public final static int ŠÍRKA = 500;
	public static int počet = 150;

	private final Zoznam<Osoba> osoby = new Zoznam<>();

	private Hlavná()
	{
		super(ŠÍRKA, (int)(ŠÍRKA * 0.8));
		Svet.nekresli();
		Svet.zbaľ();
		Svet.vystreď();
		skry();

		for (int i = 0; i < počet; ++i)
			osoby.pridaj(new Osoba());
		for (int i = 0; i < 25; ++i)
			osoby.daj(i).infikuj();

		Svet.spustiČasovač(0.020);
		Svet.prekresli();
	}

	public void reštart()
	{
		Svet.zastavČasovač();
		for (int i = 0; i < počet; ++i)
		{
			osoby.daj(i).reštart();
			if (i < 25) osoby.daj(i).infikuj();
		}
		Svet.spustiČasovač();
	}

	@Override public void tik()
	{
		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			for (int j = i - 1; j >= 0; --j)
			{
				Osoba o2 = osoby.daj(j);
				o1.kontakt(o2);
			}
		}

		if (Svet.neboloPrekreslené())
			Svet.prekresli();
	}

	@Override public void klik()
	{
		reštart();
	}

	public static void main(String[] args)
	{
		new Hlavná();
	}
}

Výsledok

obrázok obrázok
Ukážka priebehu simulácie po spustení…

V druhej verzii pridáme možnosť sledovania a vykresľovania štatistík (to jest: ukladanie počtov osôb, ktoré sú v konkrétnom časovom okamihu v konkrétnom stave a vykreslenie zozbieraných údajov vo forme grafu).

ikonavirus­‑02.7z 5,75 kB (5,62 KiB), 3. 4. 2020

 

Trieda Osoba.java

~

import knižnica.*;

public class Osoba extends GRobot
{
	// Rozsahy pre atribúty simulácie (nižšie):
	public static int imunita1 = 10;
	public static int imunita2 = 20;
	public static int odolnosť1 = 150;
	public static int odolnosť2 = 300;
	public static int inkubácia1 = 300;
	public static int inkubácia2 = 600;
	public static int odchyt1 = 100;
	public static int odchyt2 = 200;

	// Konštanty stavov:
	public final static int ČISTÝ = 0;
	public final static int NAKAZENÝ = 1;
	public final static int CHORÝ = 2;
	public final static int KARANTÉNA = 3;
	public final static int VYLIEČENÝ = 4;
	public final static int ZOMREL = 5;

	public final static int MAX_STAV = ZOMREL + 1;

	// Hranica, po ktorej prekročení bude osoba považovaná
	// za nakazenú:
	private int imunita;

	// Energia, po ktorej vyčerpaní počas procesu liečenia
	// osoba zomiera:
	private int odolnosť;

	// Časový limit, po ktorého uplynutí sa stáva nakazená
	// osoba chorou:
	private int inkubácia;

	// Reakčný čas potrebný na to, aby chorú osobu odchytili
	// do karantény:
	private int odchyt;

	// Aktuálna úroveň nainfikovanosti osoby (jej hodnota
	// musí najprv prekročiť hodnotu imunity, aby osoba ochorela
	// potom hodnota klesá a po dosiahnutí nuly sa osoba stáva
	// vyliečenou = imúnnou):
	private int infekcia;

	// Aktuálny stav osoby:
	private int stav;


	// Konštruktor.
	public Osoba()
	{
		ohranič(PLOT);
		vypĺňajTvary();
		veľkosť(5);
		zdvihniPero();
		aktivuj(false);
		reštart();
	}


	// Metóta slúžiaca na reštartovanie osoby (pri spúšťaní novej simulácie).
	public void reštart()
	{
		imunita = (int)Svet.náhodnéCeléČíslo(imunita1, imunita2);
		odolnosť = (int)Svet.náhodnéCeléČíslo(odolnosť1, odolnosť2);
		inkubácia = (int)Svet.náhodnéCeléČíslo(inkubácia1, inkubácia2);
		odchyt = (int)Svet.náhodnéCeléČíslo(odchyt1, odchyt2);

		infekcia = 0;
		stav = ČISTÝ;

		náhodnáPoloha();
		náhodnýSmer();
		rýchlosť(5, false);
	}

	// Týmto sa nastaví osobe taká uroveň infekcie, aby bola pri najbližšom
	// vykonaní reakcie „aktivita“ označená za infikovanú.
	public void infikuj()
	{
		infekcia = imunita + 1;
	}

	// Metóda na prečítanie aktuálneho stavu.
	// (Poznámka: Treba ho porovnať s konštantami stavu – toto nie je
	// „správny“ spôsob implementácie stavu, mali by sme použiť
	// „enumeráciu“ – vymenovací údajový typ, čo je v Jave špeciálny
	// druh triedy. Doriešime to v ďalšej verzii.)
	public int stav()
	{
		return stav;
	}

	// Metóda na zmenu stavu osoby.
	public void stav(int nový)
	{
		if (ZOMREL == stav) return;

		if (CHORÝ == nový) infekcia *= 2;
		if (KARANTÉNA == nový) odolnosť *= 2;

		// (Poznámka: Tu by sme mali kontrolovať, či nastavujeme
		// stav v povolenom rozsahu, ale to sa automaticky vyrieši
		// po použití „enumerácie.“)
		stav = nový;

		if (KARANTÉNA == stav || ZOMREL == stav) rýchlosť(0, false);
		else rýchlosť(5, false);
	}

	// Metóda kontrolujúca a simulujúca kontakt dvoch osôb.
	public void kontakt(Osoba o)
	{
		// (Poznámka: Aj keby sme mali, nekontrolujeme, či osoba
		// „o“ náhodou nie je táto osoba, pretože v hlavnej triede
		// je zabezpečené, aby sa to nestalo.)

		if (KARANTÉNA == stav || KARANTÉNA == o.stav) return;

		if (vzdialenosťOd(o) < 10)
		{
			int i1 = 0, i2 = 0;
			if (NAKAZENÝ == o.stav && VYLIEČENÝ > stav) ++i1;
			if (NAKAZENÝ == stav && VYLIEČENÝ > o.stav) ++i2;
			infekcia += i1;
			o.infekcia += i2;
		}
	}

	@SuppressWarnings("fallthrough")
	@Override public void aktivita()
	{
		switch (stav)
		{
		case ČISTÝ:
			if (infekcia > imunita) stav(NAKAZENÝ);
			break;

		case NAKAZENÝ:
			if (--inkubácia <= 0) stav(CHORÝ);
			break;

		case CHORÝ:
			if (--odchyt <= 0) stav(KARANTÉNA);
			// nobreak

		case KARANTÉNA:
			if (--infekcia <= 0) stav(VYLIEČENÝ);
			if (--odolnosť <= 0) stav(ZOMREL);
			break;

		// case VYLIEČENÝ: break;
		// case ZOMREL: break;
		}
	}

	@Override public void mimoHraníc()
	{
		náhodnýSmer();
	}

	@Override public void kresliTvar()
	{
		switch (stav)
		{
		case ČISTÝ: farba(svetlomodrá); break;
		case NAKAZENÝ: farba(tmavožltá); break;
		case CHORÝ: farba(svetločervená); break;
		case KARANTÉNA: farba(svetlopurpurová); break;
		case VYLIEČENÝ: farba(tmavotyrkysová); break;
		case ZOMREL: farba(tmavošedá); break;
		}
		krúžok();
		if (KARANTÉNA == stav)
			kružnica(2 * veľkosť());
	}

	public static void main(String[] args) { Hlavná.main(args); }
}

Trieda Hlavná.java

~

import knižnica.*;

public class Hlavná extends GRobot
{
	// Farba „hmla“ je čiastočne priehľadná biela farba, ktorou prekryjeme
	// graf, aby bolo lepšie vidieť prvky simulácie (inštancie osôb).
	public final static Farba hmla = new Farba(255, 255, 255, 128);
	public final static int ŠÍRKA = 500;
	public static int počet = 150;
	public static double mierka = (double)((int)(ŠÍRKA * 0.8)) / (double)počet;

	private final Zoznam<Osoba> osoby = new Zoznam<>();
	private final int[][] štatistika = new int[ŠÍRKA][Osoba.MAX_STAV];
	private int prekreslenie = 0;
	private int čas = 0;

	private Hlavná()
	{
		super(ŠÍRKA, (int)(ŠÍRKA * 0.8));
		Svet.nekresli();
		Svet.zbaľ();
		Svet.vystreď();
		skry();

		for (int i = 0; i < počet; ++i)
			osoby.pridaj(new Osoba());
		for (int i = 0; i < 25; ++i)
			osoby.daj(i).infikuj();
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;

		// Simuláciu sa pokúsime trochu urýchliť:
		Svet.spustiČasovač(0.010);
		Svet.prekresli();
	}


	private void štatistika()
	{
		for (int i = 0; i < štatistika.length; ++i)
		{
			skočNa(Svet.najmenšieX() + i, Svet.najmenšieY());
			for (int j = 0; j < štatistika[i].length; ++j)
			{
				switch (j)
				{
				case Osoba.ČISTÝ: farba(svetlomodrá); break;
				case Osoba.NAKAZENÝ: farba(tmavožltá); break;
				case Osoba.CHORÝ: farba(svetločervená); break;
				case Osoba.KARANTÉNA: farba(svetlopurpurová); break;
				case Osoba.VYLIEČENÝ: farba(tmavotyrkysová); break;
				case Osoba.ZOMREL: farba(tmavošedá); break;
				}
				vpred(štatistika[i][j] * mierka);
			}
		}
		skočNa(stred);
		farba(hmla);
		vyplňObdĺžnik(Svet.najväčšieX(), Svet.najväčšieY());
	}

	private void vzorka()
	{
		if (čas >= ŠÍRKA)
		{
			čas = ŠÍRKA - 1;

			for (int i = 1; i < štatistika.length; ++i)
				for (int j = 0; j < štatistika[i].length; ++j)
					štatistika[i - 1][j] = štatistika[i][j];

			for (int j = 0; j < štatistika[čas].length; ++j)
				štatistika[čas][j] = 0;
		}

		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			++štatistika[čas][o1.stav()];
		}

		++čas;
	}


	public void reštart()
	{
		Svet.zastavČasovač();
		čas = 0;
		for (int i = 0; i < počet; ++i)
		{
			osoby.daj(i).reštart();
			if (i < 25) osoby.daj(i).infikuj();
		}
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;
		Svet.spustiČasovač();
		prekreslenie = 0;
	}

	@Override public void tik()
	{
		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			for (int j = i - 1; j >= 0; --j)
			{
				Osoba o2 = osoby.daj(j);
				o1.kontakt(o2);
			}
		}
		vzorka();

		if (++prekreslenie % 4 == 0)
			if (Svet.neboloPrekreslené())
			{
				podlaha.vymažGrafiku();
				štatistika();
				Svet.prekresli();
			}
	}

	@Override public void klik()
	{
		reštart();
	}

	public static void main(String[] args)
	{
		new Hlavná();
	}
}

Výsledok

obrázok obrázok
Ukážka priebehu simulácie po spustení…

V tejto (tretej) verzii pridáme jednu dôležitú vlastnosť – umožníme osobe zostať doma, čiže obmedzíme pohyblivosť osôb (a tým aj šírenie nákazy). Táto verzia tiež zahŕňa transformáciou atribútu stavu z celého čísla na vymenovací údajový typ („enumeráciu,“ ktorú sme spomínali v komentároch v minulej verzii).

Všimnite si, že čas pobytu doma je v ponúknutom zdrojovom kóde nulový. Ak ponecháme hodnoty doma1doma2 nulové, tak budú výsledky simulácie úplne rovnaké ako pri predchádzajúcej verzii.

ikonavirus­‑03.7z 7,35 kB (7,18 KiB), 5. 4. 2020

 

Trieda Osoba.java

~

import knižnica.*;

public class Osoba extends GRobot
{
	// Rozsahy pre atribúty simulácie (nižšie):
	public static int imunita1 = 10;
	public static int imunita2 = 20;
	public static int odolnosť1 = 150;
	public static int odolnosť2 = 300;
	public static int inkubácia1 = 300;
	public static int inkubácia2 = 600;
	public static int odchyt1 = 100;
	public static int odchyt2 = 200;
	public static int doma1 = 0;
	public static int doma2 = 0;
	public static int vonku1 = 100;
	public static int vonku2 = 100;

	// Vymenovací typ (enumeračná trieda) stavov.
	private enum Stav
	{
		ČISTÝ, NAKAZENÝ, CHORÝ, KARANTÉNA, VYLIEČENÝ, ZOMREL,
		POČET_STAVOV
	}

	// Hranica, po ktorej prekročení bude osoba považovaná
	// za nakazenú:
	private int imunita;

	// Energia, po ktorej vyčerpaní počas procesu liečenia
	// osoba zomiera:
	private int odolnosť;

	// Časový limit, po ktorého uplynutí sa stáva nakazená
	// osoba chorou:
	private int inkubácia;

	// Reakčný čas potrebný na to, aby chorú osobu odchytili
	// do karantény:
	private int odchyt;
	private int doma;
	private int vonku;

	// Aktuálna úroveň nainfikovanosti osoby (jej hodnota
	// musí najprv prekročiť hodnotu imunity, aby osoba ochorela
	// potom hodnota klesá a po dosiahnutí nuly sa osoba stáva
	// vyliečenou = imúnnou):
	private int infekcia;

	// Aktuálny stav osoby:
	private Stav stav;

	// Príznak slúžiaci na rozlíšenie toho, či je osoba fyzicky
	// doma. Je to doplnok k stavu.
	private boolean jeDoma;


	// Konštruktor.
	public Osoba()
	{
		ohranič(PLOT);
		vypĺňajTvary();
		veľkosť(5);
		zdvihniPero();
		aktivuj(false);
		reštart();
	}


	// Metóda vracajúca farbu podľa zadanej číselnej hodnoty stavu.
	// (Užitočné pri zafarbovaní grafu.)
	public static Farba farbaStavu(int stav)
	{
		if (Stav.ČISTÝ.ordinal() == stav) return svetlomodrá;
		if (Stav.NAKAZENÝ.ordinal() == stav) return tmavožltá;
		if (Stav.CHORÝ.ordinal() == stav) return svetločervená;
		if (Stav.KARANTÉNA.ordinal() == stav) return svetlopurpurová;
		if (Stav.VYLIEČENÝ.ordinal() == stav) return tmavotyrkysová;
		if (Stav.ZOMREL.ordinal() == stav) return tmavošedá;
		return žiadna;
	}

	// Metóda vracajúca farbu podľa zadaného stavu.
	// (Užitočné pri zafarbovaní osôb.)
	public static Farba farbaStavu(Stav stav)
	{
		switch (stav)
		{
		case ČISTÝ: return svetlomodrá;
		case NAKAZENÝ: return tmavožltá;
		case CHORÝ: return svetločervená;
		case KARANTÉNA: return svetlopurpurová;
		case VYLIEČENÝ: return tmavotyrkysová;
		case ZOMREL: return tmavošedá;
		}
		return žiadna;
	}


	// Metóta slúžiaca na reštartovanie osoby (pri spúšťaní novej simulácie).
	public void reštart()
	{
		imunita = (int)Svet.náhodnéCeléČíslo(imunita1, imunita2);
		odolnosť = (int)Svet.náhodnéCeléČíslo(odolnosť1, odolnosť2);
		inkubácia = (int)Svet.náhodnéCeléČíslo(inkubácia1, inkubácia2);
		odchyt = (int)Svet.náhodnéCeléČíslo(odchyt1, odchyt2);
		doma = (int)Svet.náhodnéCeléČíslo(doma1, doma2);
		vonku = (int)Svet.náhodnéCeléČíslo(vonku1, vonku2);
		infekcia = 0;
		stav = Stav.ČISTÝ;
		jeDoma = doma > 0;

		náhodnáPoloha();
		náhodnýSmer();
		rýchlosť(jeDoma ? 0 : 5, false);
	}

	// Týmto sa nastaví osobe taká uroveň infekcie, aby bola pri najbližšom
	// vykonaní reakcie „aktivita“ označená za infikovanú.
	public void infikuj()
	{
		infekcia = imunita + 1;
	}

	// Metóda na prečítanie číselnej hodnoty aktuálneho stavu.
	// (Uľahčuje tvorbu štatistík.)
	public int stav()
	{
		return stav.ordinal();
	}

	// Metóda vracajúca číselnú hodnotu maximálneho počtu stavov.
	// (Potrebné pri inicializácii poľa štatistík.)
	public static int početStavov()
	{
		return Stav.POČET_STAVOV.ordinal();
	}

	// Metóda na zmenu stavu osoby.
	public void stav(Stav nový)
	{
		if (Stav.ZOMREL == stav) return;

		if (Stav.CHORÝ == nový) infekcia *= 2;
		if (Stav.KARANTÉNA == nový) odolnosť *= 2;

		stav = nový;

		if (Stav.KARANTÉNA == stav || Stav.ZOMREL == stav) rýchlosť(0, false);
		else rýchlosť(jeDoma ? 0 : 5, false);
	}

	// Metóda na porovnanie aktuálneho stavu tejto osoby so zadaným stavom.
	// Ak je zadaný stav „väčší“ oproti aktuálnemu, tak je výsledkom záporné
	// číslo, ak sú rovnaké, tak nula, inak kladné číslo.
	// (Užitočné pri hromadnom porovnaní stavov.)
	public int porovnajStav(Stav stav)
	{
		return this.stav.ordinal() - stav.ordinal();
	}

	public void choďDomov()
	{
		if (Stav.ZOMREL == stav) return;

		if (!jeDoma)
		{
			doma = (int)Svet.náhodnéCeléČíslo(doma1, doma2);
			vonku = (int)Svet.náhodnéCeléČíslo(vonku1, vonku2);
			jeDoma = doma > 0;
			rýchlosť(Stav.KARANTÉNA == stav || jeDoma ? 0 : 5, false);
		}
	}

	public void choďVon()
	{
		if (Stav.ZOMREL == stav) return;

		if (jeDoma && vonku > 0)
		{
			jeDoma = false;
			rýchlosť(Stav.KARANTÉNA == stav ? 0 : 5, false);
		}
	}

	// Metóda kontrolujúca a simulujúca kontakt dvoch osôb.
	public void kontakt(Osoba o)
	{
		// (Poznámka: Aj keby sme mali, nekontrolujeme, či osoba
		// „o“ náhodou nie je táto osoba, pretože v hlavnej triede
		// je zabezpečené, aby sa to nestalo.)

		if (Stav.KARANTÉNA == stav || Stav.KARANTÉNA == o.stav ||
			jeDoma || o.jeDoma) return;

		if (vzdialenosťOd(o) < 10)
		{
			int i1 = 0, i2 = 0;
			if (Stav.NAKAZENÝ == o.stav &&
				porovnajStav(Stav.VYLIEČENÝ) < 0) ++i1;
			if (Stav.NAKAZENÝ == stav &&
				o.porovnajStav(Stav.VYLIEČENÝ) < 0) ++i2;
			infekcia += i1;
			o.infekcia += i2;
		}
	}

	@SuppressWarnings("fallthrough")
	@Override public void aktivita()
	{
		if (jeDoma)
		{
			if (doma > 0) --doma;
			else choďVon();
		}
		else
		{
			if (vonku > 0) --vonku;
			else choďDomov();
		}

		switch (stav)
		{
		case ČISTÝ:
			if (infekcia > imunita) stav(Stav.NAKAZENÝ);
			break;

		case NAKAZENÝ:
			if (--inkubácia <= 0) stav(Stav.CHORÝ);
			break;

		case CHORÝ:
			if (--odchyt <= 0) stav(Stav.KARANTÉNA);
			// nobreak

		case KARANTÉNA:
			if (--infekcia <= 0) stav(Stav.VYLIEČENÝ);
			if (--odolnosť <= 0) stav(Stav.ZOMREL);
			break;

		// case VYLIEČENÝ: break;
		// case ZOMREL: break;
		}
	}

	@Override public void mimoHraníc()
	{
		náhodnýSmer();
	}

	@Override public void kresliTvar()
	{
		farba(farbaStavu(stav));
		krúžok();
		if (Stav.KARANTÉNA == stav || jeDoma)
			kružnica(2 * veľkosť());
	}

	public static void main(String[] args) { Hlavná.main(args); }
}

Trieda Hlavná.java

~

import knižnica.*;

public class Hlavná extends GRobot
{
	// Farba „hmla“ je čiastočne priehľadná biela farba, ktorou prekryjeme
	// graf, aby bolo lepšie vidieť prvky simulácie (inštancie osôb).
	public final static Farba hmla = new Farba(255, 255, 255, 128);
	public final static int ŠÍRKA = 500;
	public static int počet = 150;
	public static double mierka = (double)((int)(ŠÍRKA * 0.8)) / (double)počet;

	private final Zoznam<Osoba> osoby = new Zoznam<>();
	private final int[][] štatistika = new int[ŠÍRKA][Osoba.početStavov()];
	private int prekreslenie = 0;
	private int čas = 0;

	private Hlavná()
	{
		super(ŠÍRKA, (int)(ŠÍRKA * 0.8));
		Svet.nekresli();
		Svet.zbaľ();
		Svet.vystreď();
		skry();

		for (int i = 0; i < počet; ++i)
			osoby.pridaj(new Osoba());
		for (int i = 0; i < 25; ++i)
			osoby.daj(i).infikuj();
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;

		// Simuláciu sa pokúsime trochu urýchliť:
		Svet.spustiČasovač(0.010);
		Svet.prekresli();
	}


	private void štatistika()
	{
		for (int i = 0; i < štatistika.length; ++i)
		{
			skočNa(Svet.najmenšieX() + i, Svet.najmenšieY());
			for (int j = 0; j < štatistika[i].length; ++j)
			{
				farba(Osoba.farbaStavu(j));
				vpred(štatistika[i][j] * mierka);
			}
		}
		skočNa(stred);
		farba(hmla);
		vyplňObdĺžnik(Svet.najväčšieX(), Svet.najväčšieY());
	}

	private void vzorka()
	{
		if (čas >= ŠÍRKA)
		{
			čas = ŠÍRKA - 1;

			for (int i = 1; i < štatistika.length; ++i)
				for (int j = 0; j < štatistika[i].length; ++j)
					štatistika[i - 1][j] = štatistika[i][j];

			for (int j = 0; j < štatistika[čas].length; ++j)
				štatistika[čas][j] = 0;
		}

		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			++štatistika[čas][o1.stav()];
		}

		++čas;
	}


	public void reštart()
	{
		Svet.zastavČasovač();
		čas = 0;
		for (int i = 0; i < počet; ++i)
		{
			osoby.daj(i).reštart();
			if (i < 25) osoby.daj(i).infikuj();
		}
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;
		Svet.spustiČasovač();
		prekreslenie = 0;
	}

	@Override public void tik()
	{
		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			for (int j = i - 1; j >= 0; --j)
			{
				Osoba o2 = osoby.daj(j);
				o1.kontakt(o2);
			}
		}
		vzorka();

		if (++prekreslenie % 4 == 0)
			if (Svet.neboloPrekreslené())
			{
				podlaha.vymažGrafiku();
				štatistika();
				Svet.prekresli();
			}
	}

	@Override public void klik()
	{
		reštart();
	}

	public static void main(String[] args)
	{
		new Hlavná();
	}
}

Výsledok

Na začiatku sme povedali, že čas pobytu doma je v zdrojovom kóde nulový, preto budú výsledky simulácie (po spustení bez úprav) úplne rovnaké ako pri predchádzajúcej verzii.

Nasledujúce ukážky zobrazujú priebeh simulácie, v ktorej bol čas pobytu doma upravený na hodnotu 10:

obrázok obrázok
obrázok obrázok
Ukážka priebehu simulácie po spustení…

Táto hodnota je taká malá, že na výsledkoch simulácie nie je vidieť takmer žiadna zmena (oproti predchádzajúcej verzii).

Po zvýšení na hodnotu 50:

obrázok obrázok
obrázok obrázok
Ukážka priebehu simulácie po spustení…

Pozorujeme výraznú zmenu – obrovské percento populácie sa vôbec nenakazilo a nákaza relatívne rýchlo zmizla. (Treba však podotknúť, že toto by platilo len keby celý svet reagoval rovnako… Tiež sú tu ďalšie faktory, ktoré simulácia nemôže vziať do úvahy.)

V poslednej verzii pridáme jedinú vec – úpravu parametrov simulácie a automatické ukladanie konfigurácie, aby sme neustále nemuseli reštartovať aplikáciu.

Poznámka: Odteraz nemá zmysel upravovať parametre simulácie v zdrojovom kóde (bez vymazania alebo aktualizácie konfiguračného súboru).

ikonavirus­‑04.7z 10,33 kB (10,09 KiB), 6. 4. 2020

 

Trieda Osoba.java

~

import knižnica.*;

public class Osoba extends GRobot
{
	// Rozsahy pre atribúty simulácie (nižšie):
	public static int imunita1 = 10;
	public static int imunita2 = 20;
	public static int odolnosť1 = 150;
	public static int odolnosť2 = 300;
	public static int inkubácia1 = 300;
	public static int inkubácia2 = 600;
	public static int odchyt1 = 100;
	public static int odchyt2 = 200;
	public static int doma1 = 0;
	public static int doma2 = 25;
	public static int vonku1 = 75;
	public static int vonku2 = 100;

	// Vymenovací typ (enumeračná trieda) stavov.
	private enum Stav
	{
		ČISTÝ, NAKAZENÝ, CHORÝ, KARANTÉNA, VYLIEČENÝ, ZOMREL,
		POČET_STAVOV
	}

	// Hranica, po ktorej prekročení bude osoba považovaná
	// za nakazenú:
	private int imunita;

	// Energia, po ktorej vyčerpaní počas procesu liečenia
	// osoba zomiera:
	private int odolnosť;

	// Časový limit, po ktorého uplynutí sa stáva nakazená
	// osoba chorou:
	private int inkubácia;

	// Reakčný čas potrebný na to, aby chorú osobu odchytili
	// do karantény:
	private int odchyt;
	private int doma;
	private int vonku;

	// Aktuálna úroveň nainfikovanosti osoby (jej hodnota
	// musí najprv prekročiť hodnotu imunity, aby osoba ochorela
	// potom hodnota klesá a po dosiahnutí nuly sa osoba stáva
	// vyliečenou = imúnnou):
	private int infekcia;

	// Aktuálny stav osoby:
	private Stav stav;

	// Príznak slúžiaci na rozlíšenie toho, či je osoba fyzicky
	// doma. Je to doplnok k stavu.
	private boolean jeDoma;


	// Konštruktor.
	public Osoba()
	{
		ohranič(PLOT);
		vypĺňajTvary();
		veľkosť(5);
		zdvihniPero();
		aktivuj(false);
		reštart();
	}


	// Metóda vracajúca farbu podľa zadanej číselnej hodnoty stavu.
	// (Užitočné pri zafarbovaní grafu.)
	public static Farba farbaStavu(int stav)
	{
		if (Stav.ČISTÝ.ordinal() == stav) return svetlomodrá;
		if (Stav.NAKAZENÝ.ordinal() == stav) return tmavožltá;
		if (Stav.CHORÝ.ordinal() == stav) return svetločervená;
		if (Stav.KARANTÉNA.ordinal() == stav) return svetlopurpurová;
		if (Stav.VYLIEČENÝ.ordinal() == stav) return tmavotyrkysová;
		if (Stav.ZOMREL.ordinal() == stav) return tmavošedá;
		return žiadna;
	}

	// Metóda vracajúca farbu podľa zadaného stavu.
	// (Užitočné pri zafarbovaní osôb.)
	public static Farba farbaStavu(Stav stav)
	{
		switch (stav)
		{
		case ČISTÝ: return svetlomodrá;
		case NAKAZENÝ: return tmavožltá;
		case CHORÝ: return svetločervená;
		case KARANTÉNA: return svetlopurpurová;
		case VYLIEČENÝ: return tmavotyrkysová;
		case ZOMREL: return tmavošedá;
		}
		return žiadna;
	}


	// Metóta slúžiaca na reštartovanie osoby (pri spúšťaní novej simulácie).
	public void reštart()
	{
		imunita = (int)Svet.náhodnéCeléČíslo(imunita1, imunita2);
		odolnosť = (int)Svet.náhodnéCeléČíslo(odolnosť1, odolnosť2);
		inkubácia = (int)Svet.náhodnéCeléČíslo(inkubácia1, inkubácia2);
		odchyt = (int)Svet.náhodnéCeléČíslo(odchyt1, odchyt2);
		doma = (int)Svet.náhodnéCeléČíslo(doma1, doma2);
		vonku = (int)Svet.náhodnéCeléČíslo(vonku1, vonku2);
		infekcia = 0;
		stav = Stav.ČISTÝ;
		jeDoma = doma > 0;

		náhodnáPoloha();
		náhodnýSmer();
		rýchlosť(jeDoma ? 0 : 5, false);
	}

	// Týmto sa nastaví osobe taká uroveň infekcie, aby bola pri najbližšom
	// vykonaní reakcie „aktivita“ označená za infikovanú.
	public void infikuj()
	{
		infekcia = imunita + 1;
	}

	// Metóda na prečítanie číselnej hodnoty aktuálneho stavu.
	// (Uľahčuje tvorbu štatistík.)
	public int stav()
	{
		return stav.ordinal();
	}

	// Metóda vracajúca číselnú hodnotu maximálneho počtu stavov.
	// (Potrebné pri inicializácii poľa štatistík.)
	public static int početStavov()
	{
		return Stav.POČET_STAVOV.ordinal();
	}

	// Metóda na zmenu stavu osoby.
	public void stav(Stav nový)
	{
		if (Stav.ZOMREL == stav) return;

		if (Stav.CHORÝ == nový) infekcia *= 2;
		if (Stav.KARANTÉNA == nový) odolnosť *= 2;

		stav = nový;

		if (Stav.KARANTÉNA == stav || Stav.ZOMREL == stav) rýchlosť(0, false);
		else rýchlosť(jeDoma ? 0 : 5, false);
	}

	// Metóda na porovnanie aktuálneho stavu tejto osoby so zadaným stavom.
	// Ak je zadaný stav „väčší“ oproti aktuálnemu, tak je výsledkom záporné
	// číslo, ak sú rovnaké, tak nula, inak kladné číslo.
	// (Užitočné pri hromadnom porovnaní stavov.)
	public int porovnajStav(Stav stav)
	{
		return this.stav.ordinal() - stav.ordinal();
	}

	public void choďDomov()
	{
		if (Stav.ZOMREL == stav) return;

		if (!jeDoma)
		{
			doma = (int)Svet.náhodnéCeléČíslo(doma1, doma2);
			vonku = (int)Svet.náhodnéCeléČíslo(vonku1, vonku2);
			jeDoma = doma > 0;
			rýchlosť(Stav.KARANTÉNA == stav || jeDoma ? 0 : 5, false);
		}
	}

	public void choďVon()
	{
		if (Stav.ZOMREL == stav) return;

		if (jeDoma && vonku > 0)
		{
			jeDoma = false;
			rýchlosť(Stav.KARANTÉNA == stav ? 0 : 5, false);
		}
	}

	// Metóda kontrolujúca a simulujúca kontakt dvoch osôb.
	public void kontakt(Osoba o)
	{
		// (Poznámka: Aj keby sme mali, nekontrolujeme, či osoba
		// „o“ náhodou nie je táto osoba, pretože v hlavnej triede
		// je zabezpečené, aby sa to nestalo.)

		if (Stav.KARANTÉNA == stav || Stav.KARANTÉNA == o.stav ||
			jeDoma || o.jeDoma) return;

		if (vzdialenosťOd(o) < 10)
		{
			int i1 = 0, i2 = 0;
			if (Stav.NAKAZENÝ == o.stav &&
				porovnajStav(Stav.VYLIEČENÝ) < 0) ++i1;
			if (Stav.NAKAZENÝ == stav &&
				o.porovnajStav(Stav.VYLIEČENÝ) < 0) ++i2;
			infekcia += i1;
			o.infekcia += i2;
		}
	}

	@SuppressWarnings("fallthrough")
	@Override public void aktivita()
	{
		if (jeDoma)
		{
			if (doma > 0) --doma;
			else choďVon();
		}
		else
		{
			if (vonku > 0) --vonku;
			else choďDomov();
		}

		switch (stav)
		{
		case ČISTÝ:
			if (infekcia > imunita) stav(Stav.NAKAZENÝ);
			break;

		case NAKAZENÝ:
			if (--inkubácia <= 0) stav(Stav.CHORÝ);
			break;

		case CHORÝ:
			if (--odchyt <= 0) stav(Stav.KARANTÉNA);
			// nobreak

		case KARANTÉNA:
			if (--infekcia <= 0) stav(Stav.VYLIEČENÝ);
			if (--odolnosť <= 0) stav(Stav.ZOMREL);
			break;

		// case VYLIEČENÝ: break;
		// case ZOMREL: break;
		}
	}

	@Override public void mimoHraníc()
	{
		náhodnýSmer();
	}

	@Override public void kresliTvar()
	{
		farba(farbaStavu(stav));
		krúžok();
		if (Stav.KARANTÉNA == stav || jeDoma)
			kružnica(2 * veľkosť());
	}

	public static void main(String[] args) { Hlavná.main(args); }
}

Trieda Hlavná.java

~

import knižnica.*;

public class Hlavná extends GRobot
{
	// Farba „hmla“ je čiastočne priehľadná biela farba, ktorou prekryjeme
	// graf, aby bolo lepšie vidieť prvky simulácie (inštancie osôb).
	public final static Farba hmla = new Farba(255, 255, 255, 128);
	public final static int ŠÍRKA = 500;
	public static int počet = 150;
	public static double mierka = (double)((int)(ŠÍRKA * 0.8)) / (double)počet;

	private final Zoznam<Osoba> osoby = new Zoznam<>();
	private final int[][] štatistika = new int[ŠÍRKA][Osoba.početStavov()];
	private int prekreslenie = 0;
	private int čas = 0;

	// Príznak, ktorý bude použitý na signalizáciu potreby uloženia
	// upravenej konfigurácie do konfiguračného súboru.
	private boolean zmenaKonfigurácie = false;

	private Hlavná()
	{
		super(ŠÍRKA, (int)(ŠÍRKA * 0.8));
		Svet.nekresli();
		Svet.zbaľ();
		// Zmena: Okno sa vystredí len pri prvom spustení.
		if (Svet.prvéSpustenie()) Svet.vystreď();
		skry();

		// Obsluha udalostí obsaujúca jedinú oblasť – čítanie a ukladanie
		// konfigurácie.
		new ObsluhaUdalostí()
		{
			@Override public boolean konfiguráciaZmenená()
			{ return zmenaKonfigurácie; }

			@Override public void zapíšKonfiguráciu(Súbor súbor)
				throws java.io.IOException
			{
				súbor.zapíšVlastnosť("imunita1", Osoba.imunita1);
				súbor.zapíšVlastnosť("imunita2", Osoba.imunita2);

				súbor.zapíšVlastnosť("odolnosť1", Osoba.odolnosť1);
				súbor.zapíšVlastnosť("odolnosť2", Osoba.odolnosť2);

				súbor.zapíšVlastnosť("inkubácia1", Osoba.inkubácia1);
				súbor.zapíšVlastnosť("inkubácia2", Osoba.inkubácia2);

				súbor.zapíšVlastnosť("odchyt1", Osoba.odchyt1);
				súbor.zapíšVlastnosť("odchyt2", Osoba.odchyt2);

				súbor.zapíšVlastnosť("doma1", Osoba.doma1);
				súbor.zapíšVlastnosť("doma2", Osoba.doma2);

				súbor.zapíšVlastnosť("vonku1", Osoba.vonku1);
				súbor.zapíšVlastnosť("vonku2", Osoba.vonku2);
			}

			@Override public void čítajKonfiguráciu(Súbor súbor)
				throws java.io.IOException
			{
				Osoba.imunita1 = súbor.čítajVlastnosť(
					"imunita1", Osoba.imunita1);
				Osoba.imunita2 = súbor.čítajVlastnosť(
					"imunita2", Osoba.imunita2);

				Osoba.odolnosť1 = súbor.čítajVlastnosť(
					"odolnosť1", Osoba.odolnosť1);
				Osoba.odolnosť2 = súbor.čítajVlastnosť(
					"odolnosť2", Osoba.odolnosť2);

				Osoba.inkubácia1 = súbor.čítajVlastnosť(
					"inkubácia1", Osoba.inkubácia1);
				Osoba.inkubácia2 = súbor.čítajVlastnosť(
					"inkubácia2", Osoba.inkubácia2);

				Osoba.odchyt1 = súbor.čítajVlastnosť(
					"odchyt1", Osoba.odchyt1);
				Osoba.odchyt2 = súbor.čítajVlastnosť(
					"odchyt2", Osoba.odchyt2);

				Osoba.doma1 = súbor.čítajVlastnosť(
					"doma1", Osoba.doma1);
				Osoba.doma2 = súbor.čítajVlastnosť(
					"doma2", Osoba.doma2);

				Osoba.vonku1 = súbor.čítajVlastnosť(
					"vonku1", Osoba.vonku1);
				Osoba.vonku2 = súbor.čítajVlastnosť(
					"vonku2", Osoba.vonku2);
			}
		};

		for (int i = 0; i < počet; ++i)
			osoby.pridaj(new Osoba());
		for (int i = 0; i < 25; ++i)
			osoby.daj(i).infikuj();
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;

		// Simuláciu sa pokúsime trochu urýchliť:
		Svet.spustiČasovač(0.010);
		Svet.prekresli();
	}


	private void štatistika()
	{
		for (int i = 0; i < štatistika.length; ++i)
		{
			skočNa(Svet.najmenšieX() + i, Svet.najmenšieY());
			for (int j = 0; j < štatistika[i].length; ++j)
			{
				farba(Osoba.farbaStavu(j));
				vpred(štatistika[i][j] * mierka);
			}
		}
		skočNa(stred);
		farba(hmla);
		vyplňObdĺžnik(Svet.najväčšieX(), Svet.najväčšieY());
	}

	private void vzorka()
	{
		if (čas >= ŠÍRKA)
		{
			čas = ŠÍRKA - 1;

			for (int i = 1; i < štatistika.length; ++i)
				for (int j = 0; j < štatistika[i].length; ++j)
					štatistika[i - 1][j] = štatistika[i][j];

			for (int j = 0; j < štatistika[čas].length; ++j)
				štatistika[čas][j] = 0;
		}

		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			++štatistika[čas][o1.stav()];
		}

		++čas;
	}


	public void reštart()
	{
		Svet.zastavČasovač();
		čas = 0;
		for (int i = 0; i < počet; ++i)
		{
			osoby.daj(i).reštart();
			if (i < 25) osoby.daj(i).infikuj();
		}
		for (int i = 0; i < štatistika.length; ++i)
			for (int j = 0; j < štatistika[i].length; ++j)
				štatistika[i][j] = 0;
		Svet.spustiČasovač();
		prekreslenie = 0;
	}

	@Override public void tik()
	{
		for (int i = osoby.počet() - 1; i >= 0; --i)
		{
			Osoba o1 = osoby.daj(i);
			for (int j = i - 1; j >= 0; --j)
			{
				Osoba o2 = osoby.daj(j);
				o1.kontakt(o2);
			}
		}
		vzorka();

		if (++prekreslenie % 4 == 0)
			if (Svet.neboloPrekreslené())
			{
				podlaha.vymažGrafiku();
				štatistika();
				Svet.prekresli();
			}
	}

	// Pole popisov parametrov konfiguračného dialógu:
	private final static String[] popisyParametrov = new String[]
		{"Rozsah imunity:", "–", null, "Rozsah odolnosti:", "–", null,
		"Rozsah inkubácie:", "–", null, "Rozsah odchytu:", "–", null,
		"Rozsah doma:", "–", null, "Rozsah vonku:", "–"};

	// Pole údajov (parametrov) simulácie (na konfiguráciu):
	private final static Object[] parametreSimulácie =
		{0.0, 0.0, '\n', 0.0, 0.0, '\n', 0.0, 0.0, '\n', 0.0, 0.0,
		'\n', 0.0, 0.0, '\n', 0.0, 0.0};

	@Override public void klik()
	{
		// Zmena: Odlišujeme klik pravým a ľavým tlačidlom. Ľavé si
		// ponechalo predchádzajúcu funkciu – reštart simulácie.
		if (ÚdajeUdalostí.tlačidloMyši(ĽAVÉ))
			reštart();
		else if (ÚdajeUdalostí.tlačidloMyši(PRAVÉ))
		{
			// Pravé tlačidlo má za úlohu otvoriť konfiguračný dialóg
			// a spracovať prípadné vykonané úpravy parametrov (po jeho
			// zavretí/potvrdení)…

			Svet.zastavČasovač();
			parametreSimulácie[0] = new Double(Osoba.imunita1);
			parametreSimulácie[1] = new Double(Osoba.imunita2);

			parametreSimulácie[3] = new Double(Osoba.odolnosť1);
			parametreSimulácie[4] = new Double(Osoba.odolnosť2);

			parametreSimulácie[6] = new Double(Osoba.inkubácia1);
			parametreSimulácie[7] = new Double(Osoba.inkubácia2);

			parametreSimulácie[9] = new Double(Osoba.odchyt1);
			parametreSimulácie[10] = new Double(Osoba.odchyt2);

			parametreSimulácie[12] = new Double(Osoba.doma1);
			parametreSimulácie[13] = new Double(Osoba.doma2);

			parametreSimulácie[15] = new Double(Osoba.vonku1);
			parametreSimulácie[16] = new Double(Osoba.vonku2);

			if (Svet.dialóg(popisyParametrov, parametreSimulácie,
				"Úprava parametrov simulácie"))
			{
				int i = 0;
				for (Object údaj : parametreSimulácie)
				{
					if (parametreSimulácie[i] instanceof Double &&
						!((Double)parametreSimulácie[i]).isNaN())
					{
						boolean zmena = false;

						int j = ((Double)parametreSimulácie[i]).intValue();
						switch (i)
						{
						case 0: if (Osoba.imunita1 != j)
							{
								Osoba.imunita1 = j;
								zmena = true;
							}
							break;
						case 1: if (Osoba.imunita2 != j)
							{
								Osoba.imunita2 = j;
								zmena = true;
							}
							break;

						case 3: if (Osoba.odolnosť1 != j)
							{
								Osoba.odolnosť1 = j;
								zmena = true;
							}
							break;
						case 4: if (Osoba.odolnosť2 != j)
							{
								Osoba.odolnosť2 = j;
								zmena = true;
							}
							break;

						case 6: if (Osoba.inkubácia1 != j)
							{
								Osoba.inkubácia1 = j;
								zmena = true;
							}
							break;
						case 7: if (Osoba.inkubácia2 != j)
							{
								Osoba.inkubácia2 = j;
								zmena = true;
							}
							break;

						case 9: if (Osoba.odchyt1 != j)
							{
								Osoba.odchyt1 = j;
								zmena = true;
							}
							break;
						case 10: if (Osoba.odchyt2 != j)
							{
								Osoba.odchyt2 = j;
								zmena = true;
							}
							break;

						case 12: if (Osoba.doma1 != j)
							{
								Osoba.doma1 = j;
								zmena = true;
							}
							break;
						case 13: if (Osoba.doma2 != j)
							{
								Osoba.doma2 = j;
								zmena = true;
							}
							break;

						case 15: if (Osoba.vonku1 != j)
							{
								Osoba.vonku1 = j;
								zmena = true;
							}
							break;
						case 16: if (Osoba.vonku2 != j)
							{
								Osoba.vonku2 = j;
								zmena = true;
							}
							break;
						}

						if (zmena) zmenaKonfigurácie = true;
					}
					++i;
				}
			}
			Svet.spustiČasovač();
		}
	}

	public static void main(String[] args)
	{
		// Spustenie automatickej konfigurácie:
		Svet.použiKonfiguráciu();
		new Hlavná();
	}
}

 

Zhodnotenie

Nižšie ponúkame grafy, ktoré táto naša jednoduchá, ale názorná simulácia ukázala. Na nich môžeme porovnať situácie, kedy ľudia nemajú žiadne obmedzenie pohybu so situáciami, kedy sú tieto opatrenia postupne zavádzané.

obrázok

obrázok obrázok
Obrázok 1: Ukážka priebehu simulácie pri žiadnom obmedzení pohybu. 
 
obrázok obrázok
Obrázok 2: Ukážka priebehu simulácie pri veľmi miernom obmedzení pohybu. 
 
obrázok obrázok
Obrázok 3: Ukážka priebehu simulácie pri výraznom obmedzení pohybu. 
 

Keď sa pozrieme na graf znázorňujúci vysoké obmedzenie pohybu (obrázok 3), nenájdeme tam šedé pásmo (pásmo zosnulých). To je dosť prekvapujúci výsledok vzhľadom na to, že nič čo by tomu malo výrazne pomôcť nebolo do simulácie explicitne zapracované. Čiže žiadne umelé zvyšovanie odolnosti občanov na základe pobytu doma, ani nič podobné. Zo simulácie (aj keď veľmi jednoduchej) to vyplynulo úplne prirodzene. (Čakal som nejaké malé číslo, ale nie nulu…)

(Samozrejme, že parametre simulácie nie sú dokonale vyladené na Covid­‑19, ale pekne to ukazuje účinnosť tohto nariadenia (pri jeho dodržiavaní).)

 

Otvorené záležitosti (na samostatné doriešenie):

  • kapacita zdravotníckych zariadení (počet lôžok);
  • konfigurovateľnosť celkového počtu osôb simulácie (aj počtu lôžok);
  • konfigurovateľnosť veľkosti plátna – to jest veľkosť plochy, na ktorej sa budú môcť osoby pohybovať, pretože aj to môže mať vplyv na celkový výsledok;
  • prípadné ďalšie významné aspekty, ako napríklad možnosť rozdelenia osôb do komunít, možnosť definovania centier, kde sa osoby môžu alebo musia stretávať (obchody, zdravotnícke zariadenia…).

 

Odkazy na ďalšie užitočné zdroje

Poznámka: Keď traja robia to isté, nie je to celkom to isté 😊. (Aj keď výsledky môžu byť veľmi podobné.)

Prosím, zvoľte verziu.