#include <stdio.h>
#include <stdlib.h>
#include <cmath.h>
#include <string.h>
#include <time.h>
#define DIM 6 //Dimensione della matrice di gioco
#define SOGLIA 0.70 //Valore soglia per la funzione random

struct game {
  int celle [DIM] [DIM];

  FILE * canale;  // canale è il puntatore all'apertura del file
  void errore (char *game)
    { fputs (game, stderr); }
  int apri (char *inizio)
  {
  this -> canale = fopen (inizio, "rt");  /*stiamo aprendo il file inizio
                                        in lettura testo*/
  if (this ->canale==NULL)   /* Se canale è = 0, ovvero se non si può aprire il file
                              sarà segnalato errore */
    this -> errore ("Errore!File non trovato!");

  return (canale != NULL);  /* Se il file si può aprire, allora sarà restituito
                              1 quando il canale è vero, 0 quando è falso */
  }

  void carica (void); //questa funzione è svolta dopo

  void chiudi ()    /* Scriviamo così al fine di controllare che il canale sia
                    effettivamente accessibile, pertanto non possiamo utilizzare
                    direttamente il comando fclose nel chiudi */
    {
      if (canale) fclose (canale);
      canale = 0;
    };
  };

  void game::carica (void)
    {
      int i,j;


    for (i=0;i<DIM;i++){
    for (j=0;j<DIM;j++){

    fscanf( this -> canale,"%d", &celle[i][j]);}
                        }
   printf("\n");
    };


//----------------------------Funzione random-----------------------------------

//Per il caricamento casuale della struttura di gioco utilizziamo
//la funzione rand/RAND_MAX per generare numeri casuali compresi tra 0 ed 1.
//Se il numero generato supera il valore impostato come soglia, assegnamo alla cella
//valore 1, ovvero la cellula viene attivata; in caso contrario assegnamo il valore 0.

struct game caricarnd(struct game lifequestomondo){
  int i,j,cell;
  double c;
  srand ( time(NULL) );
  for (i=0;i<DIM;i++){
   for (j=0;j<DIM;j++){
     c=(double)rand()/RAND_MAX;
      if(c>SOGLIA){
        cell=1;}
        else cell=0;

     lifequestomondo.celle[i][j]= cell;
   }
  }
  return lifequestomondo;

  };


//-----------------------------------Funzione contavicini-----------------------------

int* contavicini (struct game lifequestomondo)
{
  int sommavicini[DIM][DIM]={0};
  int toro[DIM+2][DIM+2]={0};
  int i,j,k;
  int sommav[DIM*DIM]={0}; //vettore che contiene tutti i valori della matrice sommavicini

//Costruiamo una matrice toro, matrice con celle interne uguali alla nostra matrice
//di gioco, lifequestomondo, angoli scambiati con quelli diametralmente opposti e bordi
//uguali a quelli di lifequestomondo ma posizionati in ordine invertito nord-sud, est-ovest.

  toro[0][0]=lifequestomondo.celle[DIM-1][DIM-1];
  toro[DIM+1][0]=lifequestomondo.celle[0][DIM-1];
  toro[0][DIM+1]=lifequestomondo.celle[DIM-1][0];
  toro[DIM+1][DIM+1]=lifequestomondo.celle[0][0];

 for (i=0;i<DIM; i++){
    toro[i+1][0]=lifequestomondo.celle[i][DIM-1];
    toro[DIM+1][i+1]=lifequestomondo.celle[0][i];
    toro[0][i+1]=lifequestomondo.celle[DIM-1][i];
    toro[i+1][DIM+1]=lifequestomondo.celle[i][0];
  }

  for (i=0;i<DIM; i++){
    for (j=0;j<DIM; j++){
  toro[i+1][j+1]=lifequestomondo.celle[i][j];}}

//Per contare i vicini definiamo le otto celle adiacenti ad una generica cella [i][j]

  for (i=1;i<DIM+1; i++){
    for (j=1;j<DIM+1; j++){
    sommavicini[i-1][j-1]=toro[i-1][j-1]+toro[i+1][j+1]+toro[i][j-1]+toro[i-1][j+1]+toro[i+1][j-1]+toro[i+1][j]+
                      toro[i-1][j]+toro[i][j+1];
  }
}

//Trasformiamo la matrice sommavicini nel vettore sommav

k=0;
  for (i=0;i<DIM; i++){
    for (j=0;j<DIM; j++){
      while (k<DIM*DIM){
      sommav[k]=sommavicini[i][j];
      k++;
      break;
      }
  }
}

      return (sommav);
}

//------------------Funzione cambiogenerazione ovvero le regole del gioco------------

struct game cambiogenerazione (struct game lifequestomondo)
{
  int i, k,j;
  int nmondo[DIM*DIM],vmondo[DIM*DIM];
  int* vicini;
  vicini=contavicini(lifequestomondo);

//Trasformiamo la matrice lifequestomondo nel vettore vmondo
  k=0;
  for (i=0;i<DIM; i++){
    for (j=0;j<DIM; j++){
      while (k<DIM*DIM){
      vmondo[k]=lifequestomondo.celle[i][j];
      k++;
      break;
      }
  }
}
//-------------------------------Regole del gioco------------------------------------

//Se il numero dei vicini di una cellula attiva è pari a due o tre, la cellula resta
//attiva; se i vicni sono in numero inferiore a due o superiore a tre la cellula
//muore per solitudine o per soffocamento. Nel caso in cui una cellula sia inattiva,
//la presenza di tre vicini la fa attivare; in tutti gli altri casi resta inattiva.

 for (i=0;i<DIM*DIM;i++){
   if (vmondo[i]==1) {
     if ((vicini[i]==2) || (vicini[i]==3))
           nmondo[i]=1;
         else nmondo[i]=0;}

 else {
         if (vicini[i]==3)
           nmondo[i]=1;
         else nmondo[i]=0;}
                         }

//Ritrasmormiamo il vettore nmondo nella matrice lifequestomondo

k=0;
while (k<DIM*DIM){
 for (i=0;i<DIM; i++){
   for (j=0;j<DIM; j++){
          lifequestomondo.celle[i][j]=nmondo[k];
          k++;
        }
    }
  }
      return (lifequestomondo);
}


//----------------------------------------Funzione main--------------------------------

void main (int argc, char * argv[] )
{
  int i,j;
  char scelta;
  struct game mondo;

printf("\n----------------------BENVENUTI ALL'INTERNO DI LIFE GAME-----------------------\n");
printf("\n------------------------- Buona simulazione a tutti!----------------------------\n");
printf("\nPuoi scegliere se caricare la struttura da file oppure farla scegliere al caso\n");
printf("\n                         Premere invio per continuare\n");

  while (getchar() !='\n');


printf("\nVuoi caricare la struttura iniziale da file? (s/n)  ");

  while ((scelta=getchar())=='\n');

if(scelta=='s') {

  if (argc<2)
    mondo.errore ("\nHai deciso di caricare la struttura da file ma manca il nome del file");

  else if (mondo.apri(argv[1]))
      {
        mondo.carica ();
        mondo.chiudi();
        printf("\nHai deciso di caricare la struttura da file. File caricato correttamente!\n");
        printf("\n");

    printf("\nPremere invio per vedere la matrice che e' stata caricata\n");

        while (getchar() !='\n');
        while (getchar() !='\n');

        int i,j;
        for (i=0;i<DIM;i++){
        for (j=0;j<DIM;j++){
        printf(" %d ", mondo.celle[i][j] );
        if (j==DIM-1) {
                  printf("\n");};

              }
            }
            printf("\n");
     }
  }

if(scelta=='n') {

    printf("\n");
    printf("\nHai deciso di caricare la matrice in maniera casuale\n");
    printf("\nPremere invio per vedere la matrice che e' stata caricata\n");

    while (getchar() !='\n');
    while (getchar() !='\n');

    mondo=caricarnd(mondo);

    for (i=0;i<DIM;i++){
      for (j=0;j<DIM;j++){
        printf(" %d ", mondo.celle[i][j] );
        if (j==DIM-1) {
                printf("\n");};

            }
          }
          printf("\n");
}

//Nel riptere più volte il cambio generazione, abbiamo usato una variabile temporanea,
//temp, per evitare che l'utente potesse modificare direttamente una variable del programma

  int NGENERAZIONI,temp;
     printf("\nQuante generazioni vuoi vedere?  ");
     scanf("%d", &temp);
     NGENERAZIONI=temp;
     while (getchar() !='\n');

  int z;
  for (z=0;z<NGENERAZIONI;z++){

    printf("\nPremere invio per passare alla generazione successiva\n");

    while (getchar() !='\n');

    mondo=cambiogenerazione(mondo);
    for (i=0;i<DIM;i++){
      for (j=0;j<DIM;j++){
        printf(" %d ", mondo.celle[i][j] );
         if (j==DIM-1) {
                printf("\n");};

            }
          }
        printf("\n");

  }

printf("-----------------------------FINE DELLA SIMULAZIONE-----------------------------\n");
while (getchar() !='\n');
}

