Write a Xiaole game in Python

When it comes to Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole Xiaole.

realization

The composition of Xiaole mainly includes three parts: game body, scorer and timer. Let's take a look at the specific implementation.

Let's take a look at the Python library required for the game.

import os
import sys
import time
import pygame
import random

Define some constants, such as window width and height, number of grid rows and columns, etc. the code is as follows:

WIDTH = 400
HEIGHT = 400
NUMGRID = 8
GRIDSIZE = 36
XMARGIN = (WIDTH - GRIDSIZE * NUMGRID) // 2
YMARGIN = (HEIGHT - GRIDSIZE * NUMGRID) // 2
ROOTDIR = os.getcwd()
FPS = 30

Then create a main window with the following code:

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Xiaoxiaole')

Take a look at the effect:

Then draw an 8 x 8 grid in the window. The code is as follows:

screen.fill((255, 255, 220))
# Grid drawing of game interface
def drawGrids(self):
	for x in range(NUMGRID):
		for y in range(NUMGRID):
			rect = pygame.Rect((XMARGIN+x*GRIDSIZE, YMARGIN+y*GRIDSIZE, GRIDSIZE, GRIDSIZE))
			self.drawBlock(rect, color=(255, 165, 0), size=1
# Draw a rectangular block box
def drawBlock(self, block, color=(255, 0, 0), size=2):
	pygame.draw.rect(self.screen, color, block, size)

Take a look at the effect:

Then put various puzzle blocks into the grid randomly, and the code is as follows:

while True:
	self.all_gems = []
	self.gems_group = pygame.sprite.Group()
	for x in range(NUMGRID):
		self.all_gems.append([])
		for y in range(NUMGRID):
			gem = Puzzle(img_path=random.choice(self.gem_imgs), size=(GRIDSIZE, GRIDSIZE), position=[XMARGIN+x*GRIDSIZE, YMARGIN+y*GRIDSIZE-NUMGRID*GRIDSIZE], downlen=NUMGRID*GRIDSIZE)
			self.all_gems[x].append(gem)
			self.gems_group.add(gem)
	if self.isMatch()[0] == 0:
		break

Take a look at the effect:

Then add the scorer and timer, and the code is as follows:

# Show score
def drawScore(self):
	score_render = self.font.render('fraction:'+str(self.score), 1, (85, 65, 0))
	rect = score_render.get_rect()
	rect.left, rect.top = (55, 15)
	self.screen.blit(score_render, rect)
# Show bonus points
def drawAddScore(self, add_score):
	score_render = self.font.render('+'+str(add_score), 1, (255, 100, 100))
	rect = score_render.get_rect()
	rect.left, rect.top = (250, 250)
	self.screen.blit(score_render, rect)
# Show remaining time
def showRemainingTime(self):
	remaining_time_render = self.font.render('count down: %ss' % str(self.remaining_time), 1, (85, 65, 0))
	rect = remaining_time_render.get_rect()
	rect.left, rect.top = (WIDTH-190, 15)
	self.screen.blit(remaining_time_render, rect)

Take a look at the effect:

When the set game time is exhausted, we can generate some prompt messages. The code is as follows:

while True:
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			pygame.quit()
			sys.exit()
		if event.type == pygame.KEYUP and event.key == pygame.K_r:
			flag = True
	if flag:
		break
	screen.fill((255, 255, 220))
	text0 = 'Final score: %s' % score
	text1 = 'Press R Key restart'
	y = 140
	for idx, text in enumerate([text0, text1]):
		text_render = font.render(text, 1, (85, 65, 0))
		rect = text_render.get_rect()
		if idx == 0:
			rect.left, rect.top = (100, y)
		elif idx == 1:
			rect.left, rect.top = (100, y)
		y += 60
		screen.blit(text_render, rect)
	pygame.display.update()

Take a look at the effect:

Having finished the relevant parts of the game graphical interface, let's take a look at the main processing logic of the game.

We use the mouse to manipulate the puzzle block, so the program needs to check whether there is a puzzle block selected. The code is as follows:

def checkSelected(self, position):
	for x in range(NUMGRID):
		for y in range(NUMGRID):
			if self.getGemByPos(x, y).rect.collidepoint(*position):
				return [x, y]
	return None

We need to exchange the positions of the puzzle blocks continuously selected by the mouse. The code is as follows:

def swapGem(self, gem1_pos, gem2_pos):
	margin = gem1_pos[0] - gem2_pos[0] + gem1_pos[1] - gem2_pos[1]
	if abs(margin) != 1:
		return False
	gem1 = self.getGemByPos(*gem1_pos)
	gem2 = self.getGemByPos(*gem2_pos)
	if gem1_pos[0] - gem2_pos[0] == 1:
		gem1.direction = 'left'
		gem2.direction = 'right'
	elif gem1_pos[0] - gem2_pos[0] == -1:
		gem2.direction = 'left'
		gem1.direction = 'right'
	elif gem1_pos[1] - gem2_pos[1] == 1:
		gem1.direction = 'up'
		gem2.direction = 'down'
	elif gem1_pos[1] - gem2_pos[1] == -1:
		gem2.direction = 'up'
		gem1.direction = 'down'
	gem1.target_x = gem2.rect.left
	gem1.target_y = gem2.rect.top
	gem1.fixed = False
	gem2.target_x = gem1.rect.left
	gem2.target_y = gem1.rect.top
	gem2.fixed = False
	self.all_gems[gem2_pos[0]][gem2_pos[1]] = gem1
	self.all_gems[gem1_pos[0]][gem1_pos[1]] = gem2
	return True

Each time we exchange puzzle pieces, we need to judge whether there are three or more consecutive puzzle pieces. The code implementation is as follows:

def isMatch(self):
	for x in range(NUMGRID):
		for y in range(NUMGRID):
			if x + 2 < NUMGRID:
				if self.getGemByPos(x, y).type == self.getGemByPos(x+1, y).type == self.getGemByPos(x+2, y).type:
					return [1, x, y]
			if y + 2 < NUMGRID:
				if self.getGemByPos(x, y).type == self.getGemByPos(x, y+1).type == self.getGemByPos(x, y+2).type:
					return [2, x, y]
	return [0, x, y]

When there are three or more puzzle blocks, these puzzle blocks need to be eliminated. The code is as follows:

def removeMatched(self, res_match):
	if res_match[0] > 0:
		self.generateNewGems(res_match)
		self.score += self.reward
		return self.reward
	return 0

After eliminating the matching puzzle blocks, we also need to randomly generate new puzzle blocks. The code is as follows:

def generateNewGems(self, res_match):
	if res_match[0] == 1:
		start = res_match[2]
		while start > -2:
			for each in [res_match[1], res_match[1]+1, res_match[1]+2]:
				gem = self.getGemByPos(*[each, start])
				if start == res_match[2]:
					self.gems_group.remove(gem)
					self.all_gems[each][start] = None
				elif start >= 0:
					gem.target_y += GRIDSIZE
					gem.fixed = False
					gem.direction = 'down'
					self.all_gems[each][start+1] = gem
				else:
					gem = Puzzle(img_path=random.choice(self.gem_imgs), size=(GRIDSIZE, GRIDSIZE), position=[XMARGIN+each*GRIDSIZE, YMARGIN-GRIDSIZE], downlen=GRIDSIZE)
					self.gems_group.add(gem)
					self.all_gems[each][start+1] = gem
			start -= 1
	elif res_match[0] == 2:
		start = res_match[2]
		while start > -4:
			if start == res_match[2]:
				for each in range(0, 3):
					gem = self.getGemByPos(*[res_match[1], start+each])
					self.gems_group.remove(gem)
					self.all_gems[res_match[1]][start+each] = None
			elif start >= 0:
				gem = self.getGemByPos(*[res_match[1], start])
				gem.target_y += GRIDSIZE * 3
				gem.fixed = False
				gem.direction = 'down'
				self.all_gems[res_match[1]][start+3] = gem
			else:
				gem = Puzzle(img_path=random.choice(self.gem_imgs), size=(GRIDSIZE, GRIDSIZE), position=[XMARGIN+res_match[1]*GRIDSIZE, YMARGIN+start*GRIDSIZE], downlen=GRIDSIZE*3)
				self.gems_group.add(gem)
				self.all_gems[res_match[1]][start+3] = gem
			start -= 1

Then repeat this process until the game time is exhausted and the game is over.

Finally, let's take a dynamic look at the game effect.

summary

In this paper, we use Python to implement a simple Xiaole game. Those who are interested can further expand the game, such as adding levels and so on.

Added by phenley on Thu, 27 Jan 2022 11:54:24 +0200