home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
500-599
/
ff502.lzh
/
CELLS
/
CELLSSource.lzh
/
cCells.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-20
|
9KB
|
361 lines
/*
* CELLS An Implementation of the WireWorld cellular automata
* as described in Scientific American, Jan 1990.
*
* Copyright 1990 by Davide P. Cervone.
* You may use this code, provided this copyright notice is kept intact.
* See the CELLS.HELP file for complete information on distribution conditions.
*/
/*
* File: cCells.c Handles cellular finite-state machine.
* To change the propagation rules, change this file.
*/
#include "Cells.h"
#include "cMemory.h"
static int ErrorReported; /* only give error once */
int GenerationDelay = 6; /* Delay() time between updates */
/*
* The electron head and tail cells are stored in a linked list so that
* the entire array need not be scanned during each generation. This
* structure holds the position of the cell, and a pointer to the next cell.
*/
struct Electron
{
struct Electron *Next;
short x,y;
};
static struct Electron *FreeList; /* the list of free Electron structures */
static struct Electron *HeadList; /* the list of Electron Head cells */
static struct Electron *TailList; /* the List of Electron Tail cells */
static struct Electron *NewHead; /* the new list of electron head cells */
/*
* Offsets used to find the adjacent cells to a given electron head
*/
short OffX[] = {-1, 0, 1, 1, 1, 0, -1, -1};
short OffY[] = {-1, -1, -1, 0, 1, 1, 1, 0};
/*
* NewElectron()
*
* Get a new Electron structure. First check to see if one is available
* in the free list (the free electron list is used to prevent unnecessary
* memory-management overhead), and if none is available, allocate a new
* structure. If we couldn't get a new electron (an no error was already
* reported) put up a message about low memory, otherwise set the electron
* X and Y values, and add it to the specified electron list.
*/
static void NewElectron(X,Y,theList)
short X,Y;
struct Electron **theList;
{
struct Electron *theElectron;
if (FreeList)
{
theElectron = FreeList;
FreeList = FreeList->Next;
} else {
NEWSTRUCT(Electron,theElectron);
}
if (theElectron)
{
theElectron->x = X;
theElectron->y = Y;
theElectron->Next = *theList;
*theList = theElectron;
} else if (ErrorReported == FALSE) {
DoError("Can't get Memory for Electron");
ErrorReported = TRUE;
}
}
/*
* FreeEntireList()
*
* Frees every electron in an electron list, and sets the list
* pointer to NULL.
*/
static void FreeEntireList(theList)
struct Electron **theList;
{
struct Electron *theElectron;
while (*theList)
{
theElectron = *theList;
*theList = (*theList)->Next;
FREESTRUCT(Electron,theElectron);
}
}
/*
* ClearLists()
*
* Clears the Electron Head, Electron Tail and New Electron Head lists.
* If the ClearFreeList flag is set, then also clear all free electrons
* as well (e.g., as part of the clean-up operations when the program
* exits). Clear the cirucit boundary values to their minimum (no circuit)
* in preparation for searching a grid for non-blank cells.
*/
void ClearLists(ClearFreeList)
int ClearFreeList;
{
if (FreeList && ClearFreeList) FreeEntireList(&FreeList);
if (HeadList) FreeEntireList(&HeadList);
if (TailList) FreeEntireList(&TailList);
if (NewHead) FreeEntireList(&NewHead);
}
/*
* SetupLists()
*
* Clear any old lists, then look through the specified grid for
* non-blank cells. Add them into the boundary values, and add HEAD and
* TAIL cells into their proper lists.
*/
void SetupLists(Grid)
UBYTE Grid[];
{
short X,Y;
short i = 0;
ClearLists(FALSE);
for (Y=0,i=0; Y<MAXGRIDH; Y++)
{
for (X=0; X<MAXGRIDW; X++,i++)
{
switch(Grid[i])
{
case TAIL:
NewElectron(X,Y,&TailList);
break;
case HEAD:
NewElectron(X,Y,&HeadList);
break;
}
}
}
}
/*
* CheckWire()
*
* Attempt to propagate an Electron Head cell to an adjacent wire cell.
* First check to see that the cell had not already been turned into
* an electron head (the NewGen cell must still be a wire). If not, then
* look at the cells adjacent to the wire cell and count how many Electron
* Head cells there are. If there are more than two, stop counting and
* set the count to 0. If there were 1 or 2 adjacent Electron Head cells,
* the set the cell to an Electron Head in the new generation grid, and
* add the cell to the new Electron Head list.
*/
static void CheckWire(X,Y)
short X,Y;
{
short i;
short count = 0;
if (CellColor(NewGen,X,Y) == WIRE)
{
for (i=0; i<8; i++)
{
if (CellColor(CurGen,X+OffX[i],Y+OffY[i]) == HEAD)
if (++count > 2) {i = 8; count = 0;}
}
if (count)
{
SetCell(NewGen,X,Y,HEAD);
NewElectron(X,Y,&NewHead);
}
}
}
/*
* PropagateHead()
*
* Check the cells adjacent to the Electron Head. If any are wire cells,
* then check attempt to propagate the electron into those cells.
*/
static void PropagateHead(X,Y)
short X,Y;
{
short i;
short x,y;
for (i=0; i<8; i++)
{
x = X + OffX[i];
y = Y + OffY[i];
if (CellColor(CurGen,x,y) == WIRE) CheckWire(x,y);
}
}
/*
* UpdateGrid()
*
* For every cell in the Tail list, set the new generation cell to WIRE
* For every cell in the Head list, set the new generation cell to TAIL
* and attempt to propagate the Electron Head to adjacent Wire cells.
*/
static void UpdateGrid(OldGrid,NewGrid)
UBYTE OldGrid[],NewGrid[];
{
struct Electron *theElectron;
for (theElectron=TailList; theElectron; theElectron=theElectron->Next)
SetCell(NewGrid,theElectron->x,theElectron->y,WIRE);
for (theElectron=HeadList; theElectron; theElectron=theElectron->Next)
{
SetCell(NewGrid,theElectron->x,theElectron->y,TAIL);
PropagateHead(theElectron->x,theElectron->y);
}
}
/*
* UpdateElectrons()
*
* For each cell in the list,
* get the cell's array posoition (guaranteed to be in range?)
* set the screen cell to the specified color
* update the current cell state
* return the last electron in the list
*/
static struct Electron *UpdateElectrons(theElectron)
struct Electron *theElectron;
{
register short i;
register struct Electron *LastElectron = NULL;
while (theElectron)
{
i = theElectron->x + theElectron->y * MAXGRIDW;
ColorScreenCell(theElectron->x-GridX,theElectron->y-GridY,NewGen[i]);
CurGen[i] = NewGen[i];
LastElectron = theElectron;
theElectron = theElectron->Next;
}
return(LastElectron);
}
/*
* UpdateBoard()
*
* Update the cells in the Tail List, the Head List and the New Head List
* (these are exactly the cells that have changed since the last generation).
* Add the Electron Tail list to the Free List.
* Turn the Electron Head list into the Electron Tail list, and the
* New Electron Head list into the current Electron Head list.
* Update the screen to show the new changes.
*/
static void UpdateBoard()
{
struct Electron *LastElectron = NULL;
LastElectron = UpdateElectrons(TailList);
UpdateElectrons(HeadList);
UpdateElectrons(NewHead);
if (LastElectron)
{
LastElectron->Next = FreeList;
FreeList = TailList;
}
TailList = HeadList;
HeadList = NewHead;
NewHead = NULL;
UpdateScreen();
}
/*
* RunCells()
*
* Clear the error flag so that the first memory error will be displayed.
* Copy the current state of the grid to the new generation grid (only the
* electron cells will be changed). Idea: Hold onto the NewHead, HeadList,
* and TailList lists and use them to update the screen instead; this
* should speed things up even more, and would mean we don't need to worry
* about the boundary stuff.
* Setup the electron lists and get the boundary or the circuit.
*
* While we are still running,
* propagate the electrons
* update the screen
* delay if necessary
* check to see if any button have been pressed
* if there are no tails left (i.e., there were no heads during the
* last generation) then stop running
* Clear all lists (including the free list)
*/
short RunCells(NotDone)
short NotDone;
{
ErrorReported = FALSE;
CopyGrid(CurGen,NewGen);
SetupLists(CurGen);
while (NotDone && Running)
{
UpdateGrid(CurGen,NewGen);
UpdateBoard(CurGen,NewGen);
if (GenerationDelay > 0) Delay(GenerationDelay);
NotDone = CheckForAction(NotDone);
if (TailList == NULL) SetStopped();
}
ClearLists(TRUE);
return(NotDone);
}
/*
* StepCells()
*
* Clear the error flag so that the first memory error will be reported.
* Setup the new generation (only electron cells are modified)
* Setup the electron lists
* Propagate the electrons
* Update the screen
* Clear all the electron lists
*/
void StepCells()
{
ErrorReported = FALSE;
CopyGrid(CurGen,NewGen);
SetupLists(CurGen);
UpdateGrid(CurGen,NewGen);
UpdateBoard(CurGen,NewGen);
ClearLists(TRUE);
}