10
\$\begingroup\$

I wrote a simple magic balls game in Python with GTK3. If the user clicks on a button with a ball, this ball will move to the selected point. If the user makes 5 consecutive balls horizontally or vertically, the balls disappear. In my move_ball function, there are many for loops that check for 5 balls. How can I simplify this?

enter image description here

# coding=utf-8
from random import randrange

import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf


class Cell(Gtk.Button):
    def __init__(self):
        Gtk.Button.__init__(self)
        self.ball_color = None
        self.is_ball = False
        self.button = Gtk.ToggleButton().new()
        self.button.set_size_request(50, 50)

    def place_ball(self, color):

        ball = GdkPixbuf.Pixbuf.new_from_file_at_size(str(color) + '.svg', 35, 35)
        image = Gtk.Image()

        image.set_from_pixbuf(ball)
        image.show()
        self.ball_color = color
        self.is_ball = True
        self.button.add(image)
        self.button.show()



# noinspection PyUnresolvedReferences
class BallsGrid(Gtk.Grid):
    def __init__(self, rows, cols):
        Gtk.Grid.__init__(self)
        self.rows = rows
        self.cols = cols
        self.cells = []
        self.dict = {1: 'blue', 2: 'fiolet', 3: 'green', 4: 'red', 5: 'yellow'}

        for row in range(rows):
            for col in range(cols):
                cell = Cell()
                self.cells.append(cell)
                self.attach(cell.button, row, col, 1, 1)

        self.place_balls(5)

    def place_balls(self, number):

        balls = 0

        while balls <= number:

            row = randrange(0, self.rows)
            col = randrange(0, self.cols)
            color = randrange(1, 6)

            i = self.get_index(row, col)

            if not self.cells[i].is_ball:
                self.cells[i].place_ball(self.dict[color])
                balls += 1

    def move_ball(self, from_point, to_point):

        color = self.cells[from_point].ball_color
        self.cells[to_point].place_ball(color)
        print self.cells[to_point]
        self.cells[from_point].button.get_child().destroy()
        self.cells[from_point].is_ball = False
        self.place_balls(3)

        (row, col) = self.get_row_col_button(to_point)

        # szukam sasiada

        index_horizontal_right = self.get_index(row + 1, col)
        index_horizontal_left = self.get_index(row - 1, col)

        index_vertical_up = self.get_index(row, col + 1)
        index_vertical_down = self.get_index(row, col - 1)

        horizontal_left, horizontal_right = 0, 0
        vertical_top, vertival_down = 0, 0

        if self.cells[index_horizontal_left].ball_color == color:
            horizontal_left += 1
        if self.cells[index_horizontal_right].ball_color == color:
            horizontal_right += 1
        if self.cells[index_vertical_up].ball_color == color:
            vertical_top += 1
        if self.cells[index_vertical_down].ball_color == color:
            vertival_down += 1

        for i in range(0, 5):

            if index_horizontal_left <= 90:
                if self.cells[index_horizontal_left].ball_color == color:
                    horizontal_left += 1
            if index_horizontal_right <= 90:
                if self.cells[index_horizontal_right].ball_color == color:
                    horizontal_right += 1
            if index_vertical_up <= 90:
                if self.cells[index_vertical_up].ball_color == color:
                    vertical_top += 1
            if index_horizontal_right <= 90:
                if self.cells[index_vertical_down].ball_color == color:
                    vertival_down += 1

            index_vertical_up += 1
            index_vertical_down -= 1
            index_horizontal_left -= 10
            index_horizontal_right += 10

        if horizontal_left == 5:
            for i in range(0, 5):
               delete_point = self.get_index(row - i, col)
               self.cells[delete_point].button.get_child().destroy()

        if horizontal_right == 5:
            for i in range(0, 5):
               delete_point = self.get_index(row + i, col)
               self.cells[delete_point].button.get_child().destroy()

        if vertical_top == 5:
            for i in range(0, 5):
                delete_point = self.get_index(row, col + i)
                self.cells[delete_point].button.get_child().destroy()

        if vertival_down == 5:
            for i in range(0, 5):
                delete_point = self.get_index(row, col - i)
                self.cells[delete_point].button.get_child().destroy()

    def get_row_col_button(self, index):
        return index / self.cols, index % self.cols

    def get_index(self, row, col):
        return (row * self.cols) + col


class App(Gtk.Window):
    def __init__(self, rows, cols):
        Gtk.Window.__init__(self)
        self.grid = BallsGrid(rows, cols)
        self.window = Gtk.Window()
        self.rows = rows
        self.cols = cols
        self.clicked = 0
        self.first_click = -1
        self.second_click = -1
        self.vbox = Gtk.VBox()
        self.window.add(self.vbox)
        self.create_grid()
        self.window.connect('destroy', Gtk.main_quit)

    def create_grid(self):
        # tworzenie poszczególnych połączeń do przycisków
        for i, cell in enumerate(self.grid.cells):
            (row, col) = self.grid.get_row_col_button(i)
            print 'Row {} col {}'.format(row, col)
            cell.button.connect('clicked', self.clicked_handler, row, col)

        self.grid.set_column_homogeneous(True)
        self.grid.set_row_homogeneous(True)
        self.vbox.pack_start(self.grid, expand=True, fill=True, padding=0)

    def clicked_handler(self, button, row, col):

        index = self.grid.get_index(row, col)

        if button.get_active():
            if self.first_click == -1:
                self.first_click = index
            else:
                self.second_click = index

            if self.first_click != -1 and self.second_click != -1:
                self.clicked = 0

                from_point = self.first_click
                to_point = self.second_click

                if self.grid.cells[from_point].is_ball:
                    self.grid.move_ball(from_point, to_point)

                print 'First {} second {}'.format(self.first_click, self.second_click)
                self.grid.cells[self.first_click].button.set_active(False)
                self.grid.cells[self.second_click].button.set_active(False)
                self.first_click, self.second_click = -1, -1

            self.clicked += 1
        else:
            self.grid.cells[index].button.set_active(False)


if __name__ == "__main__":
    win = App(10, 10)
    win.window.show_all()
    Gtk.main()
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

You probably mean this part of your code:

    if horizontal_left == 5:
        for i in range(0, 5):
           delete_point = self.get_index(row - i, col)
           self.cells[delete_point].button.get_child().destroy()

    if horizontal_right == 5:
        for i in range(0, 5):
           delete_point = self.get_index(row + i, col)
           self.cells[delete_point].button.get_child().destroy()

    if vertical_top == 5:
        for i in range(0, 5):
            delete_point = self.get_index(row, col + i)
            self.cells[delete_point].button.get_child().destroy()

    if vertival_down == 5:
        for i in range(0, 5):
            delete_point = self.get_index(row, col - i)
            self.cells[delete_point].button.get_child().destroy()

You may replace it with this:

    CORNERS = ((-1, 0), (+1, 0), (0, +1), (0, -1))
    coords =  (horizontal_left, horizontal_right, vertical_top, vertival_down)

    for (x, y), pos in zip(CORNERS, coords):
        if pos == 5:
            for i in range(0, 5):
               delete_point = self.get_index(row + x*i, col + y*i)
               self.cells[delete_point].button.get_child().destroy()

(as you originally did the similar things 4 times).


The explanation:

The builtin function zip() creates pairs from the corresponding elements:

(
    ((-1, 0), horizontal_left), 
    ((+1, 0), horizontal_right),
    ((0, +1), vertical_top),
    ((0, -1), vertival_down)
)

and in the for loop

for (x, y), pos in zip(CORNERS, coords):

its values are unpacked with expression

    (x, y), pos

You may use the similar method in other parts of your code, too.

\$\endgroup\$
1
  • \$\begingroup\$ You may replace for i in range(0, 5) with for i in range(5). \$\endgroup\$
    – Daniel
    Aug 3, 2017 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.