2. Domácí úkol: Conwayova hra života
Úvod
Cílem prvního úkolu je procvičit si práci s vlastními objekty (struct) a manipulacemi s dvourozměrnými poli. K tomuto účelu použijeme klasický příklad celulárního automatu známého pod jménem Hra života. Jde o dvourozměrný model navržený britským matematikem Johnem Hortonem Conwayem v roce 1970, který ilustruje vznik komplikovaného chování v systému kontrolovaném velmi jednoduchými pravidly.
Teoretický background
Popis modelu je poměrně jednoduchý. Hra se odehrává v nekonečné mřížce tvořené čtvercovými buňkami, které mohou být buď živé, nebo mrtvé. Každá z buněk interaguje se svými sousedy podle následujících pravidel:
- živá buňka umírá, pokud má méně než dva živé sousedy (vymírání),
- živá buňka přežívá, pokud má dva nebo tři sousedy,
- živá buňka umírá, pokud má více než tři živé sousedy (přelidnění),
- mrtvá buňka ožívá, pokud má právě tři živé sousedy (reprodukce).
K popisu takovýchto pravidel slouží zkrácená notace B3/S23
: mrtvá buňka ožívá (Born 3) pokud má právě tři živé sousedy a živá buňka přežívá (Survive 2, 3) pokud má dva nebo tři sousedy, ve všech ostatních situacích umírá.
Přechod z jednoho stavu do druhou probíhá současnou aplikací uvedených pravidel. Tomuto kroku v čase, vývoje od jedné generace do druhé, se také říká tick.
Udáním počátečního stavu (rozložení živých buněk v mřížce) a pravidel se pak systém vyvíjí samostatně a můžeme zkoumat co vše se v takovémto světě může odehrát (viz přiložené testy případně wiki stránka).
Implementační pokyny
V souboru main.jl
naleznete hrubou kostru implementace, kterou je potřeba doplnit.
Ukázky použití lze nalézt v přiložených testech v adresáři test
.
Zde probereme základní části programu.
Conway
Objekty typu Hra je popsána objekty typu Conway
, konstruktor tohoto typu přijímá
- počáteční stav (čtvercová/obdélníková matice s více než jedním řádkem/sloupcem a celočíselnými prvky, nuly odpovídají mrtvým buňkám a jedničky živým buňkám),
- nepovinný argument s klíčem
B
udávající pole počtu sousedů při kterém mrtvá buňka oživne, výchozí hodnota jeB = [2]
(typVector{Int64}
), - nepovinný argument s klíčem
S
udávající pole počtu sousedů při kterém živá buňka přežije, výchozí hodnota jeS = [2, 3]
(typVector{Int64}
).
Například tedy vytvoříme hru rozměru 5 krát 5 takto:
julia> g = Conway(rand([0, 1], 5, 5))
xxxxx
x x x
x x
x x
xxx
Zde vidíme i "pěknou" grafickou reprezentaci, kterou získáme dodefinováním metody Base.show
pro objekty našeho typu, a kde jsou živé buňky označeny křížky.
Způsob tohoto výpisu netestuji a nechávám ho na vás, zkuste být kreativní.
Pouze doporučuji zůstat u maticovém/grafického pohledu, kdy počátek souřadnic je v levém horním rohu obrázku.
Podobně máte volnou ruku ve volbě vnitřní reprezentace stavu "světa" a volbě, zda bude objekt mutable
.
Konstruktor kontroluje rozměry matice, nepovolujeme nudle sestávající z právě jednoho sloupce nebo řádku.
Dále mřížku chápeme s periodickými hraničními podmínkami. Tj. je-li náš svět modelován maticí 5 krát pět, tak buňka na prvním řádku s indexy (1, 3)
má za sousedy i buňky v posledním řádku s indexy (5, 2)
, (5, 3)
a (5, 4)
.
Podobně tomu je pro první a poslední sloupec.
Pomocné funkce
Pro sledování vlastností vývoje dále implementujte dvě pomocné funkce
world(game::Conway)
: vrátí aktuální pohled na svět jakožto matici,population(game::Conway)
: vrátí aktuální počet živých buněk v populaci.
Časový vývoj
Konečně implementujte funkci tick!
, která provede časový vývoj, přesun od jedné generace k druhé, přesněji:
tick!(game::Conway)
provede právě jeden časový krok a vrátí hru (změněný objekt typuConway
),tick!(game::Conway, steps::Int64)
provedesteps
kroků a také vrátí hru, případně vyvolá výjimku při záporném počtu kroků.
Další poznámky
V souboru main.jl
můžete definovat i další vlastní pomocné funkce/metody.
Doporučuji také doplnit docstringy implementovaných metod a objektů.
Jako bonus se můžete pokusit vytvořit i animaci časového vývoje (stačí v terminálu).
Testy
Svou implementaci můžete lokálně snadno otestovat příkazem julia test/runtests.jl
, výstup by měl vypadat přibližně takto:
$ julia test/runtests.jl
┌ Info:
│ Running tests in test_conway.jl...
└ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test Summary: | Pass Total Time
Conway constructor and related functions | 7 7 0.3s
┌ Info:
│ Running tests in test_tick.jl...
└ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test Summary: | Pass Total Time
Time evolution (the tick! method) | 8 8 0.2s
Test Summary: | Pass Total Time
Time evolution: toad | 2 2 0.1s
Test Summary: | Pass Total Time
Time evolution: blinker | 2 2 0.0s
Test Summary: | Pass Total Time
Time evolution: glider | 1 1 0.0s
Test Summary: | Pass Total Time
Time evolution: longer time periods and larger worlds | 1 1 9.1s
Test Summary: | Pass Total Time
Time evolution: custom rules | 8 8 0.0s
Tyto testy se spouští i automaticky na Gitlabu.
Součástí vyhodnocení řešení bude porovnání rychlosti i robustnosti vaší implementace s ostatními studentskými řešeními.
Řešení a odevzdání
Opět vytvořte větev odvozenou z větve assignment/02-conway
a nezvěte ji solution/02-conway
.
Do solution/02-conway
vložte své řešení editací souboru main.jl
případně přidáním testů do složky test
.
Až budete se svým řešením spokojeni, vytvořte MR (to můžete i dříve, aspoň uvidíte výsledek testů, pokud je nespouštíte lokálně) a přiřaďte mě k němu jako assignee
.
Tímto aktem úkol odevzdáte.