

背景需求
【教学类-52-17】20250803动物数独_空格尽量分散_只有一半关卡数(N宫格通用版3-10宫格)0图、1图、2图、6图、有答案、无答案 组合版24套-CSDN博客文章浏览阅读926次,点赞30次,收藏13次。【教学类-52-17】20250803动物数独_空格尽量分散_只有一半关卡数(N宫格通用版3-10宫格)0图、1图、2图、6图、有答案、无答案 组合版24套
https://blog.csdn.net/reasonsummer/article/details/149879626?spm=1011.2415.3001.5331
因为做了24套交叉样式的动物数独PDF


我想直接给客户发送24套样式,自选,但是连我自己的都没有耐心把一个PDF从头拉到尾,所以我觉得不需要交叉合并,只需要提供三个文件夹
1.操作卡(0图、1图、2图、6图 )
2.任务卡(1图、2图、6图 )
3.答案卡(1图、2图、6图 )
修改过程:
花了两天时间,反复测试,修改参数。终于获取了三个文件夹样式。
# -*- coding:utf-8 -*-
'''
制作动物/脸谱数独N宫格通用版(3-10的黏贴关卡 A4 1图2图6图的所有组合 还有一个限定A4大小的0图(3-6宫格)
1.无答案:9种3:3,两两组合
2.有答案:27种 3:3:3,三三组合(实际需要其中9种)
3.横竖两个不能都是空格,空格尽量分散排列,如9格,目前1-5可以分散,空额不相连,6-9不行)
4.奇数宫格:(奇数*奇数+1)/2;偶数宫格:(偶数*偶数+1)/2
5.一次性生成3-6宫格,计算总时长
6.不需要24套,只要做3个文件夹操作卡(0,1,2,6图)、任务卡(1,2,6图)、答案卡(1,2,6图)
作者:AI对话大师,deepseek、阿夏
时间:2025年08月06日
'''
import os,math
import time
from docx import Document
from docx.shared import Cm, Pt, RGBColor
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docxtpl import DocxTemplate
import pandas as pd
from docx2pdf import convert
from win32com.client import constants, gencache
from win32com.client.gencache import EnsureDispatch
from datetime import datetime
import random
import shutil
import xlwt
import xlrd
from PyPDF2 import PdfMerger
from docx.shared import Pt, Cm, RGBColor
from docx.oxml.shared import qn
start_time = datetime.now()
# 改为循环处理多个宫格数
hsall_list = list(range(8,9)) # 要生成的宫格数列表
for hsall in hsall_list:
start = 0
end = hsall
hs = hsall
# 为每个宫格数创建独立的文件夹
path = fr'C:\Users\jg2yXRZ\OneDrive\桌面\20250806动物数独N宫格(3类一半)'
os.makedirs(path, exist_ok=True)
# 原有的A4尺寸等设置保持不变
A4size = [6.5, 4.88, 3.9, 3.25, 2.79, 2.44, 2.17]
A4gz = [3, 4, 5, 6, 7, 8, 9]
A4pg=[3,4,5,6,7,8,9]
A4gzs=[3,4,5,6,7,8,9]
psize=[19.6,14.1,9.39]
gzsize=[6.4,4.8,3.84,3.2,2.74,2.39,2.12,1.91]
gzsgg=list(range(3, 10))
pg=[1,2,6]
gzs=[1,2,2]
print('-----第一板块、动物操作卡(大图片卡一页1图、1页2图 一页6图)-------')
# Get page settings based on hs value
for w in range(len(A4gz)):
if hs == A4gz[w]:
psize.append(A4size[w])
pg = [1, 2, 6, A4pg[w]]
gzs = [1, 2, 2, A4gzs[w]]
k = ['1图关卡', '2图关卡', '6图关卡', f'A4的{hs}图卡片']
print(k)
for pt in range(len(pg)):
# Create temp folders
temp_word_path = os.path.join(path, '零时Word')
temp_pdf_path = os.path.join(path, f'05_动物数独_{hs}宫格_操作卡_pdf')
os.makedirs(temp_word_path, exist_ok=True)
os.makedirs(temp_pdf_path, exist_ok=True)
# Load template document
doc = Document(os.path.join(path, f'动物数独({k[pt]}).docx'))
table = doc.tables[0]
# Get animal images
pic_folder = os.path.join(path, '02动物图片')
pic_list = [os.path.join(pic_folder, f) for f in os.listdir(pic_folder)][start:end] * hs
# Group images based on layout
group_size = pg[pt]*pg[pt] if pt == 3 else pg[pt]
groups = [pic_list[i:i + group_size] for i in range(0, len(pic_list), group_size)]
for group_idx, group in enumerate(groups):
doc = Document(os.path.join(path, f'动物数独({k[pt]}).docx'))
table = doc.tables[0]
for cell_idx, img_file in enumerate(group):
row = int(cell_idx / gzs[pt])
col = cell_idx % gzs[pt]
cell = table.cell(row, col)
cell.paragraphs[0].clear()
if os.path.exists(img_file):
run = cell.paragraphs[0].add_run()
run.add_picture(img_file, width=Cm(psize[pt]), height=Cm(psize[pt]))
cell.paragraphs[0].alignment = 1
# Add title for A4 layout
if pt == 3:
title_table = doc.tables[1]
title_cell = title_table.cell(0, 0)
title_cell.text = f'动物数独 {hs}*{hs} 操作卡'
para = title_cell.paragraphs[0]
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = para.runs[0]
run.font.name = '黑体'
run.font.size = Pt(35)
run.font.color.rgb = RGBColor(0, 0, 0)
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
# Save and convert to PDF
doc.save(os.path.join(temp_word_path, f'{group_idx+1:02d}页.docx'))
convert(os.path.join(temp_word_path, f'{group_idx+1:02d}页.docx'),
os.path.join(temp_word_path, f'{group_idx+1:02d}页.pdf'))
time.sleep(3)
# Merge PDFs
pdf_files = sorted([os.path.join(temp_word_path, f) for f in os.listdir(temp_word_path) if f.endswith('.pdf')])
merger = PdfMerger()
for pdf in pdf_files:
merger.append(pdf)
output_name = f"01_动物数独_{hs}宫格_操作卡_1页{hs*hs}图(一图{A4size[pt]}CM,只要打印1张A4).pdf" if pt == 3 else f"01_动物数独_{hs}宫格_操作卡_1页{k[pt][:-2]}(一图{psize[pt]}CM).pdf"
merger.write(os.path.join(temp_pdf_path, output_name))
merger.close()
# Clean up
print('-----第二板块、动物任务卡和答案卡一套,只要JPG-------')
import random
from win32com.client import constants, gencache
from win32com.client import genpy
from win32com.client.gencache import EnsureDispatch
from docx import Document
from docx.shared import Pt, RGBColor
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH
import docxtpl
import pandas as pd
from docx2pdf import convert
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
import copy
print('------3-1、生成指定数量的关卡(按最大空缺数限制)------')
n = hsall # 宫格数(例如3)
g = n * n # 总格子数(例如9)
# 根据宫格数确定最大空缺数
if n % 2 == 1: # 奇数宫格
max_empty = (g + 1) // 2 # 例如3宫格:(9+1)/2=5
else: # 偶数宫格
max_empty = g // 2 # 例如4宫格:16/2=8
# 只生成1到max_empty关(例如3宫格只生成5关)
a = []
for empty in range(1, max_empty + 1): # 直接按空缺数生成
# 计算对应的百分比(四舍五入)
percentage = round(empty * 100 / g)
print(f'{n}宫格,难度{percentage:03d},抽取{percentage:03d}%:实际有{empty:03d}空,已有图案{g-empty:03d}')
a.append(f'{n}宫格,难度{percentage:03d},抽取{percentage:03d}%:实际有{empty:03d}空,已有图案{g-empty:03d}')
# 去重和排序(虽然现在应该已经是有序且不重复的)
b = []
for element in a:
parts = element.split(":")
info = parts[1]
b.append(info)
b = list(set(b))
b.sort(reverse=False)
f = []
for d in range(len(b)):
for c in a:
if c[-15:] == b[d]:
f.append(c)
break
# 移除第一个(空0的无效项)如果有的话
if f and int(f[0][20:23]) == g: # 检查是否全满(空0)
f.pop(0)
print(f'实际生成关卡数:{len(f)}')
# 提取空缺数和已有图案数
empty_counts = []
filled_counts = []
for p in f:
empty_counts.append(int(p[20:23]))
filled_counts.append(int(p[12:15]))
print('空缺数序列:', empty_counts)
print('已有图案数:', filled_counts)
print('------2、制作宫格随机数字------')
def generate_non_adjacent_blanks(hs, num_blanks):
"""生成不连续的空格位置"""
max_blanks = (hs*hs + 1)//2 if hs % 2 == 1 else (hs*hs)//2
num_blanks = min(num_blanks, max_blanks) # 确保不超过最大空格数
positions = list(range(hs*hs))
blanks = []
# 尝试生成不连续的空格
max_attempts = 1000
attempts = 0
while len(blanks) < num_blanks and attempts < max_attempts:
temp_blanks = []
remaining_positions = positions.copy()
while len(temp_blanks) < num_blanks and remaining_positions:
pos = random.choice(remaining_positions)
row = pos // hs
col = pos % hs
# 检查是否与已有空格相邻
valid = True
for blank in temp_blanks:
blank_row = blank // hs
blank_col = blank % hs
# 检查是否在同一行或同一列且相邻
if (row == blank_row and abs(col - blank_col) == 1) or \
(col == blank_col and abs(row - blank_row) == 1):
valid = False
break
if valid:
temp_blanks.append(pos)
# 移除相邻位置
adjacent_positions = []
if col > 0: adjacent_positions.append(pos-1)
if col < hs-1: adjacent_positions.append(pos+1)
if row > 0: adjacent_positions.append(pos-hs)
if row < hs-1: adjacent_positions.append(pos+hs)
for adj_pos in adjacent_positions:
if adj_pos in remaining_positions:
remaining_positions.remove(adj_pos)
remaining_positions.remove(pos) if pos in remaining_positions else None
if len(temp_blanks) == num_blanks:
blanks = temp_blanks
break
attempts += 1
# 如果无法生成不连续的空格,则返回随机空格
if len(blanks) < num_blanks:
blanks = random.sample(positions, num_blanks)
return blanks
# 修改这里 - 使用empty_counts而不是g
for kk in range(len(empty_counts)): # 遍历每个空缺数
ll=['{}'.format(hs)]
mm=['11']
nn=['36']
for r in range(len(ll)):
if hs ==int(ll[r]):
db=int(mm[