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;
}
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; }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 ) {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);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 leggi1#2, e quindi perdi il 2 successivo.Se la write avviene un char alla volta, invece, potresti leggere
1senza 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.