Worst Case Manhattan Exclusion
Python 2, 216 182 bytes
def f(W,H,R):L={(i%W,i/W)for i in range(W*H)};M={(x,y)for x,y in L if min(x,W-x)+min(y,H-y)>R};g=lambda s:min([1+g(s-{((a+x)%W,(b+y)%H)for x,y in L-M})for a,b in s]or[1]);return g(M)
Input like f(16,16,8)
. Uses pretty much the same algorithm as @trichoplax's sample, but with sets. Initially I didn't place the first item at (0, 0)
, but this made it choke on the last few cases.
All cases above complete within 10 seconds, well under the limit. In fact, the cases are small enough that I had a little room to be less efficient, allowing me to remove a check which checked for duplicate states.
(Thanks to @trichoplax for golfing help)
Expanded:
def f(W,H,R):
# All cells
L={(i%W,i/W)for i in range(W*H)}
# Mask: Complement of exclusion zone around (0, 0)
M={(x,y)for x,y in L if min(x,W-x)+min(y,H-y)>R}
# Place recursively
g=lambda s:min([1+g(s-{((a+x)%W,(b+y)%H)for x,y in L-M})for a,b in s]or[1])
return g(M)
Python 3, 270 262 260 251 246 226
(Thanks to Sp3000 for:
-~
instead of+1
, which lets me lose a space afterreturn
on the last line.- losing superfluous parentheses around
W*H
. - lambdas...
- putting everything on one line.
- python modulo
%
gives positive result for negative numbers, to save another 20 bytes)
This is the JavaScript example answer from the question ported into Python 3.
To avoid having to pass so many function arguments around, I've moved the two supporting functions inside the calculate function so that they share its scope. I've also condensed each of these functions into a single line to avoid the cost of indentation.
Explanation
This fairly brute force approach places the first item at (0, 0), and then marks all the excluded squares. It then recursively places an item at all remaining valid squares until all squares are excluded, and returns the minimum number of items required.
Golfed code:
def C(W,H,R):r=range;M=lambda g:min([M(G(g,x,y))for x in r(W)for y in r(H)if g[x+W*y]]or[-1])+1;G=lambda g,x,y:[g[a+W*b]if min((x-a)%W,(a-x)%W)+min((y-b)%H,(b-y)%H)>R else 0for b in r(H)for a in r(W)];return-~M(G([1]*W*H,0,0))
Ungolfed code:
def calculate(W, H, R):
starting_min = W * H + 1
cells = [0] * (W * H)
grid_state = grid_with_item_added(cells, 0, 0, W, H, R)
return min_from_here(grid_state, starting_min, W, H, R) + 1
def min_from_here(grid_state, starting_min, W, H, R):
no_cells = True
min = starting_min
for x in range(W):
for y in range(H):
if grid_state[x + W * y] == 0:
no_cells = False
new_grid_state = grid_with_item_added(grid_state, x, y, W, H, R)
m = min_from_here(new_grid_state, starting_min, W, H, R)
if m < min:
min = m
if no_cells:
return 0
else:
return min + 1
def grid_with_item_added(grid_state, x, y, W, H, R):
grid = grid_state[:]
for a in range(W):
for b in range(H):
if manhattan_distance(a, b, x, y, W, H) <= R:
grid[a + W * b] = 1
return grid
def manhattan_distance(a, b, c, d, W, H):
horizontal = min(abs(W + c - a) % W, abs(W + a - c) % W)
vertical = min(abs(H + d - b) % H, abs(H + b - d) % H)
return horizontal + vertical
if __name__ == '__main__':
import sys
arguments = sys.argv[1:]
if len(arguments) < 3:
print('3 arguments required: width, height and radius')
else:
print(calculate(int(arguments[0]), int(arguments[1]), int(arguments[2])))
The ungolfed code defines a function and also includes code to allow it to be called from the command line. The golfed code just defines the function, which is sufficient for standard code golf questions.
If you would like to test the golfed code from the command line, here it is with the command line handling included (but not golfed):
Command line testable golfed code
def C(W,H,R):r=range;M=lambda g:min([M(G(g,x,y))for x in r(W)for y in r(H)if g[x+W*y]]or[-1])+1;G=lambda g,x,y:[g[a+W*b]if min((x-a)%W,(a-x)%W)+min((y-b)%H,(b-y)%H)>R else 0for b in r(H)for a in r(W)];return-~M(G([1]*W*H,0,0))
if __name__ == '__main__':
import sys
arguments = sys.argv[1:]
if len(arguments) < 3:
print('3 arguments required: width, height and radius')
else:
print(C(int(arguments[0]), int(arguments[1]), int(arguments[2])))