Snake in C++ [RISOLTO]

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
danlugli

Re: Snake in C++

Messaggio da danlugli »

Sono sequenze di più caratteri; se sostituisci

Codice: Seleziona tutto

cout << "un tasto non stampabile" << endl;
con:

Codice: Seleziona tutto

printf ("0x%x\n", int(c));
dovresti vederle per intero (immagino che il primo sia un escape).
Dopodiché invece di farci uno switch ci fai un minuscolo automa a stati finiti.
Saluti
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++

Messaggio da m@ »

minuscolo automa a stati finiti? ehm...la mia conoscenza è veramente infima... :(
M@
danlugli

Re: Snake in C++

Messaggio da danlugli »

Eccone uno:

Codice: Seleziona tutto


#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#include <termios.h>
#include <iostream>
#include <stdexcept>
#include <sstream>

using namespace std;

// Per pulizia ho raccolto il codice relativo a termios in una classe
class termiosModifier {
  termios before, after;
public:
  termiosModifier () {
    tcgetattr (STDIN_FILENO, &before);
    after = before;
    after.c_lflag &= (~ICANON);
    after.c_lflag &= (~ECHO);
    tcsetattr (STDIN_FILENO, TCSANOW, &after);
  }
  ~termiosModifier () {
    tcsetattr (STDIN_FILENO, TCSANOW, &before);
  }
};

// per pulizia ho raccolto il codice relativo alla select in una classe
class selectWrapper {
  fd_set rfds0;
  int maxfd;
  struct timeval tv;
  public:
  selectWrapper () {
    FD_ZERO (&rfds0);
    FD_SET (STDIN_FILENO, &rfds0);
    maxfd = STDIN_FILENO;
  }
  ~selectWrapper () {
  }
  int waitForKey (char & c) throw (runtime_error) {
    // con 1 secondo il serpente sara' lento
    // prova a mettere tv.tv_sec = 0; tv.tv_usec = 100000; (1/10 di secondo)
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    fd_set rfds = rfds0;
    int nready = select (maxfd + 1, &rfds, 0, 0, &tv);
    if (nready < 0) {
      ostringstream message;
      message << "errno = " << errno << ends;
      throw runtime_error (message.str ());
    }
    if (nready > 0) {
      if (FD_ISSET (STDIN_FILENO, &rfds)) {
       read (STDIN_FILENO, &c, 1);
      }
      else {
       throw runtime_error ("dovrebbe esserci qualcosa ma non c'e'"); 
      }
    }
    return nready;
  }
};

// Questo e' l'automa a stati finiti
class eventGetter {

  selectWrapper sw;
  
  public:
  
  typedef enum {
    TIMEOUT,
    LEFT_ARROW,
    RIGHT_ARROW,
    DOWN_ARROW,
    UP_ARROW,
    PRINTABLE,
    INCOMPLETE
  } eventType;
  
  private:
  
  // Gli stati dell'automa sono queste tre funzioni
  eventType awaitingAnyChar (char & c) throw (runtime_error) {
    // stato base, accetto qualsiasi cosa
    for (;;) {
      if (sw.waitForKey (c) == 0) return TIMEOUT;
      if (c == '\e') {
        currentState = &eventGetter::gotEsc;
        return INCOMPLETE;
      }
      if (isprint (c)) return PRINTABLE;
      // carattere non stampabile diverso da ESC: lo filtro
    }
  }
  eventType gotEsc (char & c) throw (runtime_error) {
    // ho gia' visto ESC, aspetto solo [ (altre possibilita' da aggiungere)
    if (sw.waitForKey (c) == 0) {
      throw runtime_error ("Timeout a meta' sequenza: com'e' possibile?"); 
    }
    if (c == '[') {
      currentState = &eventGetter::gotEscOpensquare;
      return INCOMPLETE;
    }
    // Qualsiasi altro carattere non lo so ancora gestire
    throw runtime_error ("Questa sequenza mi e' ancora sconosciuta.");
  }
  eventType gotEscOpensquare (char & c) throw (runtime_error) {
    // ho visto ESC[ e adesso mi aspetto A, B, C oppure D
    if (sw.waitForKey (c) == 0) {
      throw runtime_error ("Timeout a meta' sequenza: com'e' possibile?"); 
    }
    switch (c) {
      case 'A':
        currentState = &eventGetter::awaitingAnyChar;
        return UP_ARROW;
      case 'B':
        currentState = &eventGetter::awaitingAnyChar;
        return DOWN_ARROW;
      case 'C':
        currentState = &eventGetter::awaitingAnyChar;
        return RIGHT_ARROW;
      case 'D':
        currentState = &eventGetter::awaitingAnyChar;
        return LEFT_ARROW;
      default:
        throw runtime_error ("Questa sequenza mi e' ancora sconosciuta.");
    }
  }
  
  // Questo e' il puntatore allo stato corrente
  eventType (eventGetter::*currentState) (char & c) throw (runtime_error);
  
  public:
  
  eventGetter () {
    currentState = &eventGetter::awaitingAnyChar;
  }
  ~eventGetter () {
  }
  eventType getNextEvent (char & c) throw (runtime_error) {
    for (;;) {
      eventType event = (this->*currentState) (c);
      if (event != INCOMPLETE) return event;
    }
  }
};


int main () {
  try {
    termiosModifier tm;
    eventGetter eg;
    for (;;) {
      char c;
      eventGetter::eventType event = eg.getNextEvent (c);
      switch (event) {
      case eventGetter::TIMEOUT:
        cout << "TIMEOUT: ne approfitto per ridisegnare il serpente" << endl;
        break;
      case eventGetter::LEFT_ARROW:
        cout << "LEFT_ARROW: il serpente va a sinistra" << endl;
        break;
      case eventGetter::RIGHT_ARROW:
        cout << "RIGHT_ARROW: il serpente va a destra" << endl;
        break;
      case eventGetter::UP_ARROW:
        cout << "UP_ARROW: il serpente va su" << endl;
        break;
      case eventGetter::DOWN_ARROW:
        cout << "DOWN_ARROW: il serpente va giu'" << endl;
        break;
      case eventGetter::PRINTABLE:
        cout << "carattere stampabile: " << c << endl;
        if (c == 'z') {
          cout << "ciao" << endl;
          return 0;
        }
        break;
      }
    }
  }
  catch (runtime_error & err) {
    cerr << err.what () << endl; 
  }
  return 0;
}
saluti.
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++

Messaggio da m@ »

sie...qua sta diventando molto piu problematico del previsto...aiutoooo :)
e tutto per una getch() del cassu!
M@
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++

Messaggio da m@ »

danlugli...scusa se ti continuo a tartassare...ma rimani la mia unica salvezza...

io una versione rozza di snake..l'avrei anche ultimata...solo che rimane un piccolo problema...

c'è un modo di far si che l'input da tastiera non sia continuo? mi spiego...

la parte di programma ceh mi hai dato si attiva sia ne caso in cui uno prema tasti...sia che scada il timeout...cosa che effettivametne deve succdere in un applicazione snake-like...

il problema è che (cme potrai testare tu stesso provando il mio giochino se ti va...) è che se tengo premuto fisso in una direzione il serpente accelera la propria velocità proprio perche continua  aricevere altri input invece di attendere la fne del count down...

secondo te c'è modo di risolvere questo problema?

chiaramente se qualcuno è in grado di rispondere lo faccia :P

sotto allego il programmetto ..basta si compili il main.cc

grazie ancora per l'attenzione

M@
Allegati
SNAKE.tar
(50 KiB) Scaricato 71 volte
danlugli

Re: Snake in C++

Messaggio da danlugli »

Vedo che nel main chiami la stessa funzione snake::make_move sia in caso di timeout che in caso di tasto premuto. Secondo me per evitare l'effetto di accelerazione dovresti chiamare make_move solo in caso di timeout, e in caso di tasto premuto dovresti solo cambiare il valore di move. Cioé in pratica commentare quattro righe:

Codice: Seleziona tutto

...    	
else if (FD_ISSET (STDIN_FILENO, &rfds)) {
    read (STDIN_FILENO, &move, 1);
    if (isprint (move)){
	if (move == 'z') {
            tcsetattr (STDIN_FILENO, TCSANOW, &before);
            break;
      	}
        //if (!beboop.make_move (move)){
		//tcsetattr (STDIN_FILENO, TCSANOW, &before);
        	//break;
      //}
}
In questo modo però ottieni l'effetto opposto, perché se tieni un tasto premuto il serpente si ferma, in quanto ci sono solo eventi di tastiera e mai un timeout (potrebbe essere una gradita pausa di riflessione per il giocatore).

Se neanche questo ti piace, bisogna riuscire a fare in modo di aggiornare il serpente sempre e comunque alla cadenza scelta (1/10 s nel nostro caso) indipendentemente dal fatto che ci siano eventi di timeout o di tastiera.

Le soluzioni sono due. Te ne dico solo una, quella più robusta e portabile ad altri unix (quella che non ti dico sfrutta una peculiarità della select sotto linux e potrebbe non funzionare ad es. sotto osx).

Dunque,  tanto per cominciare è più pulito dichiarare il campionamento (intervallo di refresh) come una costante invece che avere dei valori numerici scolpiti in giro per il codice:

Codice: Seleziona tutto

const int sampling = 100000; // microsecondi
Lo userai per inizializzare il timeout della select:

Codice: Seleziona tutto

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = sampling;
Prima del loop degli eventi dichiari:

Codice: Seleziona tutto

int elapsed = 0; // tempo trascorso dall'ultimo aggiornamento del serpente in microsecondi
Dentro al loop, subito prima della select:

Codice: Seleziona tutto

struct timeval before_select, after_select;
gettimeofday (&before_select, 0);
subito dopo la select:

Codice: Seleziona tutto

gettimeofday (&after_select, 0);
// Per generalita' converto tutto in us, anche se tv_sec dovrebbe sempre essere 0
elapsed += (after_select.tv_sec * 1000000 + after_select.tv_usec);
elapsed -= (before_select.tv_sec * 1000000 + before_select.tv_usec);
if (elapsed >= sampling) {
  beboop.make_move (move);
  elapsed -= sampling;
}
Non dovrebbero esserci problemi di overflow perche' un intero con segno a 3d bit tiene fino a 2G.

Ogni volta che la select scatta, aggiungo ad elapsed il tempo che sono stato fuori. Se questo è maggiore o uguale all'intervallo di rinfresco che mi sono prefisso, aggiorno il serpente. Non è precisissimo perché non tiene conto del tempo trascorso dentro al programma, ma non credo che faccia grossa differenza.

Nota che ho scritto elapsed -= sampling invece che elapsed=0. Questo regolarizza un po', perché se una volta arrivo in ritardo, recupero la volta dopo.

A questo punto il serpente lo devi aggiornare solo lì, e quindi devi commentare anche l'aggiornamento al timeout oltre a quello per evento tastiera che abbiamo già commentato prima:

Codice: Seleziona tutto

if (nready == 0) {			
	//if (!beboop.make_move (move)){
		//tcsetattr (STDIN_FILENO, TCSANOW, &before);
        	//break;
	//}
}
Buon divertimento (beato te)
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++

Messaggio da m@ »

caspita.ehehhehe....mo me lo rileggo un paio di volte...poi ti dico inihhihii...grazie cmq ;)
M@
Avatar utente
TonT
Prode Principiante
Messaggi: 156
Iscrizione: venerdì 1 settembre 2006, 19:39
Località: là sui monti con annette

Re: Snake in C++

Messaggio da TonT »

io lascerei l' accelerazione  ;D

cmq per usare getch() ho installato il pacchetto libncurses5-dev
e poi ho compilato con l' opzione -lncurses

però ora ho un altro problema..compila..parte il programma..e non si ferma a leggere il carattere..vedi se a te fa lo stesso..io ho provato con questo stupido programmino..

Codice: Seleziona tutto

#include <stdio.h>
#include <ncurses.h>

int main() {
   int c;
   c = getch();
   printf("\nValore c: %d \n",c);
}
stampa -1..
Ultima modifica di TonT il giovedì 12 ottobre 2006, 21:03, modificato 1 volta in totale.
...Ma io, senza legge, rubai in nome mio,
quegli altri, nel nome di dio...
danlugli

Re: Snake in C++

Messaggio da danlugli »

Prova così:

Codice: Seleziona tutto

#include <stdio.h>
#include <ncurses.h>
int main() {
   int c;
   initscr ();
   cbreak ();
   noecho ();
   c = getch();
   printw("\nValore c: %d \n",c);
   printw ("premi un tasto per uscire da ncurses\n");
   getch ();
   endwin ();
}
initscr() chiama, tra le altre cose, tcgetattr() per salvare lo stato corrente del terminale.
cbreak() chiama tcsetattr() con il bit ICANON spento.
noecho() chiama tcsetattr() con il bit ECHO spento.
endwin() chiama, tra le altre cose, tcsetattr() per ripristinare le cose come stavano prima.

man ncurses
man initscr
...
sperimentate, ragazzi, sperimentate...
Ashura
Prode Principiante
Messaggi: 199
Iscrizione: venerdì 13 ottobre 2006, 9:55

Re: Snake in C++

Messaggio da Ashura »

mioddio... ma come fai a sostenere che quella roba sia C++?

scrivere un programma in C, usando le iostream e salvare il file con estenzione .cpp non significa sviluppare in C++ eh...

semplicemente in questo modo compilerai il tuo codice C col compilatore C++...
danlugli

Re: Snake in C++

Messaggio da danlugli »

Quale roba?
A chi ti rivolgi?
Hai seguito tutto il thread?
Sai di che cosa stai parlando?
Avatar utente
origin of
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1028
Iscrizione: venerdì 30 giugno 2006, 17:04
Località: Lucca

Re: Snake in C++

Messaggio da origin of »

Ashura ha scritto: mioddio... ma come fai a sostenere che quella roba sia C++?

scrivere un programma in C, usando le iostream e salvare il file con estenzione .cpp non significa sviluppare in C++ eh...

semplicemente in questo modo compilerai il tuo codice C col compilatore C++...

leggi tutto il 3d prima di rispondere, usa classi etc.., c++ allo stato puro :D
Utente archlinux, ex utente ubuntu.
Contatto jabber: originof@jaim.at
MIO SITO: http://www.happycode.it
Ikitt

Re: Snake in C++

Messaggio da Ikitt »

Ashura ha scritto: mioddio... ma come fai a sostenere che quella roba sia C++?

scrivere un programma in C, usando le iostream e salvare il file con estenzione .cpp non significa sviluppare in C++ eh...
[...]
Beh considerando che il C++ e` multiparadigma e che incorpora la libreria standard C nella propria, non posso dirmi proprio d'accordo con quanto quotato :)
danlugli

Re: Snake in C++

Messaggio da danlugli »

Ashura ha scritto: mioddio... ma come fai a sostenere che quella roba sia C++?

scrivere un programma in C, usando le iostream e salvare il file con estenzione .cpp non significa sviluppare in C++ eh...
Una curiosità... non m'ero accorto di usare le iostream... dov'è che le uso?
Un'altra curiosità... come fai a sapere che il file ha estensione .cpp?
Una nota finale... 'estensione' si scrive con due esse.
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++

Messaggio da m@ »

cavolo ho riletto ora quelloc he mi hai scritto...e cercato di modifcare il mio, ma ho preferito lasciare la biscia con l'accelerazione..ehehhe...è piu mio...
di gia mi hai dato la funzione del main...per la getch....ora mi dai anche quella per il FRENO :)....no no...mi accontento cosi :P...ehehehhe
grazie a tutti...metto risolto ;)
M@
danlugli

Re: Snake in C++ [RISOLTO]

Messaggio da danlugli »

Pigro!
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++ [RISOLTO]

Messaggio da m@ »

non hai tutti i torti....solo che ora riniziano i corsi universitari...e mi devo mettere sotto...poi c'ho da fare tantissime cose.. e quella di snake era solo uno sfizietto.. magari chissa...più in la mi ci mettero ;)
M@
danlugli

Re: Snake in C++ [RISOLTO]

Messaggio da danlugli »

Sto scherzando!
Avatar utente
m@
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 476
Iscrizione: sabato 19 agosto 2006, 15:59
Località: Viareggio
Contatti:

Re: Snake in C++ [RISOLTO]

Messaggio da m@ »

vai tranqui...eqhhehe grazie²  (b2b)
M@
Nerd_Gamer_250
Prode Principiante
Messaggi: 1
Iscrizione: sabato 16 novembre 2024, 7:25

Re: Snake in C++ [RISOLTO]

Messaggio da Nerd_Gamer_250 »

Usa kbhit, fa esattamente quello che chiedi. Se cerchi in internet troverai istruzioni chiare. :D
Chiusa

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 9 ospiti