Generating two-element tuples of coordinates in a function

Viewed 1597

I need a function to pass coordinates as tuples, e.g. (x, y).

This is for my first project.

I want to create a Chessboard with pygame by simply blit white and black squares on the display.

Now, I have managed to blit all the white squares at the right destinations.

What I am trying to do here, is to automate the creation of white squares (later the same will happen with black ones), and more precisely, I am trying to pass the following tuples:

    # first column
    window.blit(square, (0, 0))
    window.blit(square, (0, 200))
    window.blit(square, (0, 400))
    window.blit(square, (0, 600))
    window.blit(square, (0, 800))
    # second column
    window.blit(square, (100, 100))
    window.blit(square, (100, 300))
    window.blit(square, (100, 500))
    window.blit(square, (100, 700))
    # third column
    window.blit(square, (200, 0))
    window.blit(square, (200, 200))
    window.blit(square, (200, 400))
    window.blit(square, (200, 600))
    window.blit(square, (200, 800))
    # fourth column
    window.blit(square, (300, 100))
    window.blit(square, (300, 300))
    window.blit(square, (300, 500))
    window.blit(square, (300, 700))
    # fifth column
    window.blit(square, (400, 0))
    window.blit(square, (400, 200))
    window.blit(square, (400, 400))
    window.blit(square, (400, 600))
    window.blit(square, (400, 800))
    # seventh column
    window.blit(square, (500, 100))
    window.blit(square, (500, 300))
    window.blit(square, (500, 500))
    window.blit(square, (500, 700))
    # seventh column
    window.blit(square, (600, 0))
    window.blit(square, (600, 200))
    window.blit(square, (600, 400))
    window.blit(square, (600, 600))
    window.blit(square, (600, 800))
    # eighth column
    window.blit(square, (700, 100))
    window.blit(square, (700, 300))
    window.blit(square, (700, 500))
    window.blit(square, (700, 700))

In my coordinator function, I have managed to write the following code, but from here on I don't really know what to do:

def coordinator():
    x = 0
    y = 0
    for column in range(0, 5):
        print((x, y))
        y += 200

    x = 200
    y = 0
    for column in range(0, 5):
        print((x, y))
        y += 200


coordinator()

Which gives as output:

(0, 0)
(0, 200)
(0, 400)
(0, 600)
(0, 800)
(200, 0)
(200, 200)
(200, 400)
(200, 600)
(200, 800)

Thanks in advance for any help.


Ok guys good news, I have managed to write some code which prints all the coordinates I need. here is my 'Main' class, and namely display.py:

import pygame
import sys
from coordinator import coordinator

pygame.init()
window_size = (800, 800)
game_window = pygame.display.set_mode(size=window_size)
pygame.display.set_caption('My Game')


while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()


    class WhiteSquare:
        height = int(window_size[0] / 8)
        width = int(window_size[1] / 8)
        a = height
        b = width
        white_square = pygame.Surface((a, b))
        white_square.fill((255, 255, 255), )

    ws = WhiteSquare()


    class BlueSquare:
        height = int(window_size[0] / 8)
        width = int(window_size[1] / 8)
        a = height
        b = width
        blue_square = pygame.Surface((a, b))
        blue_square.fill((0, 0, 255), )

    bs = BlueSquare()


    class ChessBoard:
        game_window.blit(ws.white_square, (0, 0))
        game_window.blit(bs.blue_square, (0, 100))





    cb = ChessBoard()

    coordinator()

    pygame.display.update()

For those solutions proposed which have got a black_square or refer to a black square in any manner, I have obviously changed my 'blue' into 'black'. I am only using a blue colour because I want to make sure that also black squares are working right on a black background (if that makes sense).

Here is the coordinator.py which works good enough by its own. The problem arises when I try to call it in the display.py file.

Have a look:

def coordinator():
    o = white_odd_columns()
    e = white_even_columns()
    print('o: ' + str(o))
    print('e: ' + str(e))


def white_odd_columns():
    for x in range(0, 800, 200):
        print('odd column: ')
        y = 0
        for first_column in range(0, 5):
            odd_coordinate = (x, y)
            print('This is odd_coordinate ' + str(odd_coordinate))
            y += 200
            #return odd_coordinate

def white_even_columns():
    for x in range(100, 800, 200):
        print('even column: ')
        y = 100
        for first_column in range(0, 4):
            even_coordinate = (x, y)
            print('This is even_coordinate ' + str(even_coordinate))
            y += 200
            #return even_coordinate

white_even_columns()
white_odd_columns()
coordinator()

Now, when I run coordinator I get the following output:

even column: 
This is even_coordinate (100, 100)
This is even_coordinate (100, 300)
This is even_coordinate (100, 500)
This is even_coordinate (100, 700)
even column: 
This is even_coordinate (300, 100)
This is even_coordinate (300, 300)
This is even_coordinate (300, 500)
This is even_coordinate (300, 700)
even column: 
This is even_coordinate (500, 100)
This is even_coordinate (500, 300)
This is even_coordinate (500, 500)
This is even_coordinate (500, 700)
even column: 
This is even_coordinate (700, 100)
This is even_coordinate (700, 300)
This is even_coordinate (700, 500)
This is even_coordinate (700, 700)
odd column: 
This is odd_coordinate (0, 0)
This is odd_coordinate (0, 200)
This is odd_coordinate (0, 400)
This is odd_coordinate (0, 600)
This is odd_coordinate (0, 800)
odd column: 
This is odd_coordinate (200, 0)
This is odd_coordinate (200, 200)
This is odd_coordinate (200, 400)
This is odd_coordinate (200, 600)
This is odd_coordinate (200, 800)
odd column: 
This is odd_coordinate (400, 0)
This is odd_coordinate (400, 200)
This is odd_coordinate (400, 400)
This is odd_coordinate (400, 600)
This is odd_coordinate (400, 800)
odd column: 
This is odd_coordinate (600, 0)
This is odd_coordinate (600, 200)
This is odd_coordinate (600, 400)
This is odd_coordinate (600, 600)
This is odd_coordinate (600, 800)
odd column: 
This is odd_coordinate (0, 0)
This is odd_coordinate (0, 200)
This is odd_coordinate (0, 400)
This is odd_coordinate (0, 600)
This is odd_coordinate (0, 800)
odd column: 
This is odd_coordinate (200, 0)
This is odd_coordinate (200, 200)
This is odd_coordinate (200, 400)
This is odd_coordinate (200, 600)
This is odd_coordinate (200, 800)
odd column: 
This is odd_coordinate (400, 0)
This is odd_coordinate (400, 200)
This is odd_coordinate (400, 400)
This is odd_coordinate (400, 600)
This is odd_coordinate (400, 800)
odd column: 
This is odd_coordinate (600, 0)
This is odd_coordinate (600, 200)
This is odd_coordinate (600, 400)
This is odd_coordinate (600, 600)
This is odd_coordinate (600, 800)
even column: 
This is even_coordinate (100, 100)
This is even_coordinate (100, 300)
This is even_coordinate (100, 500)
This is even_coordinate (100, 700)
even column: 
This is even_coordinate (300, 100)
This is even_coordinate (300, 300)
This is even_coordinate (300, 500)
This is even_coordinate (300, 700)
even column: 
This is even_coordinate (500, 100)
This is even_coordinate (500, 300)
This is even_coordinate (500, 500)
This is even_coordinate (500, 700)
even column: 
This is even_coordinate (700, 100)
This is even_coordinate (700, 300)
This is even_coordinate (700, 500)
This is even_coordinate (700, 700)
o: None
e: None

The problem I have got is that in coordinator.py, when I run the coordinator() function, I would like to see a list of tuples with all the coordinates, so that I can then easily access them through the list index.

If that works, then hopefully I can pass the coordinates to the ChessBoard class.

I will keep trying your solutions, although they are a bit too difficult to understand for me since I have just started with python and pygame.

This said, thanks for your time guys, I will keep an eye on your answers.

3 Answers

You'll want to use the modulo operator to generate "every-other" style coordinates.

for row in range(8):
    for col in range(8):
        if col % 2 == row % 2:
            x = col * 100
            y = row * 100
            window.blit(square, (x, y))

has the same effect as your original code.

To draw both black and white squares in a single pass,

for row in range(8):
    for col in range(8):
        is_black = (col % 2 == row % 2)
        x = col * 100
        y = row * 100
        window.blit((black_square if is_black else white_square), (x, y))

(assuming you have black_square and white_square sprites)

At the end of this answer, I'm giving you the function that will produce all coordinates for the chessboard, together with the right square to blit:

for square, coord in coordinator((ws.white_square, bs.blue_square)):
    window.blit(square, coord)

but I'll first cover how you can solve this problem with a few different loop approaches.

The simplest thing to do is to create a list, and appending your coordinates:

def coordinator():
    coordinates = []
    x = 0
    y = 0
    for column in range(0, 5):
        coordinates.append((x, y))
        y += 200

    x = 200
    y = 0
    for column in range(0, 5):
        coordinates.append((x, y))
        y += 200
    return coordinates

and the caller can then loop over the list for each tuple:

for coord in coordinator():
    window.blit(square, coord)

Note that you can simplify your loops greatly; y can be calculated by multiplying, and you could just append both the (0, ...) and (200, ...) tuples in the same iteration:

def coordinator():
    coordinates = []
    for column in range(5):
        coordinates.append((0, column * 200))
        coordinates.append((200, column * 200))
    return coordinates

or, instead of a column counter, you could tell range() to make steps of 200:

def coordinator():
    coordinates = []
    for y in range(0, 900, 200):
        coordinates.append((0, y))
        coordinates.append((200, y))
    return coordinates

You could also make x a loop, over the two values that it takes:

def coordinator():
    coordinates = []
    for x in (0, 200):
        for y in range(0, 900, 200):
            coordinates.append((x, y))
    return coordinates

This then can be turned into a single list comprehension:

def coordinator():
    return [(x, y) for x in (0, 200) for y in range(0, 900, 200)]

You don't really need the whole list, you could also make this a generator expression; this produces values on demand, and once each coordinate has been used, the memory is cleared again:

def coordinator():
    return ((x, y) for x in (0, 200) for y in range(0, 900, 200))

I only replaced [... for target in iterable] with (... for target in iterable) to tell Python to not keep everything in memory.

Of course, this all only produces the output you already created with print() statements.

To produce all possible white squares (100x100 squares across 9 rows, with 5 odd squares in even rows, 4 even squares in odd rows), you can just use a full range() for x and add another such loop for the odd columns:

for x in range(0, 900, 200):
    for y in range(0, 900, 200):
        coordinates.append((x, y))
for x in range(100, 900, 200):
    for y in range(100, 900, 200):
        coordinates.append((x, y))

Note that the only thing that changed for the second set of loops is the start value for y. You could, in fact, work out what that start value is from x, if x, divided the square size (100) is even, you want y to start at 0, otherwise you want y to start at 100. Make x step in increments of 100:

for x in range(0, 900, 100):
    even = (x // 100) % 2 == 0  # true for even, false for odd
    for y in range(0 if even else 100, 900, 200):
        coordinates.append((x, y))

This produces all your expected coordinates, from (0, 0), (0, 200), (0, 400), (0, 600), (0, 800) for the first row and (100, 100), (100, 300), (100, 500), (100, 700) for the second row all the way up to (800, 0), (800, 200), (800, 400), (800, 600), (800, 800) for the last row.

Note that it is better to not hard-code the numbers in the above, it would be better for the width, height and square size to be configurable. As a function, that then becomes:

def coordinator(boardsize=900, squaresize=100):
    coordinates = []
    for x in range(0, boardsize, squaresize):
        even = x // squaresize % 2 == 0  # true for even, false for odd
        for y in range(0 if even else squaresize, boardsize, squaresize * 2):
            coordinates.append((x, y))
    return coordinates

which produces your exact output:

>>> from pprint import pprint
>>> pprint(coordinator())
[(0, 0),
 (0, 200),
 (0, 400),
 (0, 600),
 (0, 800),
 (100, 100),
 (100, 300),
 (100, 500),
 (100, 700),
 (200, 0),
 (200, 200),
 (200, 400),
 (200, 600),
 (200, 800),
 (300, 100),
 (300, 300),
 (300, 500),
 (300, 700),
 (400, 0),
 (400, 200),
 (400, 400),
 (400, 600),
 (400, 800),
 (500, 100),
 (500, 300),
 (500, 500),
 (500, 700),
 (600, 0),
 (600, 200),
 (600, 400),
 (600, 600),
 (600, 800),
 (700, 100),
 (700, 300),
 (700, 500),
 (700, 700),
 (800, 0),
 (800, 200),
 (800, 400),
 (800, 600),
 (800, 800)]

This is not enough however, as this is just the white squares. You'd have to flip the logic to produce the blue squares.

However, it would actually be easier to just produce all squares, and let the function tell you what square to put there. You'd generate all coordinates, incrementing x and y by 100 ((0, 0), (0, 100), (0, 200), ..., (100, 0), (100, 100), (100, 200), ..., etc.) and then combine those with alternating white and blue squares.

The easiest way to do this, is to use the itertools module; it has a function to generate the Cartesian product needed for the (x, y) coordinates (itertools.product()) and it has a function to repeatedly alternate between the squares (itertools.cycle()):

from itertools import cycle, product

def coordinator(squares, boardsize=900, squaresize=100):
    # (x, y), from (0, 0) through to (800, 800)
    coordinates = product(range(0, boardsize, squaresize), repeat=2)
    # endless series of white, blue, white, blue, white, blue, ...
    squares_cycle = cycle(squares)
    return zip(squares_cycle, coordinates)

This produces (square, (x, y)) values for you; I used the strings 'white' and 'blue' only to demonstrate the result:

>>> pprint(list(coordinator(("white", "blue"))))
[('white', (0, 0)),
 ('blue', (0, 100)),
 ('white', (0, 200)),
 ('blue', (0, 300)),
 ('white', (0, 400)),
 ('blue', (0, 500)),
 ('white', (0, 600)),
 ('blue', (0, 700)),
 ('white', (0, 800)),
 ('blue', (100, 0)),
 ('white', (100, 100)),
 ('blue', (100, 200)),
 ('white', (100, 300)),
 ('blue', (100, 400)),
 ('white', (100, 500)),
 ('blue', (100, 600)),
 ('white', (100, 700)),
 ('blue', (100, 800)),
 ('white', (200, 0)),
 ('blue', (200, 100)),
 ('white', (200, 200)),
 ('blue', (200, 300)),
 ('white', (200, 400)),
 ('blue', (200, 500)),
 ('white', (200, 600)),
 ('blue', (200, 700)),
 ('white', (200, 800)),
 ('blue', (300, 0)),
 ('white', (300, 100)),
 ('blue', (300, 200)),
 ('white', (300, 300)),
 ('blue', (300, 400)),
 ('white', (300, 500)),
 ('blue', (300, 600)),
 ('white', (300, 700)),
 ('blue', (300, 800)),
 ('white', (400, 0)),
 ('blue', (400, 100)),
 ('white', (400, 200)),
 ('blue', (400, 300)),
 ('white', (400, 400)),
 ('blue', (400, 500)),
 ('white', (400, 600)),
 ('blue', (400, 700)),
 ('white', (400, 800)),
 ('blue', (500, 0)),
 ('white', (500, 100)),
 ('blue', (500, 200)),
 ('white', (500, 300)),
 ('blue', (500, 400)),
 ('white', (500, 500)),
 ('blue', (500, 600)),
 ('white', (500, 700)),
 ('blue', (500, 800)),
 ('white', (600, 0)),
 ('blue', (600, 100)),
 ('white', (600, 200)),
 ('blue', (600, 300)),
 ('white', (600, 400)),
 ('blue', (600, 500)),
 ('white', (600, 600)),
 ('blue', (600, 700)),
 ('white', (600, 800)),
 ('blue', (700, 0)),
 ('white', (700, 100)),
 ('blue', (700, 200)),
 ('white', (700, 300)),
 ('blue', (700, 400)),
 ('white', (700, 500)),
 ('blue', (700, 600)),
 ('white', (700, 700)),
 ('blue', (700, 800)),
 ('white', (800, 0)),
 ('blue', (800, 100)),
 ('white', (800, 200)),
 ('blue', (800, 300)),
 ('white', (800, 400)),
 ('blue', (800, 500)),
 ('white', (800, 600)),
 ('blue', (800, 700)),
 ('white', (800, 800))]

All your code has to do is give this function your ws.white_square and bs.blue_square square values, then use the output of each iteration step to blit with:

for square, coord in coordinator((ws.white_square, bs.blue_square)):
    window.blit(square, coord)

The advantage of this approach is that you can now vary the size of the board as well as vary the size of the squares. Your board has 9 x 9 squares and so must produce alternating rows:

1  2  3  4  5  6  7  8  9
┌───────────────────────────┐
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 1
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 2
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 3
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 4
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 5
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 6
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 7
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 8
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 9
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
└───────────────────────────┘

but actual chess boards have eight rows and columns:

1  2  3  4  5  6  7  8
┌────────────────────────┐
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 1
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 2
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 3
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 4
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 5
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 6
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│ 7
│░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓│
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│ 8
│▓▓▓░░░▓▓▓░░░▓▓▓░░░▓▓▓░░░│
└────────────────────────┘

Note that in this case there are the same number of white and blue squares on each row.

To generate that kind of board, all you have to do is adjust your board size, and optionally adjust the square size. A 800x800 pixel board would produce the correct output instantly:

for square, coords in coordinator((ws.white_square, bs.blue_square), 800):
    # ...

because the range() value is easily adjusted an the squares keep on alternating, regardless.

I think I have done it! I get the exact output I wanted (got a brand new problem now, but will post a new question).

Here is the coordinator:

# Returns two list of tuples, one for even columns (white squares), and
# one for odd columns (white squares).

# Needs to be implemented to achieve the same for black squares.

def coordinator():
    odd_columns = white_odd_columns()
    even_columns = white_even_columns()
    columns = [odd_columns, even_columns]
    # print(columns)
    return columns
    # print('odd_columns: ' + str(odd_columns))
    # print('even_columns: ' + str(even_columns))

# Returns a list with coordinates
# for white squares in odd columns
def white_odd_columns():
    odd_coordinates = []
    for x in range(0, 800, 200):
        y = 0
        for first_column in range(0, 5):
            odd_coordinates.append((x, y))
            y += 200
    # print('This should be the complete list of odd coordinates' + str(odd_coordinates))
    return odd_coordinates

# Returns a list with coordinates
# for white squares in even columns
def white_even_columns():
    even_coordinates = []
    for x in range(100, 800, 200):
        y = 100
        for first_column in range(0, 4):
            even_coordinates.append((x, y))
            y += 200
    # print('This should be the complete list of even coordinates' + str(even_coordinates))
    return even_coordinates

# white_even_columns()
# white_odd_columns()
coordinator()

And here is the output (a list of two lists of tuples - odd columns, and even columns, basically all the coordinates):

[[(0, 0), (0, 200), (0, 400), (0, 600), (0, 800), (200, 0), (200, 200), (200, 400), (200, 600), (200, 800), (400, 0), (400, 200), (400, 400), (400, 600), (400, 800), (600, 0), (600, 200), (600, 400), (600, 600), (600, 800)], [(100, 100), (100, 300), (100, 500), (100, 700), (300, 100), (300, 300), (300, 500), (300, 700), (500, 100), (500, 300), (500, 500), (500, 700), (700, 100), (700, 300), (700, 500), (700, 700)]]

Process finished with exit code 0

I consider my question answered, but I will keep your ones in mind when I will update the final solutions. Thank you, guys.

Related