PyQt5 - Snake Game - GeeksforGeeks

03 Jun, 2020
In this article, we will see how we can design the simple snake game using PyQt5.
Snake is the common name for a video game concept where the player maneuvers a line which grows in length, with the line itself being a primary obstacle. The concept originated in the 1976 arcade game Blockade, and the ease of implementing Snake has led to hundreds of versions (some of which have the word snake or worm in the title) for many platforms.
Implementation steps :1. Create a main window add status bar to it, to show the score and create an object of board class and add it as central widget2. Create a class named board which inherits the QFrame3. Inside the board class create a timer object which calls the timer method after certain amount of time4. Inside the timer method call other action of the snake game like movement, food eaten and if snake committed suicide5. Create a key press event method that check if arrow keys are pressed and change the direction of the snake according to it.6. Create a paint event method that draws snake and the food7. Create move method to move the snake according to the direction8. Create food eaten method that checks the snake current position and position if food is eaten remove the current food increment the snake length and drop a new food at random location.9. Create check suicide method that checks if snakehead position is similar to the body position or not, if matches stop the timer and show the message
Below is the implementation
# importing librariesfrom PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * import randomimport sys  # creating game windowclass Window(QMainWindow):    def __init__(self):        super(Window, self).__init__()          # creating a board object        self.board = Board(self)          # creating a status bar to show result        self.statusbar = self.statusBar()          # adding border to the status bar        self.statusbar.setStyleSheet("border : 2px solid black;")          # calling showMessage method when signal received by board        self.board.msg2statusbar[str].connect(self.statusbar.showMessage)          # adding board as a central widget        self.setCentralWidget(self.board)          # setting title to the window        self.setWindowTitle('Snake game')          # setting geometry to the window        self.setGeometry(100, 100, 600, 400)          # starting the board object        self.board.start()          # showing the main window        self.show()  # creating a board class# that inherits QFrameclass Board(QFrame):      # creating signal object    msg2statusbar = pyqtSignal(str)      # speed of the snake    # timer countdown time    SPEED = 80      # block width and height    WIDTHINBLOCKS = 60    HEIGHTINBLOCKS = 40      # constructor    def __init__(self, parent):        super(Board, self).__init__(parent)          # creating a timer        self.timer = QBasicTimer()          # snake        self.snake = [[5, 10], [5, 11]]          # current head x head        self.current_x_head = self.snake[0][0]        # current y head        self.current_y_head = self.snake[0][1]          # food list        self.food = []          # growing is false        self.grow_snake = False          # board list        self.board = []          # direction        self.direction = 1          # called drop food method        self.drop_food()          # setting focus        self.setFocusPolicy(Qt.StrongFocus)      # square width method    def square_width(self):        return self.contentsRect().width() / Board.WIDTHINBLOCKS      # square height    def square_height(self):        return self.contentsRect().height() / Board.HEIGHTINBLOCKS      # start method    def start(self):        # msg for status bar        # score = current len - 2        self.msg2statusbar.emit(str(len(self.snake) - 2))          # starting timer        self.timer.start(Board.SPEED, self)      # paint event    def paintEvent(self, event):          # creating painter object        painter = QPainter(self)          # getting rectangle        rect = self.contentsRect()          # board top        boardtop = rect.bottom() - Board.HEIGHTINBLOCKS * self.square_height()          # drawing snake        for pos in self.snake:            self.draw_square(painter, rect.left() + pos[0] * self.square_width(),                             boardtop + pos[1] * self.square_height())          # drawing food        for pos in self.food:            self.draw_square(painter, rect.left() + pos[0] * self.square_width(),                             boardtop + pos[1] * self.square_height())      # drawing square    def draw_square(self, painter, x, y):        # color        color = QColor(0x228B22)          # painting rectangle        painter.fillRect(x + 1, y + 1, self.square_width() - 2,                         self.square_height() - 2, color)      # key press event    def keyPressEvent(self, event):          # getting key pressed        key = event.key()          # if left key pressed        if key == Qt.Key_Left:            # if direction is not right            if self.direction != 2:                # set direction to left                self.direction = 1          # if right key is pressed        elif key == Qt.Key_Right:            # if direction is not left            if self.direction != 1:                # set direction to right                self.direction = 2          # if down key is pressed        elif key == Qt.Key_Down:            # if direction is not up            if self.direction != 4:                # set direction to down                self.direction = 3          # if up key is pressed        elif key == Qt.Key_Up:            # if direction is not down            if self.direction != 3:                # set direction to up                self.direction = 4      # method to move the snake    def move_snake(self):          # if direction is left change its position        if self.direction == 1:            self.current_x_head, self.current_y_head = self.current_x_head - 1, self.current_y_head              # if it goes beyond left wall            if self.current_x_head < 0:                self.current_x_head = Board.WIDTHINBLOCKS - 1          # if direction is right change its position        if self.direction == 2:            self.current_x_head, self.current_y_head = self.current_x_head + 1, self.current_y_head            # if it goes beyond right wall            if self.current_x_head == Board.WIDTHINBLOCKS:                self.current_x_head = 0          # if direction is down change its position        if self.direction == 3:            self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head + 1            # if it goes beyond down wall            if self.current_y_head == Board.HEIGHTINBLOCKS:                self.current_y_head = 0          # if direction is up change its position        if self.direction == 4:            self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head - 1            # if it goes beyond up wall            if self.current_y_head < 0:                self.current_y_head = Board.HEIGHTINBLOCKS          # changing head position        head = [self.current_x_head, self.current_y_head]        # inset head in snake list        self.snake.insert(0, head)          # if snake grow is False        if not self.grow_snake:            # pop the last element            self.snake.pop()          else:            # show msg in status bar            self.msg2statusbar.emit(str(len(self.snake)-2))            # make grow_snake to false            self.grow_snake = False      # time event method    def timerEvent(self, event):          # checking timer id        if event.timerId() == self.timer.timerId():              # call move snake method            self.move_snake()            # call food collision method            self.is_food_collision()            # call is suicide method            self.is_suicide()            # update the window            self.update()      # method to check if snake collides itself    def is_suicide(self):        # traversing the snake        for i in range(1, len(self.snake)):            # if collision found            if self.snake[i] == self.snake[0]:                # show game ended msg in status bar                self.msg2statusbar.emit(str("Game Ended"))                # making background color black                self.setStyleSheet("background-color : black;")                # stopping the timer                self.timer.stop()                # updating the window                self.update()      # method to check if the food cis collied    def is_food_collision(self):          # traversing the position of the food        for pos in self.food:            # if food position is similar of snake position            if pos == self.snake[0]:                # remove the food                self.food.remove(pos)                # call drop food method                self.drop_food()                # grow the snake                self.grow_snake = True      # method to drop food on screen    def drop_food(self):        # creating random co-ordinates        x = random.randint(3, 58)        y = random.randint(3, 38)          # traversing if snake position is not equal to the        # food position so that food do not drop on snake        for pos in self.snake:            # if position matches            if pos == [x, y]:                # call drop food method again                self.drop_food()          # append food location        self.food.append([x, y])  # main methodif __name__ == '__main__':    app = QApplication([])    window = Window()    sys.exit(app.exec_())
Output :
PyQt-exercise
Python-gui
Python-PyQt
Python
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
How to Install PIP on Windows ?
Check if element exists in list in Python
How To Convert Python Dictionary To JSON?
Python Classes and Objects
How to drop one or multiple columns in Pandas Dataframe
Python | Get unique values from a list
Defaultdict in Python
Python | os.path.join() method
Create a directory in Python
Python | Pandas dataframe.groupby()