//dit(this) programma(programm) speelt(plays)

// Conway's life in Java

//Sieuwert van Otterloo 1998

//SMotterl@math.ruu.nl



import java.applet.*;

import java.awt.*;



class cell

{

    final static int lowerbound=1,upperbound=32567;

    //make sure upperbound <= 32567,lowerbound>0

    public int x,y;//the coordinates of the cell



    public static int center()//the center of the universe of a cell

    //it's square, so just one int to return

    {

        return (lowerbound+upperbound)/2;

    }





    cell(int i,int j)

    {

      x=i;

      y=j;

    }



    public static boolean check(int x,int y)

    //is a cell (x,y) in the universe?

    {

        if(x<lowerbound||x>=upperbound||

            y<lowerbound||

            y>=upperbound)

            return false;

        return true;

    }



    public long tolong()

    /*converts the cell to a long containing both x and y

    This 'zipping in' should make the program faster*/

    { 	return x+32768*y;

    }



    public static int xfromlong(long l)

    //extracts an x-coordinate from a long

    {    return (int)l%32768;

    }

    public static int yfromlong(long l)

    //extracts an y-coordinate from a long

    {    return (int)l/32768;

    }

}



class world

// a world represents the universe for one moment

{

    static int maxcells=8000;//the maximum amount living cells

    cell[] celltable;//all living cells:the rest is dead

    int time,cells;//cells:number living cells. time:not used yet



    world(int t)//construct an empty world

    {

        time=t;

        celltable= new cell[maxcells];

        cells=0;

    }



    public void empty()//kill all cells in this world

    {

     for(cells=cells;cells>0;cells--)

        celltable[cells-1]=null;

     time=0;

    }



    world(world former)//construct the world next to former

    {

        celltable= new cell[maxcells];

        cells=0;

        time=former.time+1;//time is updated

        long[] buurtabel=new long[8*former.cells];

        //buur means neighbour, tabel means array(or table).

        int buren=0;

        long place,eencel;



        for(int i=0;i<former.cells;i++)//for every cell

        {place=former.celltable[i].tolong();//zip it in, for speed

         //add his 8 neighbours to the buurtabel.

	     buurtabel[buren]=place+1;buren++;

         buurtabel[buren]=place-1;buren++;

         buurtabel[buren]=place+32767;buren++;

         buurtabel[buren]=place+32768;buren++;

         buurtabel[buren]=place+32769;buren++;

         buurtabel[buren]=place-32767;buren++;

         buurtabel[buren]=place-32768;buren++;

         buurtabel[buren]=place-32769;buren++;

        }

        /*The 'buurtabel' contains a cellnumber iff that cell is

	a neighbour of some cell. If the cell is neighbouring more cells,

	the cellnumber is as many times in the buurtabel*/



	while(buren>0)//until the list is empty

        {eencel=buurtabel[0];//grab the first

         int aantal=0,index=0;

         while(buren>0&&index<buren)//search the whole list

         {



            if(buurtabel[index]==eencel)//if eencel occurs

            {

                aantal++;//count it

                buren--;//delete it.

                buurtabel[index]=buurtabel[buren];

            }

            else

                index++;//proceed searching

         }

         int eencelx=cell.xfromlong(eencel);

         int eencely=cell.yfromlong(eencel);//unzip eencel

         //AND HERE ARE THE RULES:

         if(aantal==3||//if it had 3 neighbours...

            (aantal==2&&former.exists(eencelx,eencely))

            //or it lived allready and has 2 neighbours..

	    )

            add(eencelx,eencely);//the cell may live.

	//THESE WERE THE RULES

        }

    }



    public void randomfill(int n)

    //put some cells in the world

    {

        empty();

        int x,y;

        int offset=cell.center();

        for(int i=0;i<n;i++)

        {

            x=(int)(Math.random()*16)+offset;

            y=(int)(Math.random()*16)+offset;

            delete(x,y);

            add(x,y);

        }



    }





    public void add(int x,int y)

    //make the cell on (x,y) live

    {

        if(cell.check(x,y))//is it in the world?

            {celltable[cells]=new cell(x,y);

             cells++;

            }

    }



    public boolean delete(int x,int y)

    //if it was indeed alive:kill it and return true

    //if it was dead:return false

    {

        for(int i=0;i<cells;i++)

        {

            if(celltable[i].x==x&&celltable[i].y==y)

            {cells--;

             celltable[i]=celltable[cells];

             return true;

            }

        }

     return false;

    }

    public boolean exists(int x,int y)

    //does the cell live:return true, otherwise: false

    {

        for(int i=0;i<cells;i++)

        if(celltable[i].x==x&&celltable[i].y==y)

            return true;

        return false;

    }



}



class worldview extends Canvas

//an interface-component capable of displaying a world

{

    world theworld;//the world that is displayed

    int x0,y0,zoom;//the cell in the upperleft corner

	//and the magnification(zoom)factor

    Color cellkleur,bgcolor,textcolor;

    //the color used to paint



    worldview(world w)

    {theworld=w;

     center();

     resize(300,300);

     zoom=10;

     cellkleur=new Color(0,255,0);

     bgcolor=new Color(255,255,255);

     textcolor=new Color(0,0,0);

    }



    public void center()

    //go to the center of the universe

    {x0=y0=cell.center();

     repaint();

    }



    public void randomfill(int n)

    {theworld.randomfill(n);

     repaint();

    }

    public void empty()

    {theworld.empty();

     repaint();

    }



    public void zoomin()

    {zoom++;

     if(zoom>20)

        zoom=20;

     repaint();

    }

    public void zoomout()

    {zoom--;

     if(zoom<2)

        zoom=2;

     repaint();

    }



    public void update(Graphics g)

    {   paint(g);}//avoids flickering



    public void paint(Graphics g)

    {

        int sx,sy;

        g.setColor(bgcolor);

        g.fillRect(0,0,300,300);

        g.setColor(textcolor);

        g.drawRect(0,0,299,299);

        

        g.setColor(cellkleur);

        for(int i=0;i<theworld.cells;i++)

        {sx=zoom*(theworld.celltable[i].x-x0);

         sy=zoom*(theworld.celltable[i].y-y0);

         g.fillRect(sx,sy,zoom-1,zoom-1);

        }

        g.setColor(textcolor);

        g.drawString(

            "time "+theworld.time+" cells "+theworld.cells

            ,1,299);

    }

    public boolean mouseDown(Event ev,int x,int y)

    {

        int cx,cy;

        cx=x/zoom+x0;

        cy=y/zoom+y0;

        if(ev.modifiers!=0)//second mousebutton

         {x0=cx-150/zoom;//move the viewpoint

          y0=cy-150/zoom;

         }

        else//flip the cell

        {if(!theworld.delete(cx,cy))

            theworld.add(cx,cy);

        }

     repaint();

     return true;

    }



    public void nextworld()

    //create the world following this one.

    {

        theworld=new world(theworld);

        repaint();

    }



}













public class lifeapplet extends Applet

implements Runnable

{

 Panel knoppen;

 Button center,next,zoomin,zoomout,empty,seed,start,stop;

 worldview aview;

 boolean running;

 Thread T;

 public void init()

 {

  resize(400,400);

  knoppen=new Panel();//'knoppen' means buttons

  knoppen.setLayout(new GridLayout(2,4));

  knoppen.add(center=new Button("center"));

  knoppen.add(next=new Button("step"));

  knoppen.add(zoomin=new Button("zoom in"));

  knoppen.add(zoomout=new Button("zoom out"));

  knoppen.add(empty=new Button("clear"));

  knoppen.add(seed=new Button("random seed"));

  knoppen.add(start=new Button("start"));

  knoppen.add(stop=new Button("stop"));

  running=false;

  add("East", knoppen);

  aview=new worldview(new world(0));

  add("Center",aview);

}





  public boolean action(Event ev, Object obj)

 //execute the action belonging to the button that

 //is touched

 {

    if(ev.target==center)

	    aview.center();

    else if(ev.target==next)

	    aview.nextworld();

	else if(ev.target==zoomin)

	    aview.zoomin();

	else if(ev.target==zoomout)

	    aview.zoomout();

    else if(ev.target==empty)

	    aview.empty();

    else if(ev.target==seed)

	    aview.randomfill(90);

    else if(ev.target==start&&!running)

	    {T=new Thread(this);T.start();running=true;}

    else if(ev.target==stop&&running)

	    {T.stop();running=false;}



    return true;

 }



 public void run(){

    while(true){

     try{Thread.sleep(200);}catch(InterruptedException e){};

     aview.nextworld();

    }

    

 }

}

