home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 5
/
FreshFish_July-August1994.bin
/
bbs
/
gfx
/
megajitter-1.3.lha
/
MegaJitter-1.3
/
mj.c
< prev
next >
Wrap
Text File
|
1994-05-19
|
92KB
|
3,039 lines
//----------------------------------------------------------------------
// MegaJitter V1.3 written by L. Vanhelsuwé (C) LVA 1992-94
// ---------- ---- ------------------------ ---------------
// Based on a slower program called "Jitter" (authors "Don & Chris").
//
// Graphical evolution simulator.
// Allow gene-based creatures to evolve within an environment.
//
// Features of this simulation include:
// - gene-based control over characteristics
// - mutation of genes at birth
// - sexual and asexual reproduction
// - creatures can be 100% herbivores... omnivores... 100% carnivores
// - real-time display of entire ecosystem
// - real-time display of statistics
// - flexible control over ecosystem global variables
// - AREXX interface
//
// History
// -------
// XX-AUG-92: Started this file.
// 12-OCT-93: Added greyscales display option for crappy grey VGA monitor.
// 02-JAN-94: Added Food vision capability to creatures.
//
// VERSION 1.2
//
// 13-MAR-94: Added a faster non-graphical (NOANIM) mode (using bytes for cells)
// + scenario settings now in variables instead of #defs.
// 26-MAR-94: Changed code to use pointers to functions for CLI selectable
// non-animated (faster) mode.
// Added dying_age and moving speed genes. "BLIND" option.
// 08-APR-94: Added sexuality... at last.
// 10-APR-94: Started adding MenuStrip with dynamic statistics selection.
// 12-APR-94: Finished Stats Menus.
// 13-APR-94: Added Options Menu with Graph speeds as mutually excluding
// sub-items.
// 16-APR-94: Added (non satisfactory) attempt to be NTSC-friendly.
// 17-APR-94: Changed defaults to be NO SEX and NO VISION
// 22-APR-94: Added checks for out-of-bounds global parameters on CMD line.
// 27-APR-94: Added herbivore/carnivore capability
// 01-MAY-94: Added my own AREXX compatibility, after thinking that the PD
// offerings were too complicated.
// 03-MAY-94: Added rexxsyslib IsRexxMsg() sanity check for incoming msgs.
//
// VERSION 1.3
//
// 11-MAY-94: Changed Oasis food growth to true circular area.
// Killed some REXX bugs, created first REXX scenarios.
// 12-MAY-94: Major gfx optimization: now using an interleaved bitmap for faster
// pixel routines.
// 17-MAY-94: Made MAX_ENERGY a gene instead of a global constant.
// 19-MAY-94: Forced screen Font to be Topaz 8*8
//
// TO DO
// -----
// - LOAD/SAVE entire state.
// - Global parameters control panel
// - Help menu
// - long vertical hardware sprite as a crosshair type cursor over graphs.
//
// Lines marked **!! are potential bug sources.
//
// DESIGN MISTAKES:
// ----------------
// - When I added the NOANIM option and therefore the necessary parallel
// representation, I started a second path of development which had to
// have the same functionality as the original pixel-based system.
// This is bad: too much work maintaining the 2 systems.
// I should have used one single data structure capable of being displayed
// as pixels or not (as in NOANIM).
//----------------------------------------------------------------------
#ifdef DEBUG
#define ENTER(x) printf("Entering..." x "\n");
#define LEAVE(x) printf("Done ..." x "\n\n");
#endif
#define MONOCHROME 1 // running on a mono display!
//#define FASTER 1 // enable to cut out IFAUDIT code...
// Major apology to the whole world out there: I am polishing this program on
// a minimalistic grey-scale VGA monitor, so, sorry about the B&W look !
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef AMIGA
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/asl.h>
#include <libraries/gadtools.h>
#include <utility/tagitem.h>
#include <rexx/rxslib.h> // for AREXX Support !
#include <rexx/errors.h>
#include <clib/exec_protos.h> // ANSI function prototypes
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/rexxsyslib_protos.h>
#define LF 10
#else // if compiled on non-Amiga machines..
typedef unsigned char UBYTE; // 8 bits
typedef unsigned short UWORD; // 16 bits
typedef unsigned long ULONG; // 32 bits
#endif
typedef unsigned int FLAG; // 32 bits **!! optimized for 68020+
typedef unsigned int RC;
//----------------------------------------------
#define CHAR_HEIGHT 8 // Y size of Font.
#define SCREEN_WIDTH 512 // Screen dimension **!! FIXED
#define SCREEN_HEIGHT 512 // HAS to be a power of 2.
#define ACRE (SCREEN_WIDTH-1) // POWER OF 2 **!! LEAVE AS IS
#define ECO_SCR_DEPTH 4 // for 16 colors
#define STATS_TOPY 12 // offset from top of screen.
#define GRAPH_HEIGHT 64
#define STAT_SCR_WIDTH 640 // Statistics Screen Width
#define STAT_SCR_DEPTH 3 // only 8 colors needed.
#define NUM_COLORS (1<<ECO_SCR_DEPTH)
#define NUM_CELLS (1<<ECO_SCR_DEPTH)
#define NEUT_CELL 0 // has to be all 0s eg. %0000
#define VEGE_CELL (NUM_CELLS-1) // has to be all 1s eg. %1111
#define WALL_CELL (NUM_CELLS-2) // has to be all 1s eg. %1110
#define MAX_COLORS (NUM_CELLS-3)
#define SYNTAX_ERROR (5)
#define FATAL_ERROR (20)
#define PROG_BUG_ERROR (100)
#define REXXERR_UNKNOWN_CMD (10)
#define REXXERR_VAL_OUTSIDE_RANGE (11)
//----------------------------------------------
// Statistic Graphs constants
//----------------------------------------------
#define TIMER_SPEED 4 // Time update speed
#define LE (STAT_SCR_WIDTH-256) // LABEL area for graphs
#define NORMAL 0
#define FLIP_IT 1 // don't change **!!
#define CURGRAPH_Y (STATS_TOPY+ graphnum*GRAPH_HEIGHT) // current graph Y BASE
//-----------------------------
// Main program data structures
//-----------------------------
ULONG time; // And God Createth Universal Time
UWORD runs=0;
ULONG daylight; // +100 to -100 (noon, midnight)
struct fast_pixel {
ULONG pixel_byte_offs; // offset of byte in plane
ULONG pixel_bit_no; // minimal pixel info
};
// The BUG data structure is this program's core data structure, therefore
// keep field positions optimized for a 32-bit data bus processor.
struct bug {
FLAG alive; // flag enabling all the other fields
// Individual time-dependent creature characteristics
ULONG age; // howmany timer ticks he's been around.
WORD energy; // 0..procr_energy (MAX +32767 **!!)
UWORD x,y; // screen coords of bug.
UWORD generation; // which generation this guy is
struct fast_pixel prevpix; // prev pixel info (for fast clear)
UBYTE dir; // 0..7
UBYTE speed_delay; // 0..3
// GENES (time-independent creature characteristics)
UBYTE right; // 0..255
UBYTE left; //
UBYTE vision; // 0..255 scaled down to ..
UBYTE sexuality; // 0..255
UBYTE dirgene; //
UBYTE oxy_produce;
UBYTE oxy_consume;
UBYTE co2_produce;
UBYTE co2_consume;
UBYTE dying_age; // 0..255 but scaled up to 0..65535
UBYTE maxsexage; // 0..255 but scaled up to 0..65535
UBYTE minsexage; // 0..255 but scaled up to 0..4095
UBYTE speed; // 0..255 but scaled down in delay
UBYTE foodtype; // 0..63 HERBI, 64..191 OMNI, 191..255 CARNI
UBYTE procr_energy; // procreate energy threshold 0..16384
UBYTE dummy;
UWORD dummyw;
};
struct bug dummy_bug; // one instance to pass as dummy arg.
typedef UBYTE CELL; // a cell in the 2-d array
typedef struct bug *BUGPTR;
// array holding MAX_BUGS simulated creatures (allocated at run-time)
BUGPTR bugs; // struct bug bugs[MAX_BUGS];
// To allow creatures to find out efficiently which "bug" structure is
// connected to a particular creature pixel on-screen, we use a parallel
// map to hold pointers to the "parent structures".
CELL *environment; // ptr to BYTE-based env of creatures
BUGPTR *owner_ptrs; // ptr to parallel PARENTS map
#define PTR_MAP_SIZE (sizeof(char *)*SCREEN_WIDTH*SCREEN_HEIGHT)
int color_divider; // factor to map bug->energy to pixel pen number
//-----------------------------------------------------------
// The following variables are counters for statistics/graphs
//-----------------------------------------------------------
ULONG numbugs;
ULONG food_in_system;
ULONG births,deaths;
ULONG total_energy;
ULONG total_age;
ULONG total_right, total_left, total_vision, total_dir;
ULONG total_dying_age, total_minsexage, total_maxsexage;
ULONG total_speed, total_sexuality;
ULONG total_foodtype, total_procr;
ULONG total_O2, total_CO2;
UBYTE min_right, max_right;
UBYTE min_left, max_left;
UBYTE min_vision, max_vision;
UBYTE min_dying_age,max_dying_age;
UBYTE min_speed, max_speed;
UBYTE min_sexuality,max_sexuality;
UBYTE min_minsexage,max_minsexage;
UBYTE min_maxsexage,max_maxsexage;
UBYTE min_foodtype, max_foodtype;
UBYTE min_procr, max_procr;
ULONG oxygen,carb_dioxide;
UBYTE stat_bufix=0; // statistics buffer INDEX
struct statistic {
FLAG displayed; // currently displayed or not
char *label; // Menu and Label string
ULONG *stat_addr; // ptr to statistic variable
UBYTE *minmax_addr; // -> minimum and maximum stats (if non-NULL)
ULONG scale; // maximum value equivalent to max scale
FLAG flip_graph; // normal or up-side-down graph ?
ULONG *buffer; // buffer[512] record circular buffer
};
#define NUM_STATS (19)
struct statistic available_stats[] = {
{ TRUE ,"Number of Bugs....................." ,&numbugs ,NULL ,34 ,NORMAL ,0 },
{ FALSE ,"Number of Bug Births..............." ,&births ,NULL ,64 ,NORMAL ,0 },
{ FALSE ,"Number of Bug Deaths..............." ,&deaths ,NULL ,64 ,NORMAL ,0 },
{ FALSE ,"Average Bug Energy................." ,&total_energy ,NULL ,32768 ,NORMAL ,0 },
{ FALSE ,"Average Bug Age...................." ,&total_age ,NULL ,65535 ,NORMAL ,0 },
{ TRUE ,"Amount of food....................." ,&food_in_system ,NULL ,5000 ,NORMAL ,0 },
{ TRUE ,"Oscillator: Day/Night.............." ,&daylight ,NULL ,100 ,NORMAL ,0 },
{ TRUE ,"Gene Avg.: Clock-wise.............." ,&total_right ,&min_right ,255 ,FLIP_IT ,0 },
{ TRUE ,"Gene Avg.: Anti Clock-wise........." ,&total_left ,&min_left ,255 ,FLIP_IT ,0 },
{ FALSE ,"Gene Avg.: Vision.................." ,&total_vision ,&min_vision ,255 ,NORMAL ,0 },
{ FALSE ,"Gene Avg.: Sexuality..............." ,&total_sexuality ,&min_sexuality ,255 ,NORMAL ,0 },
{ TRUE ,"Gene Avg.: Lifespan................" ,&total_dying_age ,&min_dying_age ,65535 ,NORMAL ,0 },
{ FALSE ,"Gene Avg.: Min. Procreation Age...." ,&total_minsexage ,&min_minsexage ,4096 ,NORMAL ,0 },
{ FALSE ,"Gene Avg.: Min. Procreation Energy." ,&total_procr ,&min_procr ,16384 ,NORMAL ,0 },
{ FALSE ,"Gene Avg.: Max. Procreation Age...." ,&total_maxsexage ,&min_maxsexage ,65535 ,NORMAL ,0 },
{ FALSE ,"Gene Avg.: Moving Slowness........." ,&total_speed ,&min_speed ,255 ,NORMAL ,0 },
{ TRUE ,"Gene Avg.: Herbi-/Omni-/Carnivore.." ,&total_foodtype ,&min_foodtype ,255 ,NORMAL ,0 },
{ FALSE ,"Amount of Oxygen..................." ,&total_O2 ,NULL ,60000 ,NORMAL ,0 },
{ FALSE ,"Amount of Carbon Dioxide..........." ,&total_CO2 ,NULL ,60000 ,NORMAL ,0 },
{ 0 ,"**!! MENU BUG: GONE ONE TO FAR *." ,NULL ,NULL ,0 ,0 ,0 }
};
#define EYE_ITEM (9)
#define SEX_ITEM (10)
#define OXY_ITEM ( (sizeof(available_stats)/sizeof(struct statistic)) -3)
#define CO2_ITEM ( (sizeof(available_stats)/sizeof(struct statistic)) -2)
UWORD graphnum; // current graph index
UWORD graph_speed; // current graph update speed
UWORD MAX_GRAPHS; // 6 or 8
UWORD STAT_WIN_HEIGHT;
FLAG NUMERIC_STATS = TRUE;
FLAG MINMAX = FALSE; // draw minima/maxima too ?
//-------------------------------------------------------------------------
// The following AmigaDOS-filled structure is used to store program globals.
// The structure is initialized by DOS from the options the user passes
// on the command line.
// The program itself post-initializes any globals not set by the user.
//-------------------------------------------------------------------------
struct RDAres { // array for ReadArgs()
ULONG audit; // report events on STDOUT ?
ULONG capture; // save all stats to file ?
ULONG noanim; // no animation of simulation ?
ULONG eyes; // enable vision gene ?
ULONG sex; // enable sexual pairing ?
ULONG showdefaults;
ULONG maxbugs;
// Starting SCENARIO variables
// ---------------------------
ULONG INIT_BUGS;
ULONG INIT_FOOD;
ULONG INIT_ENERGY;
ULONG INIT_VARIANCE;
// ULONG INIT_O2;
// ULONG INIT_CO2;
ULONG MUTATE_RANGE;
ULONG FOOD_RATE;
ULONG FOOD_ENERGY;
ULONG X_AND;
ULONG Y_AND;
ULONG OASIS_SIZE;
ULONG ntsc; // User supplies NTSC/PAL information !
} options;
#define ASEXUAL (FALSE)
#define SEXUAL (TRUE)
#define REPORT (options.audit)
#define CAPTURE (options.capture)
#define NOANIM (options.noanim)
#define VISION (options.eyes)
#define SEX (options.sex)
#define SHOWDEFAULTS (options.showdefaults)
#define MAX_BUGS (options.maxbugs)
#define INIT_BUGS (options.INIT_BUGS)
#define INIT_FOOD (options.INIT_FOOD)
#define INIT_ENERGY (options.INIT_ENERGY)
#define INIT_VARIANCE (options.INIT_VARIANCE)
//#define INIT_O2 (options.INIT_O2)
//#define INIT_CO2 (options.INIT_CO2)
#define MUTATE_RANGE (options.MUTATE_RANGE)
#define FOOD_RATE (options.FOOD_RATE)
#define FOOD_ENERGY (options.FOOD_ENERGY)
#define X_AND (options.X_AND)
#define Y_AND (options.Y_AND)
#define OASIS_SIZE (options.OASIS_SIZE)
#define NTSC (options.ntsc)
#define IFAUDIT if (REPORT)
#ifdef AMIGA
char MJIT_DOS_TEMPLATE[] =
"AUDIT/S,DATALOG/S,NOANIM/S,VISION/S,SEX/S,SHOWDEFAULTS/S,MAX_BUGS/K,\
INIT_BUGS/K,INIT_FOOD/K,INIT_ENERGY/K,INIT_VARIANCE/K,MUTATE_RANGE/K,\
FOOD_RATE/K,FOOD_ENERGY/K,X_AND/K,Y_AND/K,\
OASIS_SIZE/K,NTSC/S";
#endif
// The Variable structure defines program globals which can be accessed or
// changed via the command line and/or the AREXX interface.
struct variable {
UBYTE flags; // global and/or init
char *name; // name of a global parameter
ULONG *address; // its address
ULONG init_val; // initial default value
ULONG min_val; // range minimum
ULONG max_val; // range maximum
};
#define PARAMETER 1
struct variable varlist[] = {
{0 ,"MAX_BUGS" ,&MAX_BUGS ,300 ,20 ,1000 },
{PARAMETER ,"INIT_BUGS" ,&INIT_BUGS ,44 ,1 ,100 },
{PARAMETER ,"INIT_FOOD" ,&INIT_FOOD ,3189 ,1 ,30000 },
{PARAMETER ,"INIT_ENERGY" ,&INIT_ENERGY ,624 ,10 ,15000 },
{PARAMETER ,"INIT_VARIANCE",&INIT_VARIANCE ,182 ,1 ,250 },
// {0 ,"INIT_O2" ,&INIT_O2 ,32000 ,1 ,100000 },
// {0 ,"INIT_CO2" ,&INIT_CO2 ,4000 ,1 ,100000 },
{PARAMETER ,"MUTATE_RANGE" ,&MUTATE_RANGE ,48 ,1 ,120 },
{PARAMETER ,"FOOD_RATE" ,&FOOD_RATE ,10 ,1 ,100 },
{PARAMETER ,"FOOD_ENERGY" ,&FOOD_ENERGY ,624 ,1 ,5000 },
{PARAMETER ,"X_AND" ,&X_AND ,1 ,1 ,260 },
{PARAMETER ,"Y_AND" ,&Y_AND ,1 ,1 ,260 },
{PARAMETER ,"OASIS_SIZE" ,&OASIS_SIZE ,0 ,0 ,50 },
{NULL ,NULL ,NULL ,NULL ,0 ,0 }
};
// The Command structure array defines commands which AREXX can issue us.
struct command {
UBYTE flags;
char *name; // name of a REXX command
RC (*address)(void *); // function's address
};
RC please_quit (void *); // some forward declarations
RC restart (void *);
RC enable_sex (void *);
RC disable_sex (void *);
RC enable_vision (void *);
RC disable_vision (void *);
RC return_bugs (void *);
RC return_runs (void *);
RC return_timesteps(void *);
RC dumpdefaults (void *);
struct command cmdlist[] = {
{0 ,"MJ_QUIT" ,&please_quit },
{0 ,"MJ_RESET" ,&restart },
{0 ,"MJ_DUMPDEFAULTS" ,&dumpdefaults },
{0 ,"MJ_SEX" ,&enable_sex },
{0 ,"MJ_NOSEX" ,&disable_sex },
{0 ,"MJ_VISION" ,&enable_vision },
{0 ,"MJ_NOVISION" ,&disable_vision },
{0 ,"MJ_BUGS?" ,&return_bugs },
{0 ,"MJ_RUNS?" ,&return_runs },
{0 ,"MJ_TIMESTEPS?" ,&return_timesteps },
{0 ,0 ,0 }
};
//-------------------------------------------------------------------------
struct move_vec {
short dx; // a vector describing a direction or move.
short dy;
};
#define NUM_NEIGHBORS 8 // as on a chess board.
// direction vectors for the 8 directions
struct move_vec xymovs[NUM_NEIGHBORS] = {
{1,0},
{1,1},
{0,1},
{-1,1},
{-1,0},
{-1,-1},
{0,-1},
{1,-1}
};
int nbor_offs[NUM_NEIGHBORS] = {
-1, 1,
-512, 512,
-513, 511,
-511, 513
};
// exponentially decreasing "scent" weights according to distance
UBYTE scent_vals[]=
{ 254 ,253 ,252 ,251 ,
250 ,250 ,250 ,250 ,
249 ,249 ,249 ,249 ,
248 ,248 ,247 ,247 ,
246 ,245 ,244 ,243 ,
242 ,240 ,240 ,240 ,
235 ,235 ,230 ,230 , // for vision beyond 32 , code will index
230 ,210 ,200 ,100}; // beyond this point **!!
char strbuf[80]; // generic string buffer
char linebuf[512]; // CAPTURE line buffer
char *lineptr; // ptr into above
FLAG quit_me; // request to terminate program
FLAG auto_reset; // reset ecosys when bugs=0
UWORD food_timer; // timer for feeding time.
UWORD sim_speed; // slow-motion or no delay ?
//-------------------------------------------------------------------------
#ifdef AMIGA
extern struct ExecBase *SysBase; // basic Kernel services
extern struct GfxBase *GfxBase; // low-level graphics
extern struct IntuitionBase *IntuitionBase; // WIMP interface
struct Library *RexxSysBase; // AREXX support
//-------------------------------------------------------------------------
// A NewScreen struct to open application Screens.
//-------------------------------------------------------------------------
struct NewScreen ns = { 0,0, 640, 400, 2,
0,1,HIRES|LACE,0,
NULL,
"MegaJITTER V1.3 Statistics (c) 1992-1994 L.Vanhelsuwé",
NULL,NULL};
char scr_name[]="MegaJITTER Ecology Simulator";
struct TextAttr txtattr = {
"topaz.font", 8, 0,0
};
struct RastPort *stat_rp;
struct BitMap *bm; // allocated via AllocBitMap()
struct Screen *eco_screen,*stat_screen;
struct Window *window;
struct Menu *menustrip = 0;
void *vi; // global VisualInfo ptr for Workbench Screen
ULONG *plane0; // ASM routines access this **!!
char version[]="$VER: MegaJitter 1.3 ©LVA 06/MAY/94";
#define REXXPORTNAME "REXX-MJ"
//-------------------------------------------------------------------------
// A NewWindow struct to open application Windows.
//-------------------------------------------------------------------------
struct NewWindow nw = {
0, STATS_TOPY,
STAT_SCR_WIDTH, 200,
255, 255, /* Default pens */
// I want to know about following IDCMP Message types
IDCMP_MENUPICK,
// INVISIBLE window flags
WFLG_BORDERLESS | WFLG_ACTIVATE,
NULL, // No gadgets in this window
(struct Image *) NULL,
(char *) NULL, // Window title
(struct Screen *) NULL, // to be filled in.
(struct BitMap *) NULL,
0, 0, /* Minimum sizes */
65535, 65535, /* Maximum sizes */
CUSTOMSCREEN /* and put it IN our screen */
};
//-------------------------------------------------------------------------
// Menu Layout in compact GadTools format.
//
// **!! NOTE Mutual exclusion masks for Graph speed subitems !
//-------------------------------------------------------------------------
#define PROJECT_MENU 0
#define OPTIONS_MENU 1
#define STATS_MENU 2
#define HELP_MENU 3
#define PROJ_MENU_RESET 0
#define PROJ_MENU_LOAD 1
#define PROJ_MENU_SAVE 2
#define DUMMY_ITEM0 3
#define PROJ_MENU_ABOUT 4
#define PROJ_MENU_AUTHOR 5
#define PROJ_MENU_QUIT 6
#define OPT_MENU_CTRL_PANEL 0
#define OPT_MENU_SIM_SPEED 1
#define OPT_MENU_STAT_SPEED 2
#define OPT_MENU_RESET 3
#define OPT_MENU_AUDIT 4
#define OPT_MENU_MINMAX 5
#define HELP_MENU_a 0
#define HELP_MENU_b 1
// First empty slot for list of statistics at offset N
#define NEWMENU_AUDIT 22
#define NEWMENU_APPEND 25
struct NewMenu mymenus[NUM_STATS+NEWMENU_APPEND+2]= { // +END_MARKER + SAFETY
{ NM_TITLE, "Project" ,0 ,0 ,0 ,0},
{ NM_ITEM, "Reset" ,"N" ,0 ,0 ,0}, // Big Bang !
{ NM_ITEM, "Load..." ,"L" ,0 ,0 ,0}, // Load
{ NM_ITEM, "Save..." ,"S" ,0 ,0 ,0}, // Save
{ NM_ITEM, NM_BARLABEL ,0 ,0 ,0 ,0},
{ NM_ITEM, "About.." ,"?" ,0 ,0 ,0},
{ NM_ITEM, "Author..." ,0 ,0 ,0 ,0},
{ NM_ITEM, "Quit" ,"Q" ,0 ,0 ,0},
{ NM_TITLE, "Control" ,0 ,0 ,0 ,0},
{ NM_ITEM, "Global Parameters..." ,0 ,0 ,0 ,0},
{ NM_ITEM, "Simulation Speed" ,0 ,0 ,0 ,0},
{ NM_SUB, "Full Speed" ,"F",CHECKED|CHECKIT|MENUTOGGLE ,0x02 ,0},
{ NM_SUB, "Slow-Motion" ,"Z", CHECKIT|MENUTOGGLE ,0x01 ,0},
{ NM_ITEM, "Statistics Speed" ,0 ,0 ,0 ,0},
{ NM_SUB, "Ultra Slow" ,"1", CHECKIT|MENUTOGGLE ,0x7E ,0},
{ NM_SUB, "Very Slow" ,"2", CHECKIT|MENUTOGGLE ,0x7D ,0},
{ NM_SUB, "Slow" ,"3", CHECKIT|MENUTOGGLE ,0x7B ,0},
{ NM_SUB, "Normal" ,"4",CHECKED|CHECKIT|MENUTOGGLE ,0x77 ,0},
{ NM_SUB, "Fast" ,"5", CHECKIT|MENUTOGGLE ,0x6F ,0},
{ NM_SUB, "Very Fast" ,"6", CHECKIT|MENUTOGGLE ,0x5F ,0},
{ NM_SUB, "Ultra Fast" ,"7", CHECKIT|MENUTOGGLE ,0x3F ,0},
{ NM_ITEM, "Reset on Extinction" ,"R",CHECKED|CHECKIT|MENUTOGGLE ,0 ,0},
{ NM_ITEM, "Toggle Audit Trail" ,"A", CHECKIT|MENUTOGGLE ,0 ,0},
{ NM_ITEM, "Toggle Minima/Maxima","M", CHECKIT|MENUTOGGLE ,0 ,0},
{ NM_TITLE, "Statistics" ,0 ,0 ,0 ,0}
// Here we dynamically append the statistics menu items...
};
BPTR capture_file; // **!! not used
struct MsgPort *rexx_port;
#endif // AMIGA OS specific structs
//-------------------------------------------------------------------------
// ---------------------------
// Now the function prototypes
// ---------------------------
void handle_bug (BUGPTR creat);
void spawn_child (BUGPTR mother, BUGPTR father, FLAG sexy);
void grow_food (void);
void open_libraries (void);
void init_defaults (struct variable *varptr);
void init_gfx (void);
void alloc_structs (void);
void close_ecosys (void);
void close_gfx (void);
void wipe_environment (void);
FLAG reset_ecosystem (void);
void reset_global_stats (void);
void collect_bug_stats (BUGPTR creat);
void update_statistics (void);
void print_stat (struct statistic *stat, ULONG num, int x);
void print_minmax (struct statistic *stat, UBYTE value, int x);
void append_stats_menus (void);
void toggle_stat (void);
void handle_IDCMP_msgs (void);
void handle_AREXX_msgs (void);
void handle_menuselection (SHORT menu);
void uncheck_menuitem (int menunum, int itemnum);
void popup_About_req (void);
void popup_Author_req (void);
void PaintStatLabels (void);
void PaintStatValues (void);
void RePaintGraphs (void);
void WriteString (UWORD x, UWORD y, char *string);
void close_REXX (void);
FLAG kill_switch (void);
FLAG get_user_options (void);
FLAG menuitem_checkstate (int menunum, int itemnum);
FLAG post_REXX_port (char * portname);
RC parse_rexx_cmd (struct RexxMsg* msg);
UBYTE mutate (UBYTE gene);
UBYTE mix (UBYTE gene1, UBYTE gene2);
int quick_req (char *message, char *exit);
short rnd (void);
// The program has two fundamentally different ways of doing things:
// 1) It uses colored pixels in a visible screen to store the ecosystem.
// 2) It uses an invisible BYTE-map to hold the ecosystem.
//
// The program itself is unaware of this difference by using function pointers
// to 2 sets of routines doing the same thing, but on 2 different representations.
void (*cell_writer) (ULONG x, ULONG y, ULONG cell, BUGPTR bug);
void (*cell_eraser) (BUGPTR bug);
CELL (*cell_typer) (ULONG x, ULONG y);
FLAG (*food_checker) (ULONG x, ULONG y);
FLAG (*clear_checker) (ULONG x, ULONG y);
#define set_cell_to(x,y,cell,bug) (*cell_writer) ((x),(y),(cell),(bug))
#define erase_cell(bug) (*cell_eraser) (bug)
#define cell_type(x,y) (*cell_typer) ((x),(y))
#define is_food(x,y) (*food_checker) ((x),(y))
#define is_empty(x,y) (*clear_checker)((x),(y))
void blind_put_cell (ULONG x, ULONG y, ULONG cell, BUGPTR bug);
void gfx_put_cell (ULONG x, ULONG y, ULONG cell, BUGPTR bug);
extern void asm_plot_pixel (struct fast_pixel *fp, ULONG x, ULONG y, UBYTE color);
void blind_wipe_cell (BUGPTR bug);
void gfx_wipe_cell (BUGPTR bug);
extern void asm_fastwipe_pixel (struct fast_pixel *fp);
CELL blind_cell_type (ULONG x, ULONG y);
extern CELL asm_read_pixel (ULONG x, ULONG y);
FLAG blind_is_food (ULONG x, ULONG y);
extern FLAG asm_is_food (ULONG x, ULONG y);
FLAG blind_is_empty (ULONG x, ULONG y);
extern FLAG asm_is_empty (ULONG x, ULONG y);
//============================================================================
//
// MAIN()
//
// The main loop consists of:
//
// - resetting per-loop statistics
// - letting all live creatures do their thing once (& accumulating stats)
// - now and again dropping some food.
// - now and again updating the scrolling graphs with simualtion statistics
//
//============================================================================
void main(int argc, char **argv) {
UWORD i;
BUGPTR creat; // pointer to current creature
if (sizeof(struct bug) & 3)
printf("Warning : Bug Structure is not LONG-word-multiple sized (sizeof: %d)\n", sizeof(struct bug));
// This program needs at least OS version 2.0 and a Motorola 68020 CPU.
if ( ((struct Library*)SysBase)->lib_Version < 39) {
printf("I'm very sorry, but this program needs at least OS V39.\n");
exit(FATAL_ERROR);
}
if ( (SysBase->AttnFlags & AFF_68020) == 0 ) {
printf("I'm very sorry, but this program needs at least a 68020 CPU.\n");
exit(FATAL_ERROR);
}
printf("MegaJitter V1.3 (C) 1992-94 Laurence Vanhelsuwé.\n");
printf("An Evolution Simulator.\n\n");
if (FindPort(REXXPORTNAME)) {
printf("MegaJitter is already running!\n\n");
printf("You can control the running program by executing AREXX scripts which\n");
printf("communicate with MJ through the \"ADDRESS '" REXXPORTNAME "'\" command.\n");
exit(FATAL_ERROR);
}
// Grab Intuition, Graphics, REXX, ... function libraries.
open_libraries();
if (get_user_options()) { // ARGUMENT PARSING done Amiga-specific !
printf("Error in user selected options. Please consult documentation.\n");
close_gfx();
exit(FATAL_ERROR);
}
if (!post_REXX_port(REXXPORTNAME)) { // create public REXX (input) port
printf("Couldn't open AREXX communications port!\n");
close_gfx();
exit(FATAL_ERROR);
}
init_gfx(); // init graphics-related stuff.
alloc_structs(); // allocate data structs
reset_ecosystem(); // init eco data structs
SetTaskPri(FindTask(0L), -5); // lower our priority: be user friendly
births = deaths = 0; // update_stats resets
while ( !kill_switch() ) { // JOYSTICK FIRE BUTTON ends !!
if (sim_speed) Delay(sim_speed); // slow-motion or not ?
IFAUDIT printf("T = %5d\n", time);
time++; // time flows in Universe..
reset_global_stats(); // reset some statistics counters
// modulate daylight using time counter.
daylight = 50+ (WORD) (50 * cos( ((double)time) / 4000 ) );
//----------------------------------------------------------------
// Process all creatures... let them live.
//----------------------------------------------------------------
creat = &bugs[0]; // -> 1st bug in ecosystem
for (i=0; i< MAX_BUGS; i++, creat++) { // scan array for living bugs
if (creat->alive) {
handle_bug(creat); // let creature do its things
}
}
//----------------------------------------------------------------
// Now collect some statistics from all live bugs..
//----------------------------------------------------------------
numbugs = 0;
creat = &bugs[0]; // -> 1st bug in ecosystem
for (i=0; i< MAX_BUGS; i++, creat++) { // scan array for living bugs
if (creat->alive) {
numbugs++;
collect_bug_stats(creat); // then collect some figures..
}
}
//----------------------------------------------------------------
// If everything died and auto_reset feature is ON: restart all.
//----------------------------------------------------------------
if (!numbugs && auto_reset) reset_ecosystem();
//----------------------------------------------------------------
// Generate food for creatures.
//----------------------------------------------------------------
if (food_timer--) {}
else {
food_timer = FOOD_RATE; // reset counter.
grow_food(); // and drop a parcel of manna.
grow_food(); // twice
}
update_statistics(); // update stats graphs
handle_IDCMP_msgs(); // check our external events...
if (RexxSysBase) handle_AREXX_msgs(); // check rexx Msgs only if REXX active
} // END OF MAIN LOOP
// God pressed fire button on joystick: kill off Universe !
close_ecosys(); // dealloc data structs
close_gfx(); // close screens.
close_REXX();
SetTaskPri(FindTask(0L), 0); // restore standard CLI priority
exit(0); // don't set any return code
}
//===============================================================
// CREATURE ROUTINE
// ----------------
// This routine is called for every live creature.
// It does some things unconditionally to all creatures like
// - ageing
// - exhausting
//
// and does other things in a gene-dependent way like
// - moving
// - seeing
// - reproducing
// - dying of old age
//
//===============================================================
void handle_bug (BUGPTR creat) {
UBYTE ran, rot, color;
register int offs, x,y, dx,dy, coordmask;
int vision,scent,distance, hurdle;
short i;
CELL cell;
BUGPTR *sexy,*neighb, partner; // ptr to bugs wanting sex.
BUGPTR victim;
creat->age++; // creatures age in sync with Time...
creat->energy--; // energy is constantly going down
#ifndef FASTER
IFAUDIT if (creat->energy < 100)
printf("Bug #%3d is dying of exhaustion (E: %d)\n", creat-bugs, creat->energy);
#endif
// If creature exhausts energy or its designed lifespan... it dies.
if (creat->energy <= 0) {
creat->alive = FALSE;
erase_cell(creat); // then creature dies...
deaths++; // track morbidity rate
#ifndef FASTER
IFAUDIT printf("Bug #%3d died of lack of energy.\n", creat-bugs);
#endif
return;
} else
// If creature beyond optimum life span, start throwing dice but bias survival
// with age overrun and creatures' energy (the fittest have a better chance).
if (creat->age >= (creat->dying_age <<8 )) {
#ifndef FASTER
IFAUDIT printf("Bug #%3d is dying of old age (AGE: %d > %d)", creat-bugs, creat->age, creat->dying_age<<8);
#endif
hurdle = creat->age - (creat->dying_age <<8); // negative bias
hurdle -= creat->energy; // positive bias
if (hurdle < 0) hurdle = 0;
if (( rnd() & 0x1FFF) < hurdle) { // dice value upto 8191
creat->alive = FALSE;
#ifndef FASTER
IFAUDIT printf("Died of old age (ENERGY: %d).\n", creat->energy);
#endif
erase_cell(creat); // then creature dies...
deaths++; // track morbidity rate
return;
}
#ifndef FASTER
else
IFAUDIT printf("But is hanging on... (ENERGY: %d).\n", creat->energy);
#endif
}
// Control speed of each creature.
if (creat->speed_delay >= 1) { // while speed delay counter ticks...
creat->speed_delay--; // don't move.
return;
}
creat->speed_delay = creat->speed >> 5; // 0..7
// At this point, our creature decides it's gonna move.
// This entails an energy penalty.
creat->energy -= 2; // moving is N times harder than not moving
// Check energy exhaustion again.
if (creat->energy <= 0) {
creat->alive = FALSE;
#ifndef FASTER
IFAUDIT printf("Bug #%3d died of lack of energy when moving.\n", creat-bugs);
#endif
erase_cell(creat); // then creature dies...
deaths++; // track morbidity rate
return;
}
// Here's the engine of biological Evolution: when a creature reaches a certain
// level of fitness, it wants to procreate.
// Procreation occurs in two modes:
//
// 1) ASEXUAL: it just "splits". The child creature is slightly mutated.
// 2) SEXUAL: the creature looks for a neighbor who's also "sex-ripe"
//
// This is the mechanism that allows stronger (and weaker) forms to emerge.
// The environment will determine the statistical chances of survival and thus
// the path of evolution and extinctions.
if (creat->energy > creat->procr_energy<<6 ) {
#ifndef FASTER
IFAUDIT printf("Bug #%3d Needs to procreate (E=%4d > %4d)\t", creat-bugs, creat->energy, creat->procr_energy<<6);
#endif
if (creat->age < (creat->minsexage<<4)) {
#ifndef FASTER
IFAUDIT printf("But is too young...(AGE: %d < %d)\n", creat->age, creat->minsexage<<4);
#endif
} else
if (creat->age > (creat->maxsexage<<8)) {
#ifndef FASTER
IFAUDIT printf("But is too old...(AGE: %d > %d)\n", creat->age, creat->maxsexage<<8);
#endif
} else
if (!SEX) {
#ifndef FASTER
IFAUDIT printf("Just splitting...\n");
#endif
spawn_child(creat, (BUGPTR) 0, ASEXUAL);
} else
if ( (UBYTE)rnd() < creat->sexuality ) {
#ifndef FASTER
IFAUDIT printf("Looking for mate...");
#endif
sexy = owner_ptrs + creat->x + (creat->y <<9);
// Check all around randy bug to see if there's another bug with
// which it can mate.
for(i=0; i<NUM_NEIGHBORS; i++) {
offs = nbor_offs[i];
// Make sure we don't index into out-of-bounds RAM
// This could occur at the very top or the very bottom of
// the array, optionally we could AND **!!
if (offs > 0 ) {
if (creat->y == ACRE) continue;
} else {
if (creat->y == 0) continue;
}
neighb = sexy + offs;
if (partner = *neighb) {
if (partner < bugs || partner > &bugs[MAX_BUGS]) {
printf("FATAL BUG: owner_ptr (%08X) doesn't point back to a creature !!\n", partner);
printf("Mother #%d (x,y)= (%d,%d)\n", creat-bugs, creat->x, creat->y);
printf("Trying to mate with offset %d (%d)\n", i, nbor_offs[i]);
exit(PROG_BUG_ERROR);
}
#ifndef FASTER
IFAUDIT printf("\nPartner found @ (%d,%d)\n", partner->x, partner->y);
#endif
// Select partners which are strong and healthy !!
if (partner->energy > creat->energy) {
#ifndef FASTER
IFAUDIT {
printf("Sex ! (BUGS: %d & %d, ENERGIES: %d & %d)\n", creat-bugs, partner-bugs, creat->energy, partner->energy);
printf("Mother (x,y)= (%d,%d)\n", creat->x, creat->y);
printf("Father (x,y)= (%d,%d)\n", partner->x, partner->y);
}
#endif
spawn_child(creat, partner, SEXUAL);
break;
} else IFAUDIT printf("Partner to weak ! (%d < %d)\n", partner->energy, creat->energy);
}
}
IFAUDIT if (i==NUM_NEIGHBORS) printf("Failed to locate.\n");
} else {
#ifndef FASTER
IFAUDIT printf("Just splitting...\n");
#endif
spawn_child(creat, (BUGPTR) 0, ASEXUAL);
}
} // IF ENERGY > PROCR_ENERGY
// This section implements food vision (if not disabled)
// A creature's vision gets modulated by the time of day.
// 0.. 25 = total darkness (blind)
// 25..100 = 0..100% vision.
scent = 0; // accumulated scent = 0
if (VISION) {
if (daylight > 25) { // if it's day ... (night = total dark)
vision = creat->vision>>2; // shrink 0..255 gene to 0..63 length range
i = vision = (int) ((float) vision * (float) (daylight-25) / 75.0);
distance = 0; // set distance index to 0
dx = xymovs[creat->dir].dx; // look in direction of
dy = xymovs[creat->dir].dy; // current movement.
coordmask = ACRE; // cache andmask 511 in register
x = (creat->x +dx) & ACRE;
y = (creat->y +dy) & ACRE;
while(i--) {
if ( ! is_empty(x, y))
scent += scent_vals[distance];
x = (x+dx) & coordmask; // & ACRE to ensure we don't look
y = (y+dy) & coordmask; // beyond world boundary **!!
distance++;
}
#ifndef FASTER
IFAUDIT if (scent) printf("Bug #%3d detected food with a vision of %3d (scent: %d)\n", creat-bugs, vision, scent);
#endif
}
}
// This section of code determines how creatures move.
// The strength of the scent LOWERS the probability of doing a gene-induced turn
rot = 0; // clear rotation accumulator
ran = (UBYTE)rnd(); // throw a dice
if (ran > scent) { // if number over scent hurdle...
if ((UBYTE)rnd() > creat->right ) rot = rot + 1;
if ((UBYTE)rnd() > creat->left ) rot = rot - 1;
creat->dir += rot;
creat->dir &= 7; // force into 0..7
#ifndef FASTER
IFAUDIT if (VISION && scent) printf(" .. And deviated from scent path !! (%d > %d)\n", ran, scent);
#endif
}
// Calculate tentative forward step (x,y)
x = (creat->x + xymovs[creat->dir].dx) & ACRE; // move into 1 of 8 dirs
y = (creat->y + xymovs[creat->dir].dy) & ACRE;
// If the creature is about to move onto a pixel with food on, "eat it" !
// Food can be either another bug or simple vegetation.
// If Bug is mainly a Veggie, there's a strong bias to refusing meat.
// If Bug is mainly a meat eater, there's a strong bias to refusing vegetation.
cell = cell_type(x, y);
//printf("Cell type bug #%3d is moving towards: %d @ (%d,%d)\n", creat-bugs, cell, x,y);
switch (cell) {
case VEGE_CELL:
if ((UBYTE)rnd() > creat->foodtype) { // small FOODTYPE = VEGETARIAN
IFAUDIT printf("Bug #%3d Eats Vegetation @ (%d,%d) (+%d E, NEW: %d) (GENE=%3d% (%s))\n",
creat-bugs,x,y, FOOD_ENERGY, creat->energy + FOOD_ENERGY,
creat->foodtype*100/256,
creat->foodtype>128?"CARNIVORE":"HERBIVORE");
creat->energy += FOOD_ENERGY; // if food found, eat it.
food_in_system--; // track amount of food left
} else {
IFAUDIT printf("Bug #%3d avoids Vegetation (GENE=%3d% (%s))\n",
creat-bugs, creat->foodtype*100/256,
creat->foodtype>128?"CARNIVORE":"HERBIVORE");
break; // don't fall through. Food obstructs move !
}
// Now fall through as if moving onto empty cell. **!!
case NEUT_CELL:
erase_cell(creat); // erase old position
creat->x = x; // move to new cell position
creat->y = y;
// color = 1 + (creat->energy >> color_divider);
// color = color < VEGE_CELL ? color : VEGE_CELL-1;
color = VEGE_CELL-1;
set_cell_to(x,y, color, creat);
if ( *(owner_ptrs+x+(y<<9)) ) {
BUGPTR rogue;
rogue = *(owner_ptrs+x+(y<<9));
printf("FATAL BUG: supposedly EMPTY cell contains an owner back-ptr !\n");
printf("Dangling back-ptr : %08X (bug # %d)\n", rogue, rogue-bugs);
printf("Corresponds to following bug:\n");
printf("%s, (%d,%d), (empty cell x,y = %d,%d)\n",
rogue->alive?"ALIVE":"DEAD", rogue->x, rogue->y, x, y);
}
*(owner_ptrs+x+(y<<9)) = creat; // update parallel array
break;
case WALL_CELL: // don't move in case of wall !
break;
default: // default case SHOULD mean bumping into bug...
victim = *(owner_ptrs + x + (y<<9));
if (victim) {
if ((UBYTE)rnd() < creat->foodtype) { // large FOODTYPE = CARNIVORE
IFAUDIT printf("Bug #%3d wants to eat bug #%3d @ (%d,%d) (GENE=%3d% (%s))\n",
creat-bugs,victim-bugs,x,y,
creat->foodtype*100/256,
creat->foodtype>128?"CARNIVORE":"HERBIVORE");
if (victim->energy < creat->energy) {
creat->energy += victim->energy; // eat weaker creature
IFAUDIT printf("Bug #%3d Eats Bug #%3d (+%d E NEW: %d)!\n",
creat-bugs, victim-bugs, victim->energy, creat->energy);
victim->alive = FALSE; // kill victim
deaths++;
erase_cell(victim); // erase old position
erase_cell(creat); // erase old position
creat->x = x; // move to new cell position
creat->y = y;
// color = 1 + (creat->energy >> color_divider);
// color = color < VEGE_CELL ? color : VEGE_CELL-1;
color = VEGE_CELL-1;
set_cell_to(x,y, color, creat);
*(owner_ptrs+x+(y<<9)) = creat; // update parallel array
} else {
IFAUDIT printf("Bug #%3d Avoids Eating Bug #%3d because 2nd bug is stronger!\n",
creat-bugs, victim-bugs);
}
} else {
IFAUDIT printf("Bug #%3d avoids Bug @ (%d,%d) (+%d E, NEW: %d) (GENE=%3d% (%s))\n",
creat-bugs,x,y, FOOD_ENERGY, creat->energy + FOOD_ENERGY,
creat->foodtype*100/256,
creat->foodtype>128?"CARNIVORE":"HERBIVORE");
}
}
}
}
//----------------------------------------------------------------
// OLD Oxygen stuff originally straight after age++;
//----------------------------------------------------------------
// oxygen += creat->oxy_produce; // discharge by-product O2
// if (oxygen >= creat->oxy_consume) { // if O2-breathing, and if there's
// oxygen -= creat->oxy_consume; // enough O2 : grab your quota
// } else creat->energy = 0; // otherwise you suffocate
//
// carb_dioxide += creat->co2_produce;
// if (carb_dioxide >= creat->co2_consume) { // idem with CO2
// carb_dioxide -= creat->co2_consume;
// } else creat->energy = 0;
//---------------------------------------------------------------------------
// This routine handles the details of giving birth to a new creature.
//
// 1) ASEXUAL
// Basically, a dead bug structure is found and then the parent is cloned
// in this space. The gene variables of the parent are slightly mutated
// in the child.
//
// 2) SEXUAL
// A dead bug structure is found and then the child genes are created from
// a mixture of both parent's genes.
//
// The energy exchange which happens is as follows:
// - the child gets 30 % of the mother's energy
// - the mother's energy goes down to 70%
//
// For sexual reproduction both parents drop to 85% of their previous level.
//---------------------------------------------------------------------------
void spawn_child (BUGPTR mother, BUGPTR father, FLAG sexy) {
BUGPTR child;
int i;
child = &bugs[0];
for (i=0; i< MAX_BUGS; i++, child++) { // scan array to find empty slot..
if (!child->alive) {
births++; // record birth
*child = *mother; // copy mother's genes for starters..
// This means:
// child->x == mother->x
// child->y == mother->y
// child->prevpix == mother->prevpix
child->age = 0; // new-born !
child->generation++; // a new generation !
if (sexy) {
child->energy = (WORD) (
30 * ( (int) mother->energy +
(int) father->energy ) / 100) ;
mother->energy = (WORD) ((85 * (int)mother->energy) / 100);
father->energy = (WORD) ((85 * (int)father->energy) / 100);
child->right = mix(mother->right, father->right);
child->left = mix(mother->left, father->left);
child->vision = mix(mother->vision, father->vision);
child->sexuality = mix(mother->sexuality,father->sexuality);
child->dirgene = mix(mother->dirgene, father->dirgene);
child->dying_age = mix(mother->dying_age,father->dying_age);
child->minsexage = mix(mother->minsexage,father->minsexage);
child->maxsexage = mix(mother->maxsexage,father->maxsexage);
child->speed = mix(mother->speed, father->speed);
child->foodtype = mix(mother->foodtype, father->foodtype);
child->procr_energy = mix(mother->procr_energy, father->procr_energy);
} else {
child->energy = (WORD) ((30 * (int)mother->energy) / 100);
mother->energy = (WORD) ((70 * (int)mother->energy) / 100);
child->right = mutate (child->right);
child->left = mutate (child->left );
child->vision = mutate (child->vision);
child->sexuality = mutate (child->sexuality);
child->dirgene = mutate (child->dirgene);
child->dying_age = mutate (child->dying_age);
child->minsexage = mutate (child->minsexage);
child->maxsexage = mutate (child->maxsexage);
child->speed = mutate (child->speed);
child->foodtype = mutate (child->foodtype);
child->procr_energy = mutate (child->procr_energy);
}
// whizz away from mother in a gene dependent way
child->dir = (child->dir +(child->dirgene>>5)) % 8;
child->speed_delay = child->speed >> 6; // 0..3
return;
}
}
}
//----------------------------------------------------------------
// Slightly modify a gene (a variable in the unsigned range 0..255).
// The variable is not allowed to wrap-around, but instead "hits the
// ceiling".
//
// Note that the mutation coding is subtle. The mutation should not
// have any bias towards lowering or increasing the gene's value **!!
// It should be STRICTLY random !
//----------------------------------------------------------------
UBYTE mutate (UBYTE gene) {
int newgene;
newgene = gene + rnd()%MUTATE_RANGE - rnd()%MUTATE_RANGE;
if (newgene > 255) return 255;
else
if (newgene < 0 ) return 0;
return (UBYTE) newgene;
}
//----------------------------------------------------------------
// Mix the 2 genes of two parent creatures to produce a child's gene.
//----------------------------------------------------------------
UBYTE mix (UBYTE gene1, UBYTE gene2) {
return mutate( (UBYTE) (( (UWORD)gene1 + (UWORD)gene2 ) /2 ));
}
//----------------------------------------------------------------
// Drop some food in our world on a spot which IS NOT YET FOOD.
// We can't put food on food OR food on a bug otherwise our food stats
// will be inaccurate.
//----------------------------------------------------------------
void grow_food(void) {
short fx,fy;
UBYTE food=VEGE_CELL;
int dummy;
register int attempts;
double ra,rr; // random angle (in rads)
// modulate food growth with night/day
// From 0..50 modulate growth between 50% and 100%
// Between 51..100 food growth is always 100%
if (rnd()%100 > (daylight+50)) return;
attempts = 20; // attemps at finding empty spot..
while (attempts--) {
if (OASIS_SIZE) {
ra = (double) rnd(); // pick a random dot inside a circle
rr = (double) (rnd() % OASIS_SIZE);
fx = ACRE/2 + (int) (cos(ra) * rr);
fy = ACRE/2 + (int) (sin(ra) * rr);
} else {
fx = (rnd()%ACRE) & (-X_AND);
dummy = fx * fx * attempts; // introduce random delay
dummy /= 20; // randomness stems from data-depen
fy = (rnd()%ACRE) & (-Y_AND);
}
fx &= ACRE; // ensure we stay within the ACRE
fy &= ACRE;
if (is_empty(fx, fy)) {
set_cell_to(fx,fy, food, &dummy_bug);
food_in_system++; // track amount of food in our world
break;
}
}
}
//----------------------------------------------------------------
// Gain access to system function libraries (Dynamic Link Libraries).
//----------------------------------------------------------------
void open_libraries(void) {
IntuitionBase = (void*) OpenLibrary("intuition.library",0);
if (!IntuitionBase) {
printf("MAJOR PROBLEM: Couldn't open INTUITION library!\n");
exit(FATAL_ERROR);
}
GfxBase = (void*) OpenLibrary("graphics.library",0);
if (!GfxBase) {
CloseLibrary((struct Library *) IntuitionBase);
printf("MAJOR PROBLEM: Couldn't open GRAPHICS library!\n");
exit(FATAL_ERROR);
}
RexxSysBase = (void*) OpenLibrary("rexxsyslib.library",0);
if (!RexxSysBase) {
printf("AREXX Not available. All of MegaJitter's AREXX functions are disabled.\n");
printf("If you do have AREXX on your system, please run RexxMast to start AREXX,\n");
printf("next time
before
you start MegaJitter.\n\n");
}
}
//----------------------------------------------------------------
// Check any user options.
//
// For example, the program can be evoked as follows:
//
// 1> MJ INIT_FOOD 2000 INIT_BUGS 20 SEX AUDIT
//----------------------------------------------------------------
FLAG get_user_options( void ) {
struct RDArgs *rdargs;
struct variable *var;
#ifndef DEBUG
if (! (rdargs = ReadArgs(MJIT_DOS_TEMPLATE, (ULONG*) &options, NULL))) {
printf("ERROR: ARGUMENTS INCORRECT.\n");
printf("Type MegaJitter ? for a full syntax template.\n\n");
return SYNTAX_ERROR;
} // Free RDA only AFTER we've parsed ptrs in options pointing into RDA **!!
#endif
// Initialize device independent cell handlers (visual routines are the default)
if (NOANIM) {
cell_writer = blind_put_cell; // all of these are POINTERS to
cell_eraser = blind_wipe_cell; // routines.
cell_typer = blind_cell_type;
food_checker = blind_is_food;
clear_checker = blind_is_empty;
} else {
cell_writer = gfx_put_cell;
cell_eraser = gfx_wipe_cell;
cell_typer = asm_read_pixel;
food_checker = asm_is_food;
clear_checker = asm_is_empty;
}
// If user didn't select the NTSC option and MJ thinks it is running on an
// NTSC machine, ask user if it wishes to switch NTSC compatibility mode on.
if (!NTSC && SysBase->ex_EClockFrequency != 709379 ) {
if (quick_req("I think this is an NTSC machine!", "CORRECT|NOPE"))
NTSC = TRUE;
}
// Any non-initialized simulation globals get initialized now.
init_defaults(&varlist[0]); // others are intialized from CMD line.
// **!! NOW we can free RDA (AFTER we've parsed ptrs)
FreeArgs(rdargs);
// Most sanity checks can be performed from pre-set data...
var = &varlist[0];
while(var->name) {
if (*var->address > var->max_val) {
printf("%s has to be in the range [%d..%d] ! (%d > %d)\n",
var->name, var->min_val, var->max_val, *var->address, var->max_val);
exit(SYNTAX_ERROR);
} else
if (*var->address < var->min_val) {
printf("%s has to be in the range [%d..%d] ! (%d < %d)\n",
var->name, var->min_val, var->max_val, *var->address, var->min_val);
exit(SYNTAX_ERROR);
}
var++; // goto next var in list
}
// Check user-initialized parameters for outrageous values.
// Inform user of range of variables if out of bounds.
if (INIT_BUGS > MAX_BUGS) {
printf("INIT_BUGS can not be larger than MAX_BUGS! (%d > %d)\n",
INIT_BUGS, MAX_BUGS);
return SYNTAX_ERROR;
}
// Copy MAX_BUGS into full scale value field of numbugs statistic.
available_stats[0].scale = MAX_BUGS;
// Show user the resulting scenario settings, if requested.
if (SHOWDEFAULTS) {
dumpdefaults((void*)0);
printf("\n\nHit ENTER to continue...\n");
getch();
}
// If we want to capture all simulation data & statistics, open recording file.
if (CAPTURE) {
capture_file = Output(); // **!! TEMPORARY
}
return 0;
}
//----------------------------------------------------------------
//
//----------------------------------------------------------------
RC dumpdefaults(void* dummy) {
printf("audit = %d (%08X)\n",REPORT,REPORT);
printf("capture = %d (%08X)\n",CAPTURE,CAPTURE);
printf("noanim = %d (%08X)\n",NOANIM,NOANIM);
printf("vision = %d (%08X)\n",VISION,VISION);
printf("sex = %d (%08X)\n",SEX,SEX);
printf("MAX_BUGS = %d (%08X)\n",MAX_BUGS,MAX_BUGS);
printf("\n");
printf("INIT_BUGS = %d (%08X)\n",INIT_BUGS,INIT_BUGS);
printf("INIT_FOOD = %d (%08X)\n",INIT_FOOD,INIT_FOOD);
printf("INIT_ENERGY = %d (%08X)\n",INIT_ENERGY,INIT_ENERGY);
printf("INIT_VARIANCE = %d (%08X)\n",INIT_VARIANCE,INIT_VARIANCE);
printf("MUTATE_RANGE = %d (%08X)\n",MUTATE_RANGE,MUTATE_RANGE);
printf("FOOD_RATE = %d (%08X)\n",FOOD_RATE,FOOD_RATE);
printf("FOOD_ENERGY = %d (%08X)\n",FOOD_ENERGY,FOOD_ENERGY);
printf("X_AND = %d (%08X)\n",X_AND,X_AND);
printf("Y_AND = %d (%08X)\n",Y_AND,Y_AND);
printf("OASIS_SIZE = %d (%08X)\n",OASIS_SIZE,OASIS_SIZE);
// printf("INIT_O2 = %d (%08X)\n",INIT_O2,INIT_O2);
// printf("INIT_CO2 = %d (%08X)\n",INIT_CO2,INIT_CO2);
return TRUE;
}
//----------------------------------------------------------------
// Set all non-initialized Simulation Global Parameters to a default value
// IF the user hasn't given a value himself.
//----------------------------------------------------------------
void init_defaults (struct variable *varptr) {
ULONG *var;
while (varptr->name) {
var = varptr->address;
if (*var) { // if RDA initialized a ptr to an arg,
*var = atoi((char*) *var); // convert ASCII arg to number
} else {
*var = varptr->init_val; // else use built-in default value.
}
varptr++; // goto next variable
}
}
//----------------------------------------------------------------
// Before every main loop : reset and recalc some global vars.
//----------------------------------------------------------------
void reset_global_stats() {
//int i;
// Calculate the mapping factor to go from
// creat->energy to creature pixel/cell number (as a right shift)
// color_divider = (MAX_ENERGY+FOOD_ENERGY) / MAX_COLORS;
// for (i=0; i< 30; i++) { // determine howmuch to right shift
// if (color_divider) {
// color_divider >>= 1;
// } else break;
// }
// i--;
// color_divider = i;
// printf("SHIFTS = %d\t\t\t%d>>%d=%d\n\n", i, MAX_ENERGY+FOOD_ENERGY, i, (MAX_ENERGY+FOOD_ENERGY)>
//---------------------------------------------------------------
// reset some statistics counters
total_age = 0;
total_energy = 0;
total_right = 0;
total_left = 0;
total_vision = 0;
total_sexuality = 0;
total_dying_age = 0;
total_minsexage = 0;
total_maxsexage = 0;
total_speed = 0;
total_foodtype = 0;
total_procr = 0;
// total_O2 = 0;
// total_CO2 = 0;
max_right= max_left= max_vision = max_dying_age = max_speed = 0;
min_right= min_left= min_vision = min_dying_age = min_speed = 255;
max_foodtype = max_sexuality = max_minsexage = max_maxsexage = 0;
min_foodtype = min_sexuality = min_minsexage = min_maxsexage = 255;
max_procr= 0;
min_procr= 255;
}
//----------------------------------------------------------------
// For every live creature: collect some of its current settings.
//----------------------------------------------------------------
#define update_minmax(v,min,max) \
if ( (v) > (max) ) (max) = (v);\
if ( (v) < (min) ) (min) = (v)
void collect_bug_stats (BUGPTR creat) {
total_age += creat->age;
total_energy += creat->energy; // track various simulation
total_left += creat->left; // variables and/or results
total_right += creat->right;
total_vision += creat->vision;
total_sexuality += creat->sexuality;
total_dying_age += creat->dying_age;
total_minsexage += creat->minsexage;
total_maxsexage += creat->maxsexage;
total_speed += creat->speed;
total_foodtype += creat->foodtype;
total_procr += creat->procr_energy;
// total_O2 += creat->oxy_produce;
// total_CO2 += creat->co2_produce;
// total_O2 -= 255 - creat->co2_produce;
// total_CO2 -= 255 - creat->oxy_produce;
// update some minima & maxima.
update_minmax (creat->right ,min_right ,max_right);
update_minmax (creat->left ,min_left ,max_left );
update_minmax (creat->vision ,min_vision ,max_vision);
update_minmax (creat->sexuality ,min_sexuality ,max_sexuality);
update_minmax (creat->maxsexage ,min_maxsexage ,max_maxsexage);
update_minmax (creat->minsexage ,min_minsexage ,max_minsexage);
update_minmax (creat->dying_age ,min_dying_age ,max_dying_age);
update_minmax (creat->speed ,min_speed ,max_speed);
update_minmax (creat->foodtype ,min_foodtype ,max_foodtype);
update_minmax (creat->procr_energy ,min_procr ,max_procr);
}
//----------------------------------------------------------------
// From the numeric statistics collected, draw some pretty graphics.
//----------------------------------------------------------------
void update_statistics() {
int linelen;
// Print Time.
//------------
lineptr = linebuf; // reset stats output line.
if ((time&(TIMER_SPEED-1)) == 0) {
SetAPen(stat_rp, 2);
sprintf(strbuf,"Time = %8d", time); // convert number to N-digit string
WriteString(240, CHAR_HEIGHT, strbuf);
}
if (time == TIMER_SPEED) {
sprintf(strbuf,"Runs = %4d", runs); // convert number to N-digit string
WriteString(240, 2*CHAR_HEIGHT, strbuf);
}
// Check if it's time to update the stats on the screen...
if (time&(graph_speed-1)) return;
if (CAPTURE) {
lineptr = stpcpy(lineptr, strbuf);
*lineptr++ = ',';
}
// Scroll graphs one pixel to the left, this gives a kind of medical EEC
// effect like real-time traces.
// Since graphs are in plane 0 only, just scroll plane 0 for speed.
stat_rp->Mask = 1; // use bitplane 0 only
SetAPen(stat_rp, 0); // for numbers graph
RectFill(stat_rp, 0, 400, 112, 511); // erase all numbers
SetAPen(stat_rp, 1);
ScrollRaster(stat_rp, 1,0, LE, STATS_TOPY-1, STAT_SCR_WIDTH-1, STAT_WIN_HEIGHT-1);
stat_rp->Mask = 3; // restore normal mask
// Calculate population-wide averages (IF numbugs <> 0 to avoid DIV BY ZERO)
if (numbugs) {
total_age /= numbugs;
total_energy /= numbugs;
total_right /= numbugs;
total_left /= numbugs;
total_vision /= numbugs;
total_sexuality /= numbugs;
total_speed /= numbugs;
total_foodtype /= numbugs;
total_maxsexage = (total_maxsexage<<8) / numbugs; // scaled to 0.. 64K
total_minsexage = (total_minsexage<<4) / numbugs; // scaled to 0..4096
total_dying_age = (total_dying_age<<8) / numbugs;
total_procr = (total_procr<<6) / numbugs; // scaled to 0.. 16K
}
PaintStatValues(); // draw all stats
if (CAPTURE) { // if stats CAPTURE is on, dump them.
*lineptr++ = LF; linelen = lineptr - linebuf;
Write (capture_file, linebuf, linelen);
}
births = deaths = 0; // they accumulate for every update
}
//----------------------------------------------------------------
// Time to update the stats graphs.
// Go through all stats, and if they're currently on display, print them.
//----------------------------------------------------------------
void PaintStatValues (void) {
struct statistic *stat;
UBYTE min,max;
UWORD i;
ULONG *stat_ptr;
graphnum = 1; // start drawing statistics graphs
stat = &available_stats[0]; // statistic entry
for (i=0; i< NUM_STATS; i++, stat++) {
// record stat in its own buffer.
stat_ptr = stat->buffer + stat_bufix; // record basic stat
*stat_ptr = *stat->stat_addr;
if (stat->minmax_addr) {
stat_ptr += 256;
*stat_ptr = (ULONG) *stat->minmax_addr; // record minimum too
stat_ptr += 256;
*stat_ptr = (ULONG) *(stat->minmax_addr+1); // and maximum.
}
if (graphnum > MAX_GRAPHS) continue;
if (stat->displayed) {
print_stat (stat, *stat->stat_addr, STAT_SCR_WIDTH-1);
if ( (time&(2*graph_speed-1)) && stat->minmax_addr && MINMAX) {
min = *stat->minmax_addr; print_minmax(stat, min, STAT_SCR_WIDTH-1);
max = *(stat->minmax_addr+1); print_minmax(stat, max, STAT_SCR_WIDTH-1);
}
graphnum++;
}
}
stat_bufix++; // index is byte, so wraps around at 255.
}
//----------------------------------------------------------------
// The user has selected a new mix of statistics to be displayed.
// We have to redraw all stats from their buffers.
//
// This is done without scrolling but simply by plotting for X=LE to SCR_WIDTH
//----------------------------------------------------------------
void RePaintGraphs (void) {
struct statistic *stat;
UBYTE ptr, min,max;
UWORD i, x;
ULONG value;
FLAG oldcapture;
oldcapture = CAPTURE; CAPTURE = FALSE; // stop value dumping while repainting
NUMERIC_STATS = FALSE; // stop numerical output while "
SetAPen(stat_rp, 1); // Use pen 1 for graphs
stat_rp->Mask = 1; // SCROLL bitplane 0 only
// in the for loop, ptr wraps around 255 to reach final value.
for (x=LE, ptr = stat_bufix+1; ptr != stat_bufix; x++, ptr++) {
graphnum = 1; // start drawing statistics graphs
stat = available_stats; // 1st statistic entry
for (i=0; i< NUM_STATS; i++, stat++) {
if (graphnum > MAX_GRAPHS) break;
if (stat->displayed) {
value = *(stat->buffer + ptr); // get a recorded stat value
print_stat (stat, value, x);
if ((x&1) && stat->minmax_addr && MINMAX ) {
min = *(stat->buffer +ptr +256); print_minmax(stat, min, x);
max = *(stat->buffer +ptr +512); print_minmax(stat, max, x);
}
graphnum++;
}
}
}
stat_rp->Mask = 3; // restore normal mask
CAPTURE = oldcapture; // restore CAPTURE status
NUMERIC_STATS = TRUE;
}
//----------------------------------------------------------------
// Print a new statistic
// - graphically and
// - numerically (if enabled)
//
// The graph is scaled to be 64 pixels fullscale.
// The numerical output is unscaled.
//----------------------------------------------------------------
void print_stat (struct statistic *stat, ULONG num, int x) {
int scalednum;
register int y;
scalednum= (num << 6 ) / stat->scale; // scale full scale to 0..63
if (stat->flip_graph) {
y = CURGRAPH_Y -GRAPH_HEIGHT + scalednum;
WritePixel(stat_rp, x, y-1);
if (NUMERIC_STATS) {
sprintf(strbuf,"%5d", stat->scale -num); // convert number to N-digit string
WriteString(LE-50, CURGRAPH_Y-3, strbuf);
}
} else {
y = CURGRAPH_Y- scalednum;
WritePixel(stat_rp, x, y-1);
if (NUMERIC_STATS) {
sprintf(strbuf,"%5d", num); // convert number to N-digit string
WriteString(LE-50, CURGRAPH_Y-3, strbuf);
}
}
// If simulation data capture is enabled, record stats in file.
if (CAPTURE) {
lineptr = stpcpy(lineptr, strbuf);
*lineptr++ = ',';
}
}
//----------------------------------------------------------------
// Print minima and maxima of a statistic
// - graphically
//
// The graph is scaled to be 64 pixels fullscale.
//----------------------------------------------------------------
void print_minmax (struct statistic *stat, UBYTE value, int x) {
register int y;
value = value >> 2;
if (stat->flip_graph) {
y = CURGRAPH_Y -GRAPH_HEIGHT + value; WritePixel(stat_rp, x, y-1);
} else {
y = CURGRAPH_Y- value; WritePixel(stat_rp, x, y-1);
}
}
//----------------------------------------------------------------
// Initialize Ecosystem related data structures ...
//
// Dynamically allocate:
// - (512*512) back-pointers array (1 Mb **!!)
// - (512*512) cell array (256 K)
// - array of BUGS structures (10 K for default 300 bugs)
// - statistics buffers (50 K roughly)
//----------------------------------------------------------------
void alloc_structs (void) {
UWORD i;
struct statistic *stat;
// Get back-pointer 2-D Array
owner_ptrs = (struct bug **) AllocMem(PTR_MAP_SIZE, MEMF_CLEAR);
if (!owner_ptrs) {
printf("FATAL INIT: Failed to allocate creature back-pointers map.\n");
exit(FATAL_ERROR);
}
// Get 2-D Cell array
environment = AllocMem(SCREEN_WIDTH*SCREEN_WIDTH, MEMF_CLEAR);
if (!environment) {
printf("FATAL INIT: Couldn't allocate RAM for environment !!\n");
exit(40);
}
// Get Array of Bug structs
bugs = (BUGPTR) AllocMem (MAX_BUGS*sizeof(struct bug), MEMF_CLEAR);
if (!bugs) {
printf("FATAL INIT: Failed to allocate pool for creatures.\n");
exit(FATAL_ERROR);
}
// Allocate statistics buffers
stat = available_stats;
for (i=0; i< NUM_STATS; i++) {
stat->buffer = AllocMem(3*256*4, MEMF_CLEAR);
if (!stat->buffer) {
printf("FATAL INIT: Couldn't allocate RAM for Statistics buffer #%d !!\n", i);
exit(40);
}
stat++;
}
}
//----------------------------------------------------------------
// Reset the entire ecosystem.
//
// Spray INIT_FOOD food parcels.
// Initialize INIT_BUGS random creatures.
//----------------------------------------------------------------
FLAG reset_ecosystem (void) {
struct statistic *stat;
BUGPTR creat;
UWORD i;
// Draw an informative dividing line between previous simulation run and present
// Only draw a dividing line for statistics which are active.
SetAPen(stat_rp,1);
Move(stat_rp, STAT_SCR_WIDTH-1, STATS_TOPY);
graphnum = 1;
stat = &available_stats[0]; // -> list of stats.
for (i=0; i< NUM_STATS; i++) {
if (graphnum > MAX_GRAPHS) continue;
if (stat->displayed) {
Draw(stat_rp, STAT_SCR_WIDTH-1, STATS_TOPY+graphnum*GRAPH_HEIGHT-1);
graphnum++;
}
stat++;
}
// Initialize food environment.
wipe_environment(); // level playing field
food_in_system = 0;
daylight = 100; // ensure full daylight (no modulation)
for (i=0; i< INIT_FOOD; i++) { // throw some food...
grow_food();
}
// Initialize a RANDOM pool of creatures (fertile, infertile, weak, strong, ...)
creat = &bugs[0];
for (i=0; i< MAX_BUGS; i++, creat++) {
creat->alive = FALSE; // set all bugs to dead
}
creat = &bugs[0];
for (i=0; i< INIT_BUGS; i++, creat++) { // create a couple of
creat->alive = TRUE; // random 1st-generation
creat->energy = INIT_ENERGY + (INIT_ENERGY * rnd()%20 / 100);
// + max 20% of INIT_ENERGY
creat->x = rnd()&ACRE;
creat->y = rnd()&ACRE;
creat->dir = rnd()&7; // creatures
creat->generation = 0;
creat->age = 0;
creat->right = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->left = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->vision = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->sexuality = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->dying_age = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->speed = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->foodtype = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->procr_energy = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->minsexage = rnd()%32; // give 'em a break !
creat->maxsexage = 255 -rnd()%INIT_VARIANCE;
creat->dirgene = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->oxy_produce = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->oxy_consume = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->co2_produce = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->co2_consume = (128-INIT_VARIANCE/2) + rnd()%INIT_VARIANCE;
creat->speed_delay = creat->speed >> 5; // 0..7
creat->prevpix.pixel_byte_offs= 0; // all 1st pixels erase (0,0)
creat->prevpix.pixel_bit_no = 0;
}
// oxygen = INIT_O2; // primordial atmosphere
// carb_dioxide = INIT_CO2;
food_timer = FOOD_RATE; // start food timer.
time = 0; // Big Bang !
runs++; // track number of simulation runs
return TRUE;
}
//----------------------------------------------------------------
// Erase all food and/or bugs from environment.
//----------------------------------------------------------------
void wipe_environment(void) {
register int i;
register ULONG *clr;
if (!NOANIM)
BltClear(bm->Planes[0], ECO_SCR_DEPTH*SCREEN_HEIGHT*SCREEN_WIDTH/8, 0);
// Wipe all cells
clr = (ULONG*) environment;
for (i=0; i< (512*512/(4*4)); i++) {
*clr++ = 0;
*clr++ = 0;
*clr++ = 0;
*clr++ = 0;
}
// Wipe all back-ptrs.
clr = (ULONG*) owner_ptrs;
for (i=0; i< (512*512/4); i++) {
*clr++ = 0;
*clr++ = 0;
*clr++ = 0;
*clr++ = 0;
}
}
//----------------------------------------------------------------
// Initialize graphics-related program elements.
//
// - open the Statistics Screen
// - if animated simulation, also open the Eco-system Screen
//----------------------------------------------------------------
#ifndef MONOCHROME
UWORD eco_colors[NUM_COLORS] =
{0xFFF, 0xAAA, 0x999, 0x888, 0x777, 0x666, 0x555, 0x444, //
0x333, 0x222, 0x111, 0x111, 0x000, 0x000, // CREATURES
0x000, 0x000}; // WALL, FOOD
UWORD stat_colors[4] = {0x111, 0xFF0, 0xC00, 0xFFF};
#else
UWORD eco_colors[NUM_COLORS] =
{0xFFF, 0xEEE, 0xAAA, 0x888,
0x666, 0x444, 0x222, 0x555};
UWORD stat_colors[4] = {0x000, 0xFF0, 0xFFF, 0xFFF};
#endif
//----------------------------------------------------------------
void init_gfx (void) {
if (NTSC) MAX_GRAPHS = 6; // reduced size Screen !!
else MAX_GRAPHS = 8;
graph_speed = 64; // NORMAL graph speed
STAT_WIN_HEIGHT = (MAX_GRAPHS*GRAPH_HEIGHT + STATS_TOPY); // enough for N graphs
ns.Width = STAT_SCR_WIDTH;
ns.Height = STAT_WIN_HEIGHT+12;
ns.Depth = STAT_SCR_DEPTH;
ns.Font = &txtattr;
stat_screen = (void*) OpenScreen(&ns);
if (!stat_screen) {
printf("FATAL INIT: Failed to open its %dx%d resolution Statistics screen!\n",
STAT_SCR_WIDTH, STAT_WIN_HEIGHT+12);
printf("NTSC Users: kindly add the NTSC flag to the command line so that MJ\n");
printf("opens a NTSC-compatible Screen. (I've been unable to unambiguously\n");
printf("determine whether a system is running NTSC or PAL. Sorry.\n");
exit(FATAL_ERROR);
}
LoadRGB4(&stat_screen->ViewPort, &stat_colors[0], 4);
vi = GetVisualInfo(stat_screen, TAG_DONE);
nw.Height = STAT_WIN_HEIGHT;
nw.Screen = stat_screen;
window = OpenWindow(&nw);
if (!window) {
printf("FATAL INIT: Failed to open Statistics window !\n");
CloseScreen(stat_screen);
FreeVisualInfo(vi);
exit(FATAL_ERROR);
}
append_stats_menus(); // finish off NewMenu array
menustrip = CreateMenus(mymenus, TAG_DONE); // Create MenuStrip
if (!menustrip) {
printf("FATAL INIT: Unable to create MenuStrip!\n");
CloseWindow(window);
CloseScreen(stat_screen);
FreeVisualInfo(vi);
exit(FATAL_ERROR);
}
LayoutMenus(menustrip, vi, TAG_DONE); // Position Menus & Items
SetMenuStrip(window, menustrip); // Attach menu to window
// Disable some menu items related to disabled features (or not-implemented).
if (! SEX) OffMenu(window, FULLMENUNUM(STATS_MENU, SEX_ITEM, 0));
if (! VISION) OffMenu(window, FULLMENUNUM(STATS_MENU, EYE_ITEM, 0));
// **!! Not implemented
OffMenu(window, FULLMENUNUM(PROJECT_MENU, PROJ_MENU_LOAD, 0));
OffMenu(window, FULLMENUNUM(PROJECT_MENU, PROJ_MENU_SAVE, 0));
OffMenu(window, FULLMENUNUM(OPTIONS_MENU, OPT_MENU_CTRL_PANEL, 0));
OffMenu(window, FULLMENUNUM(STATS_MENU, OXY_ITEM, 0));
OffMenu(window, FULLMENUNUM(STATS_MENU, CO2_ITEM, 0));
stat_rp = window->RPort;
PaintStatLabels(); // Draw initial stat variables
//----------------------------------------------------
if (! NOANIM) {
bm = AllocBitMap(SCREEN_WIDTH, SCREEN_HEIGHT, ECO_SCR_DEPTH,
BMF_CLEAR | BMF_DISPLAYABLE | BMF_INTERLEAVED, NULL);
if(!bm) {
printf("FATAL INIT: Failed to AllocBitMap() contiguous 512*512*4 Eco bitmap!\n");
CloseWindow(window);
CloseScreen(stat_screen);
FreeVisualInfo(vi);
exit(FATAL_ERROR);
}
// Note the INTERLEAVED bitmap organization for max pixel performace **!!
eco_screen = (void*) OpenScreenTags( NULL,
SA_DisplayID, HIRESLACE_KEY,
SA_Interleaved, TRUE,
SA_Width, SCREEN_WIDTH,
SA_Height, SCREEN_HEIGHT,
SA_Depth, ECO_SCR_DEPTH,
SA_Title, scr_name,
SA_ShowTitle, FALSE,
SA_BitMap, bm,
TAG_END
);
if (!eco_screen) {
printf("FATAL INIT: Failed to open 512x512 Eco Screen!\n");
FreeBitMap(bm);
CloseWindow(window);
CloseScreen(stat_screen);
if (vi) FreeVisualInfo(vi);
exit(FATAL_ERROR);
}
LoadRGB4(&eco_screen->ViewPort, &eco_colors[0], NUM_COLORS);
plane0 = (ULONG*) eco_screen->RastPort.BitMap->Planes[0];
}
}
//-------------------------------------------------------------------------
// Print the statistics labels and draw the horizontals separating graphs.
//-------------------------------------------------------------------------
void PaintStatLabels (void) {
struct statistic *stat;
UWORD i;
SetAPen(stat_rp, 2);
graphnum = 1; // offset so that Text aligns with base of graph
stat = available_stats; // statistic entry
for (i=0; i< NUM_STATS; i++, stat++) {
if (graphnum > MAX_GRAPHS) break;
if (stat->displayed) {
WriteString( 3, CURGRAPH_Y-3, stat->label);
graphnum++;
}
}
i = graphnum;
graphnum = 0;
while (i--) {
Move(stat_rp, LE, CURGRAPH_Y-1);
Draw(stat_rp, STAT_SCR_WIDTH-1, CURGRAPH_Y-1);
graphnum++;
}
}
//-------------------------------------------------------------------------
// Here we finish off the rest of the array of NewMenu structs.
//
// Part of the MenuStrip is dynamically generated at init time from the
// list of STATISTICAL VARIABLES.
//-------------------------------------------------------------------------
void append_stats_menus(void) {
register struct NewMenu *newmenu;
register struct statistic *stat;
UWORD i;
IFAUDIT mymenus[NEWMENU_AUDIT].nm_Flags |= CHECKED;
newmenu = mymenus + NEWMENU_APPEND; // -> first empty slot
for (i=0; i< NUM_STATS; i++) {
stat = &available_stats[i];
newmenu->nm_Type = NM_ITEM;
newmenu->nm_Label = stat->label;
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = CHECKIT|MENUTOGGLE;
if (stat->displayed) newmenu->nm_Flags |= CHECKED;
newmenu->nm_MutualExclude = 0L;
newmenu++;
}
newmenu->nm_Type = NM_END; // Terminate NewMenu array with the required
newmenu->nm_Label = NULL; // end marker
newmenu->nm_CommKey = NULL;
newmenu->nm_Flags = 0;
newmenu->nm_MutualExclude = 0L;
auto_reset = TRUE;
}
//----------------------------------------------------------------
// Modify the list of selected statistics to be displayed on the screen.
//
// Since Intuition allows several menuitems to be CHECKed and un-CHECKed
// without sending an IntuiMessage for each change, we have to go through
// all MenuItems, checking their state, and possibly un-setting some so
// that we don't exceed MAX_GRAPHS current graps on the Stats Screen.
//
//----------------------------------------------------------------
void toggle_stat ( void ) {
struct statistic *stat;
UWORD i;
stat = available_stats; // statistics array
for (i=0; i< NUM_STATS; i++) {
if (menuitem_checkstate(STATS_MENU, i)) stat->displayed = TRUE;
else stat->displayed = FALSE;
stat++;
}
graphnum = 1;
stat = available_stats; // statistics array
for (i=0; i< NUM_STATS; i++) {
if (graphnum > MAX_GRAPHS) {
stat->displayed = FALSE;
uncheck_menuitem(STATS_MENU, i);
}
if (stat->displayed) graphnum++;
stat++;
}
SetRast(stat_rp, 0); // erase screen
PaintStatLabels(); // redraw labels
RePaintGraphs(); // and redraw graphs from buffers
}
//----------------------------------------------------------------
// Take away a menuitem's CheckMark (the tick on the left).
//----------------------------------------------------------------
void uncheck_menuitem (int menunum, int itemnum) {
struct Menu *menu;
struct MenuItem *menuitem;
menu = menustrip; // -> Linked list of Menus.
while (menunum--) {
menu = menu->NextMenu; // -> first item in this menu
}
menuitem = menu->FirstItem;
while(itemnum--) {
menuitem = menuitem->NextItem; // find Nth menu item
}
menuitem->Flags &= ~CHECKED; // un-set CHECK mark !
}
//----------------------------------------------------------------
// return whether this menuitem has his checkmark SET or CLEAR.
//----------------------------------------------------------------
FLAG menuitem_checkstate (int menunum, int itemnum) {
struct Menu *menu;
struct MenuItem *menuitem;
menu = menustrip; // -> Linked list of Menus.
while (menunum--) {
menu = menu->NextMenu; // -> first item in this menu
}
menuitem = menu->FirstItem;
while(itemnum--) {
menuitem = menuitem->NextItem; // find Nth menu item
}
return (FLAG) (menuitem->Flags & CHECKED); // test CHECK mark
}
//----------------------------------------------------------------
// "Blind" cell-manipulation routines.
// These maintain a consistent ecosystem in an array of byte cells,
// not a screen full of pixels representing cells, as is usual.
//----------------------------------------------------------------
#define cell_offs(x,y) ( (x) + (( (UWORD)(y) ) <<9 ) )
//----------------------------------------------------------------
void blind_put_cell (ULONG x, ULONG y, ULONG cell, BUGPTR bug) {
#ifdef DEBUG
if (x>511 || y>511) printf("Bug: blind_put_cell(%d,%d) !\n", x,y);
#endif
*(environment + cell_offs(x,y)) = cell;
}
//----------------------------------------------------------------
void blind_wipe_cell (BUGPTR bug) {
register ULONG offs;
offs = cell_offs(bug->x, bug->y);
*(environment+ offs) = NEUT_CELL;
*(owner_ptrs+ offs) = (BUGPTR) 0L;
}
//----------------------------------------------------------------
CELL blind_cell_type (ULONG x, ULONG y) {
#ifdef DEBUG
if (x>511 || y>511) printf("Bug: blind_cell_type(%d,%d) !\n", x,y);
#endif
return *(environment+cell_offs(x,y)) ;
}
//----------------------------------------------------------------
FLAG blind_is_food (ULONG x, ULONG y) {
#ifdef DEBUG
if (x>511 || y>511) printf("Bug: blind_is_food(%d,%d) !\n", x,y);
#endif
return (FLAG) (*(environment+cell_offs(x,y)) == VEGE_CELL);
}
//----------------------------------------------------------------
FLAG blind_is_empty (ULONG x, ULONG y) {
#ifdef DEBUG
if (x>511 || y>511) printf("Bug: blind_is_empty(%d,%d) !\n", x,y);
#endif
return (FLAG) (*(environment+cell_offs(x,y)) == NEUT_CELL);
}
//================================================================
void gfx_put_cell (ULONG x, ULONG y, ULONG cell, BUGPTR bug) {
#ifdef DEBUG
if (x>511 || y>511) printf("Bug: gfx_put_cell(%d,%d) !\n", x,y);
#endif
asm_plot_pixel(&bug->prevpix, x,y, cell );
}
//----------------------------------------------------------------
void gfx_wipe_cell (BUGPTR bug) {
asm_fastwipe_pixel(&bug->prevpix);
*(owner_ptrs+ cell_offs(bug->x, bug->y)) = (BUGPTR) 0L;
}
//----------------------------------------------------------------
// Clean up Simulation-related things before returning to OS.
//----------------------------------------------------------------
void close_ecosys (void) {
struct statistic *stat;
UWORD i;
FreeMem((char*)environment, SCREEN_WIDTH*SCREEN_WIDTH);
FreeMem((char*)owner_ptrs, PTR_MAP_SIZE);
FreeMem((char*)bugs, MAX_BUGS*sizeof(struct bug));
stat = available_stats;
for (i=0; i< NUM_STATS; i++) { // free all statistics buffers
FreeMem(stat->buffer, 3*256*4);
stat++;
}
}
//----------------------------------------------------------------
// Clean up graphics-related things before returning to OS.
//----------------------------------------------------------------
void close_gfx (void) {
#ifdef AMIGA
if (IntuitionBase) {
if (window) {
if (menustrip) {
ClearMenuStrip(window);
FreeMenus(menustrip);
}
CloseWindow(window);
}
if (stat_screen) CloseScreen(stat_screen);
if (vi) FreeVisualInfo(vi);
if (!NOANIM) {
if (eco_screen) CloseScreen(eco_screen);
if (bm) FreeBitMap(bm);
}
CloseLibrary((struct Library *) IntuitionBase);
}
if (GfxBase) CloseLibrary((struct Library *)GfxBase);
if (RexxSysBase) CloseLibrary((struct Library *)RexxSysBase);
#endif
}
//-------------------------------------------------------------------------
// Check if there are any Intuition Events waiting for us.
// Process them according to their type.
//-------------------------------------------------------------------------
void handle_IDCMP_msgs (void) {
struct IntuiMessage *msg;
ULONG msgtype;
SHORT msgcode;
SHORT mousex,mousey;
APTR IAddr; // ptr to Intuition object (Gadget,..)
// For Intuition Messages use the gadtools GetMsg/ReplyMsg variants
// so that gadtools can intercept gadget clicks which it (and not we)
// should process (e.g. for CYCLE_KIND or LISTVIEW_KIND gadgets !).
while (msg = GT_GetIMsg(window->UserPort)) {
msgtype = msg->Class; // copy Message fields and
msgcode = msg->Code;
mousex = msg->MouseX;
mousey = msg->MouseY;
IAddr = msg->IAddress;
GT_ReplyIMsg(msg); // reply swiftly
#ifdef DEBUGX
printf("IDCMP MSGTYPE:%8lx (CODE:%8lx) ", msgtype, msgcode);
switch (msgtype) {
case CLOSEWINDOW: printf("CLOSEWINDOW\n"); break;
case ACTIVEWINDOW: printf("ACTIVEWINDOW\n"); break;
case MENUPICK: printf("MENUPICK:\n"); break;
case MOUSEMOVE: printf("MOUSEMOVE:\n"); break;
case MOUSEBUTTONS: printf("MOUSEBUTTONS\n"); break;
case VANILLAKEY: printf("VANILLAKEY\n"); break;
case GADGETUP: printf("GADGETUP:\n"); break;
case INTUITICKS: printf("INTUITICKS\n"); break;
case REFRESHWINDOW: printf("REFRESHWINDOW\n"); break;
default: printf("-- UNKNOWN --\n");
}
#endif
switch (msgtype) {
// case CLOSEWINDOW: handle_quit(); break;
// case ACTIVEWINDOW: handle_activation(); break;
case MENUPICK: handle_menuselection(msgcode); break;
// case MOUSEMOVE: handle_mousemove(); break;
// case MOUSEBUTTONS: handle_click(); break;
// case VANILLAKEY: handle_keypress( (char) msgcode); break;
// case GADGETUP: handle_gadgets(); break;
// case INTUITICKS: break;
// case REFRESHWINDOW: GT_BeginRefresh((struct Window*)IAddr);
// GT_EndRefresh((struct Window*)IAddr, TRUE);
// break;
default:
printf("BUG: Unknown IDCMP MSGTYPE:%x (CODE:%x)\n", msgtype, msgcode);
}
}
}
//-------------------------------------------------------------------------
// A MENUPICK event has arrived.
// The user selected a meny item somewhere.
// Find out which Menu, which Menuitem and possibly which sub-item.
//-------------------------------------------------------------------------
void handle_menuselection(SHORT menu) {
switch (MENUNUM(menu)) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
case PROJECT_MENU:
switch (ITEMNUM(menu)) {
//--------------------------------------------------------
case PROJ_MENU_RESET:
if (quick_req("Sure you want to reset Ecosystem ?", "YES|NO")) {
reset_ecosystem();
}
break;
//--------------------------------------------------------
case PROJ_MENU_LOAD:
break;
//--------------------------------------------------------
case PROJ_MENU_SAVE:
break;
//--------------------------------------------------------
case PROJ_MENU_ABOUT:
popup_About_req();
break;
//--------------------------------------------------------
case PROJ_MENU_AUTHOR:
popup_Author_req();
break;
//--------------------------------------------------------
case PROJ_MENU_QUIT:
if (quick_req("Sure you want to quit?", "YES|NO")) {
quit_me = TRUE;
}
break;
}
break;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
case OPTIONS_MENU:
switch (ITEMNUM(menu)) {
case OPT_MENU_SIM_SPEED:
switch(SUBNUM(menu)) {
case 0: sim_speed = 0 ; break;
case 1: sim_speed = 15 ; break;
}
break;
case OPT_MENU_STAT_SPEED:
switch(SUBNUM(menu)) {
case 0: graph_speed =1024; break;
case 1: graph_speed = 512; break;
case 2: graph_speed = 256; break;
case 3: graph_speed = 64; break; // Default
case 4: graph_speed = 16; break;
case 5: graph_speed = 8; break;
case 6: graph_speed = 4; break;
}
break;
case OPT_MENU_RESET: auto_reset = ~auto_reset; break;
case OPT_MENU_AUDIT: REPORT = ~REPORT; break;
case OPT_MENU_MINMAX: MINMAX = ~MINMAX; break;
}
break;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
case STATS_MENU:
toggle_stat();
break;
}
}
//-------------------------------------------------------------------------
// REXX Support routines follow..
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Create REXX port.
//
// Insert our PUBLIC port in front of the list so that the rest of the world
// can find us quickly, bypassing lethargic priority 0 ports.
//-------------------------------------------------------------------------
FLAG post_REXX_port(char * portname) {
rexx_port = CreatePort(portname, 50L);
if (rexx_port) return TRUE; else return FALSE;
}
//-------------------------------------------------------------------------
// Close down REXX communications port. (but first flush port of late commers)
//-------------------------------------------------------------------------
void close_REXX(void) {
struct RexxMsg *msg;
Forbid(); // the whole operation should be atomic !
while (msg = (struct RexxMsg*) GetMsg((struct MsgPort*) rexx_port)) {
msg->rm_Result1 = RC_FATAL;
msg->rm_Result2 = 999;
ReplyMsg((struct Message*)msg); // reply to the REXX resident process
}
DeletePort(rexx_port);
Permit();
}
//-------------------------------------------------------------------------
// Check if there are any REXX commands waiting for us.
//-------------------------------------------------------------------------
void handle_AREXX_msgs (void) {
struct RexxMsg *msg;
while (msg = (struct RexxMsg*) GetMsg((struct MsgPort*) rexx_port)) {
if (! IsRexxMsg(msg)) {
printf("\n");
printf("WARNING ! MegaJitter is receiving rogue non-AREXX messages addressed\n");
printf("to its AREXX message port.. please ask programs not to send these\n");
printf("type of messages. Thanks.\n\n");
} else
if ((msg->rm_Action & 0xFF000000 ) != RXCOMM) {
printf("REXX Message received which isn't a COMMAND message (Action=%08X)\n",
msg->rm_Action);
} else {
msg->rm_Result2 = 0;
msg->rm_Result1 = parse_rexx_cmd(msg); // returns as 'RC' in REXX
}
ReplyMsg((struct Message*)msg); // reply to the REXX resident process
}
}
//-------------------------------------------------------------------------
// We've received a REXX command message containing a string to be
// analyzed and acted upon.
//
// Legal types of REXX commands are:
//
// <SIMPLE_CMD> /* This should be a single token */
//
// <VARIABLE> <decimal value> /* This would assign value to variable */
//
//-------------------------------------------------------------------------
RC parse_rexx_cmd(struct RexxMsg* msg) {
struct variable *var;
struct command *cmd;
char *str, *str2;
ULONG newval;
str = msg->rm_Args[0];
//printf("AREXX command received : '%s'\n", str);
while (*str == ' ' || *str == '\t') str++; // skip leading white spc.
str2 = str;
// find end of first token (if only token, token is terminated by \0 !)
while ( *str2 > ' ') str2++;
*str2++ = 0; // turn 1st token into C-string
// Try to match any of our single-word commands...
cmd = &cmdlist[0];
while(cmd->name) {
if (strcmp(str, cmd->name) == 0) {
// printf("Executing command '%s' from REXX..\n", cmd->name);
return (*cmd->address)( msg ); // call function via pointer.
// printf("Done.\n");
}
cmd++; // goto next command in list
}
// Then try to match any global variables which we allow to be changed.
var = &varlist[0]; // go through all var descriptors
while (var->name) {
if (var->flags & PARAMETER) {
// And check if REXX command wants us to assign a value to this variable.
if (strcmp(str, var->name) == 0) {
while (*str2 == ' ' || *str2 == '\t') str2++; // skip white spc.
newval = atoi(str2); // read new value
// if assigned value is within legal range of this variable, assign it.
if (newval >= var->min_val && newval <= var->max_val) {
*var->address = newval;
// printf("VAR %s set to %d\n", var->name, *var->address);
return 0; // and we're done..
} else return REXXERR_VAL_OUTSIDE_RANGE; // if outside range : ERROR !
}
}
var++;
}
return REXXERR_UNKNOWN_CMD; // 'invalid variable name'
}
//-------------------------------------------------------------------------
// AREXX Commands:
//
// "SEX"
// "VISION"
// "NOSEX"
// "NOVISION"
//-------------------------------------------------------------------------
RC enable_sex (void *dummy) {
if (!SEX) {
OnMenu(window, FULLMENUNUM(STATS_MENU, SEX_ITEM, 0));
SEX = TRUE;
return TRUE;
}
return FALSE;
}
RC enable_vision (void *dummy) {
if (!VISION) {
OnMenu(window, FULLMENUNUM(STATS_MENU, EYE_ITEM, 0));
VISION = TRUE;
return TRUE;
}
return FALSE;
}
RC disable_sex (void *dummy) {
if (SEX) {
OffMenu(window, FULLMENUNUM(STATS_MENU, SEX_ITEM, 0));
SEX = FALSE;
return TRUE;
}
return FALSE;
}
RC disable_vision (void *dummy) {
if (VISION) {
OffMenu(window, FULLMENUNUM(STATS_MENU, EYE_ITEM, 0));
VISION = FALSE;
return TRUE;
}
return FALSE;
}
RC restart (void *dummy) {
reset_ecosystem();
runs = 1; //
return TRUE;
}
//-------------------------------------------------------------------------
// AREXX Query commands:
//
// "BUGS?"
// "RUNS?"
// "TIMESTEPS?"
//-------------------------------------------------------------------------
RC return_bugs (void * ptr) {
return numbugs;
}
RC return_runs (void * ptr) {
return runs;
}
RC return_timesteps (void * ptr) {
return time;
}
//-------------------------------------------------------------------------
// User selected the "About..." option in the Project menu.
// Give him some info.
//-------------------------------------------------------------------------
void popup_About_req(void) {
static struct EasyStruct aboutES = {
sizeof (struct EasyStruct),
0,
"MegaJitter (C) Laurence Vanhelsuwé 1992-1994", "\
MegaJitter is a graphical ecosystem simulator featuring:\n\
\n\
- gene-based control over organism's characteristics\n\
- mutation of genes at birth\n\
- sexual and asexual reproduction\n\
- real-time display of entire ecosystem\n\
- real-time display of statistics\n\
- command line control over ecosystem's global parameters\n\
- AREXX interface\n\
\n\
MegaJitter is written in 'C' and optimized 030+ assembler.\n" ,
"OK",
};
EasyRequest(window, &aboutES, NULL);
}
//-------------------------------------------------------------------------
// User selected the "Author..." option in the Project menu.
// Give him some info.
//-------------------------------------------------------------------------
void popup_Author_req(void) {
static struct EasyStruct aboutES = {
sizeof (struct EasyStruct),
0,
"MegaJitter (C) Laurence Vanhelsuwé 1992-1994",
"Address: Laurence Vanhelsuwé\n\
Christinastraat 105\n\
B-8400 Oostende\n\
Belgium\n\
\n\
Born : 18-MAY-1966\n\
\n\
E-Mail : not yet (who can give me an Internet box ?)\n" ,
"OK",
};
EasyRequest(window, &aboutES, NULL);
}
//-------------------------------------------------------------------------
// This is a generic routine to bring up small message requesters.
//-------------------------------------------------------------------------
int quick_req (char *message, char *exit) {
struct EasyStruct quickES;
quickES.es_StructSize = sizeof (struct EasyStruct);
quickES.es_Flags = 0;
quickES.es_Title = "MegaJitter says...";
quickES.es_TextFormat = message;
quickES.es_GadgetFormat = exit;
return EasyRequest(window, &quickES, NULL);
}
//----------------------------------------------------------------
// Print a string at pixel coordinates (x,y)
//----------------------------------------------------------------
void WriteString (UWORD x, UWORD y, char *string) {
int len;
len = strlen(string);
#ifdef AMIGA
Move(stat_rp, x, y); Text(stat_rp, string, len);
#endif
}
//----------------------------------------------------------------
// Returns TRUE if any quitting condition is true.
// Currently possible conditions:
// - quit_me flag is TRUE
//----------------------------------------------------------------
FLAG kill_switch (void) {
if (quit_me) return TRUE; else return FALSE;
}
//----------------------------------------------------------------
// Simply set a flag requesting main loop to terminate entire application.
//----------------------------------------------------------------
RC please_quit (void *dummy) {
quit_me = TRUE;
return TRUE;
}
//----------------------------------------------------------------
// Very fast pseudo-random number generator.
// (Uses the video circuitry's beam position registers.)
//
// **!! This obviously won't pass any statistical randomness tests, but
// it does a reasonable job and the RND() function needs to be super quick
// since it's called so frequently.
//----------------------------------------------------------------
short rnd(void) {
static unsigned int seed; // STATIC means the seed stays put between calls
seed ^= seed>>16; // use high word to massage bits of low word.
#ifdef AMIGA
seed += *(int*)0xdff004; // use video beam coords regs VPOSR/VHPOSR as random number
#endif
return (short) ((short)seed & (short)0x7FFF); // return a POSITIVE SHORT integer
}
//----------------------------------------------------------------