I have some code I written. It was written in a hurry and it's really, really ugly. But if you get to understand it, you'll understand the basic algorithm.
The program uses this function, then smooths it using Gaussian blur. Then it creates a texture for the environment (really easy, blending several colors based upon height, so it doesn't look great).
While it's a really bad program, made as quickly as possible, and it's quite ugly, it displays how powerful the simple algorithm is. Taking this and spending some more time on fixing it would make the result a lot better looking. Which is quite doable with improvising, and I will definitely do it soon enough. Anyways, here's a screenshot:
http://img17.imageshack.us/img17/7963/worldt.png
EDIT: Ah, damn, I only just noticed you were talking about 2D, not 3D. 2D is quite similar to this, except that you only need a single step per displacement.
Code:
void Terrain::generateFractal(HeightMap &hm, double smoothness)
{
unsigned int width, depth;
width = hm.getWidth();
depth = hm.getDepth();
// The terrain width/height must be in the format (1<<x)+1
if(width <= 1 ||depth != width || ((width-1) & (width-2))) {
DEBUG("Invalid width/depth of terrain for fractal terrain generation: must be equal and a power of 2 plus one.");
return;
}
int delta_step = width-1;
double disposition_multiplier = 1.0/pow(2, smoothness);
double disposition = HeightMap::max_height;
while(delta_step > 1) {
// Do the square step
for(unsigned int left_x = 0; left_x < width - 1; left_x += delta_step) {
for(unsigned int top_z = 0; top_z < depth - 1; top_z += delta_step) {
int right_x = left_x + delta_step, bottom_z = top_z + delta_step;
int center_x = (left_x + right_x) / 2, center_z = (top_z + bottom_z) / 2;
double avg_height, new_value;
HeightMap::height_type new_height;
// Calculate the height of the center-point
avg_height = hm.getHeight(left_x, top_z)/4.0 + hm.getHeight(right_x, top_z)/4.0 + hm.getHeight(left_x, bottom_z)/4.0 + hm.getHeight(right_x, bottom_z)/4.0;
new_value = avg_height + ((rand() % (int)(2*disposition)) - disposition);
if(new_value > HeightMap::max_height)
new_value = HeightMap::max_height;
else if(new_value < HeightMap::min_height)
new_value = HeightMap::min_height;
hm.setHeight(center_x, center_z, new_value);
}
}
// Do the diamond step
for(unsigned int left_x = 0; left_x < width - 1; left_x += delta_step) {
for(unsigned int top_z = 0; top_z < depth - 1; top_z += delta_step) {
int right_x = left_x + delta_step, bottom_z = top_z + delta_step;
int center_x = (left_x + right_x) / 2, center_z = (top_z + bottom_z) / 2;
double avg_height, new_value;
HeightMap::height_type new_height;
// Loop through the four diamond points
const int dx[] = { -1, 0, 1, 0 };
const int dz[] = { 0, -1, 0, 1 };
for(unsigned int dir = 0; dir < 4; dir++) {
int diamond_x, diamond_z;
diamond_x = center_x + dx[dir] * delta_step / 2;
diamond_z = center_z + dz[dir] * delta_step / 2;
// Get the surrounding points and find the average of them
unsigned int num_points = 0;
avg_height = 0;
for(unsigned int avg_dir = 0; avg_dir < 4; avg_dir++) {
int avg_x, avg_z;
avg_x = diamond_x + dx[avg_dir] * delta_step / 2;
avg_z = diamond_z + dz[avg_dir] * delta_step / 2;
if(avg_x < 0 || avg_z < 0 || avg_z >= depth || avg_x >= width)
continue;
num_points++;
avg_height += hm.getHeight(avg_x, avg_z);
}
avg_height /= num_points;
// Now, calculate a random offset
new_value = avg_height + ((rand() % (int)(2*disposition)) - disposition);
if(new_value > HeightMap::max_height)
new_value = HeightMap::max_height;
else if(new_value < HeightMap::min_height)
new_value = HeightMap::min_height;
hm.setHeight(diamond_x, diamond_z, new_value);
}
}
}
// Do the next step with less disposition
delta_step /= 2;
disposition *= disposition_multiplier;
}
}