/* guardieladripm.c
 * Stefano Salvi - 19/9/02
 * Gioco 'guardie e ladri'.
 * Un 'ladro' (rappresentato da un '$') fugge a caso sullo schermo.
 * Una 'guardia' (raprresentata da un '#') lo insegue, pilotato dai tasti del giocatore.
 * Il gioco termina quando la guardia raggiunge il ladro.
 * 
 * Per realizzare il gioco utilizzeremo tre processi:
 * 1) processo 'padre'. Gestisce la visualizzazione e verifica se la guardia ha preso il ladro.
 *    Per fare questo deve memorizzarsi le coordinate del ladro.
 *    Quando il gioco e' terminato, 'uccide' i figli e termina (se usa le pipe) oppure invia
 *    un messaggio ai figli, che terminano, ed attende la loro terminazione.
 *
 * 2) processo 'figlio - ladro'. Genera casualmente uno spostamento, che comunica al 'padre'
 *    per la visualizzazione
 *
 * 3) processo 'figlio - guardia'. Legge la tastiera e calcola la nuova posizione, in base 
 *    ai tasti letti. Comunica questa nuova posizione al 'padre'.
 *
 * I tre processi comunicano tramite un'unica pipe oppure con una coda di messaggi. 
 * Ognuno dei due figli scrivera' un'apposita struttura nella pipe. Il Padre leggera' in 
 * continuazione dalla pipe ed agira' di conseguenza.
 * Nel caso della coda di messaggi, periodicamente la guardia inviera' tramite la coda la 
 * sua posizione al ladro.
 * Il padre inviera' un messaggio ad entrambi i figli, per farli terminare.
 *
 *
 * X compilare:
 * gcc guardieladripm.c -o guardieladrip -lncurses (versione pipe)
 * gcc -DMESSAGE -oguardieladrim guardieladripm.c -lncurses (versione messaggi)
 */

#include <stdio.h>
#include <curses.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>


#define RAGGIO	 1	/* di quanto si sposta il ladro ad ogni ciclo */

#define	CAMPOTYP 2	/* Messaggi per il campo */
#define	LADROTYP 3	/* Messaggi per il ladro */
#define	GUARDIATYP 4	/* Messaggi per la guardia */

#define	SU	 65	/* Freccia su */
#define GIU	 66	/* Freccia giu */
#define	SINISTRA 68	/* Freccia sinsitra */
#define DESTRA	 67	/* Freccia destra */

#define	MAXX	 80	/* Numero di colonne */
#define MAXY	 24	/* Numero di righe */

// #define	MESSAGE

/* Struttura per la comunicazione tra figli e padre */
struct pos{
#ifdef	MESSAGE
  long mtype;	//tipo di messaggio
#endif
  char c;	//ladro($) o guardia(#)
  int x;	//posizione x
  int y;	//posizione y
};


void ladro (int canale);
void guardia (int canale);
void campo (int canale);

int main()
{
#ifdef	MESSAGE
int msqid;	// riferimento della coda dei messaggi
#else
int p[2];	// Array per la 'pipe' (coppia di descrittori)
#endif
int status;	// stato di ritorno dei processi figli

int pidLadro;	// Pid del figlio 'ladro'
int pidGuardia;	// Pid del figlio 'guardia'
	
  initscr();		// Inizializza curses
  noecho();		// caratteristica della tastiera
  curs_set(0);		// elimina il cursore

#ifdef	MESSAGE
  // Crea la coda dei messaggi
  msqid = msgget(IPC_PRIVATE,IPC_CREAT | S_IREAD | S_IWRITE);
#else
  pipe(p);		// Crea la pipe per comunicare	
#endif

  pidLadro = fork();	// Genera il primo 'figlio'

  if(pidLadro==0)       // pidLadro == 0 -> e' il figlio (ladro)
  {
#ifdef	MESSAGE
    ladro (msqid);	// Esegue il lavoro del ladro
#else
    ladro (p [1]);	// Esegue il lavoro della guardia
#endif
  }		
  else
  {			// Siamo ancora nel padre
    pidGuardia=fork();  // Genera il secondo figlio (guardia)

    if(pidGuardia==0)   // pidGuardia == 0 -> e' il figlio (guardia)
    {
#ifdef	MESSAGE
      guardia (msqid);	// Esegue il lavoro della guardia
#else
      guardia (p [1]);	// Esegue il lavoro della guardia
#endif
    }
    else
    {			// Siamo ancora nel padre
#ifdef	MESSAGE
      campo (msqid);	// Svogliamo il lavoro del padre (gestire il campo)
#else
      campo (p [0]);	// Svogliamo il lavoro del padre (gestire il campo)
#endif
    }
  }
		
#ifdef	MESSAGE
  waitpid(pidLadro,&status,0);
  waitpid(pidGuardia,&status,0);
#else
  kill(pidLadro,1);	// Termina il ladro (con segnale 1 - uno qualunque va' bene);
  kill(pidGuardia,1);	// Termina la guardia (con segnale 1 - uno qualunque va' bene)

  getchar();		// Attendiamo la pressione di un tasto, prima di chiudere
#endif

  endwin();		// Torniamo in modalita' normale

  return 0;		// Termine del programma
}

/* Funzione 'ladro'.
 * Genera un 'movimento casuale' in un raggio RAGGIO rispetto al punto corrente,
 * nel campo di 80X25 caratteri.
 * Il movimento tende comunque a spostare il ladro lontano dalla guardia.
 * La guardia comunica i suoi spostamenti al ladro (se uso la messagebox)
 * Il movimento viene comunicato al padre tramite la messagebox o la pipe.
 */
void ladro (int canale)
{
struct pos pladro;	// Posizione corrente del ladro
#ifdef	MESSAGE
struct pos pinput;	// Posizione della guardia, ricevuta
#endif
int deltax;		// Spostamento orizzontale
int deltay;		// Spostamento verticale
int fugax = 0,fugay = 0;// Spostamento del centro, per allontanarsi

  // Posizione iniziale del ladro: in alto a sinistra.
  pladro.x = 1;
  pladro.y = 1;
  pladro.c='$';		// lo caratterizza come ladro
#ifdef	MESSAGE
  pladro.mtype=CAMPOTYP; //canale di invio della posizione del ladro al padre
  // Posizione iniziale della guardia: il ladro sta' fermo.
  pinput.x = 1;
  pinput.y = 1;
#endif

  while(1)	// Ciclo infinito. Verra' terminato con una 'return'
  {
#ifdef	MESSAGE
    if (msgrcv(canale,&pinput,sizeof(pinput)-sizeof(long),LADROTYP,IPC_NOWAIT) >0 )
    {
      if(pinput.c=='0')
      {
        return;
      }
    }

    fugax =  pladro.x - pinput.x;	// Individua la direzione della guardia
    // normalizza fugax (-1, 0, 1)
    if (fugax < 0)
    {
      fugax = -1; 
    }
    else if (fugax > 0)
    {
      fugax = 1;
    }

    fugay =  pladro.y - pinput.y;	// Individua la direzione della guardia
    // normalizza fugay (-1, 0, 1)
    if (fugay < 0)
    {
      fugay = -1; 
    }
    else if (fugay > 0)
    {
      fugay = 1;
    }
#endif

    /* Calcola uno spostamento casuale da -RAGGIO ... RAGGIO.
     * Abbiamo 2*RAGGIO+1 combinazioni (le RAGGIO negative, le RAGGIO positive e lo 0).
     * 'random() % (RAGGIO*2+1)' ritorna combinazioni da 0 a 2*RAGGIO: quelle che servono
     * ma tutte positive. Per averne RAGGIO negative, dovremo toglier RAGGIO+1
     */
    deltax= ((random() % (RAGGIO*2+1)) - (RAGGIO + 1) + fugax);

    // Se con lo spostamento esce, inverto lo spostamento
    if(pladro.x + deltax < 1 || pladro.x + deltax >= MAXX)
    {
        deltax = -deltax;	// Inverto lo spostamento
    }

    pladro.x += deltax;		// Spostamento orizzontale

    /* Calcola uno spostamento casuale da -RAGGIO ... RAGGIO.
     * per l'algoritmo vedere il deltax
     */
    deltay = ((random()%(RAGGIO*2+1)) - (RAGGIO + 1) + fugay);

    // Se con lo spostamento esce, inverto lo spostamento
    if(pladro.y + deltay < 1 || pladro.y + deltay >= MAXY)
    {
      deltay = -deltay;
    }

    pladro.y += deltay;		// Spostamento verticale

#ifdef	MESSAGE
    msgsnd(canale,&pladro,sizeof(pladro)-sizeof(long),0);
#else
    write(canale,&pladro,sizeof(pladro));  //passo al padre la posizione del ladro
#endif
			
    usleep(200000);		// Pausa per 'dare il ritmo'
  }
}


/* Funzione 'guardia'.
 * Legge i tasti premuti dall'utente. Sposta la guardia nella direzione
 * indicata dalle frecce. Si ferma ai bordi del campo di 80X25 caratteri.
 * Il movimento viene comunicato al padre tramite la messagebox o la pipe.
 * Se uso la messagebox, ogni 10 spostamenti il movimento viene anche comunicato al ladro
 */
void guardia (int canale)
{
struct pos pguardia;	// Posizione corrente della guardia
#ifdef	MESSAGE
struct pos rcampo;	// Risposta dal campo (terminazione)
int i = 0;
#endif

  // Posizione iniziale : in basso a destra
  pguardia.x = MAXX - 1;
  pguardia.y = MAXY - 1;
  pguardia.c='#';		// lo caratterizza come 'guardia'
#ifdef	MESSAGE
  pguardia.mtype=CAMPOTYP;	//canale dell'invio della posizione della guardia al padre

  //passo al padre la posizione iniziale
  msgsnd(canale,&pguardia,sizeof(pguardia)-sizeof(long),0);
#else
  write(canale,&pguardia,sizeof(pguardia));  // passo al padre la posizione iniziale
#endif


  while(1)	// Ciclo infinito. Verra' terminato con una 'return'
  {
  char c;
    c = getch();				// Legge il tasto

    if (c==SU && pguardia.y > 0)		// Freccia SU
    {
      pguardia.y-=1;				// Cala la y
    }

    if(c==GIU  && pguardia.y < MAXY - 1)	// Freccia GIU
    {
      pguardia.y+=1;				// Cresce la y
    }

    if((c==SINISTRA)&&pguardia.x > 0)		// Freccia SINISTRA
    {
      pguardia.x-=1;				// Cala la x
    }

    if(c==DESTRA && pguardia.x < MAXX - 1)	// Freccia DESTRA
    {
      pguardia.x+=1;				// Cresce la x
    }

#ifdef	MESSAGE
    //passo al padre la posizione la posizione corrente
    pguardia.mtype=CAMPOTYP;	//canale dell'invio della posizione della guardia al padre
    msgsnd(canale,&pguardia,sizeof(pguardia)-sizeof(long),0);
					
    if (msgrcv(canale,&rcampo,sizeof(rcampo)-sizeof(long),GUARDIATYP,IPC_NOWAIT) >0 )
    {
      if(rcampo.c=='0')
      {
        return;
      }
    }

    i++;
    if(!(i%10))
    {
      pguardia.mtype=LADROTYP;	//canale dell'invio della posizione della guardia al ladro
      msgsnd(canale,&pguardia,sizeof(pguardia)-sizeof(long),0);
    }				
#else
    write(canale,&pguardia,sizeof(pguardia));  //passo al padre la posizione della guardia
#endif
  }
}

/* Funzione di visualizzazione e controllo (padre).
 * Attende 'eventi' dalla pipe o dalla messagebox (nella quale scrivono entrambi i figli).
 *
 * Se non e' il primo evento (per ogni figlio), prima cancello la posizione
 * vecchia.
 *
 * Se la posizione della guardia corrisponde a quella del ladro, termina il gioco.
 */ 
void campo (int canale)
{
struct pos ladro,guardia, transito;
 
  ladro.x=-1;	// Mai usato
  guardia.x=-1;	// Mai usato

  while(-1)	// Ciclo infinito: terminero' con un return
  {
#ifdef	MESSAGE
    if (msgrcv(canale,&transito,sizeof(transito)-sizeof(long),CAMPOTYP,0) >0 )
    {				
#else
    read(canale,&transito,sizeof(transito));	// Leggo l'evento
#endif
      if(transito.c=='$')			// e' il ladro
      {
        if (ladro.x>=0)				// Se non e' la prima volta
        {
          mvaddch(ladro.y,ladro.x,' ');		// Cancello il vecchio ladro
        }
        ladro=transito;				// Aggiorno la posizione del ladro
      }
      else 					//e' la guardia
      {
        //devo prima cacellare il la pos precedente della guardia
        if (guardia.x>=0)			// Se non e' la prima volta
        {
          mvaddch(guardia.y,guardia.x,' ');	// Cancello la vecchia guardia
        }
        guardia=transito;			// Aggiorno la posizione della guardia
      }

      mvaddch(transito.y,transito.x,transito.c); // Disegno il nuovo 'personaggio'
      curs_set(0);				// Rielimino il cursore
      refresh();				// Aggiorno le modifiche (cancell. e disegno)

      if(guardia.x==ladro.x&&guardia.y==ladro.y)
      {	// Il ladro ha raggiunto la guardia
#ifdef	MESSAGE
	transito.c='0';
        // Invia messaggio di terminazione al ladro
	transito.mtype=LADROTYP; //canale di invio al ladro della fine del programma
   	msgsnd(canale,&transito,sizeof(transito)-sizeof(long),0);
        // Invia messaggio di terminazione alla guardia
   	transito.mtype=GUARDIATYP; //canale di invio alla guardia della fine del programma
   	msgsnd(canale,&transito,sizeof(transito)-sizeof(long),0);
#endif
        return;	
      }
#ifdef	MESSAGE
    }
#endif
  }
}



