Site Index:

Journal
Wedding
Old Stuff
Pictures
Programming
Ramblings
Links

/*
 
   Well, this is kinda complicated, but beautifully simple. The main algorithm is a
measly thirty or so lines, a good deal of which is curly braces. The rest is just
support crap to make it convenient to work with, while still maintaining modularity
(I've made it an applet, and was able to leave almost all the code unchanged.). All
the functions are pretty well explained ( I hope.. ).
   There are really only three variables in this guy: initial conditions, edge
conditions, and line generation rules. I've currently (6/2/02) got the last one
fairly dynamic, but it still all relies on a hard-coded _three_ above/adjacent
cells to generate a given cell. This idea could just as easily have been done with
any number of input cells to generate the given cell, but we're doing three.
   I would like to make the initial conditions user definable somehow. Maybe give
some kind of function as input? Should be able somehow specify if we want one lone
ON cell in the middle, or some pseudo-random line easily... Thinking... For now, I've
got it user set-able between either random or using the old static function, which
requires a recompile to change... Better but not great.
   I have the edge conditions user set-able, but only as a static always ON or OFF.
Maybe I can find a formulaic way to do it?
 
     I'll pull out all of the old code that is commented out later; I wanna keep it in
to show progression through different versions...
 
 
 // most simple commented lines are code commented out
 
 // ** comments with doublestars are logical explanations
 
 /*
     multi-line comments explain a method
     
     or maybe it's just a block of commented/debug code
 */
 
 /*
 <applet code="Cell.class" WIDTH=1550 HEIGHT=1100></applet>
 */
 
 import java.util.Random;
 import java.awt.*;
 import java.awt.event.*;
 
 public class Cell extends java.applet.Applet implements ActionListener, ItemListener
 {
     private static boolean [] lastLine;
     private static int SIZE;
     private static int WIDTH;
     private static int REND_MULT = 5; //how many time more stuff we render than display
     private static boolean [] key = new boolean [8];
     private static int howMany = 1000; // How many lines to print out, in addition to initial
     private static boolean lEdge = false; // simple edge rule
     private static boolean rEdge = false; // simple edge rule
     private static boolean randStart = false; //else, patterned start
     private static int starts = 1; //How many points appear on first line, evenly spaced
     private static int pattern = 1; //pattern in binary to output as initConds
     private static int patPeriod = -1; //Period of pattern repitition, -1 = full width, one centered repetition
 
     //Variables for XPM output
     private static char on = '@';
     private static char off = ' ';
     private static boolean xpm = false;
 
     //More variable declarations at the applet code later on
 
 // ** Command-line specific program code
 
 /*
     main control... gets the wax going...
 */
     public static void main ( String [] args )
     {
         SIZE = 1500; //this is just the display size, we render out 5 times as much
         WIDTH = SIZE*REND_MULT; //render width
 
         parseArgs ( args );
 
         initConds ();
 
         //XPM
         if ( xpm )
         {
             off = '.';            
 
             System.out.println ( "/* XPM */" );
             System.out.println ( "static char *cellout[] = {" );
             System.out.println ( "/* width height num_colors chars_per_pixel */" );
             System.out.println ( "\"\t" + (WIDTH/REND_MULT) + "\t" + (howMany + 1) + "\t2\t1\"," );
             System.out.println ( "\". c #000\"," );
             System.out.println ( "\"@ c #fff\"," );
             System.out.println ( "/* pixels */" );
         }
 
         printout ( lastLine, 255 );
 
     // ** Here's where we ask for the magic, we generate and printout as many lines as this loop will run
         for ( int j = 0; j < howMany; j++ )
         {
             genLine();
             printout ( lastLine, howMany - j );
         }
 
         //XPM
         if ( xpm )
         {
             System.out.println ( "};" );
         }
     }
 
 /*
     a simple function to take a line as we've defined it and print it semi-reasonably
         only useful in a massively scalable text box,
             like xterm for X11, which can display chars as pixels, almost
 */
     private static void printout ( boolean [] line, int diff )
     {
         // ** Added some trickiness.. only wanna print the middle fifth
 
         int renderWidth = (line.length)/REND_MULT; // the width of what we render
             // Could just look at size, but this is likely safer
 
         //XPM
         if ( xpm ) {
             System.out.print ( "\"" ); }
 
         for ( int i = renderWidth*2; i <= renderWidth*3; i++ ) // Only want to print the middle part
         {
             if ( !line[i] ) //if ( line[i] == false )
                 System.out.print ( off );
             else
                 System.out.print ( on );
         }
         //XPM
         if ( xpm ) {
             if ( diff > 1 )
             {
                 System.out.print ("\"," );
             }
             else
             {
                 System.out.print ("\"" );
             }
         }
         System.out.println ();
     }
 
 /*
     parse the command-line arguments
 */
     private static void parseArgs ( String [] arg )
     {
         if ( arg.length < 1 )
         {
             usage();
             System.exit(1);
         }
 
         int rule = -42; // Soon to be our inputted argument as an integer; give a dumb default, so no compiler error/warning
         // ** Our input in integer format
         try {
             rule = Integer.parseInt ( arg[0] );
         }
         catch ( NumberFormatException e )
         {
             System.out.println ( "Rule argument must be an integer between O and 255 inclusive, not '" + arg[0] + "'" );
             usage();
             System.exit(1);
         }
 
         // ** How many lines to generate? We default to 1000 if the input is bad or missing
         try {
             howMany = Integer.parseInt ( arg[1] );
         }
         catch ( NumberFormatException e )
         {
             // Nothing... howMany defaults to 1000
         }
         catch ( ArrayIndexOutOfBoundsException e )
         {
             // Nothing, we're all good, we have a default
         }
 
         // ** How wide? We dafault to 1500
         try {
             SIZE = Integer.parseInt ( arg[2] );
             WIDTH = SIZE*REND_MULT;
         }
         catch ( NumberFormatException e )
         {
             //Nothing... SIZE defaults to 1500
         }
         catch ( ArrayIndexOutOfBoundsException e )
         {
             //Nothing... same
         }
 
         if ( 255 < rule || 0 > rule )
         {
             System.out.println ( "Rule argument must be an integer between O and 255 inclusive, not " + rule );
             usage();
             System.exit(1);
         }
 
         if ( arg.length >= 4 )
         {
             randStart = arg[3].equals("1");
         }
         if ( arg.length >= 5 )
         {
             lEdge = arg[4].equals("1");
         }
 
         if ( arg.length >= 6 )
         {
             rEdge = arg[5].equals("1");
         }
 
         if ( arg.length >= 7 )
         {
             xpm = arg[6].equals("-x");
         }
 
         //Realized I should seperate key[] generation from command line parsing...
         keyGen(rule);
     }
 
 // ** General code, used by all versions of program
 
 /*
     translate the inputted integer into a ruleset represented by key[]
 */
     private static void keyGen ( int in )
     {
         if ( in > 255 || in < 0 )
         {
             //How did this happen? Poor checking beforehand. Barf!
             System.out.println ( "Invalid value passed to keyGen(int): Bailing!" );
             System.exit(1);
         }
 
         // ** A useful number to help parse the input bitwise
         int div = 128;
         // ** log base 2 of current value of div
         int count = 7;
 
         // ** Run over the number a few times, pulling out the summed powers of 2 that make it up
         while ( div > 0 )
         {
             if ( ( in - div ) >= 0 ) {
                 in -= div;
                 key[count] = true;
             }
             else {
                 key[count] = false;
             }
             div /= 2;
             count--;
         }
 
         // ** We end up with an array of booleans, that represent the inputted number in binary
         // **   indexed by power as either a 1 or a 0 in the bool array
         // ** Get it? I think it'll be useful in specifying rulesets..
     }
 
 /*
     initialization functions
         we set the initial conditions here
         how we do that is completely arbitrary and hard-coded right now...
     all we do right now is either do it random, or do it hardcoded, based on a boolean
 */
     private static void initConds ()
     {
         lastLine = new boolean [WIDTH];
 
         if ( randStart )
             randConds ();
         else
             patConds ();
     }
 
     private static void randConds ()
     {
         Random rand = new Random();
 
     // **Construct the initial line, the initial condition
     // **Here we can make the first line as random or as regular as we please
     // **Wanna find a way to make this more dynamic too
         for ( int i = 0; i < lastLine.length; i++ )
         {
             int thisInt = rand.nextInt ( 3 );
             if ( 0 == thisInt )
                 lastLine[i] = true;
             else
                 lastLine[i] = false;
         }
 
     }
 
     private static void patConds () //patterned starting conditions
     {
         for ( int i = 0; i < lastLine.length; i++ )
         {
             lastLine[i] = false;
         }
 
         int section = SIZE / ( starts + 1 );
         for ( int i = 1; i <= starts; i++ )
         {
             lastLine[SIZE*2 + section*i] = true;
         }
 
     }
 
 /*
     the magic happens here...
 */
     private static void genLine ()
     {
         boolean [] thisLine = new boolean [WIDTH];
         int total = 0;
 
 // ** This loop looks at each of three cells above and adjacent to the current one
 // ** and figures out what the current one should be based on rules in key[]
         for ( int i = 0; i < thisLine.length; i++ )
         {
             // ** Cell above and to the left
             try {
                 total += ( lastLine[i-1] ? 4 : 0 );
             }
             catch ( ArrayIndexOutOfBoundsException e )
             {
                 total += ( lEdge ? 4 : 0 );
             }
 
             // ** Cell directly above
             total += ( lastLine[i] ? 2 : 0 );
 
             // ** Cell above and to the right
             try {
                 total += ( lastLine[i+1] ? 1 : 0 );
             }
             catch ( ArrayIndexOutOfBoundsException e )
             {
                 total += ( rEdge ? 1 : 0 );
             }
 
             // ** key[] contains our rules, each index being 
             // ** whether that total value will indicate a true or false
             // ** As such, its _really_ easy to implement here....
 
             try {
                 thisLine[i] = key[total];
             }
             catch ( ArrayIndexOutOfBoundsException e )
             {
                 //Bug catching, this should never happen... but it might...
                 System.out.println ();
                 System.out.println ( "Index out of bounds: " + total );
                 System.exit (1);
             }
 
             total = 0;
         }
         lastLine = thisLine;
     }
 
 // ** Also a command line method, but I don't wanna move it
     private static void usage ()
     {
         System.out.println ( "usage:\n   java Cell <rule-code> <lines> <width> <RandStart> <Left Edge> <Right Edge>" );
 
         System.out.println ( "\n   rule-code: integer between 0 and 255 inclusive" );
         System.out.println ( "   lines: # of lines to generate, beyond the initial (optional)" );
             System.out.println ( "      Defaults to 1000" );
         System.out.println ( "   width: # of cells in a line (optional)" );
             System.out.println ( "      Defaults to 1500" );
         System.out.println ( "   RandStart: 1 or 0 indicating whether to start with random initial line (optional)" );
             System.out.println ( "      Defaults to 0 (off)" );
         System.out.println ( "   Left Edge: 1 or 0 indicating state of this edge (optional)" );
             System.out.println ( "      Defaults to 0 (off)" );
         System.out.println ( "   Right Edge: 1 or 0 indicating state of this edge (optional)" );
             System.out.println ( "      Defaults to 0 (off)" );
 
         System.out.println ( "\nAny optional argument must be preceded by all preceding arguments" );
     }
 
 // ** Applet stuff
     private TextField kInput = new TextField ( "", 3 ); // key[] input
     private TextField sInput = new TextField ( "", 3 ); // int starts input
     private Label keyLabel = new Label ( "KeyCode:" );
     private Label startsLabel = new Label ( "# of Starts:" );
     private Checkbox Randbox = new Checkbox ( "Random Start" );
     //private Checkbox Lbox = new Checkbox ( "Left Edge" );
     //private Checkbox Rbox = new Checkbox ( "Right Edge" );
 
     private int fromTop = 50; // number of pixels from top to drawing area
 
 /*
     gotta setup our variables before we run; similar to the beginning of main() in the console version
 */
     public void init ()
     {
         // ** Initializing width and height of painting window based on HEIGHT and WIDTH
         // ** Notice they're not the same; the applet itself is bigger
         SIZE = Integer.parseInt ( getParameter ( "WIDTH" )) - 10;
         WIDTH = SIZE*REND_MULT;
         howMany = Integer.parseInt ( getParameter ( "HEIGHT" )) - fromTop;
 
         setSize ( SIZE + 10 , howMany + fromTop );
 
         add ( keyLabel );
         add ( kInput );
         add ( Randbox );
         //add ( Lbox );
         //add ( Rbox );
         add ( startsLabel );
         add ( sInput );
 
         kInput.addActionListener ( this );
         sInput.addActionListener ( this );
 
         Randbox.addItemListener ( this );
         //Lbox.addItemListener ( this );
         //Rbox.addItemListener ( this );
     }
 
 /*
     does the same as the latter half of main()
 */
     public void paint ( Graphics page )
     {
         page.setColor ( new Color(0,0,0) );
 
         initConds ();
 
         drawLine ( lastLine , 0, page);
 
         for ( int i = 0; i < howMany; i++ )
         {
             genLine ();
             drawLine ( lastLine, i+1, page);
         }
     }
 
 /*
     the one that draws a line in the applet
 */
     private void drawLine ( boolean [] line, int off, Graphics page)
     {
         // ** Only wanna draw that middle fifth again
         int drawWidth = (line.length)/REND_MULT;
 
         for ( int i = drawWidth*2; i < drawWidth*3; i++ )
         {
             // ** 5 is the offset from the left
             // ** fromTop is the offset from the top of the applet to the top of the cells
             // ** off is the current line number
             // ** So, we're drawing lines that are one pixel high and wide
             // ** by using the same points for each end of the "line" (effectively "point")
             if ( line[i] )
                 page.drawLine ( 5+(i-drawWidth*2), fromTop+off, 5+(i-drawWidth*2), fromTop+off );
             //else
                 // draw nothing and move on
         }
     }
 
     public String getAppletInfo ()
     {
         return  "Name: Brian Kelly\n"
                 + "Topic: Assignment 7\n"
                 + "About: iterative cell generation\n"
                 + "\nIt makes pretty patterns...";
     }
 
 //ActionListener function
     public void actionPerformed ( ActionEvent e )
     {
         if ( e.getSource() == kInput )
         {
             int ev = 0;
             try {
                 ev = Integer.parseInt (e.getActionCommand ());
             }
             catch ( NumberFormatException x )
             {
                 //Do nothing, we've got a valid default
             }
         
             if ( ev < 256 && ev > -1 )
             {
                 keyGen ( ev );
                 repaint ();
             }
             else {
                 System.out.println ( "Bad key input '" + ev + "'" );    
             }
         }
         else if ( e.getSource() == sInput )
         {
             int ev = 1;
             try {
                 ev = Integer.parseInt ( e.getActionCommand ());
             }
             catch ( NumberFormatException x )
             {
                 //Do nothing, we've got a valid default
             }
 
             if ( ev >= 0 )
             {
                 starts = ev;
                 repaint();
             }
             else
             {
                 System.out.println ( "Bad key input '" + ev + "'" );
             }
         }
     }
 
 //ItemListener function
     public void itemStateChanged ( ItemEvent e )
     {
         if ( e.getItem().equals ( "Random Start" ) )
         {
             if ( e.getStateChange() == ItemEvent.SELECTED )
             {
                 randStart = true;
             }
             else
             {
                 randStart = false;
             }
         }
         else if ( e.getItem().equals ( "Left Edge" ) )
         {
             if ( e.getStateChange() == ItemEvent.SELECTED )
             {
                 lEdge = true;
             }
             else
             {
                 lEdge = false;
             }
         }
         else if ( e.getItem().equals ( "Right Edge" ) )
         {
             if ( e.getStateChange() == ItemEvent.SELECTED )
             {
                 rEdge = true;
             }
             else
             {
                 rEdge = false;
             }
         }
         else
         {
             // Indicated we've got an item that's not properly set up
             System.out.println ( "Blech.." );
         }
     }
 
 }