[pipe] Auto da corsa

Il file zip contiene gli eseguibili per Linux 64 bit, Linux 32 bit, e Mac.

Il gioco consiste nel guidare un’auto da corsa evitando gli ostacoli che via via si incontrano. La pista è delimitata da barre verticali ‘|’, l’auto è una ‘O’ e gli ostacoli sono ‘@’.

|               |
|@@@@@@@@@@@ @@@|
|               |
|               |
|               |
|               |
|       O       |

La coordinata del passaggio tra gli ostacoli viene inviata sulla pipe carPipeOut nel formato x#. Ad esempio 11# indica che il passaggio si trova sulla colonna 11. L’auto è inizialmente in posizione 7.

È possibile visualizzare il contenuto della pipe da shell con il comando cat (eseguirlo da un terminale diverso):

$ cat carPipeOut
11#

Per spostare l’auto si deve inviare il carattere ‘l’ (left) o ‘r’ (right) sulla pipe carPipeIn. Gli altri caratteri vengono ignorati.

Anche in questo caso si può inviare manualmente il comando nel modo seguente (in questo esempio spostiamo l’auto a destra di 4 posizioni)

$ echo "rrrr" > carPipeIn

per ridirezionare i comandi direttamente da tastiera è possibile utilizzare cat:

$ cat > carPipeIn

A questo punto digitando ‘l’ o ‘r’ e mandando a capo il comando viene inviato all’auto. Guidare l’auto a mano è complicato ovviamente, ma l’invio diretto dei dati sulla pipe aiuta a capire il funzionamento del gioco.

Per vincere si deve scrivere un programma in grado di superare 20 round!
… In bocca al lupo!

Soluzione

Ecco una possibile soluzione (commentata) dell’esercitazione:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define WR_PIPE "carPipeIn"
#define RD_PIPE "carPipeOut"
#define WIDTH 15
#define ROUNDS 20
#define LEFT 'l'
#define RIGHT 'r'

/*
L'obiettivo del programma è di fornire al programma di test la sequenza di mosse necessarie
affinché la macchina raggiunga la fine del percorso evitando tutti gli ostacoli e senza uscire
di strada utilizzando le pipe come canale di comunicazione.
Per fare ciò, viene iterato per 20 volte (pari al numero di round) il seguente schema:
- leggi dalla pipe "carPipeOut" la posizione del passaggio attraverso il quale far passare la
  macchina;
- calcola la direzione dello spostamento ('l' o 'r') e il numero di mosse da fare;
- crea la stringa corrispondente alle mosse da fare e scrivila in "carPipeIn".
*/

int main() {
	int fw, fr, carPos = WIDTH / 2, holePos, i, j, numMoves;
	char buffer[WIDTH], direction;

	/* Crea le pipe (se non esistono già) e aprile: 'carPipeIn' in sola scrittura
	e 'carPipeOut' in sola lettura. Termina il programma se si verifica un errore
	nell'apertura di una delle due. */
	mkfifo(WR_PIPE, 0666);
	mkfifo(RD_PIPE, 0666);
	fw = open(WR_PIPE, O_WRONLY);
	if (fw < 0) {
		perror("Impossibile aprire la pipe in scrittura.");
		exit(EXIT_FAILURE);
	}
	fr = open(RD_PIPE, O_RDONLY);
	if (fr < 0) {
		perror("Impossibile aprire la pipe in lettura.");
		exit(EXIT_FAILURE);
	}

	for (i = 0; i < ROUNDS; i++) {
		/* Leggi la posizione corrente dell'ostacolo dalla pipe e ottieni il
		valore numerico corrispondente. */
		j = 0;
		while (read(fr, buffer + j, 1) && buffer[j] != '#')
			j++;
		sscanf(buffer, "%d#", &holePos);
		/* Il numero di mosse da fare è dato dalla differenza, in valore assoluto,
		tra la posizione corrente e quella del passaggio libero. */
		numMoves = abs(carPos - holePos);
		/* Se occorre spostarsi, determina la direzione di spostamento (data dal segno
		della differenza tra la posizione corrente e quella del passaggio), crea la
		stringa contenente le mosse da fare e inviala nella pipe. Aggiorna anche la
		posizione della macchina. */
		if (numMoves != 0) {
			direction = (carPos - holePos < 0) ? RIGHT : LEFT;
			memset(buffer, direction, numMoves);
			write(fw, buffer, numMoves);
			carPos = holePos;
		}
	}
	
	/* Chiudi le pipe. */
	close(fw);
	close(fr);

	return EXIT_SUCCESS;
}

4 thoughts on “[pipe] Auto da corsa”

  1. Salve, nonostante il mio codice sia molto simile alla soluzione postata, non capisco perché non mi scrive la stringa contenente gli spostamenti da fare nella pipe. Grazie.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define PIPEIN "carPipeIn"
    #define PIPEOUT "carPipeOut"
    #define WIDTH 15
    #define ROUNDS 20
    #define LEFT 'l'
    #define RIGHT 'r'
    
    int main() {
      int mypos = WIDTH/2;
      int wr, rd,i=0,wins, pos,diff, flag=0;
      char str[WIDTH];
      char buffer[4];
      // se non esistono già creo le pipe
      mkfifo(PIPEIN, 0666);
      mkfifo(PIPEOUT, 0666);
      // apro la pipe in lettura
      if ((rd = open (PIPEOUT,O_RDONLY)) < 0 ) {
        perror("errore apertura pipe");
        exit(1);
      }
      // apro la pipe in scrittura
      if ((wr = open (PIPEIN,O_RDONLY)) < 0 ) {
        perror("errore apertura pipe");
        exit(1);
      }
    
      for(wins = 0; wins < ROUNDS; wins++){
        strcpy(str, "");
        // leggo e salvo il valore corrispondente al passaggio
        while(read(rd, buffer+i, 1) &&  buffer[i] != '#') i++;
        //buffer[i+1] = '\0';
        sscanf(buffer, "%d#", &pos);
        /* se la posizione del passaggio e maggiore di quella dell auto mi sposto
         a destra, altrimenti a sinistra. Calcolo il numero di spostamenti da fare
         e lo salvo in diff */
        if(mypos < pos){
          diff = pos - mypos;
          flag = 1;// se 1 allora RIGHT
        }else{
          diff = mypos - pos;
          flag = 0; // se 0 allora LEFT
        }
        //creo la stringa contente le mosse da fare
        if(!flag) memset(str, LEFT, diff);
        else memset(str, RIGHT,diff);
        //scrivo sulla pipe la stringa appena creata
        write(wr, str, diff);
        //aggiorno la posizione dell' auto con quella del passaggio
        mypos = pos;
      }
      //chiudo le pipe
      close(wr);
      close(rd);
      return 0;
    }
    
  2. Bug subdolo, lo vedresti gestendo l’errore sulla write e stampando con perror:

    if ((wr = open (PIPEIN,O_RDONLY)) < 0 ) {
    

    deve essere

    if ((wr = open (PIPEIN,O_WRONLY)) < 0 ) {
    
  3. Buongiorno prof,
    sapendo a priori la dimensione della stringa da leggere (e quindi del buffer che deve contenerla) c’è qualche differenza nel codice seguente?

    j = 0;
            while (read(fr, buffer + j, 1) && buffer[j] != '#')
                j++;
    // leggo direttamente la posizione nel formato x#
    read(fr, &buffer, 2);
    
  4. Funziona solo se la dimensione è fissa (non in questo caso perché i numeri possono essere di più cifre) e solo se la scrittura avviene con un’unica write (atomica, sotto PIPE_BUF).

    Se la dimensione non è fissa potresti leggere un pezzo dell’input successivo. Ad esempio se nella pipe c’è 1#2# e tu leggi 3 char leggi 1#2, e quindi perdi il 2 successivo.

    Se la write avviene un char alla volta, invece, potresti leggere 1 senza leggere # perché non è ancora stato scritto.

    Per questi motivi, è consigliabile leggere in un ciclo fino al raggiungimento di un separatore o di una lunghezza predefinita.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.