Design Puzzle Game “Eight Queens” with PyGame – A Detailed Code Analysis

In previous post, I introduced how to use PyGame to convert a Scratch project into a Python project. If you are interested in that project, you could refer to this post: Forest Maze.

In today’s post, I would like to introduce another project written in Python and run on PyGame. Compared to previous project, this one requires more Python coding knowledge, such as defining class and creating multiple objects, using self-defined module to check the validity of chess layout, etc.

This game is named Eight Queens. If you have not heard the game Eight Queens, you could search the Internet. The eight queens puzzle is the problem of placing eight chess queens on an 8×8 chessboard so that no two queens threaten each other. Thus, a solution requires that no two queens share the same row, column, or diagonal.

Let’s start to analysis step by step now. The final effect of the game is shown in the following video.

Generally, there are two methods of designing this game. You could design an algorithm to make computer generate a solution automatically for you, as shown on this page: https://www.geeksforgeeks.org/8-queen-problem/.

In this game, we are designing a game where user could put the chess and change chess position, so that they could find a solution after many times of trial and error.

The whole project consists of two separate Python files. Puzzle_game.py is to use PyGame coding structure to interact with the user and show the visual interface. The other one eight_queens.py is used to define the back-end data processing and validity checkup.

Puzzle_game.py

This file contains the core code of the whole puzzle game.

At the beginning of the file, the program imports several modules, including pygame, math and eight_queens. Module “eight_queens” is the self-defined module I would introduce later.

After importing the module, from line 8 to line 41, the program defines a class called “QueenSprite”. This class represents the queen chesses on the chessboard. For a chessboard of 8*8, there are 8 queens to be placed in total. In the __Init__ function, the class defines several properties. “image” property is assigned an image object, representing the queen chess image. “posn” property is of a tuple type, representing the x and y position of the object, while “dragging” property is a Boolean value, representing if the object is being dragged or not. The other two properties “target_posn” and “y_velocity” are related to other functions which are not implemented in the current project, so we could ignore them here.

From line 20 to 41, the program defines some methods of the class. Method “drag_with_mouse” checks if the dragging property is True. If it is, the method will update the object’s “posn” property to follow mouse position closely. Method “draw” will blit the object image to the display surface. When we call “blit”, we can specify where an image should be placed on the main surface. The term “blit” is widely used in computer graphics, and means to make a fast copy of pixels from one area of memory to another.

The last method is “mouse_touch_sprite”. It returns a Boolean value and is used to check if a QueenSprite object touches mouse.

If you are familiar with Scratch programming, it is easy to find a matching programming block of Scratch. “drag_with_mouse” is corresponding to the programming block “go to mouse-pointer” in Scratch programming, while “mouse_touch_sprite” is like the programming block of “if touching mouse-pointer”.

For “draw” method, you could not find corresponding block in Scratch, because in Scratch, you do not need to take care of “blit” or update the display of each sprite. PyGame assumes you have developed enough skills, so you need to design your own functions through using the foundational methods offered by PyGame library.

The above is the definition of class QueenSprite. From line 44, the code starts the function “draw_board”, which is the main function of the whole project. It first initiates pygame status by calling “pygame.init()” and defines a tuple [(255, 0, 0), (0, 0, 0)], which represents Red and Black colors.

Please note that the input parameter “n” represents the grid size of the chessboard. If n = 8, it means that chess board is 8 * 8. You could define a bigger chessboard, such as 13 * 13 grid size, but that means more complexity for your users to solve this puzzle.

From line 49 to 51, the program separates the chessboard into n pieces and re-calculate the overall size. It then calls pygame.display() method to set window Caption, font type and font size.

From line 58 to line 63, the program loads the ball image. It also calculates the ball position’s offset against the top-left corner of each chess grid. In this demo project, I use ball image to represent Queen. You could replace it with more queen-like images.

At line 69 and 70, the program creates an empty list to store QueenSprite objects. The chess_board list is used to store the QueenSprite object’s position information. When Eight_queens.py judges the validity of the chess layout, it interacts with chess_board list.

The variable “FPS” is used to set the frame rate of Pygame. At line 169, the purpose of code “fpsClock.tick(FPS)” is to ensure that frame rate is fixed to the value of FPS, so that the game would not run too fast. Without setting this frame rate, the Pygame could run at a frame rate as high as 3000 per seconds, depending on your computer performance.

From line 84 to line 169, the program enters into the main loop of the Pygame structure. A modern way to write games (now that we have fast computers and fast graphics cards) is to redraw everything from scratch on every iteration of the game loop. That is what Pygame does.

Within each loop, the chessboard needs to redraw itself. The code from line 86 to line 92 implements this functionality. It draws itself by repeating with nested loops. For each time of drawing, it changes filling color, so the final effect is a black and red grid chessboard.

When chessboard is drawn, from line 95, the program enters an event loop. This structure is defined to catch the keyboard and mouse events. When mouse is pressed down, the program will check if any QueenSprite object touches the mouse. If it does, the program sets the object’s dragging property to True and clears the corresponding item’s value of chess_board list.

From line 115 to 132, the program checks the behavior when mouse is up. It first checks if Queen chess put in current cell conflicts with any existing QueenSprite objects. If there is any horizontal, vertical or diagonal QueenSprite conflict, the program will refuse putting the QueenSprite down.

From line 134 to 140, if the program verifies that it is a valid move, it needs to further check if the program is dragging an existing QueenSprite object. If it does, the program sets the object’s “posn” property to current cell position, and sets its “dragging” property to False. That means, the QueenSprite is settled down at the current position without moving with mouse any more.

At line 144, there is a variable “drag_existing” to indicate if the program is dragging existing QueenSprite object. If the variable value is False, it means the user is putting down a new chess, so the program creates a new QueenSprite at the current position and appends it to all_sprites list.

From line 154 to line 164, the program draws the sprites onto the display surface through using “blit” method. However, till this moment, user could not see them yet.

This is not a bug, but designed like this. If your graphics display hardware tries to read from memory at the same time as the program is writing to that memory, they will interfere with each other, causing video noise and flicker. To get around this, PyGame keeps two buffers in the main surface — the back buffer that the program draws to, while the front buffer is being shown to the user. Each time the program has fully prepared its back buffer, it updates the front buffer by calling “pygame.display.update()” (at line 168), and the display shown to the user changes.

The following module is imported by the above Puzzle_game.py. It contains two functions. The main function is “has_clashes_2”, which checks if any two QueenSprite objects are conflicting with each other. If there is any conflict, it returns False, otherwise, it returns True. This function is called by the above file at line 129 to check diagonal conflicts.

The functions in this module is not related to Pygame code structure, so I just explain it briefly here.

Eight_queens.py Module

That is all the code for this puzzle game. Hope this article could help you learn more about how to design your Python game and utilize the concepts of OOP. Enjoy the coding and have fun!

More Python related lessons, courses and workshop, please pay attention to the website update.

Note: All the analysis articles are copyright products of http://www.thecodingfun.com. Anyone re-posting them should credit author and original source. Anyone using them for commercial purposes or translating them into other languages should notify TheCodingFun and get confirmation first. All Rights Reserved.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.