/* guardieladrip.c
 * Stefano Salvi - 15/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.
 *
 * 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. Ognuno dei due figli scrivera' un'apposita
 * struttura nella pipe. Il Padre leggera' in continuazione dalla pipe ed agira' di conseguenza.
 *
 *
 * X compilare:
 * gcc guardieladrip.c -o guardieladrip -lncurses
 */

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


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

#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 */

/* Struttura per la comunicazione tra figli e padre */
struct pos{
  char c;	// 'soggetto' che invia il dato
  int x;	// Coordinate
  int y;	// Idem
};

void ladro (int pipeout);
void guardia (int pipeout);
void campo (int pipein);

int main()
{
int p[2];	// Array per la 'pipe' (coppia di descrittori)
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

  pipe(p);		// Crea la pipe per comunicare	

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

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

    if(pidGuardia==0)   // pidGuardia == 0 -> e' il figlio (guardia)
    {
      guardia (p [1]);	// Esegue il lavoro della guardia
    }
    else
    {			// Siamo ancora nel padre
      campo (p [0]);	// Svogliamo il lavoro del padre (gestire il campo)
    }
  }

  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
  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 viene comunicato al padre tramite la pipe 'pipeout'.
 */
void ladro (int pipeout)
{
struct pos pladro;
int deltax;		// Spostamento orizzontale
int deltay;		// Spostamento verticale

  // Posizione iniziale del ladro: in alto a sinistra.
  pladro.x = 1;
  pladro.y = 1;
  pladro.c='$';	// lo caratterizza come ladro

  write(pipeout,&pladro,sizeof(pladro));  //passo al padre la posizione iniziale del ladro

  while(1)    // ciclo infinito. viene terminato 'a forza' dal padre
  {
    /* 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));

    // 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));

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

    pladro.y += deltay;		// Spostamento verticale
			
    write(pipeout,&pladro,sizeof(pladro));  //passo al padre la posizione del ladro

    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 pipe 'pipeout'.
 */
void guardia (int pipeout)
{
struct pos pguardia;

  // Posizione iniziale : in basso a destra
  pguardia.x = MAXX - 1;
  pguardia.y = MAXY - 1;
  pguardia.c='#';	// lo caratterizza come 'guardia'

  write(pipeout,&pguardia,sizeof(pguardia));  //passo al padre la posizione iniziale

  while(1)
  {
  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
    }

    write(pipeout,&pguardia,sizeof(pguardia));  //passo al padre la posizione della guardia
  }
}

/* Funzione di visualizzazione e controllo (padre).
 * Attende 'eventi' dalla pipe (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 pipein)
{
struct pos ladro, guardia, letto;

  ladro.x=-1;	// Mai usato
  guardia.x=-1;	// Mai usato

  do {
    read(pipein,&letto,sizeof(letto));	// Leggo l'evento

    if(letto.c=='$')			// e' il ladro
    {
      if (ladro.x>=0)			// Se non e' la prima volta
      {
        mvaddch(ladro.y,ladro.x,' ');	// Cancello il vecchio ladro
      }
      ladro=letto;			// 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=letto;			// Aggiorno la posizione della guardia
    }

    mvaddch(letto.y,letto.x,letto.c);	// Disegno il nuovo 'personaggio'
    curs_set(0);			// Rielimino il cursore
    refresh();				// Aggiorno le modifiche (cancellazione e disegno)
    // Continuo fino a che' le coordinate non coincidono
  } while (guardia.x != ladro.x || guardia.y != ladro.y);
}

