Category : Printer + Display Graphics
Archive   : CRITTER4.ZIP
Filename : CRIT4.C

 
Output of file : CRIT4.C contained in archive : CRITTER4.ZIP
/*
Program: Critters
Version: 4.01 11-Aug-1989

Language: ANSI C w/ MS-DOS extensions

Compilers: Zortech C/C++ 1.07e
Microsoft C 5.10 & QuickC 2.01
Borland Turbo C 2.0

Environ: MS-DOS w/EGA or Hercules video adapter

Purpose: Simulates simple life forms which evolve.

Written by: Scott Robert Ladd
705 West Virginia
Gunnison, CO 81230

MCI ID: srl
FidoNet: 1:104/45.2
*/

#ifdef HERC
#include "crit_hgc.h"
#else
#include "crit_ega.h"
#endif

#include "cs_disp.h"
#include "conio.h"
#include "dos.h"
#include "io.h"
#include "limits.h"
#include "stddef.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"

/* structure which defines a critter */
struct critter
{
/* genetic values */
int move_gene[8];
int gene_cnt;

int sense;
int color;
int energy_max;
int age_max;
int digestion;
int move_energy;
int maturity;
int energy_repro;

int energy_level;
int age;

int posx;
int posy;

struct critter * next;
struct critter * prev;
};

const char * version = "4.01";

/* constants which define the program environment */
int start_crit = 20; /* initial number of critters */
int start_food = 1500; /* initial food supply */

int max_x = 632; /* maximum x dimension of "world" */
int max_y = 304; /* " y " " " */

int max_energy = 1000; /* max energy a critter can have */
int max_age = 250; /* max age critter can reach w/o reproducing */
int max_mgene = 3; /* max "magnitude" of a movement gene */
int min_mgene = 1; /* min " " " " */
int max_sense = 3; /* max sense value */

int food_value = 80; /* energy value of a piece of food */
int move_cost = 2; /* energy cost for critter to move once */
int repro_energy = 600; /* minimum energy for a critter to reproduce */
int repro_age = 100; /* minimum age for a critter to reproduce */
int start_energy = 400; /* initial energy level for starting critters */
int food_growth = 2; /* amount of food created each generation */
int mut_chance = 20; /* chance of a mutation occuring */

/* boolean values for program behavior */
const int old_age_kills = 1; /* true */
const int dead_are_food = 0; /* false */

/* colors of critters and food */
const short food_color = 7; /* grey */
const short crit_color = 15; /* white */

/* movement definition table */
const int delta_table[8][2] = { { 0, -3}, { 3, 0}, { 0, 3}, {-3, 0},
{ 3, 3}, { 3, -3}, {-3, 3}, {-3, -3} };

/* food location table and access macros */
unsigned char food_table[24400];

/* root and tail of critter linked list */
struct critter * first_critter = NULL;
struct critter * last_critter = NULL;

/* informational display counts */
long int critter_count = 0;
long int generation = 0;

/* macro to get a random value between 1 and x */
#define randval(x) ((rand() % x) + 1)

/* function prototypes */
void main(void); /* program control and logic */
void finish(void); /* program exit handler */
int yes_no(char * prompt); /* asks a yes/no question */
int get_int(char * prompt, int value, int minval, int maxval); /* read int value */
void display_params(void); /* show parameters on-screen */
void load_params(void); /* loads a parameter file from disk */
void save_params(void); /* saves proigram parameters to disk */
void enter_params(void); /* user input of program parameters */
void set_params(void); /* parameter setting control routine */
void setup(void); /* initial set-up of program */
void reproduce(struct critter * work_critter); /* critter reproduction/death cycle */
void movement(struct critter * work_critter); /* critter motion */
void make_food(void); /* adds new food to world */
void show_stats(void); /* displays current information on the world */
void dump(void); /* dumps current critters to disk */
int stop_this(void); /* checks for a keyboard entry */
int is_food (int x, int y); /* determines if a screen location contains food */
void put_food(int x, int y); /* places food at a screen location */
void del_food(int x, int y); /* removes the food from a screen loaction */

void main(void)
{
struct critter * temp_critter;

set_params();
setup();

do {
++generation;

temp_critter = first_critter;

while (temp_critter != NULL)
{
movement(temp_critter);
reproduce(temp_critter);
temp_critter = temp_critter->next;
}

make_food();
show_stats();
}
while (!stop_this());

finish();
}

void finish(void)
{
graphoff();

exit(0);
}

int yes_no(char * prompt)
{
int done = 0;
int result;
char input;

printf("%s (Y/N)? ",prompt);

do {
while (!kbhit()) ;

input = (char)getch();

if ((input == 'y') || (input == 'Y'))
{
done = 1;
result = 1;
}
else if ((input == 'n') || (input == 'N'))
{
done = 1;
result = 0;
}
else
putchar('\a');
}
while (!done);

printf("%c\n",input);

return result;
}

int get_int(char * prompt, int value, int minval, int maxval)
{
int done = 0;
char input[6];
int temp;

if (minval > maxval)
{
temp = maxval;
maxval = minval;
minval = temp;
}

if (value < minval)
value = minval;
else if (value > maxval)
value = maxval;

do {
printf("%s [def = %i, min = %i, max = %i]? ",prompt,value,minval,maxval);

fgets(input,6,stdin);

if (input[0] != '\n')
value = atoi(input);

if (value < minval)
{
printf("\aSORRY! That number is too small!\n");
value = minval;
}
else if (value > maxval)
{
printf("\aSORRY! That number is too large!\n");
value = maxval;
}
else
done = 1;
}
while (!done);

return value;
}

void display_params(void)
{
printf("\n\nThe current parameters are: \n\n");

printf("%5i starting of critters\n",start_crit);
printf("%5i initial pieces of food\n",start_food);
printf("%5i maximum x dimension of world\n",max_x);
printf("%5i maximum y dimension of world\n",max_y);
printf("%5i maximum critter energy\n",max_energy);
printf("%5i maximum critter age w/o reproducing\n",max_age);
printf("%5i maximum movement gene\n",max_mgene);
printf("%5i minimum movement gene\n",min_mgene);
printf("%5i maximum sense value\n",max_sense);
printf("%5i energy value of food\n",food_value);
printf("%5i cost of movement\n",move_cost);
printf("%5i energy required for reproduction\n",repro_energy);
printf("%5i minimum age for reproduction\n",repro_age);
printf("%5i initial energy for first critters\n",start_energy);
printf("%5i amount of food added after each move\n",food_growth);
printf("%5i chance of a mutation occuring\n",mut_chance);
}

void load_params(void)
{
int done = 0;
long f_size;
FILE * f;
char filename[13];

do {
printf("Enter a parameter (.CRI) file name (no extension): ");
fgets(filename,10,stdin);

if (filename[0] != '\n')
{
filename[strlen(filename) - 1] = '\x00';

strcat(filename,".CRI");

f = fopen(filename,"r");

if (f == NULL)
printf("\aSORRY! That file can not be found!\n");
else
done = 1;
}
else
{
printf("OKAY! We will use the defaults anyway!\n");
done = 1;
}
}
while (!done);

if (f != NULL)
{
#if defined(__ZTC__)
f_size = filesize(filename);
#else
f_size = filelength(fileno(f));
#endif

if (f_size >= (16 * sizeof(int)))
{
fread(&start_crit, sizeof(int),1,f);
fread(&start_food, sizeof(int),1,f);
fread(&max_x, sizeof(int),1,f);
fread(&max_y, sizeof(int),1,f);
fread(&max_energy, sizeof(int),1,f);
fread(&max_age, sizeof(int),1,f);
fread(&max_mgene, sizeof(int),1,f);
fread(&min_mgene, sizeof(int),1,f);
fread(&max_sense, sizeof(int),1,f);
fread(&food_value, sizeof(int),1,f);
fread(&move_cost, sizeof(int),1,f);
fread(&repro_energy,sizeof(int),1,f);
fread(&repro_age, sizeof(int),1,f);
fread(&start_energy,sizeof(int),1,f);
fread(&food_growth, sizeof(int),1,f);
fread(&mut_chance, sizeof(int),1,f);
}
else
{
printf("\aSORRY! %s is not a parameter file for Critters!\n",filename);
printf("Critters will be using default values!");
}

fclose(f);
}
}

void save_params(void)
{
int done = 0;
FILE * f;
char filename[13];

do {
printf("Enter a parameter (.CRI) file name (no extension): ");
fgets(filename,10,stdin);

if (filename[0] != '\n')
{
filename[strlen(filename) - 1] = '\x00';

strcat(filename,".CRI");

f = fopen(filename,"w");

if (f == NULL)
printf("\aSORRY! That file can't be created!\n");
else
done = 1;
}
else
{
printf("OKAY! Saving the parameters now...\n");
done = 1;
}
}
while (!done);

if (f != NULL)
{
fwrite(&start_crit, sizeof(int),1,f);
fwrite(&start_food, sizeof(int),1,f);
fwrite(&max_x, sizeof(int),1,f);
fwrite(&max_y, sizeof(int),1,f);
fwrite(&max_energy, sizeof(int),1,f);
fwrite(&max_age, sizeof(int),1,f);
fwrite(&max_mgene, sizeof(int),1,f);
fwrite(&min_mgene, sizeof(int),1,f);
fwrite(&max_sense, sizeof(int),1,f);
fwrite(&food_value, sizeof(int),1,f);
fwrite(&move_cost, sizeof(int),1,f);
fwrite(&repro_energy,sizeof(int),1,f);
fwrite(&repro_age, sizeof(int),1,f);
fwrite(&start_energy,sizeof(int),1,f);
fwrite(&food_growth, sizeof(int),1,f);
fwrite(&mut_chance, sizeof(int),1,f);

fclose(f);
}
}

void enter_params(void)
{
putchar('\n');

/* accept values */
start_crit = get_int("Initial number of critters",start_crit,1,100);
start_food = get_int("Initial food particles",start_food,0,10000);

max_x = get_int("Maximum x dimension",max_x,50,632);
max_y = get_int("Maximum y dimension",max_y,50,304);

max_energy = get_int("Max. energy a critter can have",max_energy,100,INT_MAX);
max_age = get_int("Max. age a critter can obtain",max_age,100,INT_MAX);
max_mgene = get_int("Max. movement gene value",max_mgene,1,10);
min_mgene = get_int("Min. movement gene value",min_mgene,1,10);
max_sense = get_int("Max. sense gene value",max_sense,1,10);

food_value = get_int("Energy value of food",food_value,1,100);
move_cost = get_int("Movement cost",move_cost,1,10);
repro_energy = get_int("Min. reproduction energy",repro_energy,1,INT_MAX);
repro_age = get_int("Min. reproduction age",repro_age,1,INT_MAX);
start_energy = get_int("Starting energy for first critters",start_energy,1,INT_MAX);
food_growth = get_int("Food growth per move",food_growth,1,10);
mut_chance = get_int("Chance of a mutation occuring",mut_chance,7,100);
}

void set_params(void)
{
printf("\nCritters v%s -- A Computer-based Ecosystem\n\n",version);

if (yes_no("Should I load values from a file"))
load_params();

display_params();

if (yes_no("\nDo you want the change these parameters"))
enter_params();

if (yes_no("\nShould I save the current parameters to a file"))
save_params();

putchar('\n');
}

void setup(void)
{
int i, j, x, y, working;
unsigned seed;
struct critter * temp_critter;

/* initialize random number generator */
seed = (unsigned)time(NULL);
srand(seed);

/* initialize graphics to EGA 16 color mode */
graphon();

/* load the character set */
cs_load("critchar.cst");

/* initialize linked list of critters */
for (i = 0; i < start_crit; ++i)
{
temp_critter = malloc(sizeof(struct critter));

if (first_critter == NULL)
{
temp_critter->prev = NULL;
first_critter = temp_critter;
}
else
{
last_critter->next = temp_critter;
temp_critter->prev = last_critter;
}

last_critter = temp_critter;
temp_critter->next = NULL;

temp_critter->energy_level = start_energy;

for (j = 0; j < 8; ++ j)
temp_critter->move_gene[j] = 1;

temp_critter->gene_cnt = 8;

temp_critter->posx = randval(max_x);
temp_critter->posy = randval(max_y);

temp_critter->age = 0;
temp_critter->sense = 0;
temp_critter->color = 15;
temp_critter->energy_max = max_energy;
temp_critter->age_max = max_age;
temp_critter->digestion = food_value;
temp_critter->move_energy = move_cost;
temp_critter->maturity = repro_age;
temp_critter->energy_repro = repro_energy;
}

memset(food_table,0,24400);

for (i = 0; i < start_food; ++i)
{
working = 1;

do {
x = randval(max_x);
y = randval(max_y);

if ( !is_food(x,y) )
{
plotpixel(x,y,food_color);
put_food(x,y);
working = 0;
}
}
while (working);
}

/* set information counts */
critter_count = start_crit;
}

void reproduce(struct critter * work_critter)
{
int i, x, y, sel, new_color;
struct critter * new_critter;

/* the critter gets older */
++work_critter->age;

/* if critter can reproduce, it does */
if ((work_critter->energy_level >= work_critter->energy_repro)
&& (work_critter->age >= work_critter->maturity))
{
/* allocate a new critter */
new_critter = malloc(sizeof(struct critter));

if (new_critter == NULL)
finish();

/* add new critter to end of critter list */
new_critter->prev = last_critter;
new_critter->next = NULL;

last_critter->next = new_critter;
last_critter = new_critter;

/* set new energy levels for parent and child */
work_critter->energy_level = work_critter->energy_level / 2;
new_critter->energy_level = work_critter->energy_level;

/* put the new critter in the same place as its parent */
new_critter->posx = work_critter->posx;
new_critter->posy = work_critter->posy;

new_critter->age = 0;

/* inherit the old genes */
for (i = 0; i <= 8; ++i)

new_critter->move_gene[i] = work_critter->move_gene[i];

new_critter->sense = work_critter->sense;
new_critter->color = work_critter->color;
new_critter->energy_max = work_critter->energy_max;
new_critter->age_max = work_critter->age_max;
new_critter->digestion = work_critter->digestion;
new_critter->move_energy = work_critter->move_energy;
new_critter->maturity = work_critter->maturity;
new_critter->energy_repro = work_critter->energy_repro;

/* mutate one gene */
sel = randval(mut_chance);

switch (sel)
{
case 1: /* mutate movement genes! */
/* select gene which is mutatated! */
i = randval(8);

if (1 == randval(2))
{
if (new_critter->move_gene[i] < max_mgene)
++new_critter->move_gene[i];
}
else
{
if (new_critter->move_gene[i] > min_mgene)
--new_critter->move_gene[i];
}

/* adjust the gene count */
++new_critter->gene_cnt;

break;
case 2: /* mutate senses! */
/* check to see if senses evolve */
if (new_critter->sense < max_sense)
++new_critter->sense;

break;
case 3: /* mutate color! */
do {
new_color = randval(15);
}
while (new_color == new_critter->color);

new_critter->color = new_color;

break;
case 4: /* mutate maximum energy */
sel = randval(2);

if (sel == 1)
new_critter->energy_max += randval(50) + 50;
else
new_critter->energy_max -= randval(50) + 50;

break;
case 5: /* mutate maximum age */
sel = randval(2);

if (sel == 1)
new_critter->age_max += randval(25) + 25;
else
new_critter->age_max -= randval(25) + 25;

break;
case 6: /* mutate digestion */
sel = randval(2);

if (sel == 1)
new_critter->digestion += randval(5) + 5;
else
new_critter->digestion -= randval(5) + 5;

break;
case 7: /* mutate movement cost */
if (new_critter->move_energy == 2)
new_critter->move_energy = 1;
else
new_critter->move_energy = 2;

break;
case 8: /* mutate maturity */
sel = randval(2);

if (sel == 1)
new_critter->maturity += randval(25) + 25;
else
new_critter->maturity -= randval(25) + 25;

break;
case 9: /* mutate reproduction energy */
sel = randval(2);

if (sel == 1)
new_critter->energy_repro += randval(25) + 25;
else
new_critter->energy_repro -= randval(25) + 25;
}

++critter_count;
}

/* check to see if the critter dies */
else if ((work_critter->energy_level == 0)
|| (work_critter->age > work_critter->age_max))
{
--critter_count;

/* if all critters have died, end the program */
if (critter_count <= 0)
{
show_stats();

while (!stop_this())
/* empty */ ;

finish();
}

/* delete character from linked list */
if (work_critter->prev == NULL)
{
first_critter = work_critter->next;
first_critter->prev = NULL;
}
else if (work_critter->next == NULL)
{
last_critter = work_critter->prev;
last_critter->next = NULL;
}
else
{
work_critter->prev->next = work_critter->next;
work_critter->next->prev = work_critter->prev;
}

/* remove body of dead critter */
for (x = work_critter->posx - 1; x <= work_critter->posx + 1; ++x)
{
for (y = work_critter->posy - 1; y <= work_critter->posy + 1; ++y)
{
if (dead_are_food)
{
put_food(x,y);
plotpixel(x,y,food_color);
}
else
plotpixel(x,y,0);
}
}

free(work_critter);
}
}

void movement(struct critter * work_critter)
{
int i, j, ix, iy, m, x, y;
int move, move_value, hi_value, weight;

/* erase critter from old position */
for (x = work_critter->posx - 1; x <= work_critter->posx + 1; ++x)
for (y = work_critter->posy - 1; y <= work_critter->posy + 1; ++y)
plotpixel(x,y,0);

if (work_critter->sense > 0)
{
hi_value = -1;
move = 0;

for (i = 0; i < 8; ++i)
{
move_value = 0;

x = work_critter->posx;
y = work_critter->posy;

for (j = 0; j < work_critter->sense; ++j)
{
x += delta_table[i][0];
y += delta_table[i][1];

if (x < 1) x = max_x;
if (x > max_x) x = 1;
if (y < 1) y = max_y;
if (y > max_y) y = 1;

for (ix = x - 1; ix <= x + 1; ++ix)
for (iy = y - 1; iy <= y + 1; ++iy)
if (is_food(ix,iy))
++move_value;
}

if (move_value == hi_value)
{
if (work_critter->move_gene[i] > work_critter->move_gene[move])
{
move = i;
}
else if (work_critter->move_gene[i] == work_critter->move_gene[move])
{
if (1 == randval(2))
move = i;
}
}
else if (move_value > hi_value)
{
hi_value = move_value;
move = i;
}
}
}
else
{
/* select movement */
m = randval(work_critter->gene_cnt);

i = 0;
move = -1;
weight = 0;

do {
weight += work_critter->move_gene[i];

if (m >= weight)
{
++i;
}
else
{
move = i - 1;
}
}
while (move == -1);
}

/* adjust critter's position */
work_critter->posx += delta_table[move][0];
work_critter->posy += delta_table[move][1];

/* wrap around the edges of the world if needed */
if (work_critter->posx < 1)
work_critter->posx = max_x;

if (work_critter->posy < 1)
work_critter->posy = max_y;

if (work_critter->posx > max_x)
work_critter->posx = 1;

if (work_critter->posy > max_y)
work_critter->posy = 1;

/* assess movement cost */
work_critter->energy_level -= work_critter->move_energy;

/* redraw critter at new position */
for (x = work_critter->posx - 1; x <= work_critter->posx + 1; ++x)
{
for (y = work_critter->posy - 1; y <= work_critter->posy + 1; ++y)
{
/* if pixel covered is a food particle, consume it! */
if (is_food(x,y))
{
if (work_critter->energy_level < work_critter->energy_max)
work_critter->energy_level += work_critter->digestion;

del_food(x,y);
}

plotpixel(x,y,work_critter->color);
}
}
}

void make_food(void)
{
int i, x, y, working;

/* drop pieces of food in blank spots */
for (i = 0; i < food_growth; ++i)
{
working = 1;

do {
x = randval(max_x);
y = randval(max_y);

if (!is_food(x,y))
{
plotpixel(x,y,food_color);
put_food(x,y);
working = 0;
}
}
while (working);
}
}

void show_stats(void)
{
char buffer[78];

sprintf(buffer,"Move %6ld Pop %3ld", generation, critter_count);

cs_puts(320,10,buffer,15,0);
}

void dump(void)
{
char filename[13];
FILE * f;
struct critter * temp;
int i;

sprintf(filename,"%ld.CRD",generation);

f = fopen(filename,"w");

if (f == NULL)
return;

fprintf(f,"CRITTERS DUMP FILE %s %s\n\n",__DATE__,__TIME__);
fprintf(f,"Move: %6ld Population: %3ld\n\n", generation, critter_count);

temp = first_critter;

i = 0;

while (temp != NULL)
{
++i;

fprintf(f,"#%4i: moves = [%1d,%1d,%1d,%1d,%1d,%1d,%1d,%1d],",i,
temp->move_gene[0],temp->move_gene[1],temp->move_gene[2],
temp->move_gene[3],temp->move_gene[4],temp->move_gene[5],
temp->move_gene[6],temp->move_gene[7]);

fprintf(f," sense = %2d, color = %2d, energy_level = %5d, age = %5d\n",
temp->sense,temp->color,temp->energy_level,temp->age);

fprintf(f," max. energy = %5d, max. age = %5d, digestion = %4d,",
temp->energy_max,temp->age_max,temp->digestion);

fprintf(f," move cost = %1d, maturity = %5d, repro energy = %5d\n\n",
temp->move_energy,temp->maturity,temp->energy_repro);

temp = temp->next;
}

fclose(f);
}

int is_food(int x, int y)
{
int offset, res;
unsigned char mask;

offset = (int)((long)y * 80L + ((long)x / 8L));
mask = (unsigned char)(0x80 >> (x % 8));

res = food_table[offset] & mask;

return (int)res;
}

void put_food(int x, int y)
{
int offset;
unsigned char mask;

offset = (int)((long)y * 80L + ((long)x / 8L));
mask = (unsigned char)(0x80 >> (x % 8));

food_table[offset] |= mask;
}

void del_food(int x, int y)
{
int offset;
unsigned char mask;

offset = (int)((long)y * 80L + ((long)x / 8L));
mask = (unsigned char)(0x80 >> (x % 8));

food_table[offset] &= ~mask;
}

int stop_this(void)
{
char ch;

if (kbhit())
{
ch = (char)getch();

if (ch == ' ')
dump();
else
return 1;
}

return 0;
}


  3 Responses to “Category : Printer + Display Graphics
Archive   : CRITTER4.ZIP
Filename : CRIT4.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/