Gra w życie -> Maszyna stanów w Groovy’m

Natknęłam się ostatnio w sieci na wpis o implementacji maszyny stanów w Groovy’m i postanowiłam wykorzystać ten pomysł w praktyce. Do implementacji wybrałam Grę w Życie. Algorytm nie jest skomplikowany, każda komórka może przyjąć jeden z dwóch stanów, a i przejść między stanami nie jest za dużo. W skrócie: istnieje dwuwymiarowa plansza z komórkami. Każda komórka może być albo 'żywa’ albo 'martwa’. Stan komórki zależy od jej 'sąsiadów’ – 8 stykających się z nią komórek. W każdym kroku sprawdza się stan komórki i zmienia się go zgodnie z następującymi zasadami:

  • jeśli żywą komórkę otacza mniej niż 2 żywych sąsiadów – komórka umiera z samotności
  • jeśli żywą komórkę otacza więcej niż 3 żywych sąsiadów – komórka umiera z „przeludnienia”
  • jeśli martwą komórkę otacza dokładnie 3 żywych sąsiadów – komórka ożywa
  • w pozostałych przypadkach, komórka nie zmienia stanu

Cały kod programu tu: github

Implementacja takiej maszyny wg wpisu to mapa, gdzie kluczem jest obecny stan komórki, a jako wartość występuje lista możliwych przejść:


static Map<State, List> state_machine_definition = [
(State.ALIVE): [
[event: Event.OVER_POPULATION, to: State.DEAD],
[event: Event.UNDER_POPULATION, to: State.DEAD],
[event: Event.STABLE, to: State.ALIVE, afterAction: 'increment'],
],
(State.DEAD) : [
[event: Event.REPRODUCTION, to: State.ALIVE, afterAction: 'reset'],
[event: Event.STABLE, to: State.DEAD]
]
]

Kod przejścia jest równie nieskomplikowany – wyciągamy z maszyny stanów możliwe przejścia dla obecnego stanu komórki. Sprawdzamy, czy jest możliwe przejście wyzwalane eventem, który został przekazany jako parametr. Jeśli wszystko jest ok, tworzymy nową komórkę z nowym stanem wynikającym z przejścia i ewentualnie wykonujemy dodatkowe akcje związane z przejściem.


static Cell transition(event, Cell cell) {
List states = state_machine_definition[cell.state]
def transition = states.find { it.event == event }
if (!transition) {
throw new Exception("invalid event $event for state ${cell.state}")
}
Cell tempCell = new Cell(transition.to, cell.lifeLong)
if (transition.afterAction) {
tempCell."${transition.afterAction}"()
}
return tempCell
}

Definicja komórki jest dość prosta. Każda komórka ma stan i dodatkowo 'długość życia’, którą wykorzystuję później do kolorowania komórek na ekranie. Używam też klasy Board, w której przechowuję dwuwymiarową tablicę z komórkami najważniejszą metodę step(), która przechodząc komórka po komórce, przy użyciu maszyny stanów ustawia kolejny stan na podstawie tego ilu żywych sąsiadów ją otacza (getEvent()).


void step() {
Board tmpBoard = new Board(this.board)
for (int x = 0; x < this.x; x++) {
for (int y = 0; y < this.y; y++) {
Cell cell = board[x][y]
Event event = getEvent(x, y, cell.state)
tmpBoard.board[x][y] = StateMachineDefinition.transition(event, cell)
}
}
this.board = tmpBoard.board
}

view raw

Board.groovy

hosted with ❤ by GitHub

Wstępnie wyjście wyrzucałam na konsolę w postaci kropek i gwiazdek:

ale zachciało mi się przedstawić efekt działania programu w postaci animacji. Nie chciałam się bawić w Swinga i AWT, JavaFX byłaby do tego za ciężka, więc zadałam pytanie na grupie „Programuj, dziewczyno!” czy nie znają czegoś odpowiedniego. I tak poznałam Processing, który zaskoczył mnie tym jak mało kodu wymaga przedstawienie tablicy jako pikseli i zrobienie z nim animacji: kod tu

One thought on “Gra w życie -> Maszyna stanów w Groovy’m

  1. Good post. I study one thing more difficult on totally different blogs everyday. It will always be stimulating to learn content from other writers and follow a little one thing from their store. I’d favor to use some with the content on my blog whether you don’t mind. Natually I’ll provide you with a hyperlink on your web blog. Thanks for sharing.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

This site uses Akismet to reduce spam. Learn how your comment data is processed.