The Game of Life…

Esporremo brevemente come creare un’applicazione iOS per il Gioco della Vita, un automa cellulare ideato dal matematico britannico John Horton Conway, utilizzando Swift e SwiftUI.

Struttura del codice

Il codice è suddiviso in tre parti principali:

  1. La classe GameOfLife, che gestisce la logica del gioco.
  2. La vista GridView, che visualizza la griglia del gioco.
  3. La vista ContentView, che contiene i controlli del gioco e include GridView.

1. La Classe GameOfLife

La classe GameOfLife si occupa della logica e dello stato del gioco. Utilizza l’annotazione @Published per segnalare a SwiftUI quando determinate proprietà cambiano, provocando il re-rendering dell’interfaccia utente.

  • Inizializzazione: Genera una griglia di dimensioni specificate, con ogni cellula settata in modo casuale a vivente o morta.
  • toggleCell(x: Int, y: Int): Cambia lo stato di una cellula specifica (viva diventa morta, morta diventa viva).
  • startOrStop(): Inizia o ferma l’evoluzione del gioco.
  • reset(): Ferma il gioco e genera una nuova griglia casuale.
  • nextGeneration(): Calcola la prossima generazione della griglia in base alle regole del Gioco della Vita.
  • aliveNeighbours(x: Int, y: Int): Conta i vicini vivi di una determinata cellula.

2. La Vista GridView

Questa vista visualizza la griglia del gioco. Utilizza un VStack di HStacks per creare la griglia, con un rettangolo per ogni cellula. Il colore del rettangolo dipende dallo stato della cellula (nero per vivente, bianco per morto). Un tap su un rettangolo inverte lo stato della cellula corrispondente.

3. La Vista ContentView

ContentView è la vista principale dell’app. Contiene una GridView e due pulsanti per controllare il gioco: uno per avviare/fermare l’evoluzione del gioco e l’altro per resettare il gioco.

La Logica del Gioco della Vita

La logica del Gioco della Vita è implementata nel metodo nextGeneration() della classe GameOfLife. Le regole del gioco sono le seguenti:

  • Una cellula viva con meno di due vicini vivi muore.
  • Una cellula viva con due o tre vicini vivi sopravvive.
  • Una cellula viva con più di tre vicini vivi muore.
  • Una cellula morta con esattamente tre vicini vivi diventa una cellula viva.
func nextGeneration() {
    var newGrid = grid
    for x in 0..<grid.count {
        for y in 0..<grid[x].count {
            let aliveNeighbours = self.aliveNeighbours(x: x, y: y)
            if grid[x][y] {
                if aliveNeighbours < 2 || aliveNeighbours > 3 {
                    newGrid[x][y] = false
                }
            } else if aliveNeighbours == 3 {
                newGrid[x][y] = true
            }
        }
    }
    grid = newGrid
}

Queste regole vengono applicate simultaneamente a tutte le cellule della griglia per calcolare la prossima generazione.

The Game of Life…

The game of life

Il Game of Life, concepito dal matematico britannico John Horton Conway nel 1970, è un automa cellulare a due dimensioni che ha catturato l’immaginazione di matematici, informatici e appassionati di giochi per oltre cinquant’anni. Con le sue semplici regole che governano la nascita, la morte e la sopravvivenza delle cellule su una griglia, il Game of Life genera una sorprendente varietà di pattern e comportamenti complessi. In questo articolo, esploreremo le regole del gioco, alcuni dei suoi pattern più noti e come implementarlo in Python.

Le regole del Game of Life

Il Game of Life si svolge su una griglia bidimensionale di celle quadrate, ognuna delle quali può essere “viva” (1) o “morta” (0). Le celle interagiscono con i loro otto vicini, che sono le celle direttamente adiacenti orizzontalmente, verticalmente e diagonalmente. A ogni passo temporale, o generazione, le seguenti quattro regole vengono applicate simultaneamente a tutte le celle:

  1. Una cella viva con meno di due vicini vivi muore per isolamento.
  2. Una cella viva con due o tre vicini vivi sopravvive alla generazione successiva.
  3. Una cella viva con più di tre vicini vivi muore per sovraffollamento.
  4. Una cella morta con esattamente tre vicini vivi diventa viva per riproduzione.

Pattern interessanti nel Game of Life

Le semplici regole del Game of Life danno origine a una vasta gamma di pattern e comportamenti. Alcuni dei più noti sono:

  1. Configurazioni oscillanti: pattern che si ripetono periodicamente dopo un certo numero di generazioni, come il Blinker, il Toad, il Beacon e il Pulsar.
  2. Navi spaziali: pattern che si spostano attraverso la griglia mentre oscillano, come il Glider e il Lightweight Spaceship (LWSS).
  3. Pattern stabili: configurazioni che non cambiano nel tempo, come il Block e la Beehive.
  4. Cannoni: pattern che producono navi spaziali o altri pattern in modo periodico, come il Glider Gun di Gosper.

Implementazione del Game of Life in Python

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def inizializza_griglia(n):
    griglia = np.random.choice([0, 1], n*n, p=[0.5, 0.5]).reshape(n, n)
    return griglia

def conta_vicini_vivi(griglia):
    vicini_vivi = np.zeros(griglia.shape, dtype=int)

    for x in range(-1, 2):
        for y in range(-1, 2):
            if x == 0 and y == 0:
                continue
            vicini_vivi += np.roll(np.roll(griglia, y, axis=0), x, axis=1)

    return vicini_vivi

def aggiorna_griglia(griglia):
    nuova_griglia = griglia.copy()
    vicini_vivi = conta_vicini_vivi(griglia)

    nuova_griglia[(griglia == 1) & (vicini_vivi < 2)] = 0
    nuova_griglia[(griglia == 1) & (vicini_vivi > 3)] = 0
    nuova_griglia[(griglia == 0) & (vicini_vivi == 3)] = 1

    return nuova_griglia

def aggiorna_frame(*args):
    global griglia
    griglia = aggiorna_griglia(griglia)
    img.set_array(griglia)
    return [img]

def main():
    global griglia, img
    n = 50  # Dimensione della griglia
    generazioni = 100  # Numero di generazioni

    griglia = inizializza_griglia(n)

    fig, ax = plt.subplots()
    img = ax.imshow(griglia, cmap='binary')
    ax.set_xticks([])
    ax.set_yticks([])

    anim = FuncAnimation(fig, aggiorna_frame, frames=generazioni, interval=200, blit=True)
    plt.show()

if __name__ == '__main__':
    main()

Per implementare il Game of Life in Python, è possibile utilizzare la libreria numpy per rappresentare e manipolare la griglia e la libreria matplotlib per visualizzare l’evoluzione del gioco nel tempo. Il codice può essere organizzato nelle seguenti funzioni:

  1. inizializza_griglia: crea una griglia iniziale con celle viventi e morte casuali.
  2. conta_vicini_vivi: calcola il numero di vicini vivi per ogni cella della griglia.
  3. aggiorna_griglia: applica le regole del Game of Life alla griglia corrente per ottenere la griglia della generazione successiva.
  4. visualizza: mostra la griglia corrente con matplotlib