/*************************************************************************
 ***                          Linear Automata                          ***
 *** Date begun: ages ago.                                             ***
 *** Last modified: 10/3/89      - to use v5 of compiler. Discovered   ***
 *** my old source code was not the latest version - it didn't match   ***
 *** the binary. Add pixel() function. Further optimized drawline (so  ***
 *** now you can't alter image width with #define LINEWIDTH) and it    ***
 *** zips along, compared with its old self.                           ***
 *************************************************************************/
/*** This program is based exclusively on an article in the Dec86 BYTE ***
 *** p181:                                                             ***
 ***        Abstract Mathematical Art - Kenneth E. Perry               ***
 *************************************************************************/

#include <exec/types.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <proto/graphics.h>

#define STARTLINE       19
#define ENDLINE         199
#define LINEWIDTH       320
#define MY_OPEN         1
#define MY_CLOSE        0

struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
struct Screen        *Screen;
struct NewScreen NewScreen = {
        0,0,320,200,2,0,1,NULL,CUSTOMSCREEN,NULL,
        "One-Dimensional Cellular Automata",
        NULL,NULL};
UWORD   cmap[4] = {0x000, 0xc00, 0x0c0, 0x00c};

short   rule[10];
short   seed[LINEWIDTH];
short   sum[LINEWIDTH];
int     curline;

void    do_libs();
void    do_screen();
void    getrule();
void    getseed();
void    sumseed();
void    drawline();

/*******
void    pixel(rp,x,y,c) ==============Commented out=======================
register struct RastPort *rp;
register int     x,y,c;
{
register UBYTE   *put;
register int     bit,j,address;

    j = (x >> 3);
    address = (y<<5) + (y<<3) + j;
    bit = 7 - (x & 7);
    for (j = 0; j < rp->BitMap->Depth; j++) {
        put = (UBYTE *)((int)(rp->BitMap->Planes[j]) + address);
        if (c & (1 << j)) *put |= (1 << bit);
        else *put &= ~(1 << bit);
    }
}
******/

void    do_libs(f) /*====================================================*/
int     f;
{
    if (f == MY_OPEN) {
        if (!(IntuitionBase = (struct IntuitionBase *)
                OpenLibrary("intuition.library",0))) {
            printf("ERROR: failed to open intuition.library!\n");
            exit(0);}
        if (!(GfxBase = (struct GfxBase *)
                OpenLibrary("graphics.library",0))) {
            printf("ERROR: failed to open graphics.library!\n");
            CloseLibrary(IntuitionBase);
            exit(0);}
    }
    else {
        CloseLibrary(GfxBase);
        CloseLibrary(IntuitionBase);
    }
}

void    do_screen(f) /*==================================================*/
int     f;
{
    if (f == MY_OPEN) {
        if (!(Screen = (struct Screen *)OpenScreen(&NewScreen))) {
            printf("ERROR: failed to open screen!\n");
            do_libs(MY_CLOSE);
            exit(0);}
        LoadRGB4(&Screen->ViewPort,cmap,4);
    }
    else CloseScreen(Screen);
}

void    getrule() /*=====================================================*/
{
char    buf[161]; /* There's always some bastard! */
int     i,j;

    while (1) {
        printf("Input 10 digit rule: ");
        gets(buf);
        if ((i = strlen(buf)) < 10) {
            printf("Padding rule with zeros ");
            for (j = i-1; j < 10; j++) {
                printf(".");
                buf[j] = '0';
            }
            printf(" done.\n");
        }
      /* Go through string to check for just 0-3 digit values */
        for (j = 0; j < 10; j++) {
            if ((buf[j] < '0') || (buf[j] > '3')) {
                printf("May only have digits from 0 to 3.\n");
                j = 11;
            }
        }
        if (j < 11) break;
    } /* End while */
    for (j = 0; j < 10; j++) rule[j] = (short)(buf[j] - '0');
}

void    getseed(f) /*====================================================*/
int     f;
{
int     j;
ULONG   sec,mic;

    CurrentTime(&sec,&mic);
    srand(sec*(mic+1));
    for (j = 0; j < LINEWIDTH; j++) seed[j] = 0;
    if (f == 1) {
        for (j = 50; j < LINEWIDTH; j += 50) seed[j] = 1;
        return;
    }

    if (f == 2) {
        seed[LINEWIDTH/2] = 1;
        return;
    }

    for (j = 0; j < 100; j++) seed[rand()%LINEWIDTH] = 1;
    for (j = 0; j < 100; j++) seed[rand()%LINEWIDTH] = 2;
    for (j = 0; j < 100; j++) seed[rand()%LINEWIDTH] = 3;   
}

void    sumseed() /*=====================================================*/
{
register int    j;
register short  *sumptr,*seedptr;

    sum[0] = seed[LINEWIDTH-1] + seed[0] + seed[1];
    sum[LINEWIDTH-1] = seed[LINEWIDTH-2] + seed[LINEWIDTH-1] + seed[0];
    sumptr = &sum[0];
  /* Ready for next loop */
    sumptr++;
    for (j = 1; j < LINEWIDTH-1; j++)
        *sumptr++ = seed[j-1] + seed[j] + seed[j+1];
    sumptr = &sum[0];   seedptr = &seed[0];
    for (j = 0; j < LINEWIDTH; j++)
        *seedptr++ = rule[*sumptr++];
}

void    drawline() /*====================================================*/
{
register int    i,j;
register UWORD  *put1,*put2,mask1,mask2,t;
register short  *seedptr;

    seedptr = &seed[0];
    j = (curline<<5)+(curline<<3);      /* curline * 40 */
    put1 = (UWORD *)((int)Screen->RastPort.BitMap->Planes[0] + j);
    put2 = (UWORD *)((int)Screen->RastPort.BitMap->Planes[1] + j);
    for (j = 0; j < 20; j++) {
        mask1 = mask2 = 0;
        for (i = 15; i >= 0; i--) {
            switch (*seedptr++) {
                case (0): break;
                case (1): mask1 |= (1<<i); break;
                case (2): mask2 |= (1<<i); break;
                case (3): t = (1<<i);
                          mask1 |= t;
                          mask2 |= t; break;
            }
        }
        *put1++ = mask1;
        *put2++ = mask2;
    }
}

void    main(argc,argv) /*===============================================*/
int     argc;
char    *argv[];
{
char    buf[80];
int     f = 0,j,flag = 0;

    if (argc > 1) {
        for (j = 1; j < argc; j++)
            switch (argv[j][0]) {
                case ('f') : f = 1; break;
                case ('F') : f = 2; break;
                case ('?') :
                    printf("USAGE: %s [f|F]\n",argv[0]);
                    exit(0);
                default: printf("WARNING: unknown option %s!\n",argv[j]);
            }
    }
    printf("[32mLinear Automata[31m, from:\n    [33mDec86 BYTE[31m p181: [32mAbstract Mathematical Art[31m - [3mKenneth E. Perry[0m\n");
    do_libs(MY_OPEN);
    do_screen(MY_OPEN);

    while (flag > -1) {
        if (!flag) {
            ScreenToBack(Screen);   
            getrule();
            ScreenToFront(Screen);
            getseed(f);
        }
        else flag--;
        for (curline = STARTLINE; curline < ENDLINE; curline++) {
            drawline();
            sumseed();
        }
        printf("OK. Screenful %d done.\n",flag);
        if (!flag) {
            gets(buf);
            if (buf[0] != 0) {
                switch (buf[0]) {
                    case ('l'): flag = 1; break;
                    case ('L'): sscanf(&buf[1]," %d",&flag);
                                break;
                    default: flag = -1;
                }
            }
        }
    }
    do_screen(MY_CLOSE);
    do_libs(MY_CLOSE);
}
