import{_ as h,D as e,c as k,j as s,a as i,I as l,a4 as t,o as n}from"./chunks/framework.DtvhUNIn.js";const m=JSON.parse('{"title":"项目——填词游戏","description":"","frontmatter":{},"headers":[],"relativePath":"技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md","filePath":"技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md"}'),d={name:"技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md"},p=s("h1",{id:"项目——填词游戏",tabindex:"-1"},[i("项目——填词游戏 "),s("a",{class:"header-anchor",href:"#项目——填词游戏","aria-label":'Permalink to "项目——填词游戏"'},"​")],-1),o=s("div",{class:"tip custom-block"},[s("p",{class:"custom-block-title"},"TIP"),s("p",null,"我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。"),s("p",null,"如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。")],-1),r={class:"tip custom-block"},c=s("p",{class:"custom-block-title"},"📥",-1),F=t(`

编写一个人工智能来完成填词游戏。

能够实现将文字转换为图片。

shell
$ python generate.py data/structure1.txt data/words1.txt output.png
|||||||||||||||
||||||||M|||||R||
||I|N|T|E|L|L|I|G|E|N|C|E||
||N||||||N|||||S||
||F|||L|O|G|I|C||||O||
||E||||||M|||||L||
||R||||S|E|A|R|C|H||V||
||||||||X|||||E||
|||||||||||||||

4.3.4.2-0

背景

你如何生成一个填字游戏?考虑到填字游戏的结构 (即网格中哪些方格需要填入字母),以及要使用的单词列表,问题就变成了选择哪些单词应该填入每个垂直或水平的方格序列。我们可以将这种问题建模为一个约束满足问题。每一个方格序列都是一个变量,我们需要决定它的值 (在可能的单词域中哪个单词将被填入该序列)。考虑一下下面的字谜结构。

4.3.4.2-1

在这个结构中,我们有四个变量,代表了我们需要填入这个字谜的四个单词 (在上图中每个单词都用数字表示)。每个变量由四个值定义:它开始的行 (i值),它开始的列 (j值),单词的方向 (纵向或横向 down or across),以及单词的长度。例如,变量1将是一个由第 1 行 (假设从顶部计数的 0 索引)、第 1 列 (假设从左边计数的 0 索引)、方向为across4的长度表示的变量。

与许多约束满足问题一样,这些变量有一元和二元约束。变量的一元约束是由其长度决定的。例如,对于变量1来说,数值BYTE可以满足一元约束,但是数值BIT则不能 (它有错误的字母数量)。因此,任何不满足变量的一元约束的值都可以立即从变量的域中删除。

变量的二元约束是由其与相邻变量的重合度给出的。变量1有一个邻居:变量2变量2有两个邻居:变量1变量3。对于每一对相邻的变量,这些变量都有一个重叠部分:一个它们共同的单方块。我们可以将这种重叠表示为每个变量有用相同字符位置的索引对。例如,变量1变量2之间的重叠可以表示为一对(1, 0),这意味着变量1在索引 1 处的字符必然与变量2在索引 0 处的字符相同 (再次假设索引从 0 开始)。因此,变量2变量3之间的重叠将被表示为一对(3, 1)变量2的值的字符3必须与变量3的值的字符1相同。

对于这个问题,我们将增加一个额外的约束条件,即所有的单词必须是不同的:同一个单词不应该在谜题中重复出现多次。

那么,接下来的挑战是写一个程序来找到一个满意的赋值:为每个变量提供一个不同的词 (来自给定的词汇表),从而满足所有的一元和二元约束。

理解

这个项目中有两个 Python 文件:crossword.pygenerate.py。第一个文件已经完全为你写好了,第二个文件有一些函数留给你去实现。

首先,让我们看一下crossword.py。这个文件定义了两个类,Variable(代表填字游戏中的变量) 和Crossword(代表填字游戏本身)。

注意,要创建一个变量,我们必须指定四个值:它的第i行,第j列,它的方向 (常数Variable.ACROSS或常数Variable.DOWN\`\`),以及它的长度(length\`\`)。

字谜类需要两个值来创建一个新的字谜:一个定义了字谜结构的structure_file(_用来代表空白单元格,任何其他字符都代表不会被填入的单元格) 和一个定义了字词列表 (每行一个) 的word_file,用来作为游戏的词汇表。这些文件的三个例子可以在项目的数据目录中找到,也欢迎你自己创建。

特别要注意的是,对于任何一个字谜对象的字谜,我们都会存储以下的数值:

Crossword对象还支持一个方法neighbors,它可以返回与给定变量重叠的所有变量。也就是说,crossword.neighbors(v1)将返回一个与变量v1相邻的所有变量的集合。

接下来,看一下generate.py。在这里,我们定义了一个CrosswordCreator类,我们将用它来解决填字游戏。当一个CrosswordCreator对象被创建时,它得到一个填字游戏的属性,它应该是一个Crossword对象 (因此具有上面描述的所有属性)。每个CrosswordCreator对象还得到一个域属性:一个字典,它将变量映射到该变量可能作为一个值的一组词。最初,这组词是我们词汇表中的所有词,但我们很快就会写函数来限制这些域。

我们还为你定义了一些函数,以帮助你测试你的代码:print将向终端打印你的填字游戏的一个给定的赋值 (每个赋值,在这个函数和其他地方,是一个字典,将变量映射到它们相应的词)。同时,save将生成一个与给定作业相对应的图像文件 (如果你无法使用这个函数,你需要pip3 install Pillow)。 letter_grid是一个被printsave使用的辅助函数,它为给定的赋值生成一个所有字符在其适当位置的 2D 列表:你可能不需要自己调用这个函数,但如果你想的话,欢迎你这样做。

最后,注意solve函数。这个函数做了三件事:首先,它调用enforce_node_consistency来强制执行填字游戏的节点一致性,确保变量域中的每个值都满足一元约束。接下来,该函数调用ac3来强制执行弧一致性,确保二元约束得到满足。最后,该函数在最初的空赋值 (空字典 dict()) 上调用backtrack,试图计算出问题的解决方案。

不过,enforce_node_consistencyac3backtrack等函数还没有实现 (以及其他函数)。这就是你的任务。

明确

完成grece_node_consistency, revise, ac3, assignment_complete, consistent, order_domain_values, selected_unassigned_variablebacktrackgenerate.py中的实现,这样如果有有解的话你的人工智能就能生成完整的字谜。

除了要求你实现的函数外,你不应该修改generate.py中的任何其他东西,尽管你可以编写额外的函数和/或导入其他 Python 标准库模块。如果你熟悉numpypandas,你也可以导入它们,但是你不应该使用任何其他的第三方 Python 模块。你不应该修改crossword.py中的任何东西。

提示

`,30);function g(y,C,A,D,B,u){const a=e("Download");return n(),k("div",null,[p,o,s("div",r,[c,s("p",null,[i("本节附件下载 "),l(a,{url:"https://cdn.xyxsw.site/code/4-Projects.zip"})])]),F])}const b=h(d,[["render",g]]);export{m as __pageData,b as default};