Game of life

0

Postanowiłem napisać grę w życie w C. Jako, że nie znam jeszcze technik programowania grafiki (poza odrobiną allegro) moja wersja implementacji będzie wyświetlana w okienku konsolowym, a żywe i martwe komórki będą reprezentowane odpowiednio za pomocą 1 i 0. Chciałbym się zapytać jak można dość efektywnie rozwiązać problem "animacji" ewolucji. Tzn. mam pierwszy stan, poddaje go obróbce i chcę żeby się wyświetlał. Myślałem żeby zastosować prostą wersję podwójnego buforowania, ale może ktoś z was zna jakiś lepszy sposób.

0

Szczerze mówiąc, ciągłe przerysowywanie 10-15-20 linii w konsoli nie będzie zbyt optymalne. Zmieniaj tylko te znaki, które musisz.

0

Pisałem parę miesięcy temu grę w życie w konsoli z użyciem <newconio> ( http://edu.i-lo.tarnow.pl/inf/prg/008_projekty/data/newconio.h , http://edu.i-lo.tarnow.pl/inf/prg/008_projekty/data/newconio.cpp ).
Wygląda to w ten sposób: http://scr.hu/screenshooter/1084042/efrdrxs

Ramki rysuję za pomocą funkcji z tej biblioteki, a potem planszę 80x30 przerysowuję całą z każdą zmianą stanu jadąc po prostu dwiema pętlami i stawiając znaki poprzez putchxy() - wygląda płynnie, bez mrugania czy czegoś w podobnego, stąd nie miałem potrzeby troszczyć się o optymalizowanie tego.

0

Proste podwójne buforowanie (konsola) z pomocą WinAPI (WriteConsoleOutputCharacter):

#include <iostream>
#include <ctime>
#include <windows.h>
using namespace std;

//znaki 'char' reprezentujace stany komorek
#define SUBAKTYWNA ((char) 32)   //czarny
#define AKTYWNA    ((char) 176)  //szary
#define NIEAKTYWNA ((char) 177)  //bialy
#define MARTWA     NIEAKTYWNA
#define ZYWA       SUBAKTYWNA  

char bufory[2][25][80]; //dwa bufory zawierajace siatke komorek - tablice char'ow reprezentujacych stany komorek, jeden bufor aktywny drugi pasywny (wymiennie)
int A = 0; //indeks aktywnego buforu, w tym buforze zapisywane sa nowe stany komorek
int P = 1; //indeks pasywnego buforu, z tego buforu odczytywane sa stany komorek, jest on rowniez wyswietlany na ekranie

int szerokosc, wysokosc; //wymiary siatki, max 80x25
int automat; //numer automatu: 1 - zycie, 2 - marsz, 3 - inwazja
int interwal; //czas w ms pomiedzy kolejnymi krokami
double gestosc; //prawdopodobienstwo z jakim pojawiaja sie zywe komorki podczas losowania

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); //uchwyt konsoli uzywany przy wyswietlaniu siatki

//wyswietla komorki i zamienia bufory
void FlipPrint()
{
   //aby wyeliminowac brzydki efekt "przewijania"
   //do wyswietlania uzyta jest funkcja WriteConsoleOutputCharacterA, 
   DWORD cWritten; 
   COORD coord= { 0, 0 }; 
   WriteConsoleOutputCharacterA(hStdOut, bufory[A][0], 80 * 25 - 1, coord, &cWritten);
   
   //zamiana indeksow buforow
   int tmp = A; A = P; P = tmp;
}

//wypelnia losowo siatke z zadana gestoscia
void WypelnijLosowo()
{
   srand(time(NULL));
   if(automat == 3) //inwazja - 3 stany komorek
      for(int i = 0; i < wysokosc; ++i)
         for(int j = 0; j < szerokosc; ++j)
            if((rand() / (double) RAND_MAX) < gestosc)
               bufory[A][i][j] = ((rand() % 2) ? AKTYWNA : SUBAKTYWNA);
            else
               bufory[A][i][j] = NIEAKTYWNA;   
   else //zycie lub marsz - 2 stany komorek
      for(int i = 0; i < wysokosc; ++i)
         for(int j = 0; j < szerokosc; ++j)
            if((rand() / (double) RAND_MAX) < gestosc)
               bufory[A][i][j] = ZYWA;
            else
               bufory[A][i][j] = MARTWA;
}

//zlicza ilosc wystapien danego typu w siatce komorek wokol komorki (i, j)
int Zlicz(int i, int j, char typ)
{
   int rezultat = 0; //zkiczana ilosc powtorzen
   int gora = i - 1, dol = i + 1, lewy = j - 1, prawy = j + 1; //krawedzie poszukiwan

   //petle zliczaja caly kwadrat (wraz ze srodkiem i,j), zatem startujemy od -1 jesli srodek ma odpowiedni typ
   if(bufory[P][i][j] == typ) rezultat = -1;

   //przy krawedziach siatki nalezy zmniejszyc obszar poszukiwan
   if(i == 0) ++gora; else 
   if(i == (wysokosc - 1)) --dol;
   if(j == 0) ++lewy; else 
   if(j == (szerokosc - 1)) --prawy;

   for(i = gora; i <= dol; ++i)
      for(j = lewy; j <= prawy; ++j)
         if(bufory[P][i][j] == typ) ++rezultat;
   return rezultat;
}

//krok symulacji Zycie
void Zycie()
{
   int zywe; //ilosc zywych komorek sasiednich
   for(int i = 0; i < wysokosc; ++i)
      for(int j = 0; j < szerokosc; ++j)
      {
         zywe = Zlicz(i, j, ZYWA);
         if((zywe == 3) || ((bufory[P][i][j] == ZYWA) && (zywe == 2)))
            bufory[A][i][j] = ZYWA;
         else
            bufory[A][i][j] = MARTWA;
      }
}

//krok symulacji Marsz
void Marsz()
{
   int aktywne; //ilosc aktywnych komorek sasiednich
   for(int i = 0; i < wysokosc; ++i)
      for(int j = 0; j < szerokosc; ++j)
      {
         aktywne = Zlicz(i, j, SUBAKTYWNA);
         if((aktywne == 3) || (aktywne == 2) || ((bufory[P][i][j] == SUBAKTYWNA) && (aktywne == 1)))
            bufory[A][i][j] = SUBAKTYWNA;
         else
            bufory[A][i][j] = NIEAKTYWNA;
      }
}

//krok symulacji Inwazja
void Inwazja()
{
   int aktywne; //ilosc aktywnych komorek sasiednich
   int subaktywne; //ilosc subaktywnych komorek sasiednich
   for(int i = 0; i < wysokosc; ++i)
      for(int j = 0; j < szerokosc; ++j)
      {
         aktywne = Zlicz(i, j, AKTYWNA);
         subaktywne = Zlicz(i, j, SUBAKTYWNA);
         if((bufory[P][i][j] == AKTYWNA) && ((aktywne >= 3) || (subaktywne >= 3)))
            bufory[A][i][j] = SUBAKTYWNA;
         else if((bufory[P][i][j] == NIEAKTYWNA) && ((aktywne + subaktywne) >= 3))
            bufory[A][i][j] = AKTYWNA;
         else if((bufory[P][i][j] == AKTYWNA) && (((aktywne + subaktywne) == 1) || ((aktywne + subaktywne) == 2)))
            bufory[A][i][j] = AKTYWNA;
         else
            bufory[A][i][j] = NIEAKTYWNA;
      }   
}

int main()
{
   cout << "Podaj wysokosc siatki (1 - 25): ";
   cin >> wysokosc; if((wysokosc > 25) || (wysokosc < 1)) wysokosc = 25;
   cout << "Podaj szerokosc siatki (1 - 80): ";
   cin >> szerokosc; if((szerokosc > 80) || (szerokosc < 1))  szerokosc = 80;
   cout << "Wybierz automat (1 - Zycie, 2 - Marsz, 3 - Invazja): ";
   cin >> automat; if(automat < 1) automat = 1; if(automat > 3) automat = 3; 
   cout << "Podaj gestosc komorek (0.0 - 1.0): ";
   cin >> gestosc; if(gestosc < 0.0) gestosc = 0.0; if(gestosc > 1.0) gestosc = 1.0; 
   cout << "Podaj interwal czasowy w ms ( >0 ): ";
   cin >> interwal; if(interwal < 1) interwal = 1;

   WypelnijLosowo();
   for(;;)
   {
      FlipPrint();
      Sleep(interwal);
      switch(automat)
      {
         case 1: Zycie(); break;
         case 2: Marsz(); break;
         case 3: Inwazja(); break;
      }
   }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1