/* guardieladrit.c
 * Stefano Salvi - 21/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 thread:
 * 1) thread 'padre'. Visualizza un contatore di mosse e verifica se la guardia ha preso il 
 *    ladro.
 *    Quando il gioco e' terminato, imposta un flag, i figli, vedendolo terminano, il padre 
 *    attende la loro terminazione.
 *
 * 2) thread 'figlio - ladro'. Genera casualmente uno spostamento e sposta il suo 
 *    'personaggio'. Genera lo spostamento a partire dalla posizione 'pubblicata' dalla guardia
 *
 * 3) thread 'figlio - guardia'. Legge la tastiera e calcola la nuova posizione, in base 
 *    ai tasti letti. Visualizza il suo 'personaggio' e comunica periodicamente al ladro la
 *    sua posizione.
 *
 * I tre processi comunicano tramite variabili globali.
 * Dato che le routine di curses non sono 'rientranti', e dato che tutti i tre thread le usano,
 * le proteggeremo tramite un 'mutex'.
 * Per evitare che il thread padre consumi troppo tempo di CPU, eseguendo controlli anche quando
 * nessuno si e' mosso, sincronizzeremo questo thread agli altri con un semaforo.
 *
 * X compilare:
 * gcc guardieladrit.c -o guardieladrit -lncurses -lpthread
 */

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

#include <pthread.h>
#include <semaphore.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	 23	/* Numero di righe */

/* Struttura per la comunicazione tra figli e padre */
struct pos{
	int x;		//posizione x
	int y;		//posizione y
};

/* Variabili globali di comunicazione tra i thread */
volatile struct pos pladro;		// Posizione corrente del ladro
volatile struct pos pguardia;		// Posizione corrente della guardia
volatile struct pos showguardia;	// Posizione corrente della guardia visibile al ladro
volatile int running;			// Flag di programma non terminato
volatile int nmov;			// Numero di movimenti della guardia (conta tempo)

/* Mutex e semaforo per la sincronizzazione tra i thread */
pthread_mutex_t	semCurses;	// Semaforo per l'accesso alle funzioni di ncurses
sem_t	semGioco;		// Semaforo per sincronizzare il campo con i 'giocatori'

/* Prototipi delle funzioni */
void *ladro (void *arg);
void *guardia (void *arg);
void campo (void);

int main()
{
pthread_t tLadro;		// Pid del figlio 'ladro'
pthread_t tGuardia;		// Pid del figlio 'guardia'
	
  initscr();			// Inizializza curses
  noecho();			// caratteristica della tastiera
  curs_set(0);			// elimina il cursore

  running = TRUE;		// Gioco non ancora finito

  pthread_mutex_init (&semCurses, NULL); // Mutex di ncurses
  sem_init (&semGioco, 0, -1);	// Semaforo del gioco, inizialmente chiuso

  /* Creo il thread del ladro */
  if (pthread_create (&tLadro, NULL, ladro, NULL))
  {
    endwin();		// Torniamo in modalita' normale
    printf ("Non riesco a creare il thread del ladro\n");
    exit;
  }

  /* Creo il thread della guardia */
  if (pthread_create (&tGuardia, NULL, guardia, NULL))
  {
    endwin();		// Torniamo in modalita' normale
    printf ("Non riesco a creare il thread della guardia\n");
    exit;
  }

  campo ();		// Svolgo il lavoro del padre (gestire il campo)

  /* Attendo la terminazione dei due thread */
  pthread_join (tLadro, NULL);		
  pthread_join (tGuardia, NULL);

  /* Elimino il mutex ed il semaforo */
  pthread_mutex_destroy (&semCurses);		
  sem_destroy (&semGioco);		

  endwin();		// Torno 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 80X24 caratteri e 'sposta' la guardia.
 * Il movimento tende comunque a spostare il ladro lontano dalla guardia.
 * La guardia comunica i suoi spostamenti al ladro, copiando periodicamente la sua
 * posizione nella variabile globale showguardia
 * Segala ogni spostamento al campo tramite il semaforo
 */
void *ladro (void *arg)
{
int deltax;		// Spostamento orizzontale
int deltay;		// Spostamento verticale
int fugax = 0,fugay = 0;// Spostamento del centro, per allontanarsi

  nmov = 0;		// Inizializza il contatore
  // Posizione iniziale del ladro: in alto a sinistra.
  pladro.x = 1;
  pladro.y = 1;

  // Posizione iniziale nota della guardia: il ladro sta' fermo.
  showguardia.x = 1;
  showguardia.y = 1;

  while(running)	// Ciclo fino a che 'campo' non dice basta
  {
    nmov ++;					// Conta i cicli
    pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
    mvaddch(pladro.y,pladro.x,' ');		// Cancello la vecchia guardia
    pthread_mutex_unlock (&semCurses);		// libero curses

    fugax =  pladro.x - showguardia.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 - showguardia.y;		// Individua la direzione della guardia
    // normalizza fugay (-1, 0, 1)
    if (fugay < 0)
    {
      fugay = -1; 
    }
    else if (fugay > 0)
    {
      fugay = 1;
    }

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

    pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
    mvaddch(pladro.y,pladro.x,'$');		// Disegno il ladro
    refresh();					// Aggiorno le modifiche (canc. e disegno)
    pthread_mutex_unlock (&semCurses);		// libero curses

    sem_post (&semGioco);			// avviso il campo che c'e' stato un movimento
			
    usleep(200000);				// Pausa per 'dare il ritmo'
  }

  return NULL;
}


/* Funzione 'guardia'.
 * Legge i tasti premuti dall'utente. Sposta la guardia nella direzione
 * indicata dalle frecce. Si ferma ai bordi del campo di 80X24 caratteri.
 * Segala ogni spostamento al campo tramite il semaforo
 * Ogni 10 spostamenti il movimento viene anche comunicato al ladro tramite la
 * variabile globale showguardia
 */
void *guardia (void *arg)
{
int i = 0;

  // Posizione iniziale : in basso a destra
  pguardia.x = MAXX - 1;
  pguardia.y = MAXY - 1;

  pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
  mvaddch(pguardia.y,pguardia.x,'#');		// Disegno la guardia inizialmente
  pthread_mutex_unlock (&semCurses);		// libero curses

  while(running)	// Ciclo fino a che 'campo' non dice basta
  {
  char c;
    c = getch();				// Legge il tasto

    pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
    mvaddch(pguardia.y,pguardia.x,' ');		// Cancello la vecchia guardia
    pthread_mutex_unlock (&semCurses);		// libero curses

    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
    }

    pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
    mvaddch(pguardia.y,pguardia.x,'#');		// Disegno la guardia
    refresh();					// Aggiorno le modifiche (canc. e disegno)
    pthread_mutex_unlock (&semCurses);		// libero curses

    sem_post (&semGioco);			// avviso il campo che c'e' stato un movimento
					
    i++;
    if(!(i%10))
    {
      showguardia = pguardia;			// Rendo visibile la posizione al ladro
    }				
  }
  return NULL;
}

/* Funzione di controllo (padre).
 * Attende 'eventi' sul semaforo.
 *
 * Visualizza un 'contasecondi', stampando il numero di spostamente del ladro diviso
 * per cinque (il ladro fa' 5 movimenti al secondo)
 *
 * Se la posizione della guardia corrisponde a quella del ladro, termina il gioco.
 */ 
void campo ()
{
  while(running)				// Ciclo finche' non viene azzerato il flag
  {
    sem_wait (&semGioco);			// Attendo che un figlio si muova

    if ((nmov % 5) == 0)			// Ogni 5 movimenti del ladro
    {
      pthread_mutex_lock (&semCurses);		// Attendo che curses sia libero
      mvprintw (MAXY,0,"T = %03d",nmov / 5);	// Scrivo il tempo nell'ultima riga
      curs_set(0);				// Rielimino il cursore
      refresh();				// Aggiorno le modifiche (canc. e disegno)
      pthread_mutex_unlock (&semCurses);	// libero curses
    }

    if(pguardia.x==pladro.x&&pguardia.y==pladro.y)
    {	// Il ladro ha raggiunto la guardia
      running = FALSE;
    }
  }
}



