Another approach that ought to work is based on turning a 1d 'noise' function (Brownian motion or something like) into a closed path. Imagine that you wanted a perfectly circular pool; then a simple way of imagining that would be to express it in polar coordinates (r,θ) : r=C for some constant C. If you wanted to put a little wobble into the boundary, you could introduce a bit of a sine wave to it: something like r=C0+C1*sin(θ), tracked for 0 <= θ <= 2*π, where C1 is much less than C0 (you don't want to have too much wobble, after all!). But that's only a bit of low-frequency noise, and you want a little more wobble — and you want your wobbles to be out of phase with each other — so add more terms representing higher frequencies:
for (int idx=1; idx < NUM_COEFFICIENTS; idx++ ) {
C[idx] = BASE_RADIUS*WOBBLE_FACTOR*rand[-1,1]/(idx*idx);
phase[idx] = rand[0, 2*pi];
}
for (float theta = 0; theta < 2*pi; theta += SMALL_STE) {
r = BASE_RADIUS;
for (int idx = 1; idx < NUM_COEFFICIENTS; idx++) {
r += C[idx] * sin(idx*theta+phase[idx]);
}
plot(r,theta);
}
Obviously there are a lot of constants to be fiddled with here (I would start with WOBBLE_FACTOR around .2 or so and NUM_COEFFICIENTS about 20), and you can look at producing bigger wobble (e.g., replacing the divide by (idx*idx) with a divide by just idx so that the higher-frequency terms aren't as damped), but for the resolutions of pools you're talking about most of that will just vanish.
Note that this will (basically) produce the outline of a lake by drawing 'around' it; if you wanted to 'scanline' your lakes you could do it by just deciding whether the center of each cell in your grid is within the lake's outline - that is, whether its radius is less than the frequency sum at that point:
for ( int y = -MAX_LAKE_RADIUS; y <= MAX_LAKE_RADIUS; y++ ) {
for ( int x = -MAX_LAKE_RADIUS; x <= MAX_LAKE_RADIUS; x++ ) {
r = sqrt(x^2+y^2);
theta = atan2(y,x);
test_r = BASE_RADIUS;
for (int idx = 1; idx < NUM_COEFFICIENTS; idx++) {
test_r += C[idx] * sin(idx*theta+phase[idx]);
}
if ( r <= test_r ) {
fill_lake(x,y);
}
}
}
This is somewhat similar to thalador's perlin-noise approach, except that we're building our noise function explicitly here (via its fourier transform, essentially) and using it to 'wobble' a 1d line (circle) rather than taking a level set.