将数独求解器从Excel转换为C#(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/converting-sudoku-solver-from-excel-to-csharp-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 15 分钟阅读 - 7181 个词 阅读量 0将数独求解器从Excel转换为C#(译文)
原文地址:https://www.codeproject.com/Articles/990520/Converting-Sudoku-Solver-from-Excel-to-Csharp
原文作者:markgwatts
译文由本站 robot-v1.0 翻译
前言
Converting Sudoku Solver from Excel to C#
将数独求解器从Excel转换为C#
介绍(Introduction)
我对1月1日的文章感兴趣,该文章涉及将Sudoku程序从VB.Net和WinForms转换为C#和WPF(I was interested by your article, January 1, on converting a Sudoku program from VB.Net and WinForms to C# and WPF) http://www.codeproject.com/Articles/855699/Complete-Sudoku-Game-in-Csharp-WPF-Silverlight(http://www.codeproject.com/Articles/855699/Complete-Sudoku-Game-in-Csharp-WPF-Silverlight) 几年前我做了一些类似但也许更极端的转换.在开始之前,我没有C#或WPF的经验–我什至没有听说过,而且我在面向对象程序设计方面的经验只不过是在Visual Basic for Excel中创建简单控件而已.我希望我的经验能使其他人相信学习C#并不像刚开始时那样令人生畏.实际上,就像长期进行一样,最困难的部分实际上是决定开始.(as I made a somewhat similar, but perhaps more extreme, conversion a couple of years ago. Before I started I had no experience with C# or WPF – I had not even heard of them and my experience with object oriented programming extended no further than creating simple controls in Visual Basic for Excel. I hope that my experiences will persuade others that learning C# is not as daunting as it at first appears. In fact, like going for a long run, the hardest part is actually deciding to start.)
背景(Background)
当我第一次了解Sudoku时,我认为在计算机上自动化解决方案将是一个有趣的挑战.因为我最近的编程是使用Visual Basic编写Excel宏,所以我决定使用Excel作为前端并在VB中编写所有代码.效果很好,但是后来我对于这些工具变得过于雄心勃勃.我曾见过其他具有不同几何形状的Sudoku问题,例如Samurai,我希望能够解决这些问题.这并不是太困难,但是我决定我需要一个能够解决任何数独问题的求解器,无论大小,几何形状或字符类型如何.(When I first learnt about Sudoku I thought it would be an interesting challenge to automate a solution on the computer. Since my only recent programming was using Visual Basic to write Excel macros I decided to use Excel as the front end and write all the code in VB. This worked very well but then I got too ambitious for these tools. I’d seen other Sudoku problems with different geometries, like Samurai, and I wanted to be able to handle these. This was not too difficult but I decided I needed a solver that would handle any Sudoku problem whatever the size or geometry or type of characters.) 我的VB代码将所有变量都保存在全局数组中,对于不同的问题重新定义它们的尺寸已成为噩梦,即使如此,它仍然不够通用.我需要一种功能更强大的编程语言,并且需要以某种方式脱离Excel作为前端,因为这需要手动重新绘制每个新的几何图形.当我在蒂涅(Tignes)的滑雪缆车上与他讨论这个问题时,我决定改用C#的理由并不比我的计算机文学女son对我的建议更好.这是一个重要的事件,所以我记得很清楚,但是后来我知道他用C进行了所有编程,并且从未真正使用过C#(他将成为我的导师!).我之所以使用WPF而不是WinForms,只是因为我不知道自己有选择权.最终,这两个决定都是极其偶然的,并且我对组合工具的强大功能和灵活性印象深刻.(My VB code held all variables in global arrays and it became a nightmare to re-dimension these for different problems and even then it was not general enough. I needed a more powerful programming language and somehow I needed to move away from Excel as the front end as this required manually redrawing each new geometry. I decided to switch to C# for no better reason than my computer literate son in law recommended this to me as I discussed the problem with him on a ski lift in Tignes. This was a momentous event so I remember it well but I subsequently learnt that he did all his programming in C and had never actually used C# (he was going to be my mentor!). I used WPF instead of WinForms simply because I didn’t know I had a choice. Both decisions were ultimately extremely fortuitous and I continue to be impressed by the power and flexibility of this combination of tools.) 在使用Excel和VB之前,我曾经使用Fortran IV(主要是1970年代的核工程学中的科学计算)进行编码,因此我的偏见是创建大型共享数组-我发现用C#很难,甚至可能做不到.经过多次挫败尝试创建尺寸可变的全局数组之后,我才开始理解类的概念,生活变得更加简单.(Before Excel and VB I used to code in Fortran IV (mainly scientific computing in Nuclear Engineering back in the 1970s) so my prejudice was to create large shared arrays – something I found very difficult, maybe even impossible, in C#. It was only after many frustrated attempts at creating variably dimensioned global arrays that I started to understand the concept of classes and life became simpler.)
类的使用(Use Of Classes)
我有一个名为" Cell"的类,该类存储问题中每个小盒子的详细信息,例如哪些可能性仍然可用以及该Cell属于" Shapes"的较大结构的详细信息.我有一个名为" Shape"的类,该类与我在其他数独程序中看到的"行,列和框"大致相同,尽管有些通用.我对形状的定义是一个或多个(不一定是连续的)单元的任何集合. Sudoku Shape始终具有与问题中未知数相同的相同数量的像元,并且具有以下规则:每个像元中的解必须不同.不同类型的形状用于杀手级问题.(I have a class called “Cell” which stores the detail of each little box in the problem, detail such as what possibilities are still available and what larger structures “Shapes” this Cell is a part of. I have a class called “Shape” which is roughly the same as the Rows, Columns and Boxes that I see in several other Sudoku programs, although somewhat more general. My definition of a Shape is any collection of one or more, not necessarily contiguous, Cells. A Sudoku Shape always has the same number of Cells equal to the number of unknowns in the problem and also has the rule that the solution in each of these Cells must be different. Different types of Shapes are used for Killer problems.) 下面包括Cell类和Shape类的编码.(The coding for the Cell class and Shape class is included below.)
/// <summary>
/// Records all detail associated with a single Cell
/// including reference to all Shapes containing this Cell
/// </summary>
public class Cell
{
/*
* Records all detail associated with a single Cell
* including reference to all Shapes containing this Cell
* Calls are:
* Add(Tag) Creates a new, unsolved, Cell with identifier Tag
* AddShape(ShapeNum) Add the ID number of a Shape containing this Cell
* RemoveShape(ShapeNum) Reverses AddShape
* ForceSolve(n) Records the Cell as "Solved" with possibility n
* ForceUnSolve() Forces the Cell to an unsolved state, sets all possibilities = true
* NumSolvedCells static get Total number of Cells which have been Solved, includes fixed Cells
* ResetNumSolvedCells static Reset the counter of Solved Cells to zero
* Tag get Identifier of this Cell, RnCm
* Solved Returns true if Cell is solved, else returns false<
* NumPossibilities gets Number of possibilities remaining for this Shape, = 1 if Solved
* Solution gets the Solution index, i.e. number 0 through NChars - 1
* NumShapes gets Number of Shapes containing this Cell
* [] ShapeNums gets array of id#s of all Shapes containing this Cell
* SolOrder gets/sets Order in which each Cell is Solved, zero through NumSolvedCells - 1
* [] Possibilities gets a boolean array of remaining possibilities
* Cell[i] gets/sets possibility []
*/
private static int numSolvedCells;
string tag;
bool solved;
int solution;
int solOrder;
int nShape = 0;
int maxShape;
bool[] possibilities;
int[] shapeNums;
/// <summary>Creates a new, unsolved, Cell with identifier Tag</summary>
/// <param name="Tag">string, the identifier for the Cell</param>
public void Add(string Tag)
{
this.tag = Tag;
possibilities = new bool[Characters.NumChars];
maxShape = 5; //Initial value, will expand if necessary, see AddShape
shapeNums = new int[maxShape];
for (int nS = 0; nS < maxShape; nS++)
{
shapeNums[nS] = -1;
}
ForceUnSolve();
}
/// <summary>Add the ID number of a Shape containing this Cell</summary>
/// <param name="ShNum">The number of the Shape to be added</param>
public void AddShape(int ShNum)
{
if (!shapeNums.Contains(ShNum))
{
expandIfFull();
shapeNums[nShape++] = ShNum; //nShape counts # of Shapes for this Cell
}
}
/// <summary>Reverses AddShape</summary>
/// <remarks>The array, shapeNums, is reordered to bring active elements forward</remarks>
/// <param name="ShNum">Number of the Shape to be removed</param>
/// <returns>true if the Shape was removed, false if the Shape was not found</returns>
public bool RemoveShape(int ShNum)
{
bool found = false;
if (shapeNums.Contains(ShNum))
{
found = true;
for (int i = 0; i < nShape; i++)
{
if (shapeNums[i] == ShNum)
{
if (i + 1 < nShape)
{
//
// Move ShNum one place to the right in the array
int temp = shapeNums[i + 1];
shapeNums[i + 1] = shapeNums[i];
shapeNums[i] = temp;
}
//
// ShNum now at end of array, replace by -1
else shapeNums[i] = -1;
}
}
nShape--; //nShape counts # of Shapes for this Cell
}
return found;
}
/// <summary>Records the Cell as "Solved" with possibility n</summary>
/// <remarks>Sets possibilities[i] = (i == n)</remarks>
/// <param name="n">Number to record (index of possibilities array)</param>
/// <returns>Number of possibilities eliminated</returns>
public int ForceSolve(int n)
{
int possEliminated;
if ((n < 0) | (n >= Characters.NumChars))
{
InOutUtilities iO = new InOutUtilities();
iO.Display_And_Pause(
" Invalid n in ForceSolve, n= ",
n.ToString(), " Cell = ", this.tag.ToString());
return 0;
}
if (this.solved) return 0;
possEliminated = recalculateNumPossibilities() - 1;
this.solved = true;
this.solOrder = numSolvedCells++;
this.solution = n;
for (int nCh = 0; nCh < Characters.NumChars; nCh++)
{
possibilities[nCh] = (nCh == n);
}
return possEliminated;
}
/// <summary>Forces the Cell to an unsolved state, sets all possibilities = true</summary>
public void ForceUnSolve()
{
this.solved = false;
this.solOrder = 0;
this.solution = -1;
for (int nCh = 0; nCh < Characters.NumChars; nCh++)
{
this.possibilities[nCh] = true;
}
}
/// <summary>Total number of Cells which have been Solved, includes fixed Cells</summary>
public static int NumSolvedCells
{
get { return numSolvedCells; }
}
/// <summary>Reset the counter of Solved Cells to zero</summary>
public static void ResetNumSolvedCells()
{
numSolvedCells = 0;
}
/// <summary>Identifier of this Cell, RnCm</summary>
public string Tag
{
get { return this.tag; }
}
/// <summary>Returns true if Cell is solved, else returns false</summary>
public bool Solved
{
get { return this.solved; }
}
/// <summary>Number of possibilities remaining for this Shape, = 1 if Solved</summary>
public int NumPossibilities
{
get
{
return recalculateNumPossibilities();
}
}
private int recalculateNumPossibilities()
{
int n = 0;
for (int nCh = 0; nCh < Characters.NumChars; nCh++)
{
if (possibilities[nCh])
{
n++;
this.solution = nCh;
}
if (n > 1) solution = -1;
}
return n;
}
/// <summary>Returns the Solution index, i.e. number 0 through NChars - 1</summary>
/// <remarks>Use Characters.Chars[c.Solution] for display, Characters.Values[c.Solution] for value</remarks>
/// <returns>Solution index, number 0 through NChars - 1</returns>
public int Solution
{
get { return this.solution; }
}
/// <summary>Number of Shapes containing this Cell</summary>
public int NumShapes
{
get { return this.nShape; }
}
/// <summary>Id number of all Shapes containing this Cell</summary>
public int[] ShapeNums
{
get { return this.shapeNums; }
}
/// <summary>Order in which each Cell is Solved, zero through NumSolvedCells - 1</summary>
public int SolOrder
{
get { return this.solOrder; }
}
/// <summary>Array giving all possible solution for this Cell</summary>
public bool[] Possibilities
{
get { return this.possibilities; }
}
/// <summary>Get or Set a possibility for this Cell</summary>
public bool this[int Index]
{
get { return this.possibilities[Index]; }
set
{
this.possibilities[Index] = value;
}
}
private void expandIfFull()
{
if (nShape == maxShape)
{
maxShape += 5;
int[] newShapeNums = new int[maxShape];
this.shapeNums.CopyTo(newShapeNums, 0);
this.shapeNums = newShapeNums;
for (int nS = nShape; nS < maxShape; nS++)
{
shapeNums[nS] = -1;
}
}
}
}
/// <summary>Stores all information associated with a single Shape</summary>
public class Shape
{
private static int maxShapeNo = -1;
private static int numActiveShapes = 0;
private int num;
private ShapeType type;
private string label;
private int sum;
private int size;
private string[] cellTags;
private string identifier;
private int[,] partitions;
private int nPartition = 0;
private int nActivePartition;
private bool noRepeatSolns;
/// <summary>Add a new Shape</summary>
/// <param name="Type">Type of Shape, chose from enum ShapeType</param>
/// <param name="Label">Label of Shape - not validated</param>
/// <param name="Sum">Sum of Cell values</param>
/// <param name="CellTags">Array of all Cell Tags, No of Cell Tags sets Size</param>
/// <returns>Number of Shape, used as unique identifier</returns>
public int Add(ShapeType Type, string Label, int Sum, string[] CellTags)
{
this.num = ++maxShapeNo;
numActiveShapes++;
this.type = Type;
this.label = Label;
this.sum = Sum;
this.size = CellTags.Count();
this.cellTags = new string[size];
CellTags.CopyTo(cellTags, 0);
this.identifier = Shared.CreateShapeIdentifier(type, label, sum, cellTags);
switch (this.type)
{
case ShapeType.Sudoku:
case ShapeType.Killer:
noRepeatSolns = true;
break;
case ShapeType.Super:
case ShapeType.Created:
noRepeatSolns = false;
break;
}
return num;
}
/// <summary>Resets the number of Shapes to zero</summary>
public static void ResetShapeCount()
{
numActiveShapes = 0;
maxShapeNo = -1;
}
/// <summary>Returns the number of the last Shape added</summary>
public static int MaxShapeNo
{
get { return maxShapeNo; }
}
/// <summary>Returns the identification number of a given Shape</summary>
public int Num
{
get { return this.num; }
}
/// <summary>Returns the Type of a given Shape</summary>
public ShapeType Type
{
get { return this.type; }
}
/// <summary>Returns the Label of a given Shape</summary>
public string Label
{
get { return this.label; }
}
/// <summary>Returns the Sum of a given Shape</summary>
public int Sum
{
get { return this.sum; }
}
/// <summary>Returns the Size (# Cells) of a given Shape</summary>
public int Size
{
get { return this.size; }
}
/// <summary>Returns the Identifier (Type, Sum, Cell Tags) of a given Shape</summary>
public string Identifier
{
get { return this.identifier; }
}
/// <summary>Returns an Array of all Cell Tags for a given Shape</summary>
public string[] CellTags
{
get { return this.cellTags; }
}
/// <summary>Indicates if a value may be repeated in a given Shape</summary>
/// <remarks>
/// Always set true (No repeats) for Sudoku and Killer Shapes
/// Always set false (Repeats allowed) for Super Shapes
/// Set true for Created Shape iff wholely contained within a Sudoku or Killer Shape
/// </remarks>
public bool NoRepeatSolns
{
get { return this.noRepeatSolns; }
set { this.noRepeatSolns = value; }
}
}
守则的一般性(Generality Of The Code)
有了这两个类,编码变得非常简单.例如,如果我们求解一个单元,则不必从包含该单元的每个行,列和框中消除该解决方案,而只需从包含该单元的每个形状中消除该解决方案.(Armed with these two classes the coding becomes very straightforward. For example, if we solve a Cell we do not have to eliminate that solution from each Row, Column and Box that contains the Cell but simply from every Shape that contains the Cell.) 创建了上述类之后,下一步就是将它们存储在排序列表中.这样避免了对数组进行标注的需求,并使保持通用代码变得简单.这意味着使用完全相同的代码来解决武士问题,如图1所示,作为常规的9X9数独问题,或者作为数字不能在对角线上重复的9X9X问题,如图2或什至是3维的Tredoku问题,图3.需要一些额外的代码(Having created the above classes the next step was to store them in sorted lists. This avoided the need to dimension arrays and made it simple to keep the code general. This means that exactly the same code is used to solve a samurai problem, Figure 1, as a regular 9X9 Sudoku problem, or as a 9X9X problem where numbers cannot repeat on the diagonals, Figure 2, or even as a 3 dimensional Tredoku problem, Figure 3. A little extra code was required to)画(draw)图3,但不需要额外的代码即可绘制和解决图4中二维映射所示的相同问题,(Figure 3 but no extra code was needed to draw and solve the identical problem shown as a 2 dimensional mapping in Figure 4,)
图1:武士问题(Figure 1: Samurai problem)
图2:9X9X问题(Figure 2: 9X9X problem)
图3:Tredoku 3维问题(Figure 3: Tredoku 3 dimensional problem)
图4:图3的解决方案,但显示为二维映射,阴影用于挑选一些Shape.(Figure 4: Solution of Figure 3 but shown as a 2 dimensional mapping, the shading is used to pick out some of the Shapes.)请注意,在9X9X和Tredoku问题中,形状并不都包含连续的像元.实际上,连续这个词没有任何意义,因为上面的图表只是将问题映射到我们可以识别的事物中的一种方式.程序哲学至关重要的是,单元格只是可以包含许多可能的解决方案的类,而形状是引用单元格集合的简单类.进一步的概括允许将未知数作为输入参数,这定义了Sudoku Shapes的大小(通常来说,Row,Column和Box的大小).然后,未知数可以映射到"任何"符号.我已将其限制为表示可以表示为单个Unicode字符的任何符号,只是为了能够在解决方案中显示标记而无需分隔符,如图5所示.(Note that in the 9X9X and Tredoku problems the Shapes do not all contain contiguous Cells. In fact the word contiguous has no meaning since the diagrams, above, are simply a way of mapping the problem into something we would recognise. It’s critical to the philosophy of the program that Cells are simply classes that can contain a number of possible solutions and Shapes are simply classes that reference collections of Cells. A further generalisation allows the number of unknowns to be an input parameter, this defines the sizes of the Sudoku Shapes (in normal parlance the sizes of the Rows, Columns and Boxes). Then the unknowns can be mapped to “any” symbol. I have restricted this to mean any symbol that can be represented as a single Unicode character, simply to be able to display mark-up during the solution without needing separators, Figure 5.)
图5.带有恶魔般字符集的环形问题(Figure 5. Toroidal problem with diabolical character set)字符信息的结构如下所示.这是程序中使用的极少数数组之一.(The struct for the character information is shown below. This is one of the very few arrays that are used in the program.)
Struct Characters
/// <summary>Information on Character set used to display the problem and solution</summary>
public struct Characters
{
/// <summary>Returns number of Characters in the Problem</summary>
public static int NumChars { get { return Chars.Count(); } }
/// <summary>Gets or Sets the Character set as a string[]</summary>
public static string[] Chars { get; set; }
/// <summary>Values associated with the Characters</summary>
public static int[] Values { get; set; }
}
演算法(Algorithms)
有了这些概念,编写实现大多数常见算法(表1)以解决非常普遍的Sudoku问题的代码就相对容易了.它不使用" X翼"算法,因为这似乎需要了解问题的几何形状.(With these concepts it was relatively easy to write code that implements most of the well-known algorithms, Table 1, for solving very general Sudoku problems. It does not use the “X-Wing” algorithm because this seems to require knowledge of the geometry of the problem.)
标准名称(Standard Name) | 我的程序(My Program) |
---|---|
裸单(Naked single) | 单元格中的单个字符(Single character in a Cell) |
隐藏的单身(Hidden single) | 形状中的单个字符(Single character in a Shape) |
裸对/三重/四重(Naked Pair / Triple / Quad) | N个单元中的N种可能性(N possibilities in N Cells) |
隐藏对/三重/四重(Hidden Pair / Triple / Quad) | N个单元中的N种可能性(N possibilities in N Cells) |
块/列/行(Block / Column / Row) | 重叠形状(Overlapping Shapes) |
X翼(X Wing) | 未实现(Not implemented) |
表1:Sudoku求解器程序中使用的算法(Table 1: Algorithms used in the Sudoku solver program)我在下面给出了裸对和隐藏对等代码的示例.这给出了一个示例,说明如果不为行,列和框的概念所困扰,那么代码可以多么简单.实际上,我有2个版本的此代码,此处显示的特殊版本速度很快,但要求至少一个Cell包含完全匹配的代码.通用版本允许使用(1,2),(2,3),(1,3)之类的组合,但速度较慢,因此仅在所有其他方法均失败时才使用.(I give an example of the code for Naked and Hidden Pairs etc, below. This gives an example of how simple the code can be if we are not encumbered with concepts of Rows, Columns and Boxes. In fact I have 2 versions of this code, the special version, shown here, is fast but requires that at least one Cell contains an exact match. The general version allows for combinations such as (1,2), (2,3), (1,3) but is slow so is only used if all else fails.)
/// <summary>Check for sets of n Cells in this Shape containing exactly n possibilities
/// <para >Special method - requires one Cell in the subset contains exactly n possibilities,
/// i.e. one Cell has n possibilities and n-1 Cells have subsets</para></summary>
/// <param name="n">Looking for n Cells in Shape s which between them have n possibilities</param>
/// <param name="s">Shape to apply this logic to</param>
/// <returns>Number of possibilities eliminated</returns>
private int nPossInNCellsInShapeSSpecial(int n, Shape s)
{
int possibilitiesEliminated = 0;
string[] cellTags = getListOfSuitableCells(n, s);
if (cellTags != null)
{
//
// Find a Cell with exactly n possibilities
foreach (string tag in cellTags)
{
Cell c = (Cell)Collections.Cells[tag];
if (c.NumPossibilities == n)
{
//
// Find all Cells with a subset of these possibilities
List<string> selectedCells = new List<string>();
selectedCells.Add(tag);
foreach (string otherTag in cellTags)
{
if (otherTag != tag)
{
Cell otherC = (Cell)Collections.Cells[otherTag];
bool subset = true;
for (int ch = 0; ch < Characters.NumChars; ch++)
{
if (otherC[ch] && !c[ch])
{
subset = false;
break;
}
}
if (subset)
{
selectedCells.Add(otherTag);
}
}
}
if (selectedCells.Count == n)
{
possibilitiesEliminated += removePossibilitesFromRemainingCells(n, s, selectedCells);
}
}
}
}
return possibilitiesEliminated;
}
用户界面(User Interface)
编程的重要部分是开发用户界面.但是大多数问题是由于我的无知和经验不足,而不是由于任务固有的困难.(A significant part of the programming was to develop the user interface. But most of the issues were due to my ignorance and inexperience rather than to the inherent difficulty of the task.) 为用户界面选择WPF最初是一个错误.我什至不知道还有另一种可能性– Windows窗体.每当我陷入困境并需要寻求帮助时,我的搜索都会找到一个WinForms解决方案(与WPF相比,WinForms似乎提供了更多的帮助),我会无意间尝试实施该解决方案,然后想知道为什么它不起作用.最终,我进行了细化,现在在搜索字符串中始终包含" wpf".但这在当时还不明显,可能会使其他学习者感到困惑.如果有关于差异以及两者为何存在以及选择每种差异的原因的更清晰的文档,这将很有帮助.我在某处读到WPF是Microsoft的首选方向,但所有帮助文档似乎都相反.上图中显示的实际解决方案网格无法在XAML中生成,因为它必须是动态的,但是创建一个TextBoxes数组然后删除不需要的网格相对容易.编写代码来生成Shape边界非常有趣,特别是对于Killer问题,我想确定Killer Shapes并将其总和放在左上方的Cell中,见图6.(Choosing WPF for the user interface was originally a mistake. I wasn’t even aware that there was another possibility – Windows Forms. Every time I got stuck and needed to search for help my search would find a WinForms solution (there seems to be far more help available for WinForms than for WPF) which I would unwittingly try to implement and then wonder why it didn’t work. Eventually I twigged and now always include “wpf” in my search string. But this was not obvious at the time and may be a source of confusion to other learners. It would be helpful if there were clearer documentation about differences and why both exist and reasons for choosing each. I read somewhere that WPF is Microsoft’s preferred direction but all the help documentation seems to indicate otherwise. The actual solution grid shown in the above diagrams could not be generated in XAML since it needed to be dynamic but it is relatively easy to create an array of TextBoxes and then delete any that are not wanted. Writing the code to generate the Shape boundaries was a lot of fun, especially for Killer problems where I wanted to identify the Killer Shapes and also put the sum in the top left Cell, see Figure 6.)
图6:典型的杀手级问题(Figure 6: Typical Killer Problem)所有其他用户界面(包括所有控件)都是使用XAML编写的,事实证明这非常容易.我喜欢这样的方式,如果我想更改控件,或者将控件放置在另一个控件中,则可以简单地剪切并粘贴XAML代码.(All the rest of the user interface, including all controls was written using XAML, and this turned out to be very easy. I like the way that, if I want to change controls around and maybe place one inside another I can simply cut and paste the XAML code.)
创建数独问题(Creating Sudoku Problems)
我想添加创建数独问题的功能,为此,我非常感谢此处发表的一篇题为"使用Microsoft Solver Foundation的数独"的文章.(I wanted to add the capability to create Sudoku problems and for this I am very grateful to an article published here entitled “Sudoku using Microsoft Solver Foundation”) http://www.codeproject.com/Articles/419389/Sudoku-using-MS-Solver-Foundation(http://www.codeproject.com/Articles/419389/Sudoku-using-MS-Solver-Foundation) 我使用MS Solver Foundation填充解决方案网格,然后连续隐藏Cells,直到剩下的最小数量仍然提供唯一的解决方案.这使得为任何几何图形创建新问题变得容易.(I use MS Solver Foundation to populate the solution grid then successively hide Cells until left with the smallest number which still gave a unique solution. This makes it easy to create new problems for any geometry.)
局限性(Limitations)
这些都是为PC编写的.由于我不具备Silverlight知识,因此我没有尝试将其编写用于电话.我也没有尝试使它成为一个交互式游戏程序,在该程序中,用户会遇到一个他试图直接在PC上解决的问题.目前,电话和互动游戏均超出了学习者的能力范围,因此我决定坚持使用Sudoku求解器和问题创建者.(This was all written for PC. I have not attempted to write it for phone since I have no Silverlight knowledge. I also did not try to make this an interactive game program where the user is presented with a problem that he attempts to solve directly on the PC. Both the phone and the interactive game are currently beyond a learner’s capability so I decided to stick with a Sudoku solver and problem creator.)
结论与总结(Conclusion And Summary)
总的来说,这是一次很棒的经历,将我的编码能力从Fortran IV中的非结构化代码提升到了现代面向对象语言的能力.我只包含了一些代码示例,因为这些示例说明了使用C#之类的语言的功能,并且完整的代码相当长,并且主要涉及用户界面.我敢肯定,有经验的程序员会写出比我更好的书.(Overall this has been a great experience which has lifted my coding capability from unstructured code in Fortran IV to a semblance of competence in a modern object oriented language. I have only included a few code examples since these illustrate the power of using a language like C# and the full code is rather long and mainly involved in the user interface. I’m sure that an experienced programmer could write this much better than I can.) 我要强调的要点是Shape的一般定义,它消除了对Row,Column和Box的单独代码的需要,再加上对Cell和Shape的类的使用,这些类隐藏了很多复杂性并使代码得以编写很一般.(The points I want to stress are the general definition of Shape, which eliminates the need for separate code for Row, Column and Box, plus the use of classes for Cell and Shape which hide a lot of the complexity and enable the code to be made very general.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# MS-Excel 新闻 翻译