Conway's game of life

Zadáno v úterý 10. 11.

K odevzdání v pondělí 16. 11.

Získat můžete až 10 bodů

Na hodině jsme rozpracovali implementaci Conwayovy hry života. Za úkol je tuto implementaci dokončit a dostat program do spustitelného (a funkčního!) stavu. Je dovoleno pracovat v týmech o dvou lidech, a já bych to dokonce doporučil; jednotlivci, kteří se rozhodnou v týmu nepracovat, žádné bonusové body nedostanou.

Některý tým (popř. týmy) opět vyberu, aby své řešení odprezentovaly.

Zde je odkaz na naši rozpracovanou a okomentovanou verzi, navíc jsem ji překopíroval níže. Hodně štěstí!

Současný kód

# stejný jako image, ale barvy, fill-mode atp nejsou stringy
include image-typed
import lists as L
import interact from reactors  # nic jiného z reactors nepotřebujeme


## PRAVIDLA

# Pokud je teď ALIVE
# 1. Pokud má 2 nebo 3 ALIVE sousedy -> zůstane ALIVE
# 2. Pokud má <2 nebo >3 -> DEAD
#
# Pokud je teď DEAD
# 1. Pokud má přesně 3 ALIVE sousedy -> ALIVE
# 2. Jinak -> zůstane DEAD


## Základní datové typy

data Cell:
  | alive
  | dead
end

# Grid je pouze alias pro List<List<Cell>>
# Fungují na něj tím pádem všechny seznamové funkce
type Grid = List<List<Cell>>


## Základ: naše hra je reactor (případně viz dokumentace)

game =
  reactor:
    # počáteční stav
    # v našem konkrétním případě je Stav reprezentován jako Grid
    init: GRID,
    # funkce, která bere současný stav a vrátí nový stav
    # neboli on-tick je funkce (Stav -> Stav)
    on-tick: step,
    # funkce, která umí stav nakreslit
    # neboli to-draw je funkce (Stav -> Image)
    to-draw: draw-grid,
    # jak často se volá funkce on-tick
    seconds-per-tick: 0.01
  end

interact(game)  # toto spustí reactor


## Počáteční stav

GRID :: Grid = [list:
  [list: alive, alive, dead],
  [list: alive, dead, dead],
  [list: dead, alive, alive]
]


## Funkce on-tick, tedy (Stav -> Stav)

fun step(current-grid :: Grid) -> Grid:
  fun go(acc :: Grid, x :: Number, y :: Number):
    if out-of-bounds(current-grid, y):
      acc
    else:
      n-count = count-alive-neighbours(current-grid, x, y)

      new-acc =
        if is-alive-at(current-grid, x, y):
          if (n-count == 2) or (n-count == 3):
            acc
          else:
            set-cell(acc, x, y, dead)
          end
        else:
          if (n-count == 3):
            set-cell(acc, x, y, alive)
          else:
            acc
          end
        end

      new-x = num-modulo(x + 1, width(current-grid))
      new-y = ...
      go(new-acc, new-x, new-y)
    end
  end

  go(current-grid, 0, 0)
end

fun is-alive-at(g :: Grid, x :: Number, y :: Number) -> Boolean:
  ...
end


fun count-alive-neighbours(g :: Grid, x :: Number, y :: Number) -> Number:
  ...
end

# ...zde budou další pomocné funkce


## Funkce to-draw, tedy (Stav -> Image)

fun draw-grid(g :: Grid) -> Image:
  L.foldr(
    lam(r, acc): align-above(x-center, acc, r) end,
    empty-image,
    L.map(draw-row, g))
end

# Alternativní implementace, ekvivalentní té s foldrem
# Ta s foldrem je sice správnější, ale ve vašem prvním
# roce programování to je nejspíš jedno :-)

# fun draw-grid(g :: Grid) -> Image:
#   cases (List) g:
#     | empty => empty-image
#     | link(r, rs) =>
#       align-above(x-center, draw-grid(rs), r)
#   end
# end

fun draw-row(r :: List<Cell>) -> Image:
  ...
end

# ...zde budou další pomocné funkce