6

How can this if-statement be simplified? It makes a plus sign: https://i.stack.imgur.com/PtHO1.png

If the statement is completed, then a block is set at the x and y coordinates.

for y in range(MAP_HEIGHT):
    for x in range(MAP_WIDTH):
        if (x%5 == 2 or x%5 == 3 or x%5 == 4) and \
            (y%5 == 2 or y%5 == 3 or y%5 == 4) and \
            not(x%5 == 2 and y%5 == 2) and \
            not(x%5 == 4 and y%5 == 2) and \
            not(x%5 == 2 and y%5 == 4) and \
            not(x%5 == 4 and y%5 == 4):
            ...
Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36
  • Maybe I didn't explain well enough. A block is set at the x and y coordinates where the if statement is completed. – Bryce Guinta Nov 03 '10 at 15:01
  • 2
    Please don't add comments. Please **update** the question to be complete. Please **fix** the question and delete the comment. – S.Lott Nov 03 '10 at 15:24
  • I'm pretty sure you could condense this a lot by using some smart slicing, but I'm a bit too lazy to work it out. – Daenyth Nov 03 '10 at 18:21

6 Answers6

15

This is the same:

if (x % 5 == 3 and y % 5 > 1) or (y % 5 == 3 and x % 5 > 1): 
David Webb
  • 190,537
  • 57
  • 313
  • 299
12

Basically you're tiling a 5x5 binary pattern. Here's a clear expression of that:

pattern = [[0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0],
           [0, 0, 0, 1, 0],
           [0, 0, 1, 1, 1],
           [0, 0, 0, 1, 0]]

for y in range(MAP_HEIGHT):
    for x in range(MAP_WIDTH):
        if pattern[x%5][y%5]:
           ...

This is a very simple and general approach which would allow the pattern to be easily modified.

martineau
  • 119,623
  • 25
  • 170
  • 301
7

There are two trivial fixes:

  • Cache the result of x % 5 and y % 5
  • Use in or chained < to test the values:

Additionally, the test for <= 4 (or < 5) is actually redundant because every value of lx and ly will be < 5.

for y in range(MAP_HEIGHT):
    for x in range(MAP_WIDTH):
        lx = x % 5 # for local-x
        ly = y % 5 # for local-y
        if lx > 1 and y > 1 and \
           not (lx == 2 and ly == 2) and \
           not (lx == 4 and ly == 2) and \
           not (lx == 2 and ly == 4) and \
           not (lx == 4 and ly == 4):

Or you may just keep a list of the actually allowed tuples:

cross_fields = [(2, 3), (3, 2), (3, 3), (3, 4), (4, 3)]

for y in range(MAP_HEIGHT):
    for x in range(MAP_WIDTH):
        if (x % 5, y % 5) in cross_fields:
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    Wow. You made it so.. readable and I didn't think of using lists/tuples at all. Both of them work, except the first clip of code has a few mistakes. The last coupled conditional statements should be not (lX == 4 and lY == 4) and there should be a lX != 0 and lY != 0. – Bryce Guinta Nov 03 '10 at 15:04
  • @Azrathud: I actually find Dave’s answer somewhat better. Anyway, thanks for the corrections. – Konrad Rudolph Nov 03 '10 at 15:06
  • I think that using the tuples is the way to go. Not only it's cleaner what it does, but it is also more flexible. – Tamás Szelei Nov 04 '10 at 10:16
  • @Daniel Roseman, & @Iain Galloway: This solution and the others that use the `in` operator all suffer from the fact that their doing a linear searche through a sequence of modulo coordinates for each x and y. If you're going to do it this way it would generally be better and faster to use a `set` whose keys were tuples of `(x%5,y%5)`. That way, regardless of whether this set was constructed from the original or an optimized `if` statement, a bitmap pattern, or from an explicit `list` or `tuple` of coordinates, the resulting statement would be just `if (x%5,y%5) in setvar:`. – martineau Nov 19 '10 at 19:59
2

Building on Konrad's answer, you can simplify it further:

for y in range(MAP_HEIGHT):
    for x in range(MAP_WIDTH):
        lx = x % 5 # for local-x
        ly = y % 5 # for local-y
        if (1 < lx < 5 and 1 < y < 5 and 
           (lx, ly) not in ((2, 2), (4, 2), (2, 4), (4, 2))):
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
1

Konrad's second answer:-

cross_fields = [(2, 3), (3, 2), (3, 3), (3, 4), (4, 3)]

for y in range(MAP_HEIGHT):
  for x in range(MAP_WIDTH):
    if (x % 5, y % 5) in cross_fields:

is probably the best one.

However, I'll contribute:-

for y in range(MAP_HEIGHT):
  for x in range(MAP_WIDTH):
    lx = x % 5
    ly = y % 5
    if (lx > 1 and ly == 3) or (ly > 1 and lx == 3):
Iain Galloway
  • 18,669
  • 6
  • 52
  • 73
0

The general solution to optimizing a logic function like this is a Karnaugh map. Your truth table would be the literal plus shape you want with rows and columns being your modular tests.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
  • Ah. So similar to martineau's answer? – Bryce Guinta Nov 04 '10 at 06:06
  • The first step would be similar, but the Karnaugh map is a technique for distilling the minimum logic expression necessary to cover the cases. – Ben Jackson Nov 04 '10 at 06:44
  • (& @Iain Galloway): The bitmap used in my answer is basically a K-map of two input variables (`x%5` and `y%5`) -- so, yes, the result would be the same as my answer but was arrived at in a completely different (and simpler IMHO) way. – martineau Nov 19 '10 at 20:15