Newsgroup: it.comp.lang.c
Titolo: Realizzare gioco 2d in C

Ciao a tutti,sono nuovo di questo NG.
Sto studiando il linguaggio C  su un libro e sono arrivato
allo svolgimento di un esercizio ma non riesco a risolverlo e quindi ad
andare avanti.Ho provato a cercare sul sito del libro (C corso completo di
programmazione) ma non ci sono le soluzioni degli esercizi (che cazzata!!!).
L'esercizio consiste nel realizzare un labirinto con interfaccia grafica
(che è l'unica cosa che sono riuscito a fare tramite le printf),dovrà
permettere solo le mosse consentite (se ad esempio provo ad andare dove è
chiuso non lo deve permettere) e dovrà riconoscere quando un utente avrà
vinto,cioè sarà arrivato all fine.
Premetto che non sono molto avanti con lo studio (devo ancora fare le
funzioni e le cose più difficili),l'esercizio è nel capitolo dei
puntatori,ma io non so da dove iniziare!!!Ho capito bene i puntatori e le
cosa precedenti però non riesco proprio a capire come fare questo esercizio.
Cioè ad esempio come devo fare per far muovere la pedina all'interno del
labirinto (io uso un * come
pedina)?Ci vorrà sicuramente un ciclo ma non riesco proprio ad immaginare
come fare.Come faccio a capire quando mi muovo verso una strada chiusa e
quindi non posso andare avanti?Come faccio a capire quando ho vinto?
Se qualcuno ha qualche idea,anche solo spiegarmi come partire,che magari un
volta sbloccato riesco da solo.
Grazie mille,ciao

 Grazie mille,ciao
Prego mille :D
-- 
Quando noi ignoranti eravamo di piů, tutto era piů bello (A. Celentano).
  ___   _   ___ _  _______   _______ ___
|   \ /_\ | _ \ |/ / _ ) \ / /_   _| __|
| |) / _ \|   / ' <| _ \\ V /  | | | _|
|___/_/ \_\_|_\_|\_\___/ |_|   |_| |___|
GIGAMAIL by X-Privat: 
http://www.x-privat.org/gigamail.php

 pedina)?
Esatto questa e` il primo interrogativo che ti devi porre!
Tu cosa hai pensato di fare? Hai gia` qualche idea? Hai gia` fatto delle
prove + o - riuscite?
Secondo me, devi risolvere questa problematica dimenticandoti un attimo di
tutti gli altri problemi...
E gia` qui temo che ci imbattiamo subito su un non piccolo problema;
infatti, e` molto probabile che le soluzioni migliori risultino OT, perche`
non standard C...
Infatti mi risulta che le funzioni per acquisire da tastiera siano scanf() e
getchar() (e simili) e queste hanno il grande problema che "intrappolano" il
processo fino alla premuta dell'INVIO, cio` vuol dire che supponiamo che
associ il tasto W per salire finche` non hai inviato non accade niente...
Supponiamo di risolverlo andando poco fuori standard, poi c'e` il non
piccolo problema di visualizzare il tuo * nella nuova posizione assunta;
infatti, si ripropone la stessa problematica, seguendo gli standard dovresti
in qualche modo cancellare lo schermo e poi ridisegnare tutto ex novo e
provare a spostarsi all'inizio e sovrascrivere...
oppure fuori standard cancellare la posizione attuale e riposizionare nella
nuova...
Questi sono le grandi problematiche, poi per quanto riguarda i muri o gli
ostacoli io metterei tutto dentro un array di char [80][25] (dove ogni cella
corrisponde ad un carattere dello schermo) e lo riempirei con valori che ti
fanno capire se sono accessibili, se sei tu oppure se e` un ostacolo... e
poi rifresci tutto con delle printf in un doppio ciclo for...

getchar()
Lui non deve giocare di persona, ma deve realizzare un programma che
trovi da solo l'uscita del labirinto e lo visualizzi graficamente. ;-)
Nessun bisogno di funzioni non standard.

labirinto?
Non so come tu abbia realizzato la mappa, ma suppongo che si tratti di
una matrice i cui indici sono usati come coordinate x e y.
Puoi usare un metodo basato sulla forza bruta.
Il tuo topo da labirinto dovrebbe esplorare tutte le caselle seguendo
un percorso, segnando ogni volta la casella in cui passa per non
ripassare mai dallo stesso punto, evitando il girotondo. Quando
incontra un vicolo cieco (tutte le strade già esplorate, o non
accessibili perché sono muri), ritorna indietro cancellando ogni
casella fino alla prima disponibile in cui c'è almeno una strada non
ancora percorsa, ricominciando a esplorare da quel punto in poi.
All'incirca il tuo topo da labirinto dovrebbe comportarsi così:
-1- ti posizioni sulla prima casella;
-2- 'segni' la casella in cui ti trovi per poter verificare che ci sei
già passato;
-3- esplori una per una tutte le caselle circostanti valide (non muri,
non segnate, non fuori dai limiti del labirinto);
	-3.1- (per ciascuna casella) è l'uscita? Se si e
	ti basta la prima soluzione (può essercene più di una)
	l'esecuzione finisce qui, altrimenti prosegue;
	-3.2- 'segni' la casella in cui ti trovi;
	-3.3- esplori una per una tutte le caselle circostanti valide;
		-3.3.1- (per ciascuna casella) è l'uscita?
		Se si e ti basta la prima soluzione (può essercene più
		di una) l'esecuzione finisce qui, altrimenti prosegue;
		-3.3.2- 'segni' la casella in cui ti trovi;
		-3.3.3- esplori una per una tutte le caselle
		circostanti valide;
 
		-3.3.3.1- (... così via annidando l'algoritmo fino a
		un vicolo cieco o fino all'uscita)
		-3.3.4 se sei qui, nessuna casella porta a una
		soluzione, quindi 'cancella' il tuo passaggio (quello
		segnato nel punto 3.3.2) e riparti dal punto 3.3.1
		posizionandoti sulla casella successiva;
	-3.4- se sei qui, nessuna casella porta a una soluzione,
	quindi 'cancella' il tuo passaggio (quello segnato nel punto
	3.2) e riparti dal punto 3.1 posizionandoti sulla casella
	successiva;
-5- se sei qui, nessuna casella circostante è valida, quindi non ci
sono soluzioni;
E così via... Volendo puoi scrivere questi sottocicli n volte dove n è
il numero necessario a coprire tutto il labirinto, ma sarebbe assurdo,
perché è abbastanza evidente che l'algoritmo è ricorsivo.
Si può riscrivere in modo più compatto in questo modo:
-1- ti posizioni sulla prima casella;
-2- è l'uscita? Se si, hai già vinto! :-)
in caso contrario prosegui;
-3- (per ogni casella circostante, purché sia *non segnata*, *non
muro*, e che non fuoriesca dal labirinto) ti sposti sulla casella e la
'segni' per ricordarti che ci sei già passato;
-4- è l'uscita? Se si e se ti basta la prima soluzione (può essercene
più di una) finisci qui, altrimenti prosegui (eventualmente se è una
soluzione, questo è il punto per stamparla);
-5- ripeti i passi da 3 a 5 (in pratica riparti dalla nuova casella
come se fosse la prima);
-6- se sei arrivato qui, significa che hai incontrato un vicolo cieco
(tutte le strade già percorse o muri), quindi:
	-6.1-  'cancella' il tuo passaggio (quello segnato
	nel punto 3)
	-6.2- ritorna alla casella precedente e riparti dal punto 3;
-7- se nessuna casella circostante è valida non ci sono soluzioni;
Nota intanto che il passo 2 e il passo 4 sono la stessa verifica, e
puoi fare in modo di metterla una volta sola nel codice. Per non
complicare troppo la lettura dell'algoritmo l'ho scritto due volte, ma
è ridondante. Funziona lo stesso, quindi non è un errore... diciamo
che esteticamente è più bello incorporarla nel ciclo, invece che
duplicarla.
Se conosci già la ricorsione il discorso si fa più semplice, ma viste
le premesse che hai descritto, ne dubito.
Per scrivere un codice senza ricorsione potresti realizzare uno stack
dove 'salvare' la posizione della casella precedente (push) e poi
recuperarla cancellandola dallo stack (pop).
In questo caso dovresti fare la push prima del punto 3 (cioè salvi la
posizione prima di cambiare casella) e la pop prima del punto 6.2
(cioè, dopo aver cancellato il tuo passaggio, ritorni alla posizione
precedente).
Per esplorare le caselle circostanti una sola volta, puoi usare due
matrici da scorrere, per esempio
char x_offset[] = {0, 1, 1, 1, 0, -1, -1, -1};
char y_offset[] = {1, 1, 0, -1, -1, -1, 0, 1};
Fai una griglia su carta di nove caselle abbastanza grandi da scrivere
una coppia di numeri su ciascuno, e considera la casella centrale come
quella di partenza.
Poi scrivi in ogni casella il numero che devi sommare alle coordinate
x e y della casella centrale per spostarti sulle successive.
La casella 'in alto' sarà 0, +1 (x è uguale, a y devi sommare 1), la
casella in alto a destra sarà +1,+1, quella a destra sarà +1,0, e cosi
via. Fatto questo, realizzi due matrici che hanno appunto i numeri da
sommare per spostarti in queste caselle limitrofe. Ecco come sono nate
le due caselle sopra specificate. A questo punto, con un solo indice,
puoi spostarti in senso orario, a partire dalla casella 'in alto'
semplicemente sommando l'i-esimo elemento della matrice sopra alla
coordinata omologa della casella.
A questo punto, se la matrice si chiama casella[x,y]
puoi scorrerla con
for(i = 0; i < 8; i++)
	se x + x_offset[i] non esce dai margini primo indice della
	matrice e y + y_offset[i] non esce dai margini del secondo
	indice, e se la casella con quelle coordinate non è un muro,
	allora:
		esplora la casella di indici
		[x + x_offset[i], y + y_offset[i] ]
etc etc...
Se utilizzi la ricorsione ogni ciclo for ha vita individuale e non
devi pensarci, se invece non la usi e devi usare uno stack 'fai da
te', allora nello stack devi anche salvare l'indice del ciclo for,
quindi lo stack deve ospitare tre valori: coordinata x, coordinata y,
indice del ciclo for.
Puoi fare tre stack distinti, se non sai maneggiare ancora bene i tipi
di dati, oppure uno stack che incorpori tutti e tre. Funziona bene in
entrambi i casi. :-)
Un altro aiuto, se già non lo sai... nulla ti vieta di cambiare a
piacimento l'indice del ciclo for.
Esempio:
for(i = 0; i < 1000; i++)
  i = 999;
Questo ciclo for non si esegue 1000 volte, ma una sola, perché i viene
subito modificato.
Analogamente, nel caso dell'esplorazione della mappa, puoi fare
qualcosa del genere:
for (i = 0; i < 8; i++) {
	verifica validità casella, e se è valida:
		push coordinata x; /* salva le coordinate e l'indice
		push coordinata y;
		push i;
		x += x_offset[i];
		y += y_offset[i];
		i = 0;
	se non è valida:
		pop coordinata x; /* ritorna alla casella precedente
		pop coordinata y;
		pop i;
	}
Se vuoi fare uno stack semplice, di dimensione fissa, quanto deve
essere grande? Non più grande del numero di caselle della matrice,
quindi semplicemente il prodotto delle sue dimensioni (m * n, per
esempio).
Per segnare le caselle già percorse, puoi usare una matrice di
booleani, o qualunque altro artificio di tua scelta, ma l'importante è
che tu lo faccia. ;-)
Hai presente la leggenda di Teseo e Arianna ? :-)
Credo di averti detto anche troppo... ti ho praticamente scritto la
soluzione del problema e se vado oltre in pratica non dovrai ragionare
neanche un pò... ora gioca un pò da solo! :-)
Mancano alcune cose, per esempio la condizione per uscire dal ciclo
for in caso di vittoria... Roba tua! :-)
Spero di averti dato una mano e di non aver commesso errori grossolani
(non mi pare... ma anche se fosse, trovare qualche vaccata in quel che
ho scritto può essere un buon esercizio :-D).
In bocca al lupo! ;-)

	}
C'è un'incoerenza... l'indentazione è corretta, ma se uso le graffe
sopra, è opportuno usarle poco sotto. :-)
for (i = 0; i < 8; i++) {
	verifica validità casella, e se è valida:
	{
		push coordinata x; /* salva le coordinate e l'indice
		push coordinata y;
		push i;
		x += x_offset[i];
		y += y_offset[i];
		i = 0;
	}
	se non è valida:
	{
		pop coordinata x; /* ritorna alla casella precedente
		pop coordinata y;
		pop i;
	}
}
Altrimenti non è molto chiaro. :-)