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.
Bug subdolo, lo vedresti gestendo l’errore sulla write e stampando con perror:
deve essere
Buongiorno prof,
sapendo a priori la dimensione della stringa da leggere (e quindi del buffer che deve contenerla) c’è qualche differenza nel codice seguente?
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
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.