001: #include<stdio.h>
002: #include<stdlib.h>
003: 
004: // definisco due costanti con le dimensioni della scacchiera
005: #define NCOLS (7)
006: #define NROWS (6)
007: 
008: // non era richiesto dal testo ma tengo traccia anche di come si e' vinto
009: #define VINTO_NO   (0)
010: #define VINTO_RIGA (1)
011: #define VINTO_COL  (2)
012: #define VINTO_DIAG (3)
013: 
014: 
015: // funzione richiesta dal codice
016: void simula4(char *filename);
017: 
018: // funzioni che definisco io e rendono il codice piu' modulare e leggibile
019: 
020: // funzione che stampa lo stato della scacchiera
021: void stampa_scacchiera(char s[NROWS][NCOLS]);
022: // funzione che inserisce la pedina di uno dei due giocatori in una specifica colonna
023: int inserisci_pedina(char s[NROWS][NCOLS], int colonna, char simbolo_giocatore);
024: // controlla se uno dei due giocatori ha vinto, internamente poi usa funzioni specifiche per le colonne, righe e diagonali
025: int vinto(char s[NROWS][NCOLS], char simbolo_giocatore, int riga, int colonna);
026: 
027: int main(int argc, char **argv){
028: 
029:   char nome[1000];
030:   printf("Inserisci il file che vuoi aprire: ");
031:   scanf("%s", nome);
032: 
033:   simula4(nome);
034: 
035:   return 0;
036: }
037: 
038: void simula4(char *filename)
039: {
040:   // definisco la scacchiera e la inizializzo come vuota
041:   char scacchiera[NROWS][NCOLS]; // uso un array di char, spazio=casella vuota altrimenti simboli dei giocatori (X e O)
042:   for(int r = 0; r < NROWS; ++r)
043:     for(int c = 0; c < NCOLS; ++c)
044:       scacchiera[r][c] = ' '; // inizialmente vuota
045: 
046:   FILE *fp = fopen(filename, "r");
047:   if(!fp)
048:   {
049:     perror("");
050:     exit(EXIT_FAILURE);
051:   }
052: 
053:   // indico i giocatori con X e O. X gioca per primo. Uso la seguente variabile per tener traccia di chi sta giocando
054:   char simbolo_giocatore = 'X';
055: 
056:   int colonna;
057: 
058:   // leggo il file mossa per mossa fino al termine del file o fino alla vittoria di qualcuno
059:   while(fscanf(fp, "%d", &colonna) == 1)
060:   {
061:     // inserisco la pedina:
062:     int riga = inserisci_pedina(scacchiera, colonna, simbolo_giocatore); // la funzione restituisce anche la riga in cui si e' "fermata " la pedina
063: 
064:     // stampo la scacchiera aggiornata:
065:     stampa_scacchiera(scacchiera);
066: 
067:     // controllo vittoria e nel caso termino:
068:     // passo anche indicazione del giocatore che ha messo la pedina perche', nel caso, solo lui puo' aver vinto, le coordinate
069:     // dell'ultima pedina semplificano il controllo
070:     int come;
071:     if((come = vinto(scacchiera, simbolo_giocatore, riga, colonna)) != VINTO_NO) 
072:     {
073:       printf("Il giocatore [%c] ha vinto la partita allineando le pedine ", simbolo_giocatore);
074:       switch(come) // non richiesto dal testo, stampo anche come si e' vinto oltre a chi
075:       {
076:   case VINTO_RIGA:
077:     printf("nella riga %d\n", riga);
078:     break;
079:   case VINTO_COL:
080:     printf("nella colonna %d\n", colonna);
081:     break;
082:   case VINTO_DIAG:
083:     printf("diagonale\n");
084:     break;
085:       }
086:       return; // esco
087:     }
088: 
089:     // commuto il giocatore per il turno successivo
090:     if(simbolo_giocatore == 'X')
091:       simbolo_giocatore = 'O';
092:     else
093:       simbolo_giocatore = 'X';
094:     // un programmatore un po' piu' scantato avrebbe usato: simbolo_giocatore = (simbolo_giocatore == 'X'?'O':'X');
095:   }
096: 
097:   // se arrivo qui, il file e' finito ma nessuno ha vinto
098:   printf("Incredibile, partita patta!\n");
099: 
100: }
101: 
102: // inserisce la pedina nella colonna di indice colonna. giocatore contiene il simbolo della pedina
103: // ovvero 'X' o 'O'
104: int inserisci_pedina(char s[NROWS][NCOLS], int colonna, char simbolo_giocatore)
105: {
106:   // a partire dall'alto cerco la prima posizione libera in quella colonna e vi scrivo il simbolo
107:   // personalmente considero le righe numerate dal basso quindi la riga di indice 0 e' la prima in basso
108:   // quella di indice 1 la seconda a partire dal basso ecc. 
109:   // era del tutto possibile la convenzione opposta. Basta che inserimento e stampa siano coerenti
110: 
111:   for(int r = 0; r < NROWS; ++r)
112:     if(s[r][colonna] == ' ')
113:     {
114:       printf("\nInserisco pedina [%c] nella colonna %d, questa finisce alla riga %d\n", simbolo_giocatore, colonna, r);
115:       s[r][colonna] = simbolo_giocatore;
116:       return r;
117:     }
118:   
119:   // i file sono stati generati casualmente, questo non era richiesto ma lo aggiungo per sicurezza
120:   printf("Errore nel file, ho tentato di inserire la pedina nella colonna %d ma era gia' piena\n", colonna);
121:   exit(1);
122: }
123: 
124: void stampa_scacchiera(char s[NROWS][NCOLS])
125: {
126:   // stampo riga per riga tenendo conto pero' che ho scelto come convenzione che la riga 0 sia
127:   // quella in basso ecc.
128:   printf("   0 1 2 3 4 5 6 \n"); 
129:   printf("  +-+-+-+-+-+-+-+\n"); 
130:   for(int r = NROWS - 1; r >= 0; --r)
131:   { 
132:     printf("%d ", r);
133:     for(int c = 0; c < NCOLS; ++c)
134:     {
135:       printf("|%c", s[r][c]);
136:     }
137:     printf("|\n  +-+-+-+-+-+-+-+\n"); 
138:   }
139:   printf("\n");
140: }
141: 
142: // funzione che controlla se nella riga passata ci sono 4 simboli consecutivi
143: // le controllo tutte sebbene si potesse ottimizzare e limitarsi all'ultima riga
144: // modificata
145: int controlla_riga(char s[NROWS][NCOLS], char simbolo_giocatore, int riga)
146: {
147:   // questa variabile la incremento ogni volta che incontro il simbolo passato
148:   // e la rimetto a 0 ogni volta che incontro un simbolo differente o una cella vuota
149:   // se raggiungo 4 ho inanellato almeno 4 simbolo_giocatore di fila e posso terminare la ricerca
150:   int sequenza = 0;
151:   for(int c=0; c<NCOLS; ++c)
152:   {
153:     if(s[riga][c] != simbolo_giocatore)
154:     {
155:       sequenza = 0;  // sequenza interrotta (o mai iniziata) riporto il mio contatore a 0
156:     }
157:     else
158:     {
159:       ++sequenza;  // incremento il numero di simbolo_giocatore di seguito
160:       if(sequenza == 4) // Bingo! Ho trovato almeno 4 simboli di seguito nella stessa riga, mi posso fermare qui
161:   return 1;
162:     }
163:   }
164: 
165:   return 0; // niente, nessua riga aveva almeno 4 pedine in fila
166: }
167: 
168: int controlla_colonna(char s[NROWS][NCOLS], char simbolo_giocatore, int colonna)
169: {
170:   int sequenza = 0;
171:   for(int r=0; r<NROWS; ++r)
172:   {
173:     if(s[r][colonna] != simbolo_giocatore)
174:     {
175:       sequenza = 0;
176:     }
177:     else
178:     {
179:       ++sequenza;
180:       if(sequenza == 4)
181:   return 1;
182:     }
183:   }
184: 
185:   return 0;
186: }
187: 
188: int controlla_diagonale_dx(char s[NROWS][NCOLS], char simbolo_giocatore, int riga, int colonna)
189: {
190:   // leggermente piu' complicato
191:   // il meccanismo e' quello gia' visto nelle due sopra ma devo spostarmi sia di riga che di colonna
192:   // per muovermi in diagonale
193:   // quindi uso un singolo ciclo in cui ogni volta aggiorno entrambe le coordinate
194: 
195:   int sequenza = 0;
196:   while(riga < NROWS && colonna < NCOLS)
197:   {
198:     if(s[riga][colonna] != simbolo_giocatore)
199:     {
200:       sequenza = 0;
201:     }
202:     else
203:     {
204:       ++sequenza;
205:       if(sequenza == 4)
206:   return 1;
207:     }  
208: 
209:     ++riga;
210:     ++colonna;
211:   }
212: 
213:   return 0;
214: }
215: 
216: // come la precedente, cambia solo come mi sposto (questo codice si potrebbe effettivamente fattorizzare)
217: int controlla_diagonale_sx(char s[NROWS][NCOLS], char simbolo_giocatore, int riga, int colonna)
218: {
219:   int sequenza = 0;
220:   while(riga < NROWS && colonna > 0)
221:   {
222:     if(s[riga][colonna] != simbolo_giocatore)
223:     {
224:       sequenza = 0;
225:     }
226:     else
227:     {
228:       ++sequenza;
229:       if(sequenza == 4)
230:   return 1;
231:     }  
232: 
233:     ++riga;
234:     --colonna;
235:   }
236: 
237:   return 0;
238: }
239: 
240: // questa funzione controlla se il giocatore con il simbolo passato ha vinto la partita
241: // restituendo vero o falso in base a questo
242: // mi avvalgo di funzioni che verificano righe, colonne e diagonali
243: int vinto(char s[NROWS][NCOLS], char simbolo_giocatore, int riga, int colonna)
244: {
245: 
246:   // controlliamo la riga modificata
247:   // la funziona chiamata restituisce 1 se nella colonna con indice r ci sono almeno 4 pedine in fila di tipo "simbolo_giocatore"
248:   if(controlla_riga(s, simbolo_giocatore, riga))
249:     return VINTO_RIGA;
250: 
251:   // controlliamo la colonna, stesso meccanismo di prima
252:   if(controlla_colonna(s, simbolo_giocatore, colonna))
253:     return VINTO_COL;
254: 
255:   // PER SEMPLICITA' USO APPROCCIO A FORZA BRUTA
256:   // controllo tutte le diagonali che hanno almeno 4 celle
257:   // quindi mi limito alle diagonali che hanno almeno 4 celle
258: 
259:   // diagonali che, partendo dal basso, si spostano verso destra
260:   // quelle che partono dalla prima riga in basso
261:   for(int c=0; c<NCOLS-3; ++c) 
262:     if(controlla_diagonale_dx(s, simbolo_giocatore, 0, c))
263:       return VINTO_DIAG;
264:   // quelle che partono dalla prima colonna a sinistra
265:   for(int r=1; r<NROWS-3; ++r) 
266:     if(controlla_diagonale_dx(s, simbolo_giocatore, r, 0))
267:       return VINTO_DIAG;
268: 
269:   // DIAGONALI CHE PARTENDO DAL BASSO SI SPOSTANO VERSO SINISTRA
270:   // quelle che partono dalla prima riga in basso
271:   for(int c=3; c<NCOLS; ++c) 
272:     if(controlla_diagonale_sx(s, simbolo_giocatore, 0, c))
273:       return VINTO_DIAG;
274:   // quelle che partono dalla prima colonna a destra
275:   for(int r=0; r<NROWS-3; ++r) 
276:     if(controlla_diagonale_dx(s, simbolo_giocatore, r, NCOLS - 1))
277:       return VINTO_DIAG;
278: 
279: 
280:   return VINTO_NO; // e niente, nessuno ha ancora vinto
281: }
282: