Game of Life - vaihe 4

Tässä vaiheessa hienosäädetään peli toimimaan oikein. Ruudut osaavat jo elää, mutta niiden värit päivittyvät liian aikaisin. Jos siis ajamme ohjelmaa yhden kierroksen, algoritmi vaihtaa yksitellen jokaisen ruudun värin, mutta nämä muutokset vaikuttavat saman kierroksen muihin ruutuihin. Asiaa voi mietiskellä tämän sivun tehtävien avulla. Muutetaan peli toimimaan niin, että kaikkien ruutujen värit vaihdetaan yhdellä kertaa kierroksen lopuksi. Silloin ohjelmamme oppii piirtämään toistuvia kuvioita, kuten esimerkkianimaatiossa.

Värien päivityksen korjaaminen

Selvitetään ensin jokaisen ruudun uusi väri ja tallennetaan nämä värit taulukkoon. Kierroksen lopuksi vaihdetaan kaikkien ruutujen väri tämän taulukon mukaan. Lisätään siis koodin alussa olevien muuttujien määritysten yhteyteen uusi taulukko:

Color[] varit;


ja tehdään taulukkoon tilaa jokaisen ruudun värille. Tämä hoituu lisäämällä LuoKentta-aliohjelman loppuun seuraava rivi:

varit = new Color[ruudut.Count];


Seuraavaksi muutetaan ElaKerran-aliohjelmaa niin, että silmukassa ei enää vaihdeta ruutujen värejä. Sen sijaan uudet värit tallennetaan taulukkoon.

void ElaKerran()
{
    for (int i = 0; i < ruudut.Count; i++)
    {
        int naapurit = LaskeNaapurit(ruudut[i]);
        varit[i] = ruudut[i].Color;
        if (ruudut[i].Color == Color.Black)
            if (naapurit < 2 || naapurit > 3)
                varit[i] = Color.White;
        if (ruudut[i].Color == Color.White)
            if (naapurit == 3)
                varit[i] = Color.Black;
    }
}


Kun silmukka on suoritettu, taulukko sisältää kaikkien ruutujen uudet värit. Lisätään vielä saman aliohjelman loppuun toinen silmukka, jossa uudet värit asetetaan ruuduille.

for (int i = 0; i < ruudut.Count; i++)
        ruudut[i].Color = varit[i];


Nyt värin vaihtaminen ei enää sotke muiden ruutujen elämää ja ohjelma toimii oikein. Voit testailla ohjelman toimintaa erilaisilla aloitusmuodostelmilla tai jopa lisätä uusia eläviä ruutuja kesken suorituksen.

Eräs kuuluisimmista kuvioista on ns. kulkuri (eng. glider), joka liikuttaa itseään ikuisesti samaan suuntaan. Toinen tunnettu viiden ruudun aloitelma on ns. R-pentomino, joka kasvaa yli tuhanneksi eläväksi ruuduksi.


Kulkuri ja R-pentomino
Kulkuri ja R-pentomino



Koko ohjelman pitäisi lopuksi näyttää suurinpiirtein tältä:

using System.Collections.Generic;
using Jypeli;
using Jypeli.Controls;    

public class GameOfLife : Game
{
    const int RUUDUN_KOKO = 50;
    List<GameObject> ruudut = new List<GameObject>();
    Color[] varit;
    Vector[] suunnat = { new Vector(0,RUUDUN_KOKO),
                     new Vector(RUUDUN_KOKO,0),
                     new Vector(0,-RUUDUN_KOKO),
                     new Vector(-RUUDUN_KOKO,0),
                     new Vector(RUUDUN_KOKO,RUUDUN_KOKO),
                     new Vector(RUUDUN_KOKO,-RUUDUN_KOKO),
                     new Vector(-RUUDUN_KOKO,RUUDUN_KOKO),
                     new Vector(-RUUDUN_KOKO,-RUUDUN_KOKO) };    

    public override void Begin()
    {
        Mouse.IsCursorVisible = true;
        IsFullScreen = false;
        Window.Width = 500;
        Window.Height = 500;    

        LuoKentta();    

        Keyboard.Listen(Key.Space, ButtonState.Pressed, ElaKerran, "Eletään yksi kierros");    

        Keyboard.Listen(Key.Escape, ButtonState.Pressed, ConfirmExit, "Lopeta peli");
    }    

    void LuoKentta()
    {
        double alkuX = -Window.Width / 2 + RUUDUN_KOKO / 2;
        double alkuY = -Window.Height / 2 + RUUDUN_KOKO / 2;    

        for (double x = alkuX; x < Window.Width / 2; x += RUUDUN_KOKO)
            for (double y = alkuY; y < Window.Height / 2; y += RUUDUN_KOKO)
                LuoRuutu(x, y);
        varit = new Color[ruudut.Count];
    }    

    void LuoRuutu(double x, double y)
    {
        GameObject ruutu = new GameObject(RUUDUN_KOKO - 2, RUUDUN_KOKO - 2);
        ruutu.Position = new Vector(x, y);    

        Mouse.ListenOn(ruutu, MouseButton.Left, ButtonState.Pressed,
        () => { VaihdaVaria(ruutu); }, "Vaihdetaan ruudun väriä");    

        ruudut.Add(ruutu);
        Add(ruutu);
    }    

    void VaihdaVaria(GameObject ruutu)
    {
        if (ruutu.Color == Color.White)
            ruutu.Color = Color.Black;
        else ruutu.Color = Color.White;
    }    

    void ElaKerran()
    {
        for (int i = 0; i < ruudut.Count; i++)
        {
            int naapurit = LaskeNaapurit(ruudut[i]);
            varit[i] = ruudut[i].Color;
            if (ruudut[i].Color == Color.Black)
                if (naapurit < 2 || naapurit > 3)
                    varit[i] = Color.White;
            if (ruudut[i].Color == Color.White)
                if (naapurit == 3)
                    varit[i] = Color.Black;
        }    

        for (int i = 0; i < ruudut.Count; i++)
            ruudut[i].Color = varit[i];
    }    

    int LaskeNaapurit(GameObject ruutu)
    {
        int naapurienMaara = 0;
        foreach (Vector v in suunnat)
        {
            GameObject naapuri = GetObjectAt(ruutu.Position + v);
            if (naapuri != null && naapuri.Color == Color.Black)
                naapurienMaara++;
        }
        return naapurienMaara;
    }
}

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.