author | andyq <andyq> | 2002-12-09 20:30:46 (UTC) |
---|---|---|
committer | andyq <andyq> | 2002-12-09 20:30:46 (UTC) |
commit | b9a448e0687558c1cb79f801161966e15d589132 (patch) (side-by-side diff) | |
tree | 4f485b85b8bc03546008dd2003c75bf60f7acd9b | |
parent | accc4e231dc34744e75d32af77eeb98f46006c27 (diff) | |
download | opie-b9a448e0687558c1cb79f801161966e15d589132.zip opie-b9a448e0687558c1cb79f801161966e15d589132.tar.gz opie-b9a448e0687558c1cb79f801161966e15d589132.tar.bz2 |
Added replay function and included own random method for replay functionality
-rw-r--r-- | noncore/games/sfcave/random.cpp | 179 | ||||
-rw-r--r-- | noncore/games/sfcave/random.h | 19 | ||||
-rw-r--r-- | noncore/games/sfcave/sfcave.cpp | 79 | ||||
-rw-r--r-- | noncore/games/sfcave/sfcave.h | 12 | ||||
-rw-r--r-- | noncore/games/sfcave/sfcave.pro | 4 |
5 files changed, 274 insertions, 19 deletions
diff --git a/noncore/games/sfcave/random.cpp b/noncore/games/sfcave/random.cpp new file mode 100644 index 0000000..c4c10bb --- a/dev/null +++ b/noncore/games/sfcave/random.cpp @@ -0,0 +1,179 @@ +/* ------------------------------------------------------------------------- + * This is an ANSI C library for multi-stream random number generation. + * The use of this library is recommended as a replacement for the ANSI C + * rand() and srand() functions, particularly in simulation applications + * where the statistical 'goodness' of the random number generator is + * important. The library supplies 256 streams of random numbers; use + * SelectStream(s) to switch between streams indexed s = 0,1,...,255. + * + * The streams must be initialized. The recommended way to do this is by + * using the function PlantSeeds(x) with the value of x used to initialize + * the default stream and all other streams initialized automatically with + * values dependent on the value of x. The following convention is used + * to initialize the default stream: + * if x > 0 then x is the state + * if x < 0 then the state is obtained from the system clock + * if x = 0 then the state is to be supplied interactively. + * + * The generator used in this library is a so-called 'Lehmer random number + * generator' which returns a pseudo-random number uniformly distributed + * 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and the + * smallest and largest possible values are (1 / m) and 1 - (1 / m) + * respectively. For more details see: + * + * "Random Number Generators: Good Ones Are Hard To Find" + * Steve Park and Keith Miller + * Communications of the ACM, October 1988 + * + * Name : rngs.c (Random Number Generation - Multiple Streams) + * Authors : Steve Park & Dave Geyer + * Language : ANSI C + * Latest Revision : 09-22-98 + * ------------------------------------------------------------------------- + */ + +#include <stdio.h> +#include <time.h> +#include "random.h" + +#define MODULUS 2147483647 /* DON'T CHANGE THIS VALUE */ +#define MULTIPLIER 48271 /* DON'T CHANGE THIS VALUE */ +#define CHECK 399268537 /* DON'T CHANGE THIS VALUE */ +#define STREAMS 256 /* # of streams, DON'T CHANGE THIS VALUE */ +#define A256 22925 /* jump multiplier, DON'T CHANGE THIS VALUE */ +#define DEFAULT 123456789 /* initial seed, use 0 < DEFAULT < MODULUS */ + +static long seed[STREAMS] = {DEFAULT}; /* current state of each stream */ +static int stream = 0; /* stream index, 0 is the default */ +static int initialized = 0; /* test for stream initialization */ + + + double Random(void) +/* ---------------------------------------------------------------- + * Random returns a pseudo-random real number uniformly distributed + * between 0.0 and 1.0. + * ---------------------------------------------------------------- + */ +{ + const long Q = MODULUS / MULTIPLIER; + const long R = MODULUS % MULTIPLIER; + long t; + + t = MULTIPLIER * (seed[stream] % Q) - R * (seed[stream] / Q); + if (t > 0) + seed[stream] = t; + else + seed[stream] = t + MODULUS; + return ((double) seed[stream] / MODULUS); +} + + + void PlantSeeds(long x) +/* --------------------------------------------------------------------- + * Use this function to set the state of all the random number generator + * streams by "planting" a sequence of states (seeds), one per stream, + * with all states dictated by the state of the default stream. + * The sequence of planted states is separated one from the next by + * 8,367,782 calls to Random(). + * --------------------------------------------------------------------- + */ +{ + const long Q = MODULUS / A256; + const long R = MODULUS % A256; + int j; + int s; + + initialized = 1; + s = stream; /* remember the current stream */ + SelectStream(0); /* change to stream 0 */ + PutSeed(x); /* set seed[0] */ + stream = s; /* reset the current stream */ + for (j = 1; j < STREAMS; j++) { + x = A256 * (seed[j - 1] % Q) - R * (seed[j - 1] / Q); + if (x > 0) + seed[j] = x; + else + seed[j] = x + MODULUS; + } +} + + + void PutSeed(long x) +/* --------------------------------------------------------------- + * Use this function to set the state of the current random number + * generator stream according to the following conventions: + * if x > 0 then x is the state (unless too large) + * if x < 0 then the state is obtained from the system clock + * if x = 0 then the state is to be supplied interactively + * --------------------------------------------------------------- + */ +{ + char ok = 0; + + if (x > 0) + x = x % MODULUS; /* correct if x is too large */ + if (x < 0) + x = ((unsigned long) time((time_t *) NULL)) % MODULUS; + if (x == 0) + while (!ok) { + printf("\nEnter a positive integer seed (9 digits or less) >> "); + scanf("%ld", &x); + ok = (0 < x) && (x < MODULUS); + if (!ok) + printf("\nInput out of range ... try again\n"); + } + seed[stream] = x; +} + + + void GetSeed(long *x) +/* --------------------------------------------------------------- + * Use this function to get the state of the current random number + * generator stream. + * --------------------------------------------------------------- + */ +{ + *x = seed[stream]; +} + + + void SelectStream(int index) +/* ------------------------------------------------------------------ + * Use this function to set the current random number generator + * stream -- that stream from which the next random number will come. + * ------------------------------------------------------------------ + */ +{ + stream = ((unsigned int) index) % STREAMS; + if ((initialized == 0) && (stream != 0)) /* protect against */ + PlantSeeds(DEFAULT); /* un-initialized streams */ +} + + + void TestRandom(void) +/* ------------------------------------------------------------------ + * Use this (optional) function to test for a correct implementation. + * ------------------------------------------------------------------ + */ +{ + long i; + long x; + double u; + char ok = 0; + + SelectStream(0); /* select the default stream */ + PutSeed(1); /* and set the state to 1 */ + for(i = 0; i < 10000; i++) + u = Random(); + GetSeed(&x); /* get the new state value */ + ok = (x == CHECK); /* and check for correctness */ + + SelectStream(1); /* select stream 1 */ + PlantSeeds(1); /* set the state of all streams */ + GetSeed(&x); /* get the state of stream 1 */ + ok = ok && (x == A256); /* x should be the jump multiplier */ + if (ok) + printf("\n The implementation of rngs.c is correct.\n\n"); + else + printf("\n\a ERROR -- the implementation of rngs.c is not correct.\n\n"); +} diff --git a/noncore/games/sfcave/random.h b/noncore/games/sfcave/random.h new file mode 100644 index 0000000..4bc7c06 --- a/dev/null +++ b/noncore/games/sfcave/random.h @@ -0,0 +1,19 @@ +/* ----------------------------------------------------------------------- + * Name : rngs.h (header file for the library file rngs.c) + * Author : Steve Park & Dave Geyer + * Language : ANSI C + * Latest Revision : 09-22-98 + * ----------------------------------------------------------------------- + */ + +#if !defined( _RNGS_ ) +#define _RNGS_ + +double Random(void); +void PlantSeeds(long x); +void GetSeed(long *x); +void PutSeed(long x); +void SelectStream(int index); +void TestRandom(void); + +#endif diff --git a/noncore/games/sfcave/sfcave.cpp b/noncore/games/sfcave/sfcave.cpp index 788606f..b5bc736 100644 --- a/noncore/games/sfcave/sfcave.cpp +++ b/noncore/games/sfcave/sfcave.cpp @@ -1,15 +1,16 @@ #include <stdio.h> #include <stdlib.h> #include <math.h> +#include <time.h> #ifdef QWS #include <qpe/qpeapplication.h> #include <qpe/config.h> #else #include <qapplication.h> #endif #include "helpwindow.h" #include "sfcave.h" #define CAPTION "SFCave 1.8 by AndyQ" @@ -17,24 +18,25 @@ #define UP_THRUST 0.6 #define NO_THRUST 0.8 #define MAX_DOWN_THRUST 4.0 #define MAX_UP_THRUST -3.5 // States #define STATE_BOSS 0 #define STATE_RUNNING 1 #define STATE_CRASHING 2 #define STATE_CRASHED 3 #define STATE_NEWGAME 4 #define STATE_MENU 5 +#define STATE_REPLAY 6 // Menus #define MENU_MAIN_MENU 0 #define MENU_OPTIONS_MENU 1 // Main Menu Options #define MENU_START_GAME 0 #define MENU_OPTIONS 1 #define MENU_HELP 2 #define MENU_QUIT 3 // Option Menu Options @@ -118,24 +120,25 @@ int main( int argc, char *argv[] ) SFCave app( speed ); a.setMainWidget( &app ); app.show(); app.start(); a.exec(); } SFCave :: SFCave( int spd, QWidget *w, char *name ) : QMainWindow( w, name ) { + replayIt = 0; #ifdef QWS showMaximized(); #else resize( 240, 284 ); #endif sWidth = width(); sHeight = height(); segSize = sWidth/(MAPSIZE-1)+1; currentMenuNr = 0; currentGameType = 0; @@ -155,52 +158,63 @@ SFCave :: SFCave( int spd, QWidget *w, char *name ) highestScore[i][j] = cfg.readNumEntry( key + gameTypes[i] + "_" + dificultyOption[j], 0 ); } currentGameType = cfg.readNumEntry( "gameType", 0 ); currentGameDifficulty = cfg.readNumEntry( "difficulty", 0 ); #endif speed = spd; // Change to 2 for PC press = false; offscreen = new QPixmap( sWidth, sHeight ); offscreen->fill( Qt::black ); - setUp(); +// setUp(); crashLineLength = -1; + state = STATE_MENU; + prevState = STATE_MENU; gameTimer = new QTimer( this, "game timer" ); connect( gameTimer, SIGNAL( timeout() ), this, SLOT( run() ) ); } SFCave :: ~SFCave() { } void SFCave :: start() { gameTimer->start( 10 ); } +void SFCave :: setSeed( int seed ) +{ + if ( seed == -1 ) + currentSeed = ((unsigned long) time((time_t *) NULL)); + else + currentSeed = seed; + PutSeed( currentSeed ); +} + int SFCave :: nextInt( int range ) { - return rand() % range; + int val = (int)(Random( ) * range); + + return val; + } void SFCave :: setUp() { - state = STATE_MENU; - prevState = STATE_MENU; - score = 0; offset = 0; nrFrames = 0; dir = 1; thrust = 0; if ( CURRENT_GAME_TYPE == SFCAVE_GAME ) { thrustUp = UpThrustVals[SFCAVE_GAME_TYPE][currentGameDifficulty];; noThrust = DownThrustVals[SFCAVE_GAME_TYPE][currentGameDifficulty];; maxUpThrust = MaxUpThrustVals[SFCAVE_GAME_TYPE][currentGameDifficulty];; maxDownThrust = MaxDownThrustVals[SFCAVE_GAME_TYPE][currentGameDifficulty];; @@ -212,24 +226,25 @@ void SFCave :: setUp() maxUpThrust = MaxUpThrustVals[GATES_GAME_TYPE][currentGameDifficulty];; maxDownThrust = MaxDownThrustVals[GATES_GAME_TYPE][currentGameDifficulty];; } else { thrustUp = UpThrustVals[FLY_GAME_TYPE][currentGameDifficulty]; noThrust = DownThrustVals[FLY_GAME_TYPE][currentGameDifficulty]; maxUpThrust = MaxUpThrustVals[FLY_GAME_TYPE][currentGameDifficulty]; maxDownThrust = MaxDownThrustVals[FLY_GAME_TYPE][currentGameDifficulty]; } crashLineLength = 0; + lastGateBottomY = 0; user.setRect( 50, sWidth/2, 4, 4 ); blockWidth = 20; blockHeight = 70; gapHeight = initialGateGaps[currentGameDifficulty]; gateDistance = 75; nextGate = nextInt( 50 ) + gateDistance; for ( int i = 0 ; i < TRAILSIZE ; ++i ) { trail[i].setX( -1 ); @@ -245,54 +260,77 @@ void SFCave :: setUp() for ( int i = 1 ; i < MAPSIZE ; ++i ) setPoint( i ); } else { maxHeight = 100; for ( int i = 0 ; i < MAPSIZE ; ++i ) mapBottom[i] = sHeight - 10; } for ( int i = 0 ; i < BLOCKSIZE ; ++i ) blocks[i].setY( -1 ); + } void SFCave :: run() { switch ( state ) { case STATE_MENU: displayMenu(); break; case STATE_NEWGAME: + setSeed( -1 ); setUp(); draw(); state = STATE_RUNNING; + replay = false; + replayList.clear(); break; + case STATE_REPLAY: + setSeed( currentSeed ); + setUp(); + draw(); + state = STATE_RUNNING; + replay = true; + if ( replayIt ) + delete replayIt; + replayIt = new QListIterator<int>( replayList ); case STATE_BOSS: drawBoss(); break; case STATE_CRASHING: case STATE_CRASHED: draw(); break; case STATE_RUNNING: { if ( nrFrames % 2 == 0 ) handleKeys(); // Apply Game rules nrFrames ++; + + if ( replay ) + { + while( replayIt->current() && *(replayIt->current()) == nrFrames ) + { + press = !press; + ++(*replayIt); + } + } + if ( CURRENT_GAME_TYPE == SFCAVE_GAME ) handleGameSFCave(); else if ( CURRENT_GAME_TYPE == GATES_GAME ) handleGameGates(); else if ( CURRENT_GAME_TYPE == FLY_GAME ) handleGameFly(); draw(); break; } } } @@ -514,30 +552,31 @@ void SFCave :: addBlock() void SFCave :: addGate() { for ( int i = 0 ; i < BLOCKSIZE ; ++i ) { if ( blocks[i].y() == -1 ) { int x1 = sWidth; int y1 = mapTop[50]; int b1Height = nextInt(mapBottom[50] - mapTop[50] - gapHeight); // See if height between last gate and this one is too big - if ( b1Height - 200 > lastGateBottomY ) + if ( b1Height - 100 > lastGateBottomY ) b1Height -= 25; - else if ( b1Height + 200 < lastGateBottomY ) + else if ( b1Height + 100 < lastGateBottomY ) b1Height += 25; lastGateBottomY = b1Height; + int x2 = sWidth; int y2 = y1 + b1Height + gapHeight; int b2Height = mapBottom[50] - y2; blocks[i].setRect( x1, y1, blockWidth, b1Height ); blocks[i+1].setRect( x2, y2, blockWidth, b2Height ); break; } } } @@ -764,25 +803,29 @@ void SFCave :: keyPressEvent( QKeyEvent *e ) case Qt::Key_Enter: dealWithMenuSelection(); break; } } else { switch( e->key() ) { case Qt::Key_Up: case Qt::Key_F9: case Qt::Key_Space: - press = true; + if ( !press ) + { + press = true; + replayList.append( new int( nrFrames ) ); + } break; case Qt::Key_M: case Qt::Key_Return: case Qt::Key_Enter: if ( state == STATE_CRASHED ) state = STATE_MENU; break; case Qt::Key_Z: showScoreZones = !showScoreZones; break; @@ -795,40 +838,44 @@ void SFCave :: keyPressEvent( QKeyEvent *e ) void SFCave :: keyReleaseEvent( QKeyEvent *e ) { if ( state == STATE_MENU ) { } else { switch( e->key() ) { case Qt::Key_F9: case Qt::Key_Space: - press = false; + case Qt::Key_Up: + if ( press ) + { + press = false; + + replayList.append( new int( nrFrames ) ); + } break; - case Qt::Key_Up: - press = false; - case Qt::Key_R: - case Qt::Key_Down: if ( state == STATE_CRASHED ) { - state = STATE_NEWGAME; + state = STATE_REPLAY; } - else - movel = true; break; + case Qt::Key_Down: + if ( state == STATE_CRASHED ) + state = STATE_NEWGAME; + break; default: e->ignore(); break; } } } void SFCave :: displayMenu() { offscreen->fill( Qt::black ); diff --git a/noncore/games/sfcave/sfcave.h b/noncore/games/sfcave/sfcave.h index 69a0e13..0d9a626 100644 --- a/noncore/games/sfcave/sfcave.h +++ b/noncore/games/sfcave/sfcave.h @@ -1,34 +1,43 @@ #include <qmainwindow.h> #include <qpainter.h> #include <qpixmap.h> #include <qpoint.h> #include <qrect.h> #include <qtimer.h> +#include <qlist.h> - +#include "random.h" #define MAPSIZE 52 #define BLOCKSIZE 6 #define TRAILSIZE 30 + + class SFCave : public QMainWindow { Q_OBJECT public: int sWidth; int sHeight; int segSize; + int currentSeed; + + QList<int> replayList; + QListIterator<int> *replayIt; + bool replay; + int blockWidth; int blockHeight; int gapHeight; int state; int prevState; int speed; int crashLineLength; static double UpThrustVals[3][3]; static double DownThrustVals[3][3]; static double MaxUpThrustVals[3][3]; static double MaxDownThrustVals[3][3]; @@ -70,24 +79,25 @@ public: int nrFrames; int dir; bool showScoreZones; bool press; double thrust; bool running; SFCave( int speed = 3, QWidget *p = 0, char *name = 0 ); ~SFCave(); void start(); + void setSeed( int seed ); int nextInt( int range ); void setUp(); void handleGameSFCave(); void handleGameGates(); void handleGameFly(); bool checkFlyGameCollision(); void moveFlyGameLandscape(); void setFlyPoint( int point ); bool checkCollision(); void moveLandscape(); void addBlock(); void addGate(); diff --git a/noncore/games/sfcave/sfcave.pro b/noncore/games/sfcave/sfcave.pro index f4b9e5d..5f49330 100644 --- a/noncore/games/sfcave/sfcave.pro +++ b/noncore/games/sfcave/sfcave.pro @@ -1,9 +1,9 @@ TEMPLATE = app CONFIG += qt warn_on release DESTDIR = $(OPIEDIR)/bin -SOURCES = sfcave.cpp helpwindow.cpp -HEADERS = sfcave.h helpwindow.h +SOURCES = sfcave.cpp helpwindow.cpp random.cpp +HEADERS = sfcave.h helpwindow.h random.h TARGET = sfcave INCLUDEPATH += $(OPIEDIR)/include DEPENDPATH += $(OPIEDIR)/include LIBS += -lqpe |