Remarks: Processing Mazes

From Maze

Revision as of 11:40, 22 July 2011 by PatsyBaudoin (Talk | contribs)
Jump to: navigation, search

The algorithm that defines the 10 PRINT maze exists independently from the Commodore 64 (C64) platform. In fact, the algorithm can be simply executed with a sheet of graph paper, a pen, and a coin to toss. The act of running the algorithm on a number of different platforms reveals what is essential to the algorithm, on the one hand, and to the specific constraints and affordances of the C64 on the other. The concision of 10 PRINT stems from its clever exploitation of the C64, as explained in section ??. For example, the code need not instruct the lines to wrap to the next line when they reach the edge of the screen because this is built into the system. The lines don't need explicit x- and y-coordinates because each character is drawn precisely next to the previous character to form the grid. More subtle defaults of the C64 include the color (light blue on blue) and the speed at which each new section of the maze is added. When 10 PRINT is ported to other platform, as demonstrated in section ??, these features of the C64 must be defined consciously within the new platform; the precise color, resolution, and speed of the maze are open to new ideas. While many elements of the C64 program can be modified, some are constraints of the system. Primarily, the 40 x 25 character screen that defines the resolution of the grid is fundamental to the hardware architecture and defines the number of units that can comprise the maze.

When the C64 was launched in 1982, it was a powerful home computer for creating computers graphics. Contemporary hardware is an interesting place to re-imagine the 10 PRINT algorithm because it has changed substantially from its predecessors. For this exploration into re-imaging 10 PRINT, we used Processing, a programming system first designed in 2001. It is built on top of Java, one of the dominant modern programming languages, but Processing exists to create graphics and simplifies some of Java's native syntax. Introduced in 1995, JAVA is most similar to C++, which itself is from 1983. C++ is an object-oriented programming language built on top of C, a procedural language originally from the early 1970s.


The first Processing version of 10 PRINT was written to take advantage of the increased resolution of contemporary screens. It does this by making the thickness and ends of the lines into variables that can be changed while the program runs. The lines of the maze can range from 0.5- to 10-pixels wide, and the lines can terminate with a rounded or square end. Like 10 PRINT, this Processing version of 10 PRINT maintains the grid of lines at 40 x 25 units, but, unlike 10 PRINT, it doesn't add each grid unit in sequence from left to right and top to bottom. In the new high-resolution version, the entire maze is refreshed at once. Using the default Processing colors to create white lines and a black background confers a mood similar to that of the original lower-contrast blues of the C64 (See Figures ?? to ??).



While creating these variations, some additional quick changes can be introduced to explore the visual aspects of the 10 PRINT maze. First, the 50-50 chance to draw a left or right line was altered so it could be weighted while the program ran to increase the chance of drawing one line instead of the other. The result is shown in Figures ?? to ??. Then, drawing graphic symbols different from the diagonal lines exposes part of the program's structure. The essence of the 10 PRINT maze algorithm is to make a coin-toss, a 50-50 random decision, to draw a left- or a right-diagonal line within a grid, from left to right, from top to bottom. The optical effect of the maze is created as these diagonals align themselves to produce walls and paths. Our eyes dance across the image as they attempt to find their way through the structure. Some symbols also create a strong, but different optical effect, while other symbols generate a boring, flat graphic. Figure ?? shows the result of using a blank image (space) and circle in place of the diagonals. This exploration into applying a different visual skin to the fundamental coin-toss structure of the 10 PRINT program reveals that the appeal of 10 PRINT derives from the random choice among two or more elements, the precise selection of which optically activate our eyes and create a culturally relevant image -- in this case, the maze.



It is important to note that these new changes can be explored directly on the C64, but it is convenient to do it in Processing because of the particular programmer's fluency in Programming -- it is easier to work through ideas and problems in one's native tongue. The 10 PRINT variations in weighting the random value in order to have a higher percentage of diagonal lines is created by changing the value after the decimal in 205.5 as, for example, in:

 10 PRINT CHR$(205.2+RND(1)); : GOTO 10

The 10 PRINT variation to show spaces and circles instead of diagonal lines also changes the value 205.5 to the number of the space character, 32, and to calculate the value of the circle, 113:

 10 PRINT CHR$(32+(INT(RND(1)+.5)*81)); : GOTO 10


After writing this first variable program in Processing to focus entirely on the image of the maze, it becomes clear that watching the maze build one unit at a time on the C64 is a major component of the experience. This behavior doesn't naturally take place with Processing because the entire screen updates as a whole, not character by character. Processing uses a technique called double-buffering, which means that the graphics are drawn to an off-screen image buffer stored in RAM, and when the image is finished it is pushed to the computer screen. For programs that feature animation or interaction, the result is a new image written and drawn to the screen about 60 times per second. Unlike in BASIC, the lines of the Processing program are not assigned numbers; each line is run in order from top to bottom and according to some higher-level rules. This becomes clearer when the program written in Processing by Ben Fry, the co-founder of Processing, to approximate the behavior of our 10 PRINT program on the C64:

 int w = 16;
 int h = 16;
 int index = 0;
 
 void setup() {
   size(640, 384);
   background(#0000ff);
   strokeWeight(1.5);
   stroke(224);
   smooth();
 }
 
 void draw() {
  int x1 = w*index;
  int x2 = x1 + w;
  int y1 = h*23;
  int y2 = h*24;
 
  if (random(2) < 1) {
    line(x2, y1, x1, y2);
  } else {
    line(x1, y1, x2, y2);
  }
 
  index++;
  if (index == width/w) {
    PImage p = get(0, h, width, h*23);
    background(#0000ff);
    set(0, 0, p);
    index = 0;    
  }
 }

The primary structure for the program is defined by the setup() and draw() blocks. Variables may be defined outside of these blocks, but everything else is sequenced through them. When the program starts, the variables outside of the blocks are declared and assigned. Next, the lines of code inside of setup() are read from top to bottom. Here, the size of the display window is set to be 640 pixels wide and 384 pixels high and the colors for the background and lines are defined. Next, the code inside draw() runs from top to bottom. The draw() block is unique because the code inside runs continuously from top to bottom until the program is terminated. The code defined by the if block within the draw() defines the random value and then draws one of the two possible lines. The code in the if block at the bottom of draw() moves the maze up when the maze line that is current drawing is filled. This code produces a program that behaves and looks more similarly to the original C64 code, but the process defined by the code is different.

There is a way within Processing to make the 10 PRINT work more similarly to the C64. This method uses the text Console of the Processing Development Environment (PDE) instead of the pixels in the display window; this Console is typically used for error messages and writing debug statements through the print() and println() functions, which are similar to CHR$() in BASIC. This technique may seem more like the C64, but the console can only print text; programs can't be written there. The other significant difference is the fact that the graphic characters of PETSCII (see p. ??) are not available in the native console font for the PDE. As a result the / and \ characters need to be used in place of diagonal graphics. This results in spaces between the lines, which destroys the illusion of a continuous maze. The Processing program that produces this approximation of 10 PRINT is also a one-line program:

 void draw() { print((random(1) < 0.5) ? '/' : '\\'); }

When the program is run in Processing, an empty display window opens, and the text is printed to the console as seen in Figure ??. This exploration raises a crucial difference between writing this program in BASIC for the C64 and writing it in Processing. The one-line Processing program is a more similar program to the BASIC program, but it produces a divergent result. To achieve a more similar visual result, the program needs to be written from the different point of view expressed in Code ?? [We might need to number code as well as figure to refer to them within the text].



Evaluating the similarities and differences between the C64 and Processing programs shows that the line endings are a subtle, but crucial change within the 10 PRINT program. In the original 10 PRINT image (see fig ??): each line terminates in a point. This is required because each of the 40 x 25 tiles that comprise the maze are constrained to an 8 x 8 pixel area. This maze is created by tiling the lines, each constrained within a 64 pixel square. In the Processing program, each line is free to extend beyond the boundaries that define a line. The screen in Processing is a continuous surface of pixels, each of which can be addressed precisely with an x- and y-coordinate rather than a tile position. The 10 PRINT program comprises 320 x 200 pixels, but the controllable resolution is 40 x 25, for a total of 1000 elements. A Processing version of the program can utilize all of the pixels on a contemporary screen which makes it possible to access millions of pixels. A standard 1080p screen, for example, provides 2,073,600 pixels to control.


With this enhanced resolution in mind, we wrote a third version of 10 PRINT in Processing that took more liberties with the original program. The number of rows and columns in the grid were variable, the direction of the line defined its color (black or white), each line was defined as a quadrilateral to give more flexibility of the shape, and a third color was used for the background. Each time the code is run, the size of the grid unit is defined at random as a power of 2 (2, 4, 8, 16, or 32) and the thickness of the lines are set randomly to 2, 4, or 8. Figures ?? and ?? show some of the varied results. With the ability to further define the graphics, the code became longer than the C64 one-liner:

 size(1020, 680);
 noStroke();
 background(0, 0, 255);
 int unit = height / (rows + 4);
 int rows = int(pow(2, int(random(1, 6))));       // 2, 4, 8, 16, 32
 int thickness = int(pow(2, int(random(1, 4))));  // 2, 4, 8
 int uthick = unit / thickness;
 int uthicker = unit + uthick;
 for (int x = int(-unit * 0.75); x < width+unit; x += unit) {
   for (int y = height/2 - rows/2 * unit; y < height/2 + rows/2 * unit; y += unit) {
     if (random(1) > 0.5) {
       fill(255);
       quad(x, y, x + unit, y + unit, x + uthicker, y + unit, x + uthick, y);
     } 
     else {
       fill(0);
       quad(x, y + unit, x + unit, y, x + uthicker, y, x + uthick, y + unit);
     }
   }
 }

The decision to display one direction of lines as white and the other as black triggers our evolved perception to create depth within this two-dimensional image. Our eyes inform us that there is a light source that is reflecting off one directional edge and creating a shadow on the other. The angle at which the lines terminate in this program enhance the affect by occlusion and terminating at the edge. This creates an isometric perspective which further enhances the perceived dimensionality in the image. These effects work well in some of the randomly determined configurations and are subverted by others.



After some reflection, we realized that despite all of the random options in the newly defined program, the line weight remains constant throughout. To check the visual effect of selecting a random line weight, we simply moved the code lines 6-8 to the beginning of the second for loop inside of draw(). The result is shown in figures ?? and ??. At this stage, the program has moved significantly away from its parent and has emerged as a qualitatively unique algorithm.


Personal tools