Introduction - Random Walks
The first chapter of Shiffman’s book introduces the student to some basics of programming, object-oriented design, animations of simulation and noise as a data-source. These summary titles make it sound quite grand, but the demos are all pretty small-scale, which is great for an introduction!
1.2 Random Walker class
The tutorial shows code for a sketch with a Walker
class that draws a pixel doing a random walk around a small window. Running the code draws a trail of dark dots across the canvas.
In Processing
I copied the code from the tutorial so I’ve not included it here.
The first time I scanned through it, I missed some of the syntax and thought the snippets were broken. The second time, when I was actually copying out the code, I realised they were broken up, but when taken together represented a full class definition. This was implied by the text, which I had understood in that way, but couldn’t initially see in the code. I’m learning Processing at the same time as the simulation ideas, so this stuff is key for me.
The use of global variables and functions in the example rubs me up the wrong way. The tutorials, and Processing itself, aren’t aimed at programmers; they’re for non-programming artists & designers learning code to support their creative practice. I come to this material from programming and keep wishing it were more complicated, or more explicit: modules, dependancy injectors and such-like. This isn’t important for the material, and would get in the way of the core training.
I want to use a switch()
instead of the list of if()
and else if()
clauses; personal stylistic preference.
In JavaScript
I reimplemented the Walker class using a constructor function and the prototype chain.
// A Walker "class"
function Walker(context, x, y) {
this.context = context;
this.x = x || context.canvas.width / 2 || 0;
this.y = y || context.canvas.height / 2 || 0;
}
Walker.prototype.step = function () {
var choice = parseInt( Math.random() * 4, 10 );
switch( choice ) {
case 0: this.x += 1; break;
case 1: this.x -= 1; break;
case 2: this.y += 1; break;
case 3: this.y -= 1; break;
}
return this;
}
Walker.prototype.display = function () {
this.context.fillStyle = 'rgb(0,0,0);';
this.context.fillRect( this.x, this.y, 1, 1 );
return this;
}
I tweaked the design slightly. The original Processing example makes use of globally available variables and methods for the environment, such as width
and stroke()
. I could have made equivalents of these availble to the class but I didn’t want to because JavaScript good practice encourages us to avoid globals. Instead I pass the drawing context into the Walker constructor and permit the Walker to know how the context will work. I’ll gladly hear alternatives to this.
Returning the instance (return this;
) from the methods is habit. I didn’t need to do this and it’s arguable whether it adds any usability to the very small object. It means I can chain calls to a Walker’s1 methods, so the draw loop looks like this:
function draw () {
walker.step().display();
}
Exercise i.1 Down and to the right
I’d already read ahead through the chapter so I didn’t think about this too hard. The solution which came to mind first was to compose an array of uneven amounts of 1s and -1s, in the constructor.
Each call to step()
, two variables are created containing a random pick from this uneven distribution of step possibilities. These variables are then added, one each to the Walker’s x and y co-ordinate. Because the array of modifiers has more 1s than -1s, the walker tends to walk down and right more often.
class Walker {
int x;
int y;
int[] directions;
Walker() {
x = width/2;
y = height/2;
directions = new int[10];
directions[0] = -1;
directions[1] = -1;
directions[2] = -1;
directions[3] = -1;
directions[4] = 1;
directions[5] = 1;
directions[6] = 1;
directions[7] = 1;
directions[8] = 1;
directions[9] = 1;
}
void display() {
stroke(0);
point(x,y);
}
void step() {
int stepx = directions[ int(random(10)) ];
int stepy = directions[ int(random(10)) ];
x += stepx;
y += stepy;
}
}
In JavaScript
Something I came across by chance (because I was rushing and being a little “sloppy”) is that I got a better result using and array of length 9, rather than one of length 10. The bias seemed gentler this way.
Walker.prototype.step = function () {
var bias = [-1, -1, -1, -1, 1, 1, 1, 1, 1 ];
var stepX = bias[ parseInt( Math.random() * 9, 10 ) ];
var stepY = bias[ parseInt( Math.random() * 9, 10 ) ];
this.x += stepX;
this.y += stepY;
return this;
}