拉丁填字游戏-Ludum Verborum(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/latin-crossword-puzzle-ludum-verborum-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 55 分钟阅读 - 27480 个词 阅读量 0拉丁填字游戏-Ludum Verborum(译文)
原文地址:https://www.codeproject.com/Articles/1372774/Latin-Crossword-Puzzle-Ludum-Verborum
原文作者:Christ Kennedy
译文由本站 robot-v1.0 翻译
前言
si latine discis ludum verbordum habere debes
拉丁语disdis ludum verbordum habere debes
practise your latin conjugation and declension by playing a challenging and fun crossword puzzle. This project uses the Cassell’s Dictionary and Wheelock’s Latin textbook to generate clues for each new crossword puzzle you play. Solve the puzzle with the help of animated friends and improve your latin language skills.
玩具有挑战性和趣味性的填字游戏,练习拉丁语的变调和歪曲.该项目使用卡塞尔字典和会德丰的拉丁教科书为您玩的每个新填字游戏生成线索.在动画朋友的帮助下解决难题,并提高您的拉丁语言能力.
- 下载CrosswordPuzzle_20190414.zip-1 MB(Download CrosswordPuzzle_20190414.zip - 1 MB) 源代码(source code)
- 下载c.Latin.Data.CrosswordPuzzle_Monkey.zip-2.9 MB(Download c.Latin.Data.CrosswordPuzzle_Monkey.zip - 2.9 MB) 动画精灵(animation sprites)
- 下载c.Latin.Data.CrosswordPuzzle_Magister.zip-6.9 MB(Download c.Latin.Data.CrosswordPuzzle_Magister.zip - 6.9 MB)
- 下载c.Latin.Data.CrosswordPuzzle_Hawk.zip-1.4 MB(Download c.Latin.Data.CrosswordPuzzle_Hawk.zip - 1.4 MB)
更新日期:2020/03/24(Update : 2020/03/24)
介绍(Introduction)
这个拉丁填字游戏是完全基于我的(This Latin Crossword Puzzle is built off of and relies entirely on my) 拉丁项目(Latin Project) .如果您对死语感兴趣,我强烈建议您看看.(. If you have any interest in the dead language I strongly recommend you have a look.) 这个填字游戏应用程序是一个有趣的游戏,可以播放很酷的动画,多种难度级别,将帮助任何初学者拉丁学生并挑战最熟练的拉丁学者.上面列出了仅下载五个文件,但本文末尾有几十个文件.前五个是源代码,三个是游戏动画的精灵,一个包含准备好播放的拼图的XML文件,而其余文件都是数据库的一部分,使得生成随机填字游戏成为可能.(This crossword puzzle application is a fun game to play with cool animations, multiple difficulty levels that will help any beginner Latin student and challenge the most skilled Latin scholars. There are only five files listed to download above but there are several dozen at the end of this article. These first five are the source code, three sprites for the game’s animations and an XML file containing puzzles ready to be played, while the rest of the files are all part of the database that makes generating random crossword puzzles possible.) 您(You)不能同时运行(cannot run both)拉丁项目(TheLatinProject)和(&)LatinCrosswordPuzzle(LatinCrosswordPuzzle)因为它们共享一个公共数据库,所以它们在同一台计算机上同时运行.(simultaneously on the same computer because they share a common database.) 在本文中,我将首先说明如何下载所有文件并进行设置以使工作正常.(In this article I will first explain how to download all the files and set it up in order to get things working.) 然后简要说明如何玩游戏.这将是简短的,因为用户界面非常直观,对任何人都不会造成任何麻烦.(Then a quick explanation on how to play the game. This will be brief because the user-interface is very intuitive and should not be any trouble for anyone.) 最后,我将讨论代码:(Finally, I will discuss the code :)
- 数据库是如何建立的(how the data-base was built)
- 如何从数据中生成填字游戏(how to generate crossword puzzles from the data)
- 用户界面(the user-interface)
- 图形和许多控制动画角色行为的有限状态机(the graphics and the many Finite-State-Machines that govern the animated character behaviors)
背景(Background)
在使用英语词典作为单词及其线索的来源之前,我曾做过一个填字游戏之谜,但那时我从未发表过我的作品,而现在这场比赛已经失败了.该项目在生成谜题方面非常相似,但具有动画<魔导师>的附加元素来帮助玩家前进.如果魔术师碰巧有一只跳舞的猴子和弓箭,那就更好了.(I had made a crossword puzzle before using an English dictionary as a source for both the words and their clues but never published my work at that time, and now that game is lost. This project is very similar with regards to generating the puzzles but has the added element of an animated Magister to help the player along. If magister happens to have a dancing monkey and a bow-and-arrow, all the better.)
提取文件(Extracting the Files)
有30多个文件可供下载和提取,我不会道歉.如果您让LatinProject工作,是因为您知道自己在做什么并且它并不那么令人恐惧.这可能很麻烦,但值得.您在任何地方都找不到其他的拉丁填字游戏,这很酷.(There are some 30+ files to download and extract and I won’t apologise. If you have the LatinProject working its because you know what you’re doing and its not that scary. It may be a hassle but well worth it. You will find no other Latin Crossword Puzzle anywhere and this one’s pretty cool.) 就是这样所有文件都被命名,以便在需要提取文件的地方轻松提取它们.您要做的第一件事是在现有(并且正在运行)的拉丁项目(最新的C#2017版本)目录中创建3个新的子目录.看看下面的屏幕截图,其中显示了您的(So here it is. All the files are named so that it’s easy to extract them where they need to be extracted. The first thing you’re going to have to do is create 3 new sub directories in your existing (and working) Latin Project(recent C#2017 version) directories. Have a look at the screen capture below that shows you what your)c:\ Latin \ Data (*c:\Latin\Data*)创建这些新子目录后,目录应该看起来像.(directory should look like when you’ve created these new sub-directories.)
现在,您已经创建了这些子目录,可以开始下载并提取本文末尾列出的所有文件.文件名称与TheLatinProject相同.第一个下划线之后的文本简要描述了压缩文件的内容,而第一个下划线之前的文本向您显示了需要从何处提取该文件.(Now that you’ve created these sub-directories you can start to download and extract all the files listed at the end of this article. The file nomenclature is the same as TheLatinProject. The text after the first under-score briefly describes the content of the compressed file while the text before the first under-score shows you where that file needs to be extracted.)
例如名为"的文件(e.g. the file named “)**c.Latin.Data.CrosswordPuzzle.Games_CW_PuzzleGenerator_files_.zip(*c.Latin.Data.CrosswordPuzzle.Games_CW_PuzzleGenerator_files_.zip*)**"(*"*)
包含CW_PuzzleGenerator_files,并且必须在” c:\ Latin \ Data \ CrosswordPuzzle \ Games "处提取.(*contains the CW_PuzzleGenerator_files and must be extracted at “c:\Latin\Data\CrosswordPuzzle\Games".*)
遵循此规则,您将立即完成.(*Follow this rule and you’ll be done in no time.*)
如果您未下载所有文件,并且拥有TheLatinProject的运行版本,则必须进入源代码的主窗体并找到(*If you don’t download all the files and you have a running version of TheLatinProject you’ll have to go into the source code’s main form and find the*) Init()
功能,如下所示,并更改(*function, here shown below, and change the*)
if (true)
线到(*line to*)
if (false)
<font color="#0b0813">
</font><font color="#0b0813">void Init()
{
if (true)
{ // normal play
if (classCW_XML.intNumPuzzlesWritten < classCW_PuzzleGenerator.conMaxPuzzlesGenerated)
bckCW_PuzzleGenerator.RunWorkerAsync();
}
else
{ // rebuild database
bckCW_DataBuilder.RunWorkerAsync();
}
placeObjects();
drawPuzzle();
}</font><font color="#0b0813">
</font>
只要确保不启动新游戏或通过使用鼠标右键上下文菜单并选择” Magister"选项来禁用Magister即可.(Just be sure not to start a new game or do anything beyong disabling Magister by using the right-mouse context menu and selecting the Magister option.)需要+36个小时的不间断处理才能完成!(It takes +36 hours of uninterrupted processing to complete!)因此,最好下载并解压缩这些文件.(So you’re probaby better off downloading and extracting these files instead.)
让我向您介绍您的新拉丁老师.他很聪明.他玩杂耍,跳舞,并且会帮助您与您的拉丁语.我们待会儿再看他.(Let me introduce to you your new Latin teacher. He’s very wise. He juggles, he dances and he’ll help you with your latin. We’ll have a better look at him later.) 准备好运行源代码后,必须确保已将精灵文件(magister.sp3,hawk.sp3和monkey.sp3)下载并解压缩到(Once you have the source code ready to run you must be certain that the sprite files (magister.sp3, hawk.sp3 & monkey.sp3) are downloaded and extracted in the)c:\ Latin \ Data \ Crossword Puzzle(c:\Latin\Data\CrosswordPuzzle)目录.(directory.) 此时,您应该设置(At this point you should be set with)
- 精灵中的3个精灵(.sp3)档案(the 3 sprite (.sp3) files in the in the)c:\ Latin \ Data \ Crossword Puzzle (*c:\Latin\Data\CrosswordPuzzle*)
- **Latin_CW_0000.bin(Latin_CW_0000.bin)**在里面(in the)c:\ Latin \ Data \ CrosswordPuzzle \ Data(c:\Latin\Data\CrosswordPuzzle\Data)
- **Latin_CW_0000.LL(Latin_CW_0000.LL)**至(to)**Latin_CW_0140.LL(Latin_CW_0140.LL)**在里面(in the)c:\ Latin \ Data \ CrosswordPuzzle \ Data(c:\Latin\Data\CrosswordPuzzle\Data)
- **CrossWordPuzzles.xml(CrossWordPuzzles.xml)**在里面(in the)c:\ Latin \ Data \ CrosswordPuzzle \ Games(c:\Latin\Data\CrosswordPuzzle\Games) 现在您已经准备好了.(and now you’re ready.) Nota Bene(Nota Bene)-2020/03/24所有(- 2020/03/24 all the).bin(.bin)和(and).二(.LL)此处列出的文件已压缩为一个文件,可从我的Google云端硬盘下载,如(files listed here have been compressed into a single file downloadable from my Google Drive as explained in the)一路走来的问题(Problems Along the Way)下面的部分.(section below.)
玩游戏(Playing the Game)
自18世纪末以来,填字游戏就已经存在,而Wikipedia的快速浏览将告诉您,主题和设计模式存在差异,这些差异似乎与文化和语言有关.当然,这个拉丁填字游戏依赖于拉丁语言,但是没有特定的主题或设计.它们是根据在拉丁语字典中找到的单词随机生成的,给出的线索取决于您当前所玩的难度.所有难题,无论难度级别如何,都是以相同的方式生成的,但是难度级别更高的线索可为您提供更多可用的信息.(Crossword puzzles have been around since the late 18th century and a quick look at Wikipedia will tell you that there are variations on themes and design patterns that seem to be cultural and language dependent. This Latin crossword puzzle is, of course, dependent on the Latin language but has no particular theme or design. They’re randomly generated with words found in the Latin dictionary and the clues you are given depend on the level of difficulty you’re currently playing. All the puzzles, regardless of difficulty level, are generated in the same way, but the clues for the easier levels give you more information to work with.) 填字游戏的概念很简单:用正确的字母填写空格以解决该谜题.(The concept of crossword puzzles is simple : fill in the blanks with the correct letters to solve the puzzle.)
上下文菜单(Context Menu)
您可以使用上下文菜单,方法是用鼠标右键单击屏幕,然后选择以下各个选项之一:(You can use the context menu by right-clicking the screen with your mouse and selecting one of the various options:)
- 新(New)-开始新游戏(- start a new game)
- 加载(Load)-从文件加载保存的游戏(- load a saved game from file)
- 救(Save)-将当前游戏保存到文件(- save the current game to file)
- 魔导师(Magister)-打开/关闭魔导师(- toggle the magister on/off)
- 困难(Difficulty)-从中选择(- select from either)简单(Easy)–(-)正常(Normal)- 要么(- or)困难(Difficulty)
- 字形(Font)-选择用于在屏幕上显示线索的字体(- select the font used to display the clues onto the screen)
- 退出(Quit)-退出应用(- to exit the app)
拼图屏幕(Puzzle Screen)
屏幕分为两个单独的部分.拼图的左侧有所有空白,您需要在其中写下适当的字母来填写答案.在右侧,您将看到提供每个单词提示的区域.要选择您要填写的单词,只需左键单击该单词中的任何字符,它将突出显示以表明您正在使用该单词.如果您选择的拼图方块是水平单词和垂直单词的一部分,则水平单词将首先突出显示,再次单击相同的正方形,所选单词将在该正方形的水平单词和垂直单词之间交替.闪烁的方块是您的光标,显示您将要使用键盘填写的方块.下面的屏幕截图显示了选中的黄色单词中的(10,6)方块被"闪烁"为黑色,从V(10,0)开始,如屏幕" cuneavisse"右侧的提示中所述.(The screen is divided into two separate parts. To the left you have the puzzle with all its blank spaces where you are expected to write the appropriate letters to fill in the answers. And to the right you will see the area where the clues to each word will be provided. To select the word you want to fill in, just left click any character in that word and it will be highlighted to tell you that that is the word you’re working on. If the puzzle square you’ve selected is part of both a horizontal word and a vertical word the horizontal word will be highlighted first, click the same square again and the selected word will alternate between that square’s horizontal and vertical words. The flashing square is your cursor showing your which square you are about to fill in using the keyboard. The screen capture below shows the (10, 6) square is ‘blinked’ black in the selected Yellow word that starts at V(10,0) as described in the clue on the right of the screen ‘cuneavisse’.) 以下两个屏幕截图均展示了为(Both screen captures below demonstrate clues that are provided for the)正常(Normal)难度级别.(difficulty level.)
您可以使用箭头键移动.将箭头键和控制键结合使用时,您可以以令人费解的方式跳至下一个"十字路口". End&Home键将使光标跳至当前单词的任一端.您还可以通过按Delete键来删除单词方块的文本.(You can use the arrow keys to move around. When you combine the arrow keys with the control key you can jump to the next ‘cross-road’ on your puzzling way. The End & Home keys will make the cursor jump to either end of the current word. You can also delete a word square’s text by pressing the delete key.) 如果您选择了简单技能级别,则游戏的行为会略有不同-在简单级别上,输入到拼图中的字母正确时将显示为绿色,错误输入时将显示为红色.同样,游戏界面不允许您覆盖在简单关卡中正确的拼图方块.(If you’ve selected the easy skill-level the game will behave slightly differently - at the easy level, the letters you enter into the puzzle will appear in green when they are correct, in red when they are wrong. Also, the game interface will not permit you to over-write a puzzle square that is correct in the easy level.)
线索(Clues)
选择一个单词后,您会在屏幕右侧看到该单词的线索.该信息左上角的数字是(X,Y)坐标中单词在拼图中的"地址",但您实际上不需要知道该单词,因为单击该单词后它会以黄色突出显示.根据您选择的技能水平,向您提供的线索会有所不同:(When you’ve selected a word you’ll see that word’s clue appear on the right side of the screen. The numbers at the top left of this information is the word’s ‘address’ in the puzzle in (X, Y) coordinates but you don’t really need to know that because the word will appear highlighted in yellow once you’ve clicked on it. The clues you are provided will differ depending on the skill level you’ve selected :) 简单(Easy)-最简单的玩法将为您提供最多的信息,以帮助您找出所需的单词.在此级别上,线索将包括单词的标题(如在词典中所显示的)及其完整定义.使用此字词标题信息,您将不得不以字词提示所提供的特定形式来拒绝或缀合该字词.(- the easiest level of play will provide you with the most information to help you figure out what word you’re looking for. At this level the clue will include the word’s heading (as it appears in the dictionary) along with its full definition. Using this word-heading information you will have to either decline or conjugate the word in the specific form which the word’s clue provides.) 正常(Normal)-这个技能等级比简单等级要难一些.屏幕上的字母不仅不再是绿色/(- this skill-level is somewhat more difficult than the easy level. Not only are the letters on the screen no longer green/)红(red)告诉您它们是对还是错,但线索可能包括该单词的标题和一个古老的拉丁文字的引文,该文字是从文字中切除的.不会告诉您语音的哪个部分,哪个词缀或词尾变化,因此您必须阅读文本并尽最大努力找出不处理单词时需要的大小写,数字和性别.动词时态和人.拼图上的所有单词都应该与其他单词交叉,因此,如果您在使用一个单词时遇到麻烦,请移至下一个单词,然后稍后再返回,一旦您填写了其他一些单词,它可能会变得更容易越过它.(telling you whether they’re right or wrong but the clue may consist of the word’s heading and a quote from an ancient Latin text with that word excised from the text. You won’t be told what part of speech it is, which conjugation or declension, so you’ll have to read the text and try your best to figure out what case, number and gender is required when you’re not dealing with a verb tense and person. All the words on the puzzle should cross with other words, so if you’re having trouble with one word, move on to the next and come back to it later, it may become easier once you’ve filled in some of the other words that cross it.) 难(Difficult)-这就是事情变得复杂的地方.在这里,您可能会得到字典中出现的单词的定义,但不会提供该单词的标题或要拒绝/共轭单词的大小写,数字,性别或人.有时,您会得到一个带有红线的引号,在该引号中已删除了您要查找的单词,而您只需要从内存或理解文本中找出答案即可填写空白.(- this is where things get complicated. Here you may be given the word’s definition as it appears in the dictionary, except you won’t be provided with that word’s heading or the case, number, gender or person that word is to be declined/conjugated. Sometimes, you’ll be provided with a quote that has a red-line where the word you’re looking for has been excised from it and you’ll just have to work it out from memory or understanding of the text to fill in the blank.) 下图是为报价提供的报价样式提示的示例(The image below is an example of a Quote-style clue provided for a)正常(Normal)游戏水平.您会看到为播放器指定了单词的标题,但未指定大小写,数字,时态或人物.(level of play. You can see that the player is given the word’s heading but not the case, number, tense or person.)
您可以使用上下文菜单,然后点击(You can select the skill-level of play by using the context menu and clicking on the)困难(Difficulty)选项在那里列出.(option listed there.) 如果您在使用屏幕上的某些拉丁语时遇到困难,则可以在线索中的任何文本上单击鼠标左键,并为您提供所选单词的定义(如果有的话).您将无法告诉应用程序像TheLatinProject一样为您提供所有单词的各种拼写,但是在古代拉丁语引用中某些单词的定义可以帮助您弄清楚所查找单词的拼写对于.它也可能对您的拉丁语有帮助.(If you’re having difficulty with some of the latin that’s on the screen, you can left-click on any of the text in the clue and be provided with a definition of the word you selected if one is available. You won’t be able to tell the app to give you all that word’s various spellings like you would with TheLatinProject but the definitions of some of the words in an ancient latin quote could help you figure out the spelling of the word you’re looking for. It might help your Latin, too.)
魔导师(Magister)
Magister对拉丁语非常热心,他可以提供很多帮助.无论您处于哪种难度级别,他都会乐于助您解决难题,如果您只是一个初学者并且需要鼓励,他会稍微加快一点.您可以通过右键单击鼠标并召唤上下文菜单来打开/关闭他,然后选择(Magister is very enthusiastic about Latin and he can be a lot of help. No matter which difficulty level you’re playing at, he will be ready to lend you a hand to solve your puzzle, he’ll just be a little quicker about it if you’re just a beginner and need encouragement. You can toggle him on/off by right-clicking the mouse and summoning the context menu then selecting the)魔导师(Magister)选项在那里.(option there.) 您会注意到magister的行为方式是否能很好地解决难题.如果屏幕上有任何错误,Magister会变得紧张,并且可能会来回移动,直到您摆脱错误或者他对看到错误感到沮丧,以至于他不得不自己走遍整个难题并从屏幕上拉出不正确的字母,将其扔出或踢出或喂给小鸟.他只是想提供帮助,所以不要生他的气,他只是对拉丁语充满热情.(You’ll notice by the way magister is behaving whether or not you’re doing well in solving your puzzle. If you have any errors on the screen, Magister will become tense and may start to pace back and forth until you either get rid of the error or he get’s so frustrated at seeing a mistake that he takes it upon himself to walk all over your puzzle and yank the incorrect letter off the screen, throw it or kick it or feed it to the birds. He’s only trying to help so don’t get mad at him, he’s just really passionate about Latin.) 您可能还会注意到他在玩耍时的注意力在哪里.除非他被他最好的朋友分心(You may also notice where his attention is while you’re playing. Unless he’s distracted by his best-friend)西缪斯(Simius),他要么跟随您的鼠标光标,要么,如果您在拼图中犯了一个错误,他可能会把紧张的注意力完全集中在您所犯的错误上,急于去那里纠正它.因此,您可以根据他是否在看您的鼠标光标来判断您是否犯了一个错误.也就是说,除非他太忙于玩弄游戏板…(, he’ll either be following your mouse-cursor around or, if you’ve made a mistake in the puzzle, he may focus his nervous attention entirely on the mistake you’ve made, anxious to go over there and correct it. So, you can tell whether or not you’ve just made a mistake depending on whether or not he’s looking at your mouse cursor. That is, unless he’s too busy juggling to look at the game board…) 您会在屏幕的右下方看到一个计时器.不要担心它不是世界末日时钟,这里没有炸弹,也没有人保持得分.不,计时器告诉您的是必须等待多长时间,直到您可以要求Magister填空,而不是消除错误.当计时器用尽时,将出现"帮助"按钮,然后您可以选择遇到问题的单词,然后按"帮助"按钮.计时器将重新启动,接下来您会知道的事情是,Magister将停止他正在做的任何事情并去拿笔,然后再回去并在所选单词中填写一个空白.如果您需要帮助,请不要尴尬,拉丁填字游戏可能很困难,他对此感到非常高兴.(You’ll notice a timer at the bottom right of the screen. Don’t worry its not a dooms-day clock, there are no bombs here and no one is keeping score. No, what the timer tells you is how long you’ll have to wait until you can ask Magister to fill in a blank, instead of removing an error. When the timer runs out a ‘Help’ button will appear and you can then select the word you’re having trouble with and press the ‘Help’ button. The timer will restart and the next thing you know, Magister will stop whatever he’s doing and go fetch a pen, then come back and fill in one of the empty blanks in the word you’ve selected. Don’t be embarassed if you ever need help, Latin crosswords can be difficult and he’s only too glad to do it.)
代码(The Code)
现在看一下里面:(And now for a look inside :)
数据库是如何建立的(how the data-base was built)
拼图生成器建立了一个专门设计用来促进填字游戏的数据库.如果您对如何(The puzzle generator builds off a data-base specifically designed to facilitate the building of a crossword puzzle. If you have a general idea of how)拉丁项目(TheLatinProject)的查询表起作用,那么您会知道它由二进制树组成,该二进制树包含772,000个单词中的772 000个不同拼写(’s Look-Up Table works then you’ll know that it consists of a binary-tree of the 772 000 different spellings of the 24 000 words in the)卡塞尔字典.(Cassell’s Dictionary.)填字游戏的数据来自这77.2万种不同的单词拼写.建立该数据库的代码首先按输入到LUT的二叉树文件中的顺序(通过记录编号零开始,递增地进行到最后的772 000个),依次处理每个记录,但并非每个拼写都产生一个可行的词(The Crossword Puzzle’s data is derived from those 772 000 different word spellings. The code that builds this data-base first goes through each of these records in the sequence they were entered into the LUT’s binary-tree file (starting from record number zero and proceeding incrementally through to the last 772 000th one) but not each spelling produces a viable word for the)LatinCrosswordPuzzle(LatinCrosswordPuzzle)游戏,因为它们中的许多文件都有多个源文件,当这些线索以古老的拉丁语报价形式出现时,它们会产生误导性的线索.例如,从形容词派生的任何名词或从名词派生的形容词,例如(game since many of them have more than one source file which would produce misleading clues when those clues are in the form of an ancient latin quote. For example, any noun derived from an adjective or adjective derived from a noun, such as)克劳苏姆-i(clausum, -i)中性名词源自动词的完全被动分词(neuter noun is derived from the perfect passive participle of the verb)克劳多,克劳德雷,克劳西,克劳苏姆(claudo, claudere, clausi, clausum).自从这个词(. Since the word)克劳苏姆(clausum)可能是形容词(could be either the adjective)克劳苏斯,-a,-um,(clausus, -a, -um,)名词或完全被动分词,该应用无法区分这三个名词,并且在显示线索和报价时会随机选择一个.并没有很多例子可以说明这种差异非常微妙,但是在那些情况下却没有,我发现这是完全不能接受的.因此,为避免该问题,将具有多个源单词的所有单词拼写视为无效,并且不包含在填字游戏生成器数据库中.我有772 000名候选人可供选择,我认为这是一个合理的解决方案.从此最终版本中生成的134个链接列表文件(与之前生成的141个LL文件相比)来看,这意味着当前数据库中大约有734 000个不同的单词(拼写),或者占%95原本的.(the noun or the perfect passive participle the app could not differentiate between the three and would randomly pick one when displaying the clue along with a quote. There are not very many examples where this difference is quite subtle but in those cases where it is not, I found it is completely unacceptable. So to avoid that problem all word-spellings that have more than one source word are considered invalid and are not included into the crossword puzzle generator data-base. With 772 000 candidates to choose from, I figured this was a reasonable solution. Judging by the 134 Linked-List files that were generated in this final version compared to the 141 LL files that were generated before, that means that there are about 734 000 different word(spellings) in the current data-base, or %95 of the original.) 有关如何执行此操作的源代码非常简单明了,如下所示:(The source-code on how it does this is fairly straight-forward and shown below:)
bool bolValidWord = true;
try
{
cLUT_BinRec = classLatin_LUT.LUT_BinRec_Load(intCW_Build_Index++);
// test for and reject any word spelling that has more than one source filename
List<string> lstFilenames = new List<string>();
classLatin_LUT_LL_Record cLatin_LL = classLatin_LUT.LUT_LL_Record_Load(cLUT_BinRec.LL);
while (cLatin_LL != null && bolValidWord)
{
if (!lstFilenames.Contains(cLatin_LL.filename))
lstFilenames.Add(cLatin_LL.filename);
if (lstFilenames.Count<2 && cLatin_LL.next >= 0)
cLatin_LL = classLatin_LUT.LUT_LL_Record_Load(cLatin_LL.next);
else
cLatin_LL = null;
bolValidWord = lstFilenames.Count <= 1;
}
}
catch (Exception)
{
goto endPosition;
}
</string></string>
一旦单词被认为是有效的并且可以插入数据库中,然后使用两个嵌套的for循环从左到右对其进行扫描,以根据字母及其相对位置对单词进行排序.每个单词的每个可能的两个字母的组合用作排序设备,以针对这两个组合生成唯一的代码,例如单词的位置2的第一个字母’C',单词的位置4的第二个字母’B'(Once a word is considered valid and can be inserted into the data-base, it is then scanned from left to right using two nested for-loops to sort the words according to the letters and their positions relative to each other. Each possible two-letter combination of each word is used as a sorting device to generate a unique code for those two combinations, e.g. first-letter ‘C’ at position 2, second-letter ‘B’ in position 4 of the word)**尼卡波(necabo)**会产生一个将C2B4组合成4个字符的唯一代码,而不管阿拉伯十进制数字中的位置需要两个字符而不是一个字符(例如第12个位置或任何大于9的数字).然后,将这四个字母的代码用作二叉树中的搜索关键字,该二叉树的叶子指向循环链接列表.每个这些链表项的数据字段都包含要包含在树中的单词.(will produce a unique code that combines C2B4 into 4 characters regardless whether the positions in arabic decimal numerals requires two characters rather than one(like the 12th position or any number higher than 9). This four-letter code is then used as the search-key in a binary-tree, the leaves of which lead to a circular-linked-list. And the data fields of each of these linked-list items contains the word to be included into the tree.) 它不是那么简单.实际上,圆形链接列表按字长排序到第二个二叉树(字长),该二叉树从第一棵树的叶子(定义两个字母组合的4个字母代码)的叶子分支出来,然后从这些辅助Bin的每个叶子-trees指向一个循环链接列表,该列表包含具有相同长度的单词,并带有匹配的2字母组合(标识4字母代码).(Its not quite that simple. Actually, the circular linked-lists are sorted by word-length into a second binary tree(word-length) that branches off from the leaf of the first tree(4-letter code defining two letter combination) then each leaf of these secondary Bin-trees point to a circular-linked-list that holds words of identical length with matching 2-letter combination(idential 4-letter code).) 我之所以使用循环链表,是因为该应用程序可以"顺时针旋转"随机机会,而不必为每个谜题使用相同的单词.使用单链接列表将要求从头到尾读取它,然后才能做出真正随机的选择,这太慢了,无法实时生成谜题.循环遍历所有选项的替代设计选择似乎更合理,因为原始LUT二叉树是使用随机顺序生成的,它们都是随机输入的,并以随机顺序出现在LatinProject LUT文件中,该单词的来源为77.2万条目.此外,拼图生成器会跟踪最近生成的25场游戏中使用的单词,以确保不会在25场游戏中重复使用同一单词,以后再介绍.(The reason why I used circular linked lists is so that the app could ‘spin-the-wheel’ of random chance and not use the same words for every puzzle. Using a singly-linked list would require it to be read from beginning to end before a truly random selection could be made which is much too slow to generate puzzles in real-time. The alternate design choice of cycling through all the options seems more reasonable since the original LUT bin-tree was generated using a random order, they are all entered randomly and appear in that random order in the LatinProject LUT file sourced to get the 772 000 word entries. Also, the puzzle generator keeps track of the words that were used in the most recent 25 games generated to be certain not to re-use the same word twice within 25 games, more on this later.) 下面显示了为主二进制树生成4个字母的代码的源代码:(The source-code that generates the 4-letter codes for the main binary-tree is shown below :)
public static string getKey(char chr1, int int1, char chr2, int int2)
{
return ((chr1.ToString()
+ chr2.ToString()
+ Convert.ToChar('A' + int1)).ToString()
+ (Convert.ToChar('A' + int2))).ToString();
}
你可以在(you can find it in the)
public class classCW_BinTree_Record
我几年前写的第一个填字游戏使用了类似的系统,但是使用复杂的基于多值的计数系统将4字母代码转换为整数值,其中第一个和第三个"小数"位置以26为底,另外两个的基数为40(最大字长),它们的总和产生一个唯一的整数,该整数随后用作随机访问文件中的索引.该系统的速度要快得多,因为它绕过了该最新版本当前使用的二叉树,但是需要一个更大的文件才能容纳所有无效的2字母的两个字母组合.(The first crossword puzzle I wrote some years ago used a similar system but converted the 4-letter code into an integer value using a complicated multi-value based counting system where the first and third ‘decimal’ place where of base 26 and the other two were base-40(max word size) and their sum resulted in a unique integer which was then used as an index in a random-access file. This system was considerably faster because it bypassed the binary-tree which this most recent version currently uses but required a much larger file in order to accomodate all the two-letter combinations that are not valid 2-letter.) 由于填字游戏不会尝试使用沿同一行彼此紧邻的单词生成谜题,因此不会在数据库中输入两个相邻的字母组合.见下文 :(Since the crossword puzzle does not attempt to generate puzzles with words that run immediately adjacent to each other along the same line, no two adjacent letter combinations are entered into the database. See below :)
if (validEntry(strWord))
{
for (int int_1 = 0; int_1 < strWord.Length - 2; int_1++)
{
for (int int_2 = int_1 + 2; int_2 < strWord.Length; int_2++)
{
classCW_BinaryTree.classCW_BinTree_Record cBinRec
= new classCW_BinaryTree.classCW_BinTree_Record(strWord, int_1, int_2);
classLatin_LUT_LL_Record cLUT_LL
= classLatin_LUT.LUT_LL_Record_Load(cLUT_BinRec.LL);
classCW_BinaryTree.classCW_LL_Record cLL
= new classCW_BinaryTree.classCW_LL_Record(strWord, cLUT_LL.filename);
classCW_BinaryTree.CW_BinTree_Insert(ref cBinRec, ref cLL);
}
}
}
这是数据库的示意图:(here’s a diagram of what the database looks like :)
尽管监督填字游戏数据库重建的代码在(although the code overseeing the rebuilding of the Crossword Puzzle data-base is in the)
<font color="#0b0813">class classCW_BuildDataTree</font>
从数据库插入和检索数据涉及的大部分工作都在(most of the work involved in inserting and retrieving data from the data base is in)
<font color="#0b0813">classCW_BinaryTree.</font>
如何从数据生成填字游戏(How to generate crossword puzzles from the data)
要开始使用此数据生成拼图,该应用程序首先会随机选择两个两个字母的组合,然后在数据库中搜索有效条目.暂时不考虑自己的线索产生,而是保留了(To start generating puzzles using this data, the app first randomly selects two two-letter combinations and searches the data-base for valid entries. Without concerning itself with the generating of clues for the moment, it keeps a running list of)
- 屏幕上当前的所有字母(位置和值)(all the letters currently on the screen (position & value))
- 水平词列表(拼写和开始位置)(list of horizontal words (spelling and start location))
- 垂直词列表(拼写和开始位置)(list of vertical words(spelling and start location))
一旦添加了两个单词并将它们放置在拼图的顶部/左侧和底部/右侧,它就会开始一个简单的算法:(Once it has added these two words and positioned them at the top/left & bottom/right of the puzzle it begins a simple algorithm :)
-随机选择一个拼图方块并将其从运行列表中删除(上面的1个)(- randomly selects a puzzle square and removes it from the running list (1 above))
-测试是否可以水平添加单词(- tests if a word can be added horizontally)
如果可以的话,尝试水平输入一个单词(try to enter a word horizontally if it can)
如果无法水平输入单词(if it fails to enter a word horizontally)
它尝试垂直输入一个(it tries to enter one vertically)
-输入单词时:(- when entering a word:)
-使用它随机选择的字母/位置(已经在木板上)(- using the letter/position it has selected at random (that is already on the board))
-它测量该字母前后的距离(- it measures the distance it has before and after that letter)
-决定将随机选择的字母用作2个字母组合中的第一个还是第二个(- decides whether to use the randomly selected letter as the 1st or 2nd in 2-letter combination)
-使用板上的另一个现有字母作为2字母组合的另一个字母(- either uses another existing letter on the board as the other letter in 2-letter combination)
或随机选择第二个字母及其位置(如果找不到或使用一个已经存在的字母)(or picks a second letter and its position at random if it cannot find or use one already present)
-测量可以适合拼图当前配置的单词的最大长度(- measures the maximum length of the word that can fit in the puzzle’s current configuration)
-将这些参数以4个字母的搜索关键字和最大字长的形式插入数据库搜索引擎(- plugs these parameters into the data-base search engine in the form of a 4-letter search key and maximum word size)
-由于循环链接列表中的每个元素都包含源自该特定拼写的单词的文件名,而Puzzle生成器会跟踪其在之前生成的25个拼图中使用过的所有单词的文件名,因此这些单词来自同一词典源文件的文件将被拒绝.(- since each element in the circular-linked-lists contains the filename of the word that begat that particular spelling, and the Puzzle generator keeps track of the filenames of all the words it has used in the previous 25 puzzles it has generated, those words that are derived from the same dictionary source file are rejected.)
-选择适合现有拼图的新单词时(- when a new word has been selected which fits into the existing puzzle)
-它被添加到适当的单词列表(水平/垂直)(- it is added to the appropriate word list(horizontal/vertical))
-这个新单词中的每个字母位置都会添加到现有的拼图方块列表中(- each letter position in this new word is added to the existing list of puzzle squares)
-最终,它不再能够轻易找到要添加到拼图中的单词,并且用尽了所有的拼图方块,然后尝试退出,然后退出并将其保存在拼图中(- eventually it can no longer easily find words to add to the puzzle and runs out of puzzles-squares for it to try, then quits and saves the puzzle in the)**CrossWordPuzzles.xml(CrossWordPuzzles.xml)**文件.(file.)
在开发此应用程序时,我花了一个半月(两个多)来测试(播放)并发现LatinProject的LUT中越来越多的错误,这些错误导致拉丁字母拼写单词不尽人意.有时,拉丁语太差劲了,以至于我不得不求助于保存游戏并加载XML文件,以查看谜语是什么…常常是小写的拼写.重建了至少十二个LUT,然后重建了PuzzleGenerator数据库之后,现在看起来它有望减少纠缠错误.因此,既然该项目的开发阶段已经完成,我已经在拼图文件存储中添加了加密功能,不再能够简单地保存游戏并从XML文件中读取答案.如果您想实施作弊模式,欢迎修改(While developing this app I spent over a month and a half(two?) testing (playing) and finding more and more errors in the LatinProject’s LUT that resulted in unsatisfactory Latin puzzle words. Sometimes, the Latin was so off that I had to resort to saving the game and loading the XML file to see what the puzzle word was… all too often abysmally spelled. After making at least a dozen LUT rebuilds and then rebuilding the PuzzleGenerator database, it now looks like its promising to give fewer embarassing errors. So, now that the development phase of this project is done, I’ve added an encryption function to the puzzle-file-storage and it is no longer possible to simple save the game and read the answer from the XML files. If you want to implement a cheat mode, you’re welcome to tinker with the) classCW_XML
文件.(file.)
所有的难题产生都在(All the puzzle generating is done in the) <span style="display: none;"> </span>
classCW_PuzzleGenerator.
public const int conMaxPuzzlesGenerated = 100;
const int conCW_WordsUsedRecently_MaxPuzzlesInList = 25;
上面显示的两个常数可以在(The two constants shown above are found in the) classCW_PuzzleGenerator
并控制拼图中维持的最大拼图数(and control the maximum number of puzzles maintained in the)CrossWordPuzzles.xml(CrossWordPuzzles.xml)文件以及可能不允许重用任何给定单词源的拼图的数量.设置(file as well as the number of puzzles from which any given word-source may not be allowed to be reused. Setting the) conMaxPuzzlesGenerated
值太高只会减慢(value too high only serves to slow down the launching of a)新(New)游戏.并且在过多的连续游戏中限制使用相同的单词会给"拼图生成器"带来麻烦,并可能导致拼图中只有几个单词并且棋盘上有太多空白.(game. And restricting the use of the same word in too many consecutive games will give the Puzzle-Generator trouble and may result in puzzles with only a few words in them and too many blank spaces on the board.)
当我刚开始这个项目并开始生成拼图时,我还没有优化数据库以使Puzzle-Generator更加方便地进行工作,因此生成拼图的过程非常缓慢且费力.而且,即使在玩家忙于解决难题时,拼图生成器在后台运行,生成拼图也可能需要十分钟,然后当用户退出应用程序时,它可能被迫退出,然后再完成工作.为了防止丢失来之不易的部分拼图数据,我实现了一个部分工作保存选项,拼图生成器用于保存部分生成的拼图,然后从下次启动该应用程序的地方开始.因此,既然现在只需几秒钟就可以生成一个新的拼图,那么节省部分工作的功能似乎已经过时了,但是它仍在使用并且可以正常工作.(When I first started this project and began generating puzzles I had not yet optimized the data-base to make it more convenient for the Puzzle-Generator to do its work and consequently the generating of puzzles was slow and laborious. What is more, even though the puzzle-generator worked in the background while the player was busy solving puzzles, it could take ten-minutes to generate a puzzle and then it may be forced to quit before completing its work when the user exit the app. To prevent the loss of this hard-earned partial-puzzle data, I implemented a partial-work save option which the puzzle-generator used to save the partially-generated puzzle and then begin from where it left off the next time the app was launched. So now that a new puzzle can be generated in only seconds, a partial-work saving feature seems over-kill but it is still in use and working fine.)
一旦启动该应用程序,Puzzle-Generator的后台工作人员就可以开始生成新的难题,直到达到最大数量的难题为止,否则当用户启动一个新的难题并且需要其他后台工作人员来生成它时,它会被中断并保存其工作.运行时的线索.(As soon as the App is launched the Puzzle-Generator background worker gets started generating new puzzles until the Maximum number of puzzles is reached, or it is interrupted and saves its work when the user starts a new puzzle and other background workers are needed to generate the clues during runtime.)
产生线索(Generating Clues)
因为用户可能既很慢地请求新线索,又希望计算机一旦被请求就迅速提供线索,所以有两个后台工作人员共同负责生成拼图线索.这两个后台工作者使用相同的工具进行工作,并根据用户的需要进行轮换.他们的名字很长,很难发音,(Because the user may both be slow to make a request for a new clue and still expect the computer to quickly provide a clue once one is requested, there are two back-ground workers that share the duty of generating the puzzle clues. These two background workers work with the the same tools and alternate depending on the user’s needs. Their names are kind of long and difficult to pronounce,)
public BackgroundWorker bckCW_ClueWriter_OnDemand = new BackgroundWorker();
public BackgroundWorker bckCW_ClueWriter_All = new BackgroundWorker();
所以,我们就称他们为(so, we’ll just call them)一经请求(OnDemand)和(&)所有(All).(.)所有(All)屏幕上显示新拼图时,您就可以开始工作.它首先遍历拼图中列出的所有水平单词,然后在完成这些单词时从垂直单词开始.每当用户选择一个新单词并希望看到该单词的线索出现在屏幕上时,都要对该单词进行测试,以查看是否已经生成了线索,如果尚未生成线索,则说明错误(gets to work as soon as a new puzzle is displayed on the screen. It cycles through all the horizontal words listed in the puzzle first, then starts on the vertical words when it has completed those. Whenever the user selects a new word and expects to see that word’s clue appear on the screen, that word is tested to see if a clue has already been generated, if it has not already been generated then the dour)所有(All)退出他在做什么,让我们(quits what he’s doing and let’s)一经请求(OnDemand)过分地(obsequieously)努力地为用户提供所需的东西.什么时候(get to work diligently giving the user what the user wants when the user wants it. When)一经请求(OnDemand)已经完成了,(is done,)所有(All)回到工作,产生其余的线索.(gets back to work generating the rest of the clues.)
在生成所有线索之后,如有必要,背景拼图生成器会启动并开始生成更多拼图.(After all the clues have been generated then, if necessary, the background Puzzle-generator kicks in and starts generating more puzzles.)
每个单词在每个技能级别上都需要一个线索:(Each word requires one clue for every skill level :)简单(easy),(,)正常(normal)和(&)难(difficult).在撰写本文时,我正在考虑更改(. As I write this I’m considering changing how)所有(All)讲述他的一天和他的工作顺序,因为在撰写本文时(goes about his day and the sequence in which he does his work, because at the time of writing)所有(All)在继续到下一个单词之前,一个接一个地生成所有三个技能级别的线索.可以对其进行优化…希望我写这篇文章时不要让您等太久.讨厌让你这样等待.所以,到这一刻,(generates all three skill-level clues one after another before moving on to the next word. This could be optimized … I hope I didn’t make you wait too long while I made that change as I was writing this article. Hate to make you wait like that. So, as of this moment,)所有(All)在选定的玩家技能级别循环遍历每个单词,测试是否需要在所有单词中一次生成每个线索一个线索,然后循环遍历每个技能级别并重复.如果用户更改了技能级别,则(cycles through each word at the selected player skill-level, tests if each clue needs to be generated one word at a time through all the words, then cycles through each skill-level and repeats. If the user changes skill-level then)**所有(All)**将继续产生错误的技能水平,直到产生所需的水平为止,或者在用户选择尚未产生线索的新单词时中断并被打断.(will continue generating the wrong skill level until it gets around to generating the ones required, or is interrupted when the user selects a new word that has not had its clue generated and)一经请求(OnDemand)向用户提供该线索之前(provides the user with that clue before)**所有(All)**再次以适当的技能级别开始,就好像从未做过一样.(starts again at the appropriate skill-level as if it had never done otherwise.)
线索生成过程最初非常慢,尤其是当遇到一个非常普通的单词并且试图从库中找到合适的引号时.正如我在顶部提到的TheLatinProject的最新更新中提到的那样,那里发生了什么,库ContentSearch的最初构建和设计旨在为用户提供包含用户正在搜索的单词的文件的详尽列表.对于TheLatinProject来说,一切都很好,而且很好,但是对于CrossWord难题和线索生成而言,为包含该单词的库中的每个文件加载单独的记录会不必要地减慢速度.因此,整个TheLatinProject ContentSearch被重建以适应这种需求.它与上述的填字游戏数据库相似,因为它具有与循环链接列表类似的功能,用于搜索的单词的文件名,但是它们不是按单词长度而是按单词频率排序,即该单词出现在每个文件中的次数.更重要的是,它们是循环链接列表,(The clue generating procedure was initially very slow, especially when it encountered a very common word and it was trying to find an appropriate quote from the library. What was happening there, as I’ve mentioned in a recent update of TheLatinProject article mentioned at the top, the library ContentSearch was initially built and designed to provide the user with an exhaustive list of files that contain the word being searched by the user. All fine and good for TheLatinProject, but for the CrossWord puzzle and clue-generating, loading a separate record for every file in the library that contains that word slowed things down unnecessarily. Therefore, the entire TheLatinProject ContentSearch was rebuilt to accomodate this need. It is similar to the Crossword Puzzle data-base described above, in that it has something similar to circular linked lists for the filenames of words being searched but they are not sorted by word length but rather by word-frequency, that is the number of times that word appears in each file. More importantly, they are circular-linked-list-)ish(ish). ContentSearch使用两个指向单个链接列表的指针:head&tail,而不是使头部和尾部彼此指向并使其成为圆形的双向链接列表,而使用双向链接列表.生成报价的过程涉及找到指向二级树的二叉树,该二级树按字长对文件名进行排序,然后遍历该二级二叉树.使用指向每个链接列表的头和尾的指针列表,它然后随机选择一个,并从这些列表中最多获取10个文件名.如果前一个列表少于10个最大列表,则这些列表不会更改,但如果最大10个文件名结尾在任何一个链接列表的中间(可能会有奇数),则最后一个列表将通过更改这些列表来更改该列表中的前几个元素并将它们移到列表的末尾,从而更改了该链接列出的辅助二叉树叶子中的头和尾指针,而纵横填字游戏的世界都是正确的.(. Instead of having a doubly-linked linked-list with the head and tail pointing to each other making them circular doubly-linked lists, the ContentSearch uses two pointers to a singly linked-list : head & tail. The process of generating a quote involves finding the binary tree that points to the secondary tree which sorts the filenames by word length, then traverses that secondary binary tree. With the list of pointers to the heads and tails to each linked list, it then randomly picks one, and takes a maximum of 10 filenames from those lists. If the first lists are shorter than the maximum of 10, those lists are not altered but should that maximum number of 10 filenames end in the middle of any one linked-list(odds are it will) then the last list is altered by taking those first few elements in that list and moving them to the back of the list, altering the head & tail pointers in that linked-listed secondary binary tree leaf and all is right with the world of crossword puzzles.)
进行此更改之前,包含给定单词的所有文件名都在单个链接列表中.需要完全加载此列表,线索生成器才能随机选择数千个可用文件中的一个,这大大降低了速度.做出更改后,线索生成过程仅需要选择一些最小数量的文件(实际上是一个文件),随机选择一个文件并生成报价.快捷方便.相信我,它更好.(Before making this change, all the filenames that contained a given word were in a single linked-list. This list needed to be loaded completely for the clue generator to randomly select one of the thousands of files available, which was slowing things down considerably. Once this change was made, the clue-generating process need only select some minimum number of files (one really) pick one at random and generate the quote. Quick and easy. trust me, its much better.)
将适当的信息显示在屏幕上非常简单,我不会赘述,但是如果您有兴趣,(Putting the appropriate information on the screen is fairly simple and I won’t get into it, but if you’re interested the) classGraphicText
我实际上解释了将文本绘制到位图上的方法(that actually draws the text onto a bitmap is explained in my) GCIDE(GCIDE) 我十年前写的文章.(article that I wrote some ten years ago.)
ü(U)用户界面(ser-interface)
该用户界面没有太多内容.现在是该写这篇文章的时候了,我想到没有太多要写的了.这里唯一的秘密成分是(there isn’t much to this user-interface. Now that’s its time to write about it it occurs to me that there isn’t very much to write about. The only secret ingredient here is the)
public TextBox txtUserInput = new TextBox();
此文本框专注于应用程序的整个生命周期.只要将焦点呈现给唯一的其他文本框,就会将其强制移回到(this textbox is focused throughout the life of the app. Whenever focus is rendered to the only other textbox, it is forced back onto) txtUserInput
**.(.)**只有两个函数可以处理此文本框,而这两个是(There are only two functions that handle this textbox and those two are)
private void TxtUserInput_KeyDown(object sender, KeyEventArgs e)
private void TxtUserInput_KeyPress(object sender, KeyPressEventArgs e)
您可以查看以下功能,以了解如何使用拒绝所有无效的键输入(You can have a look at these functions to see how all none-valid key-entries are rejected using the) e.suppress
文本框属性.这也是使用Control/箭头键组合移动光标的地方.(textbox property. This is also where the cursor is moved around using the Control/arrow key combinations.)
除了键盘接口,您还有鼠标.由于屏幕完全由单个图片框组成,可根据需要刷新该图片框,因此鼠标界面仅由该图片框的MouseMove和MouseClick事件处理程序组成.此图片框只有三个区域,分别是:(Besides the keyboard interface, you have the mouse. Since the screen is comprised entirely of a single picturebox which is refreshed as needed, the mouse interface consisted solely of MouseMove and MouseClick event handlers for that PictureBox. There are only three regions of this picture box and those are :)
- 拼图展示区(the puzzle display area)
- 提示显示区(clue display area)
- 帮助按钮(help button)
自从(Since the)
classGraphicText
用于显示线索的文本,还用于确定鼠标下的单词,以便在请求时向用户提供拉丁词典单词条目.绘制拼图区域很简单,只需知道每个拼图正方形的大小,然后将鼠标位置相对于拼图区域左上角的区域进行划分即可解决鼠标光标的x-y坐标下的拼图正方形.只有帮助按钮在提示区域内具有子区域,并且仅在绘制按钮时才需要.(is used to display the clue’s text, it is also used to determined what word is under the mouse in order to provide the user with Latin dictionary word entries when requested. Mapping out the puzzle area is a simple matter of knowing the size of each puzzle square and dividing the mouse-position relative to the top-left region of the puzzle-area to resolve for the puzzle-square beneath the mouse-cursor’s x-y coordinate. There’s only the help button that has a sub-region within the clue’s region and is necessary only when the button is being drawn.) 这些事件处理程序在主窗体中很容易找到,并且不会给您带来太多麻烦.(These event-handlers are easy to find in the main form and should not give you too much trouble.)
G(G)图形和许多有限状态机(raphics and the many Finite-State-Machines)
如果您还没有,我建议您阅读我的(If you haven’t already, I recommend you read my) 精灵编辑器2017(Sprite-Editor 2017) 文章.这是我几个月前编写的应用程序,用于生成我的所有图形.它太酷了.在这个应用程式中(article. It is the application that I wrote a few months ago and use to generate all my graphics. Its pretty cool. In this app, the)魔导师(Magister),(,)猴(Monkey)和(and)鹰(Hawk)字符都是精灵.正如您在要下载的文件列表中所看到的,每个精灵文件都不同.同样,如果您想了解更多有关如何制作精灵并生成一些快速简便的动画的信息,请参考本段开头提到的上一篇文章.(characters are all sprites. Each one a different sprite file as you’ve seen in the list of files to download. Again, if you want to learn more about how to make sprites and generate some quick and easy animations, I refer you to my previous article mentioned at the start of this paragraph.) 在屏幕上移动的其他对象是(The other objects that move around on the screen are either)导弹(Missiles)要么(or)字母(Letters)这两种类型都有自己的一类,可以跟踪其位置和速度.例如,每当Magister玩弄.当时,魔导师只是屏幕上的一个精灵,他玩弄的字母是称为(and both of these types have a class of their own that keeps track of their positions and velocities. For example, whenever Magister is juggling. Magister is only one sprite on the screen at that time and the letters he is juggling are separate entities called)字母(Letters).魔导师的精灵(. The Magister sprite’s)杂耍(Juggle)动画仅显示Magister在玩杂耍时动了动双手,没有任何东西.在应用程序中,通过测量另一只手的反手位置(当另一只手抓到他即将抛出的字母时)来进行杂耍.然后,它计算字母的轨迹,使其向上移动到某个预定的中点,然后及时回落,以到达另一只手将被其抓住的位置.左,右,左,右,三个字母一次又一次地在空中飞来飞去.您可以在(animation only shows Magister moving his hands in the motion of juggling without anything in them. The juggling is done in the app by measuring the position of the hand opposite of the one that is throwing at the time when that opposite hand will catch the letter he is about to throw. Then it calculates a trajectory for the letter to travel upwards to some predetermined mid-point then back down again in time to reach the position the opposite hand will be when its time for him to catch it. Left, right, left, right, three letters flying around two in the air at a time and round and round he goes. You can see this in the)**classMagister(classMagister)**的(’s)杂耍(Juggle())功能.下面显示的是执行此操作的一些代码.您可以在下载源代码时查看源代码,以了解其工作原理,但是为了简洁起见,我在此Switch-Case语句中删除了大多数不同的情况,仅保留了(function. Shown below is some of the code that does this. You can have a look at the source-code when you download it to see how it works but for the sake of brevity I cut out most of the different cases in this Switch-Case statement only leaving the)左投(LeftThrow)演示如何做到这一点.(to demonstrate how this was done.)
enuJugglingFrames eJugglingFrame = (enuJugglingFrames)cData.cAnimationData.intFrameIndex;
switch (eJugglingFrame)
{
case enuJugglingFrames.LeftCatch:
{
...
}
break;
case enuJugglingFrames.LeftThrow:
{
if (((classAnimationTag)cData
.cAnimationData
.cAnimation
.tag).intRepeatAnimation > 1)
{ // calculate starting position of thrown letter
classAnimation_Frame cFrame_start
= cData
.cAnimationData
.cAnimation.lstFrames[(int)enuJugglingFrames.LeftThrow];
classAnimation_Frame_LimbData cLeftHandData
= (classAnimation_Frame_LimbData)cFrame_start
.getData(enuMagister_Limbs.Hand_Left.ToString());
Point ptStart = cMath.AddTwoPoints(cData.pt, cLeftHandData.ptDrawCenter);
// calculate end position of thrown letter
classAnimation_Frame cFrame_End
= cData
.cAnimationData
.cAnimation
.lstFrames[(int)enuJugglingFrames.RightCatch];
classAnimation_Frame_LimbData cRightHandData
= (classAnimation_Frame_LimbData)cFrame_End
.getData(enuMagister_Limbs.Hand_Right.ToString());
Point ptEnd = cMath.AddTwoPoints(cData.pt, cRightHandData.ptDrawCenter);
// calculate Apex position of thrown letter
classAnimation_Frame_LimbData cHatData
= (classAnimation_Frame_LimbData)cFrame_start
.getData(enuMagister_Limbs.Hat.ToString());
Point ptApex = cMath.AddTwoPoints(cData.pt, cHatData.ptDrawCenter);
ptApex.X = cData.pt.X - (ptApex.X - ptEnd.X) / 2;
ptApex.Y -= 100;
classLetter cLetterLeft = lstJugglingLetters[0];
lstJugglingLetters.Remove(cLetterLeft);
lstJugglingLetters.Add(cLetterLeft);
// calculate positions of ThrownLetter as it climbs
double[] dblFraction_UpPath = { 0.4, 0.7, 0.9, 1.0 };
PointF ptfDelta_Up = new PointF(ptApex.X - ptStart.X, ptApex.Y - ptStart.Y);
cLetterLeft.lstPath.Clear();
for (int intStepUpCounter = 0;
intStepUpCounter < dblFraction_UpPath.Length;
intStepUpCounter++)
{
Point ptUp
= new Point(ptStart.X
+ (int)(ptfDelta_Up.X
* dblFraction_UpPath[intStepUpCounter]),
ptStart.Y
+ (int)(ptfDelta_Up.Y
* dblFraction_UpPath[intStepUpCounter]));
cLetterLeft.lstPath.Add(ptUp);
}
// calculate positions of ThrownLetter as it falls
double[] dblFraction_DownPath = { 0.1, 0.3, 0.6, 1.0 };
PointF ptfDelta_Down = new PointF(ptEnd.X - ptApex.X, ptEnd.Y - ptApex.Y);
for (int intStepDownCounter = 0;
intStepDownCounter < dblFraction_DownPath.Length;
intStepDownCounter++)
{
Point ptDown
= new Point(ptApex.X
+ (int)(ptfDelta_Down.X
* dblFraction_DownPath[intStepDownCounter]),
ptApex.Y
+ (int)(ptfDelta_Down.Y
* dblFraction_DownPath[intStepDownCounter]));
cLetterLeft.lstPath.Add(ptDown);
}
cLetterLeft.bolDraw
= ((classAnimationTag)cData
.cAnimationData
.cAnimation
.tag).intRepeatAnimation > 1;
}
}
break;
case enuJugglingFrames.RightCatch:
{
...
}
break;
case enuJugglingFrames.RightThrow:
{
...
}
}
break;
case enuJugglingFrames.LeftWait:
case enuJugglingFrames.RightWait:
break;
}
这出奇的容易,比我预期的容易得多.但是,Hawk动画要困难一些,因为即使Monkey在藤蔓上跳舞,跌落和摆动,他也从未爬上Magister的手臂,也从未触及拼图区域中的游戏方块.因此,这只猴子看起来很酷,并鼓励您知道,只有在板上没有错误时,您才能看到他的滑稽动作,鹰的例行程序是飞过屏幕,落在魔导师的手臂上,等待魔导师的指示,然后飞走然后陡峭地俯冲下来,抓住那不属于它的任性字母,然后再因放错位置的字母再次飞走而给我带来了更多麻烦.(This came off surprisingly easily, a lot easier than I expected. The Hawk animation, however, was somewhat more difficult because even though the Monkey dances and tumbles and swings from a vine, he never climbs onto Magister’s arm and he never touches the game squares in the puzzle area. So the monkey may be cool to watch and encouraging to know that you can only see him at his antics when there are no errors on the board, the hawk’s routine to fly across the screen, land on magister’s arm, wait for Magister’s instructions, then fly off and come down in steep dive to catch the wayward letter that doesn’t belong where it is before flying off again with the misplaced letter gave me considerably more trouble.) 每个Sprite,Magister,Monkey和Hawk都有单独的类以及单独的有限状态机.这些FSM是一种相对简单的方法,可以使角色完成您想要的任务.它们是角色可以进入的"状态"列表,当处于给定状态时,角色知道该怎么做,因为您已出于特定目的将其隔离.对于Magister而言,他具有以下一般状态:(Each sprite, Magister, Monkey & Hawk all have separate classes as well as separate Finite-State-Machines. These FSMs are a relatively simple way to get characters to do what you want them to. They are a list of ‘states’ that the character can be in and when in a given state that character knows just what to do because you’ve isolated it for that specific purpose. In the case of Magister, he has these general states :)
- 嬉戏(Frolic,)
- beginToWorry,(beginToWorry,)
- 担心,(Worry,)
- makeACorrection,(makeACorrection,)
- 庆祝(Celebrate)
- & 帮帮我.(& Help.) 如果板上没有错误,他会嬉戏.对他来说,这意味着:玩杂耍,快乐地挥手,跳舞或演奏手风琴.当板上出现错误时,他会"开始担心",在这里他还没有紧张的步伐,但是不再玩杂耍或演奏手风琴,他只会分散注意力,一边向侧面看去.难题.当经过一定的时间(动画周期)并且拼图中的所有错误尚未得到纠正时,他会"担心".对于Magister来说,这意味着他会紧张地来回走动,只是擦拭额头并为用户的拉丁困扰而焦虑地敲打脚.在每个动画序列的结尾,都有一个随机的机会,他将更改状态和" MakeACorrection".当他进行更正时,有一个单独的FSM指导他选择一种更正形式(大锤,弓箭,手或鹰的轻扫,仅举几例)以及哪些错误放置在板上的字母纠正.当构成特定FSM_MakeACorrection的有限状态序列完成后,如果板上仍然存在错误,他将再次陷入"担心"状态,或者他可能会嬉闹.(He’ll frolic when there are no errors on the board. To him that means: juggle, wave happily, dance or play the accordion. When there’s an error on the board, he’ll ‘begin-to-worry’, here he doesn’t quite pace nervously yet, but no longer juggles or plays the accordion, he’ll just sort of wave distractedly while looking sideways towards the puzzle. When a certain time-period elapses (cycles of the animations) and all the errors in the puzzle have not yet been corrected, he’ll ‘worry’. Which, for Magister, means he’ll pace back-and-forth nervously pausing only to wipe his brow and tap his foot with anxiety for the user’s troubled latin. At the end of each animation sequence there’s a random chance he’ll change state and ‘MakeACorrection’. When he’s making a correction there is a separate FSM which guides him to select a form of correction (sledge-hammer, bow & arrow, swipe of the hand or hawk, to name a few) and which of the incorrectly placed letters off the board to correct. When that sequence of finite states that comprise that particular FSM_MakeACorrection is completed he will then either fall into the ‘Worry’ state again, if there are still errors on the board, or he may frolic.) 仅当用户按下"帮助"按钮时,才进入"帮助"状态.在他在棋盘上写下帮助玩家的信之后,他可以庆祝(如果游戏完成),担心(如果棋盘上仍然有错误)或Frolic(如果没有棋盘).(The Help state is only reached when the user presses the Help button. And after he writes down a letter on the board to help the player, he may then celebrate (if the game is completed), Worry, if there are still errors on the board, or Frolic if there are not.) 与大多数其他动画没有任何区别的唯一动画是手风琴演奏.看一下它,如果您使用过SpriteEditor或阅读了该文章,您可能会发现该动画有点奇怪,因为手在手风琴一侧转动了曲柄.我所做的是在Magister Sprite中添加了手风琴盒,HandleShaft和Handle.然后,在该动画的第一帧中,将手柄置于其旋转的顶部.第二帧显示靠近底部的手柄.然后,我使用SpriteEditor的AutoInsertIntermediateFrames功能在这两个之间生成中间帧,并完成了手柄的旋转.请注意,魔导师的手从来没有伸过把手,此时的把手本身正在旋转.因此,我浏览了动画的帧,然后将手放在每个帧的手柄上,然后工作就完成了,Magister为他的宠物猴子演奏了手风琴(我知道猴子有尾巴但还没有打扰更改名称(The only animation that was any different from most other animations is the accordion playing. Have a look at it, if you’ve used the SpriteEditor or read that article you may find that animation a little odd because the hand turns a crank on the side of the accordion. What I did was added the accordionBox, HandleShaft & Handle to the Magister Sprite. Then I positioned the handle at the top of its rotation in the first frame of that animation. The second frame showed the handle near the bottom. I then generated the intermediate frames between those two using the SpriteEditor’s AutoInsertIntermediateFrames feature and completed the handle’s rotation. Notice at no time has the Magister’s hand reached for the handle, the handle at that point was rotating by itself. So, I stepped through the animation’s frames and positioned the hand over the handle for each frame and the job was done, Magister plays the grind-accordion for his pet-monkey (I’m aware that monkeys have a tail but have yet to bother changing the name of)**西缪斯(Simius)**的源代码中的精灵,反映了他实际上是黑猩猩的事实,并将继续将他称为"猴子",直到简`古道尔(Jane Goodall)正式提出申诉为止.顺便说一句,爱她).(’s sprite in the source-code to reflect the fact that he’s actually a chimpanzee and will continue to refer to him as a ‘monkey’ until Jane Goodall makes an official complaint. love her, by the way).) 这就是我要写的所有内容.(And that’s about all I want to write on the subject.) 角色的各个类中包括各种FSM.我应该指出,我实现这些FSM的方式不一致. Magister的通用FSM的第一个实现有两个用于当前状态和下一个状态的变量,而我稍后实现的大多数变量仅具有一个当前状态,而Switch-Case代码指的是标记案例末尾发生的情况而不是开始.它不是最好的代码.但是它很稳定,我对这个项目感到厌倦,所以我没有解决未解决的问题.(The various FSMs are included in the characters' respective classes. I should point out that I was not consistent in the way I implemented these FSMs. The first implementation of Magister’s general FSM has two variables for the current-state and the next-state while most of the ones I implemented later only have the one current state and Switch-Case code refers to what happens at the end of the labeled case as opposed to the start. Its not the best code. but its stable and I’m tired of this project, so I’m not fixing what ain’t broke.)
动画标签(Animation Tags)
如果你去(If you go to the) classMagister
您可能会注意到,这是定义了Magister所有sprite的动画标签的地方.动画标签未包含在我的Sprite Editor文章中,并且我只是在处理LatinCrossWordPuzzle项目时才添加了它们,因此在这里我将提及它们.许多Visual Studio对象(例如TextBoxes和Panels)都有一个(you may notice that that is where all of the Magister’s sprite’s animation tags are defined. The animation tags were not included in my Sprite Editor article and I only added them while working on the LatinCrossWordPuzzle project, so I’ll mention them here. Many Visual Studio objects such as TextBoxes and Panels and such have a) Tag
该属性使您可以通过将对象首先转换为通用类型来将任何内容绑定到该对象(property that allows you to tie anything to that object by first converting it to a generalized type called) object
.事实证明,这些额外的数据位可能非常有用,因此就像派一样容易,我在其中添加了一个(. It turns out these extra bits of data can be very useful, so as easy as pie, I added one to the) classSpriteAnimation
.本质上,(. Essentially, a) Tag
只不过是指向持有通用类型的地址的指针(is nothing more than a pointer to an address that holds the generic type) Object
.所以在这里,魔导师的精灵有一个(. So here, the Magister’s sprite has a) classAnimationTag
如下所示:(which is shown here below :)
public class classAnimationTag
{
public int intRepeatAnimation = 1;
public int intActionFrame = 0;
public Point[] ptSteps;
public enum enuPathType {
independent,
X_only,
Y_only,
Y_tracks_X,
X_tracks_Y,
_numPathType};
public classAnimationTag(ref classAnimation cAnimation,
int TimesRepeat,
int ActionFrame,
Point[] steps,
enuPathType pathType)
{
intRepeatAnimation = TimesRepeat;
intActionFrame = ActionFrame;
ptSteps = steps;
PathType = pathType;
}
enuPathType _pathtype = enuPathType.Y_tracks_X;
public enuPathType PathType
{
get { return _pathtype; }
set { _pathtype = value; }
}
}
初始化Magister的精灵时,其所有动画都必须具有标签.这些标签由上面显示的数据组成,然后转换为对象并分配给它们各自的动画.某些参数(When the Magister’s sprite is initialized, all of its animations must have a tag. These tags are comprised of the data shown above and then converted to object and assigned to their respective animations. Some of the parameters for the) classAnimationTag
的实例化可能并不明显,所以让我们看一下:(’s instantiation may not be obvious so let’s have a look:)
-
第一个是对其关联的动画的引用(冗余但很方便)(the first one is a reference to the animation it is tied to (redundant perhaps but convenient))
-
重复(TimesRepeat)-告诉处理Magister动画的功能,在Magister移至其他物体之前,特定动画要重复多少次.有些动画仅执行一次,而其他动画在使用时设置为随机值(例如跳舞或嬉戏动画),而另一些动画(例如walk_left,walk_right)则设置为1或2(也许是3)以测量Magister的步幅取决于他朝任一方向走的距离.当我谈论(- tells the functions that handle Magister’s animations how many times a specific animation is to be repeated before Magister moves on to something else. Some animations are only to be executed once while others are set to random values when they are used (like dance or frolicking animations) while still others (like walk_left, walk_right) are set to 1 or 2 (perhaps 3) to measure out Magister’s steps depending on how far he is walking in either direction. More on this below when I talk about the)
classPath
-
动作框架(ActionFrame)-某些动画需要在动画中进行操作(例如,SledgeHammer动画必须导致Letter Magister敲打棋盘,以便在锤子到达时飞离游戏板).这些是在(- some animations require an action to be performed mid-animation (e.g. the SledgeHammer animation must result in the Letter Magister is striking off the board to fly off the game board when the hammer reaches it). These are handled at the beginning of the)
classMagister's nextFrame()
该函数使用"切换/案例"来确定应该运行的动画,具体取决于正在运行的动画.(function which uses a Switch/Case to determine what it is that is supposed to happen depending on which animation is running.) -
脚步(steps)-这些步骤是一组笛卡尔点,它们告诉动画处理功能在该动画的任何一帧期间将Magister(或其他sprite)放置在何处.大多数动画不会导致Magister的位置发生变化,并且没有空步列表,但是Magister的步行动画,Monkey的车轮和Hawk的飞行都可以.必须有确切的点数,因为动画中有帧,在我谈论以下内容时,我将在下面进行解释(- the steps are an array of cartesian points that tell the animation handling functions where to place Magister(or other sprite) during any one frame of that animation. Most animations do not result in Magister’s position being changed and have a null list for steps, but Magister’s walking animation, the Monkey’s cartwheeling and the Hawk’s flying all do. There must be the exact number of points as there are frames in the animation and I will explain more below when I talk about)
classPath
.(.) -
pathType(pathType)-的(- the)
classPath
有几种不同的方法可以计算给定精灵在指定动画的每一帧中将移动多少像素,这就是信息初始化的地方.(has several different ways of calculating how many pixels a given sprite will move for every frame of a specified animation and this is where that information is initialized.)
classPath(classPath)
的(The) classPath
实现了将精灵从屏幕上的一个点移动到另一点的便利.由于CrosswordPuzzle不执行任何碰撞检测测试,并且子画面将不受阻碍地到达其目的地,因此(was implemented to facilitate the moving of sprites from one point on the screen to another. Since the CrosswordPuzzle does not perform any collision detection tests and the sprites will go to their destinations unimpeded, the) classPath
可以轻松地计算给定动画所需的迭代次数,以使子画面到达那里,还必须移动子画面在X,Y方向上必须移动多少像素,以使其沿路径平滑过渡.它的工作原理并不过分复杂.如上所述,每个动画都有其自己的动画标签,其中包含计算精灵运动的数据和参数,形式为(can easily calculate how many iterations of a given animation will be required to get the sprite there as well as how many pixels in the X, Y directions that sprite must be shifted to give it a smooth transition along its way. How it works is not overly complicated. As mentioned above, each animation has its own animation-tag which holds the data and parameters required to calculate the sprite’s movements in the form of) steps
和(&) pathType
.由于步骤列表中的每个点都详细说明了精灵必须在x-y轴上移动的距离,因此这些点的总和等于该精灵在该动画的一个完整周期内将移动的距离. Sprite很少会移动到最初绘制动画的单个周期来带走他,因此,(. Since each point in the steps list details how far the sprite must be moved in the x-y axes, the sum of these points equals how far that sprite will move during one complete cycle of that animation. Rarely will the sprite move exactly as far as a single cycle of the animation was originaly drawn to take him, therefore the) classPath
步骤的其余步骤是从角色的特定开始位置到结束位置的过渡,同时角色打算绘制指定的动画以使他到达那里.(steps in the do the rest of the work for the specific start-location to end-location transition the character is intending to do while drawing a specified animation to get him there.)
精灵需要移动的总距离除以理想情况下通过选定动画的一个周期可以移动的总距离.然后将这个数字四舍五入到最接近的整数,然后将该整数用作(The total distance the sprite needs to travel is divided by the total distance it can travel under ideal circumstances through one cycle of the selected animation. This number is then rounded up or down to the nearest whole number and that whole number is then used as the) TimesRepeat
动画标签的字段.这个(field of the animation’s tag. This) TimesRepeat
然后,在精灵从A点到B点过渡的整个过程中,像测试任何常规整数变量一样对场进行测试,跟踪动画的迭代次数,然后大概在该精灵需要位于的位置停止运动结束.(field is then tested just as any regular integer variable throughout the completion of the sprite’s transition from point A-to-B, keeping track of how many times the animation has iterated and then stopping, presumably, right where that sprite needs to be at the end of its movement.)
现在,(Now that the) classPath
知道动画将被重复多少次,如上所述,很少会将此值恰好等于动画的(knows how many times the animation will be repeated, as mentioned above, rarely will this value be exactly what the animation’s) steps
点列表总计.因此,用于平滑动画的理想行进距离(由步数之和给出的距离)与它到达屏幕上实际需要行进的距离之间的分数被用于修改运行总和的(list of points adds up to. Therefore, the fraction between the ideal distance to travel(that given by the sum of the steps) for a smooth animation and the distance it actually needs to travel to get to where it needs to be on the screen is used to modify the running sum of the) steps
点数组.(array of points.)
例如如果动画的3个完整周期移动了精灵,则精确的值将在(e.g. if 3 complete cycles of the animation moving the sprite the exact values stated in the) steps
点列表(在应用程序的生命周期中从未改变)要大于行进的总距离,然后每一步都会改变以反映这一差异,从而最终得出的总和正是您所需要的.(list of points(which never change during the lifetime of the application) is greater then the total distance to travel then each step will be altered to reflect that difference so that the final sum is exactly what you need.)
而不是改变个人(Instead of altering the individual) Point
中的值(values in the) steps
数组,它将这些点的总和除以该值除以总和,然后取该分数并将其乘以它需要行进的总距离以获得更准确的结果.这类似于拿卷尺并计算他需要在第n帧处的位置,而不是使用8’办公尺并在每帧中累积误差.(array, it takes a running sum of those points, divides this value by the total, then takes that fraction and multiplies it with the total distance it needs to travel to get more accurate results. This is similar to taking a measuring tape and calculating where he needs to be at some frame n rather than using an 8' office-ruler and accumulating errors every frame.)
三个精灵人物(魔导师,鹰和猴子)中的每个都有自己的角色(Each of the three sprite characters (Magister, Hawk & Monkey) have their own) class
,并且每个类都有一个(, and each of these classes has an instance of the) classPath
.每当角色需要移动时,便会识别其动画,然后才确定(. Whenever a character needs to move, its animation is identified and only then is the) classPath
的实例告诉目的地.自从(’s instance told the destination. Since the) classPath
已经知道要使用哪个动画,然后进行上述计算,并创建一个点列表,该角色将在该动画的每次迭代的每个帧处指向该点.然后,当需要将精灵绘制到屏幕上时,(already knows which animation to use, it then does the calculations described above and creates a list of points where that character is going to be at each frame of each iteration of that animation. Then, when the sprite needs to be drawn to the screen, the) classPath
提供沿路径的下一个点并将其从列表中删除,直到用完该点为止,就像该动画的最后一次迭代完成其整个周期一样,Magister就在那里准备将错误的条目踢出拼图板,微笑并漫步回到屏幕右侧附近的位置.(provides the next point along the path and removes it from the list until it runs out of points just as the last iteration of that animation completes its full cycle and Magister is right there ready to kick an erroneous entry off the puzzle board, smile and saunter back to his spot near the right of the screen.)
一路走来的问题(Problems Along the Way)
在开发该游戏期间存在一些问题.当我刚开始这个项目时,我以为我花了一个半月的时间来恢复活力并恢复生机.(There were a few problems during the development of this game. When I first started this project I thought that the month and a half I had put into refreshing and bringing back to life) 拉丁项目(TheLatinProject) 很好相信我投入的所有工作都可以使它接近完美,因此我确信纵横填字游戏将是改进已经很不错的东西的一种快速而酷的方法.但是,在早晨半小时内使用TheLatinProject却没有发现任何可怕的错误是一回事,而玩一个拉丁填字游戏有时又使动词与Ozzy Osbourne结合并拒绝拉丁名词以及塔利班.在玩游戏时,立即发现TheLatinProject有一些错误,当您随便使用它时,多年来可能不会被注意到,而当它们出现在填字游戏的谜题线索中时,这是明显的错误.因此,我一次又一次地发现TheLatinProject的查找表并修复了拉丁问题,重新编译了LUT(延迟了7小时),然后重建了CrosswordPuzzle的数据库(最初是10天!现在是36个小时以上),只需要重复一次经过数小时的测试,发现更多问题后,再次执行该过程.不用说TheLatinProject有了很大的改进.现在比几个月前要好得多,但是经过十几次的重建以及这些重建带来的令人沮丧的延误之后,我彻底厌倦了这个项目.并乐于享受它的完成.(was pretty good. Believing that all that work that I put into it had made it near perfect, I was sure that the Crossword Puzzle would be a quick cool way to improve something that was (is) already very good. However, it is one thing to use TheLatinProject for a half-hour in the morning and not notice anything terribly wrong with it, quite another to play a latin crossword puzzle that sometimes conjugates verbs as well as Ozzy Osbourne and declines Latin nouns as well as the Taliban. It became immediately obvious while playing the game that TheLatinProject had a few too many errors that could go by unnoticed for years when you use it casually but are glaringly wrong when they appear in the cross-word puzzle clue. So time after time, I found and fixed Latin problems with TheLatinProject’s Look-Up Table, recompiled the LUT (7 hours delay) then rebuilt the CrosswordPuzzle’s data-base (innitially 10 days!!! now 36+ hours) only to have to repeat the process again after just a few hours of testing when more problems were found. Needless to say TheLatinProject is much improved. It is far better now than it was just a few months ago but after a dozen or so rebuilds and the frustrating delays these rebuilds entailed I am thoroughly fed-up with this project. And will be glad to enjoy it now that its done.) 2020/03/024(2020/03/024)以下zip文件仍然不错,但我已进行了许多更正.无需下载和解压缩下面的每个(旧的和未更正的)文件,您可以从我的计算机上下载压缩文件(the following zip files are still good but I’ve made many corrections. Instead of downloading and extracting each of the (old and uncorrected)files below you can download a compressed file off my) Google云端硬碟(Google Drive) .只需下载(. Just download the)CrosswordPuzzle.zip(CrosswordPuzzle.zip)从那里下载文件并将其内容提取到您的(file from there and extract its content into your)C:\ Latin \ Data (C:\Latin\Data*)目录,而不必下载此处列出的旧.zip文件.这些更正反映在zip文件中列出的Latin Project的定义中(directory and not bother downloading the old .zip files listed here. These corrections are reflected in the Latin Project’s definitions as listed in the zip file) 对拉丁词典的更正(Corrections Made to Latin Dictionary) .您可以下载该文件,提取文本文件,然后使用Latin Project的管理员编辑工具对词典进行更改.(. You can download this file, extract the text file and make the changes to the dictionary using the Latin Project’s Administrator’s Editing tools.*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_130-133_.zip-7.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_130-133_.zip - 7.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_125-129_.zip-9.2 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_125-129_.zip - 9.2 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_120-124_.zip-9.1 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_120-124_.zip - 9.1 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_115-119_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_115-119_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_110-114_.zip-9.1 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_110-114_.zip - 9.1 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_105-109_.zip-9.1 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_105-109_.zip - 9.1 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_100-104_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_100-104_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_095-099_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_095-099_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_090-094_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_090-094_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_085-089_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_085-089_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_080-084_.zip-9.1 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_080-084_.zip - 9.1 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_075-079_.zip-9 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_075-079_.zip - 9 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_070-074_.zip-9.1 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_070-074_.zip - 9.1 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_065-069_.zip-9.2 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_065-069_.zip - 9.2 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_060-064_.zip-9.3 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_060-064_.zip - 9.3 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_055-059_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_055-059_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_050-054_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_050-054_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_045-049_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_045-049_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_040-044_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_040-044_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_035-039_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_035-039_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_030-034_.zip-9.3 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_030-034_.zip - 9.3 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_025-029_.zip-9.3 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_025-029_.zip - 9.3 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_020-024_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_020-024_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_015-019_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_015-019_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_010-014_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_010-014_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_005-009_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_005-009_.zip - 9.4 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_LL_000-004_.zip-7 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_LL_000-004_.zip - 7 MB*)
- 下载c_Latin_Data_Crossword_Data_Latin_CW_Bin_000_.zip-9.4 MB(*Download c_Latin_Data_Crossword_Data_Latin_CW_Bin_000_.zip - 9.4 MB*)
- 下载c_Latin_Data_CrosswordPuzzle_games_data_.zip-40.7 KB(*Download c_Latin_Data_CrosswordPuzzle_games_data_.zip - 40.7 KB*) (请在不久的将来检查更新,但现在我该制作街机风格的游戏了,否则我会破解).((check for updates in the near future but for now its time for me to build an arcade style game or I will crack).)
更新 :(Updates :)
2019/06/05(2019/06/05)-自从第一篇文章发表以来,我每天都在解决填字游戏,并且继续在(- I’ve been solving Crossword Puzzles everyday since first publishing this article and continue to find problems in the) 拉丁项目(LatinProject) 现在肯定比以前少了,但可能还剩下一些,所以不时检查一下,看看是否有一批新的数据可以帮助您改善在家中运行的版本.自从(there are definitely fewer now than there were before but there are probably a few left, so check in every now and again and see if there’s fresh batch of data for you to improve the version you’re running at home. Since the) 拉丁项目"(Latin Project') 必须重新构建其查找表,然后才能重新编译该项目的数据,您需要从该项目以及这些文件中下载最新文件.此项目的源代码或精灵没有新的更改,因此您只需要下载本文底部的文件(紧随本文上方)(had to have its Look-Up-Table rebuild before this project’s data could be recompiled, you’ll want to download the latest files from that project as well as these ones. There are no new changes to this project’s source-code or sprites so you only need to download the files at the bottom of this article (immediately above this text)) N.B.(N.B.)此处列出的文件名不能准确描述需要扩展到的目录.正确的目录(the file names here listed do not accurately describe which directory they need to be expanded into. The correct directory for the)二(LL)和(&)箱子(Bin)文件(不是底部的games_data.zip文件)应该全部(files (not the games_data.zip file at the bottom) should all be) c:\ Latin \ Data \ Crossword(c:\Latin\Data\Crossword)难题(Puzzle)\数据(\Data*) 和(and)不(not) c:\ Latin \ Data \ Crossword \ Data (根据文件名的指示)(c:\Latin\Data\Crossword\Data\ (as the file names instruct)) 刚才提到的游戏文件为:(the games file just mentioned goes :) c:\ Latin \ Data \ Crossword(c:\Latin\Data\Crossword)难题(Puzzle)(*) 并将扩展为c:\ Latin \ Data \ CrosswordPuzzle \ Games (and will expand to c:\Latin\Data\CrosswordPuzzle\Games*) 在其自己的.(on its own.) 原谅错误,我确定您可以解决.我太懒了,无法全部更改并再次上传.(Forgive the error, I’m sure you can handle it. I’m just too lazy to change them all and upload them again.) 2020/03/24(2020/03/24)-每天玩一个游戏,我就能逐渐发现并解决数据问题(- playing a game a day I’ve been able to gradually find and fix problems with the data*) 享受这个游戏,causa Latinam est Gaudium!(*Enjoy this game, causa Latinam est Gaudium!*)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Win10 Windows Visual-Studio Design game 新闻 翻译