This challenge is about creating these neat "green spray paint" patterns: (more pictures below)
Loosely explained, they are generated by starting with a black image and a point in the center. That point is successively moved by a randomly chosen offset or delta in x and y. Every pixel the point visits has 1 added to its green color channel. This process is then repeated with many many more points, all starting from the center, generating an entire green pattern.
Your task is to write the shortest program possible that takes in the necessary parameters and displays or outputs the resulting green spray paint image. This is code golf so the shortest code in bytes wins!
Specifics
The images require the following parameters, which must be inputs to your program in any order you choose:
Positive integer S is the width and height (Size) of the output image in pixels.
Non-negative integer N is the number of points to move over the image one by one, each adding a trail of green.
Non-negative integer M is the maximum number of moves that each point can take. (Without this they'd never stop.)
D is a list of pairs of integers
[(dx1, dy1), (dx2, dy2), ...]
that are deltas each point may be offset by each move.- You may also take this flattened
[dx1, dy1, dx2, dy2, ...]
or as two lists[dx1, dx2, ...]
and[dy1, dy2, ...]
.
- You may also take this flattened
Generate the patterns by starting with a pure black S×S pixel image. Then repeat the following process N times, after which your image will be ready to be output:
- Start a point in the center of the image. (This means
(floor(S/2), floor(S/2))
for most image coordinate systems.) - Add 1 to the green color channel of the pixel currently below the point (up to a max of 255).
- Consider moving the point by each of the deltas in D so it becomes like
(x + dx, y + dy)
. Keep track of the valid moves.- A valid move is one that keeps the point inside the bounds of the image, i.e. over a pixel.
- If there are no valid moves or the current point has taken M moves then stop and restart at step 1 with a new point.
- Otherwise, have the current point take a random valid move. Every valid move should have an equal chance.
- Go to step 2 to move the current point again.
Your implementation does not need to follow these precise steps as long as the results produced are the same. Of course they won't be exactly the same due to randomness, but it's easy to visually tell when things are working as expected.
Be sure to keep these corner cases in mind:
When N is 0 there are no points so the output should always be a totally black S×S image.
When M is 0 or D is empty it means no moves can be made, so only the starting pixel will have color. (See example 5.)
D may contain duplicate values that are valid moves, effectively meaning that move is more likely. (See example 7.)
Reference Program
Ungolfed reference code in Python 3. Not strictly a solution since it is hardcoded to output the first example from above.
S = 250
N = 800
M = 7000
D = [(1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1)] # chess knight moves
import random
from PIL import Image
img = Image.new('RGB', (S, S), 'black')
pix = img.load()
for i in range(N):
if (i + 1) % 10 == 0: print(f'{(i + 1)/N:.1%}') # progress tracker, not required output
x, y, = S//2, S//2
m = 0
while True:
pix[x, y] = 0, pix[x, y][1] + 1, 0
valid = [(x + dx, y + dy) for dx, dy in D if 0 <= x + dx < S and 0 <= y + dy < S]
if m >= M or not valid: break
x, y = random.choice(valid)
m += 1
#img.save('spraypaint.png') # uncomment to save image
img.show()
Examples
S = 250 N = 800 M = 7000 D = [(1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1)]
(from above)
S = 250 N = 1000 M = 3000 D = [(2, -1), (-2, -1), (-3, 0), (4, 0), (0, 2), (0, -1)]
(from above)
S = 250 N = 400 M = 10000 D = [(60, 59), (60, -59), (-59, 60), (-59, -60)]
S = 400 N = 600 M = 10000 D = [(0, 1), (-1, 0), (0, -1), (1, 0)]
S = 51 N = 1000 M = 1000 D = []
S = 51 N = 1000 M = 1 D = [(-5, 5), (9, 9), (-15, 1), (20, -25)]
S = 300 N = 1000 M = 1000 D = [(1, 0), (1, 0), (1, 0), (1, 0), (-3, 0), (0, 3), (0, -3)]
(from above)
S = 345 N = 123 M = 21212 D = [(1, -4), (2, -3), (3, -2), (4, -1), (-9, 9)]
S = 200 N = 1 M = 1500000 D = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
S = 300 N = 4000 M = 1000 D = [(7, 1), (-4, 3), (-4, -3)]
S = 300 N = 1500 M = 1000 D = [(10, -10), (-10, 10), (5, 5), (-5, -5), (1, 0), (-1, 0)]
S = 240 N = 1000 M = 10000 D = [(80, 81), (80, -81), (-81, 80), (-81, -80)]
Bonus
This isn't required but I would love to see what other neat patterns are possible. Put any cool images you make in your answer!
0x00FF00
(the maximum green color) and the point travels to it, what happens? Does it become0x010000
(tiny bit of red) or0x000000
(black)? \$\endgroup\$