Recently I saw a lot of people playing idioms crossword game, so first use PyGame to make a bar, it took a long time to finally complete, attached to the effect of the picture.There are two main py files
The encapsulated operation class idiom_lib.py
# -*- coding=utf-8 -*-
import sys
import random
if sys.version_info < (3.0):
reload(sys)
sys.setdefaultencoding('utf-8')
elif sys.version_info <= (3.3) :import imp
imp.reload(sys)
else:
import importlib
importlib.reload(sys)
Class used to store a verse
class IdiomInfo(object) :
def __init__(self,idiom) :
self.idiom = idiom # Verse text
self.dire = 0 # Verse direction 0 means horizontal and 1 means vertical
self.word_arr = [] # list of WordInfo objects for each kanji in the verse
def to_str(self) :
arr = []
for word_info in self.word_arr:
arr.append('%s %s %s'%(word_info.i,word_info.j,word_info.word))
return '%s,%s,%s'%(self.idiom, self.dire, '|'.join(arr))
Class used to hold a single text
class WordInfo(object) :
def __init__(self, word, i, j) :
self.i = i # the x coordinate of the text
self.j = j # the y coordinate of the text
self.word = word # Text content
self.is_lock = True # Whether the text needs to be filled in
self.state = -1
self.hide_index = -1
self.op_hide_index = -1
# Corresponding to the middle area of the number of grid construction matrix class, easy to manage the text in the middle
class Matrix(object) :
rows = 0 # lines
cols = 0 # the number of columns
data = [] # All text
def __init__(self, rows, cols, data=None) :
self.rows = rows
self.cols = cols
if data is None: data = [None for i in range(rows * cols)]
self.data = data
# set text
def set_val(self, x, y, val) :
self.data[y * self.cols + x] = val
# fetch text
def get_val(self, x, y) :
return self.data[y * self.cols + x]
def exist_val_four_around(self, x, y, ignore_set) :
Param x: column number :param Y: line number :param ignore_set: grid to ignore :return: ""
move_arr = [(-1.0), (1.0), (0, -1), (0.1)] # Left, right, up, down, four directions
for dx,dy in move_arr:
tx = x + dx
ty = y + dy
if (tx,ty) in ignore_set: continue
if tx < 0 or tx >= self.cols or ty <0 or ty >= self.rows: continue
if self.data[ty * self.cols + tx]: return True
return False
# Verse manipulation library
class IdiomLib() :
def __init__(self, block_num=12) :
self.word_dic={} # What verse does a word have, the format is: {word :[verse, verse],... }
self.word_arr=[] # List of all text
self.block_num=block_num # Number of horizontal and vertical grids
self.matrix = Matrix(self.block_num, self.block_num) # Text matrix for the middle region
self.idiom_dic={} # List of verses for the current level in the format: {verse: verse object IdiomInfo}
self.all_word_num=0 # The amount of text in the current level
self.hide_arr = [] [[x,y, text,None],...]
Load the verse from the file
def load_idiom_from_file(self, filename='words.txt') :
if sys.version_info < (3.0):
f = open(filename)
else:
f = open(filename,encoding='UTF-8')
all_idiom = f.readlines()
f.close()
for idiom in all_idiom:
if sys.version_info < (3.0):
idiom = idiom.strip().decode('utf-8')
else:
idiom = idiom.strip()
for word in idiom:
if word not in self.word_dic:
self.word_dic[word] = [idiom]
else:
self.word_dic[word].append(idiom)
self.word_arr = list(self.word_dic.keys())
def check_new_idiom(self, new_idiom, new_dire, word_info) :
"" Check if the new new verse is valid, invalid scene 1. Text is found beyond boundaries after typesetting 2. Syntax :param new_idiom: Param new_info: current text object :return: ""
windex = new_idiom.index(word_info.word)
cx,cy = word_info.i, word_info.j
ignore_set = set([(cx,cy)])
new_idiom_word_arr=[]
for i in range(-windex,-windex+len(new_idiom)):
if i==0:
new_idiom_word_arr.append(word_info)
else:
tx = cx+i if new_dire == 0 else cx
# Laterally out of bounds
if tx < 0 or tx >= self.block_num: return None.None
ty = cy if new_dire == 0 else cy+i
# Out of bounds vertically
if ty < 0 or ty >= self.block_num: return None.None
Is there any text around every word except the first
if self.matrix.exist_val_four_around(tx, ty, ignore_set): return None.None
old_word_info = self.matrix.get_val(tx, ty)
if old_word_info:
return None.None
new_word_info = WordInfo(new_idiom[i+windex], tx, ty)
new_idiom_word_arr.append(new_word_info)
return new_idiom_word_arr,windex
def add_idiom_to_matrix(self, idiom_num) :
if idiom_num == 0: return 0
for idiom,idiom_info in self.idiom_dic.items(): Walk through the selected verse
dire = idiom_info.dire
new_dire = 1 - dire # Verses alternate horizontal and vertical because horizontal =0, vertical =1,1- horizontal = vertical,1- vertical = horizontal
for word_info in idiom_info.word_arr: Walk over every word in the selected verse
word = word_info.word
idiom_list = self.word_dic[word]
for new_idiom in idiom_list: # Walk through the verse of the word
if new_idiom in self.idiom_dic: continue # If verse is already selected, skip
new_idiom_word_arr,windex = self.check_new_idiom(new_idiom, new_dire, word_info) # Check verse validity
if new_idiom_word_arr:
new_idiom_info = IdiomInfo(new_idiom)
new_idiom_info.dire = new_dire
# Place each word of the verse in the text matrix
for new_index in range(len(new_idiom_word_arr)):
new_word_info = new_idiom_word_arr[new_index]
if new_index == windex:
new_idiom_info.word_arr.append(word_info)
else:
self.matrix.set_val(new_word_info.i, new_word_info.j , new_word_info)
new_idiom_info.word_arr.append(new_word_info)
self.idiom_dic[new_idiom] = new_idiom_info
# Continue to add the next verse
return len(new_idiom) -1 + self.add_idiom_to_matrix(idiom_num - 1)
return 0
def get_idiom_matrix(self, idiom_num) :
self.idiom_dic={}
cx = int(self.block_num/2) -1
cy = int(self.block_num/2) -1
# Pick a random word
n = random.randint(0.len(self.word_arr)-1)
word = self.word_arr[n]
# Take the first verse in the list of verses made up of this word
idiom = self.word_dic[word][0]
wn = len(idiom)
# Save the first verse in the dictionary
self.idiom_dic[idiom] = IdiomInfo(idiom)
# Place each word of the verse in the text matrix
for i in range(len(idiom)):
word_info = WordInfo(idiom[i],cx-int(wn/2) +1+i,cy)
self.matrix.set_val(cx-int(wn/2) +1+i,cy,word_info)
self.idiom_dic[idiom].word_arr.append(word_info)
# Add the next verse
wn += self.add_idiom_to_matrix(idiom_num-1)
return wn
def get_hide_arr(self, percent) :
self.hide_arr=[] [[x,y, text,None],...]
idiom_word_arr = [] # list type: [[verse,[text object, text object...]],...]
for k,v in self.idiom_dic.items():
arr = []
for word_info in v.word_arr:
arr.append(word_info)
idiom_word_arr.append([k, arr])
# Sort the verse from most to least number of words
idiom_word_arr.sort(key=lambda x:-len(x[-1]))
idiom_index = 0
while len(self.hide_arr) < self.all_word_num*percent:
tmp_arr = idiom_word_arr[idiom_index%len(idiom_word_arr)][1] # Get a set of words for a verse
n = random.randint(0.len(tmp_arr)-1) # Select a random position in a set of text
info = tmp_arr.pop(n) # remove text
word=info.word
info.word = ' ' # Empty the text on the grid
info.hide_index = len(self.hide_arr) # Record the location index in the text hidden list
info.is_lock = False Text on the grid is clickable
self.hide_arr.append([info.i,info.j,word,None]) Add text to the hidden list
idiom_index+=1 # Go to the next verse
return self.hide_arr
def get_next_select(self, x, y) :
Select the next selected grid based on the specified location :param x: :param y: :return:"
arr = []
for i in range(self.block_num):
for j in range(self.block_num):
info = self.matrix.get_val(i, j)
if info is not None and len(info.word) == 0:
dist = (i-x)*(i-x)+(j-y)*(j-y)
if i<x: dist+=0.2 # the grid is to the left, increasing the distance by 0.2, and sorting will be further back
if j<y: dist+=0.4 Increase the distance by 0.4 to sort behind
arr.append((i,j,dist))
if len(arr) == 0:
return None
Sort by the distance of all optional cells
arr.sort(key=lambda x:x[-1])
return (arr[0] [0],arr[0] [1])
def check_idiom(self) :
for idiom, idiom_info in self.idiom_dic.items():
tmp_idiom_str = ' '
word_arr = idiom_info.word_arr
for word_info in word_arr:
word = word_info.word
if len(word) > 0:
tmp_idiom_str+=word
if len(tmp_idiom_str) == len(idiom):
state = 1 if tmp_idiom_str == idiom else 2
else:
state = 0
for word_info in word_arr:
ifword_info.state ! =1: word_info.state = state
for idiom, idiom_info in self.idiom_dic.items():
word_arr = idiom_info.word_arr
for word_info in word_arr:
ifword_info.state ! =1:
return False
return True
stage = 1
def init(self, new_stage) :
idiom_num = int(new_stage/5) +3
if new_stage>100:
percent = 0.7 The percentage of hidden text does not change after level # 100
else:
percent = 0.2+(new_stage*1.0/100) * (0.7-0.2)
self.matrix = Matrix(self.block_num, self.block_num)
# Generate a set of verses
self.all_word_num = self.get_idiom_matrix(idiom_num)
Extract some hidden words from the poem in proportion
self.get_hide_arr(percent)
The first missing word in the middle region is selected by default
self.select_rect = self.hide_arr[0] [0],self.hide_arr[0] [1]
if __name__ == '__main__':
pass
# lib = IdiomLib(block_num=10)
# lib.load_idiom_from_file()
# arr = []
# for i in range(1,101):
# lib.init(i)
# idiom_arr = []
# for k,v in lib.idiom_dic.items():
# idiom_arr.append(v.to_str())
# hide_arr = []
# for x,y,word,op in lib.hide_arr:
# hide_arr.append('%s %s %s'%(x,y,word))
# arr.append({'hide_num':len(hide_arr),'block_num':lib.block_num, 'word_num':lib.all_word_num,'idiom_arr':'; '.join(idiom_arr),'hide_arr':'; '.join(hide_arr)})
# #arr.sort(cmp=lambda x,y:cmp(x['hide_num']*2+x['word_num'], y['hide_num']*2+y['word_num']))
# arr.sort(key=lambda x:x['hide_num']*2+x['word_num'])
# import json
# f = open('idiom.json','w+')
# f.write(json.dumps(arr))
# f.close()
Copy the code
The main program main. Py
# -*- coding=utf-8 -*-
import sys
import random
import pygame
from pygame.locals import *
from idiom_lib import IdiomLib
if sys.version_info < (3.0):
reload(sys)
sys.setdefaultencoding('utf-8')
elif sys.version_info <= (3.3) :import imp
imp.reload(sys)
else:
import importlib
importlib.reload(sys)
# Number of horizontal and vertical characters
block_num=12
lib = IdiomLib(block_num=block_num)
lib.load_idiom_from_file()
# pixels vacated at the top (total height vacated at the top is header_height+main_space)
header_height = 30
# Empty pixels around the middle region
main_space = 20
# Height and width of each text in pixels
block_size = 36
# How many pixels to indent per grid
bspace = 2
space = 20
# Total width = Text pixels * Number of text + four peripherals *2
width = block_size * block_num + main_space * 2
# Total height = Top height + Text pixel * Number of text + four peripherals *2+ bottom height to be selected (i.e. : text pixel *3+ white space on both sides to be selected *3)
height = header_height + block_size * block_num + main_space * 2 + block_size * 3 + space * 3
pygame.init()
screen = pygame.display.set_mode((width,height))
screencaption = pygame.display.set_caption(U 'Fill in the blanks with poetry')
font = pygame.font.Font(u'syht.otf'.int(block_size*0.8))
dray_blue = 50.50.200
white = 255.255.255
#textImage = font. Render (u' hello ', True, white)
Load and stretch the background image
bg_image = pygame.image.load('bg.jpeg')
bg_image = pygame.transform.scale(bg_image,(width, height))
# Load and stretch the middle area of the image
bg2_image = pygame.image.load('bg2.jpeg')
bg2_image = pygame.transform.scale(bg2_image,(block_size*block_num,block_size*block_num))
Load and stretch the Megabyte background image for each Chinese character
block_bg_image = pygame.image.load('tzg.jpg')
block_bg_image = pygame.transform.scale(block_bg_image,(block_size-bspace*2,block_size-bspace*2))
stage = 1
lib.init(stage) Initialize the first level
Get the height of the text width to keep the text centered
stage_textImage = pygame.font.Font(u'syht.otf'.30).render(U '%s'%stage, True, dray_blue)
stage_font_width, stage_font_height = stage_textImage.get_size()
stage_x = int((width - stage_font_width)/2)
stage_y = int((header_height - stage_font_height)/2) +int(main_space/2)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == MOUSEBUTTONDOWN:
pressed_array = pygame.mouse.get_pressed()
if pressed_array[0]:
x, y = pygame.mouse.get_pos()
Calculates the selected grid index
xi = (x - main_space) // block_size
yi = (y - header_height - main_space) // block_size
if xi>=0 and xi<block_num and yi>=0 and yi<block_num:
Extract the object of the selected grid
info = lib.matrix.get_val(xi, yi)
Info. State ==1 is not allowed to click back. Info. Hide_index >=0 is not allowed to click back
if info andinfo.state ! =1 and info.hide_index >= 0:
if info.op_hide_index >= 0:
lib.hide_arr[info.op_hide_index][-1] = None
info.word = ' '
info.op_hide_index = -1
lib.check_idiom()
lib.select_rect = xi, yi
sx = main_space
sy = header_height + main_space+ block_size*block_num +space
n = 0 # The text number of the selection area below
# This loop is used to determine if the mouse is hovering over the text in the selection area below
for hi in range(len(lib.hide_arr)): Walk through the list of hidden words
tmp_x = sx + (n%block_num)*block_size # Start x pixel position of the NTH word
tmp_y = sy + int(n/block_num)*block_size # Position of the initial pixel in the y direction of the NTH word
if lib.hide_arr[hi][-1] is None and x >= tmp_x and x <= tmp_x+block_size-bspace*2 and y >= tmp_y and y<= tmp_y+block_size-bspace*2:
# Remove the object from the selected fillin
info = lib.matrix.get_val(lib.select_rect[0],lib.select_rect[1])
# Assign to the object
info.word = lib.hide_arr[hi][2]
info.op_hide_index = hi Record the location of the source to be selected to exit the text
info.state = 0 The state is not completed
lib.hide_arr[hi][-1] = lib.select_rect
# Select the next recommended fill location
lib.select_rect = lib.get_next_select(lib.select_rect[0],lib.select_rect[1])
flag = lib.check_idiom()
if flag:
stage += 1
lib.init(stage)
stage_textImage = pygame.font.Font(u'syht.otf'.30).render(U '%s'%stage, True, dray_blue)
break
n += 1
screen.blit(bg_image, (0.0))
screen.blit(stage_textImage, (stage_x,stage_y))
# Panel corresponds to the artboard in the middle area
panel = screen.subsurface((main_space,header_height+main_space,block_size*block_num,block_size*block_num))
panel.blit(bg2_image, (0.0))
# Draw the text in the middle position
for i in range(block_num):
for j in range(block_num):
info = lib.matrix.get_val(i,j)
if info is not None:
bx = block_size*i+bspace
by = block_size*j+bspace
panel.blit(block_bg_image, (bx,by))
if info.state == 1:
textImage = font.render(info.word, True, (30.144.30)) # Word green on verse correctly completed
elif info.is_lock == 1:
textImage = font.render(info.word, True, (100.100.100)) # Do not click on gray
elif info.state == 2:
textImage = font.render(info.word, True, (255.0.0)) # error red
else:
textImage = font.render(info.word, True, dray_blue) # default blue
tw, th = textImage.get_size()
dx=int((block_size-bspace*2-tw)/2)
dy=int((block_size-bspace*2-th)/2)
panel.blit(textImage, (bx+dx,by+dy))
if (i,j) == lib.select_rect: It is recommended to fill the position with a red border
pygame.draw.rect(panel,(255.0.0),(bx,by,block_size-bspace*2,block_size-bspace*2),2)
# Draw the text in the selected text area below
sx = main_space
sy = header_height + main_space+ block_size*block_num +space
n = 0
for i,j,word,op in lib.hide_arr:
screen.blit(block_bg_image, (sx + (n%block_num)*block_size,sy + int(n/block_num)*block_size))
if op is None:
textImage = font.render(word, True, dray_blue)
tw, th = textImage.get_size()
dx=int((block_size-bspace*2-tw)/2)
dy=int((block_size-bspace*2-th)/2)
screen.blit(textImage, (dx+sx+ (n%block_num)*block_size,dy+sy+ int(n/block_num)*block_size))
n+=1
pygame.display.update()
Copy the code
That’s it, but there are a few extra dependencies: Bg.jpeg for the background of the entire interface bG2.jpeg for the background of the top half tzg.jpg For the background of each text grid words.txt a list file of idioms (one idiom per line), Syht. otf a font library for normal display of Chinese
You can adjust these two parameters if you are too busy or too small
block_size = 32
block_num=12
Copy the code
Block_size specifies the size of the grid. How many grids can be displayed vertically or horizontally in the upper part of block_num
Block_size = 26, block_num=18Block_size = 40, block_num=10
The complete code and resources have been uploaded: github: github.com/zhangenter/…