C#中的数独游戏(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/sudoku-game-in-c-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 10 分钟阅读 - 4719 个词 阅读量 0C#中的数独游戏(译文)
原文地址:https://www.codeproject.com/Articles/12473/Sudoku-Game-in-C
原文作者:Gokuldas
译文由本站 robot-v1.0 翻译
前言
The sudoku game in C#.
C#中的数独游戏.
介绍(Introduction)
几个月前,我遇到了数独游戏.我每天乘火车上下班到我的办公室.在日常报纸上,这个难题是我最喜欢的在火车上打发时间的方式之一.我想为什么不使用C#开发此游戏?本文讨论了该游戏在C#中的实现.该游戏提供了三个复杂度级别:简单,中等和复杂.用户可以查看答案几秒钟以获取线索.我希望您会喜欢玩这个游戏.您需要在PC或笔记本电脑上安装.NET Framework才能运行此程序.(I came across the Sudoku game a few months ago. I commute by train daily to my office; this puzzle, in everyday newspaper, was one of my favorite ways to pass time in the train. I thought why not develop this game in C#? This article discusses the implementation of this game in C#. The game provides three complexity levels: simple, medium and complex. The user can view the answer for a few seconds to get a clue. I hope you will enjoy playing this game. You require .NET Framework installed on your PC or laptop to run this program.)
数独规则(Rules of Sudoku)
游戏有简单的规则.本文介绍的游戏以9X9矩阵排列数字,这是该游戏最常见的形式.规则如下:(The game has simple rules. The game presented in this article arranges numbers in 9X9 matrix, which is the most common form of this game. The rules are as follows:)
- 每行和每列中的数字应在1到9之间,并且只能出现一次.(The number, in each row and column, should be between 1 and 9 and should appear only once.)
- 9X9矩阵由九个3X3矩阵组成.因此,这些子集中的每个子集中的数字也应该在1到9之间,并且应该只出现一次.(The 9X9 matrix is made of nine 3X3 matrices. So, the number in each of these subsets should also be between 1 and 9 and should appear only once.) 现在我们知道了规则,让我们看一下实现.(Now that we know the rules, let us see the implementation.)
实作(Implementation)
实现该游戏的主要课程是(The main class implementing this game is) Sudoku
并在(and is implemented in)*Sudoku.cs(Sudoku.cs)*文件.该视图使用(file. The view is implemented using a) DataGrid
实现视图的主要形式是(and the main form for implementing the view is in)SudokuMainForm.cs(SudokuMainForm.cs)文件.基本设计思想是首先生成解决方案,然后根据复杂度级别对某些斑点进行掩盖.最初,我花了一些时间通过使用1到9之间的随机数填充独立的集合来创建解决方案.然后,我尝试填充其他集合.这有点复杂,需要时间.因此,我决定以一个唯一的解决方案为基础,然后通过交换行,列,集合和反转数字来得出其他唯一的解决方案.这样,我可以生成数千个组合.(file. The basic design idea is to first generate the solution and then unmask certain spots based on the complexity level. Initially, I spent some time to create a solution by populating sets which were independent using random numbers between 1 and 9. Then, I tried to fill other sets. This was a bit complex and was taking time. Hence, I decided to take one unique solution as the base and then derive other unique solutions by swapping rows, columns, sets and reversing numbers. This way, I could generate 1000s of combinations.) GenerateGame()
的方法(method of the) Sudoku
类使用这种技术.另一部分实现视图.我用过(class uses this technique. The other part implements the view. I have used) DataGrid
这对于以表格形式表示数据非常有用.我用过(which is a very useful control to represent data in tabular form. I have used) DataSet
这更容易绑定到网格.当我使用二维数组保留问题和答案集时,我暴露了两个属性(which is easier to bind to the grid. As I am using two dimensional array to keep the problem and answer sets, I have exposed two properties) ProblemSet
和(and) AnswerSet
它返回二维数组中的数据作为(which return the data in two dimensional array as a) DataSet
.(.)
游戏的另一个重要部分是提供数据验证.的(Another important part of the game is to provide data validations. The) ColumnChanging
的事件(event of the) DataTable
是非常有用的,并且我已使用此事件来处理数据验证,例如"有效数字",“答案位置未更改"和"数字不是重复的”.该事件的事件参数使我们能够为受影响的列设置适当的错误消息.我提供了一个(is quite useful and I have used this event to handle data validations such as “valid number”, “answer position not changed” and “number is not a duplicate”. The event arguments of this event enable us to set the appropriate error message for the affected column. I have provided a) button
控件显示答案,显示答案几秒钟,然后带回问题.如果问题很复杂并且可以获取一些线索,则可以使用此方法.下一节将描述代码.我只会解释重要的方法.您可以通过上面提供的链接下载源代码来查看完整的实现.(control to show the answer, which displays the answer for a few seconds and then brings back the problem. You can use this if the problem is complex and for getting some clues. The next section describes the code. I will only explain the important methods. You can see the full implementation by downloading the source code using the links given above.)
使用代码(Using the Code)
Sudoku
类是实现游戏的主要类.(class is the main class implementing the game.) GenerateGame(GameLevel level)
方法生成新游戏.下面给出了此代码:(method generates the new game. The code for this is given below:)
// Method: GenerateGame
// Purpose: Generates game based on complexity level.
public void GenerateGame(GameLevel level)
{
// InitialiseSet
// This first creates answer set by using Game combinations
InitialiseSet();
int minPos,maxPos,noOfSets;
// Now unmask positions and create problem set.
switch(level)
{
case GameLevel.SIMPLE:
minPos=4;
maxPos=6;
noOfSets=8;
UnMask(minPos,maxPos,noOfSets);
break;
case GameLevel.MEDIUM:
minPos=3;
maxPos=5;
noOfSets= 7;
UnMask(minPos,maxPos,noOfSets);
break;
case GameLevel.COMPLEX:
minPos=3;
maxPos=5;
noOfSets = 6;
UnMask(minPos,maxPos,noOfSets);
break;
default:
UnMask(3,6,7);
break;
}
在上面的代码中,(In the above code,) GameLevel
是类型(is of type) enum
它定义了复杂性级别.的(and it defines the complexity levels. The) InitialiseSet()
方法生成唯一的解决方案.然后,根据复杂度级别,(method generates the unique solution. Then, based on the complexity level, the) Unmask()
方法将保留一些答案并创建(method will keep some answer spots and create the) ProblemSet
通过清除其他职位.在这里,重要的属性是(by clearing other positions. Here, the important property is the) GameSet
返回(that returns the) DataSet
用于绑定到(which is used to bind to the) DataGrid
.的(. The) DataSet
返回三组数据:(returns three sets of data:) ProblemSet
,是(, a copy of) ProblemSet
和(and) AnswerSet
.一次检索所有数据集(. This retrieval of all sets of data in a single) DataSet
简化了加载和保存功能:(simplifies the load and save functionality:)
// Property:GameSet
// Return Problem Set as DataSet
public DataSet ProblemSet
{
get{ return FormDataSet();}
}
上面的属性调用(The above property invokes the) private
方法,(method,) FormDataSet()
.然后,此方法会生成(. This method then generates the) Dataset
从数组返回到调用者.数据验证使用(from the array and returns to the caller. Data validations are handled using the) columnchanging
的事件(event of the) DataTable
.以下代码段显示了事件处理程序代码:(. The following code snippet shows the event handler code:)
// Handler method for data validations.
private void CurrentSet_ColumnChanging(object sender,
System.Data.DataColumnChangeEventArgs e)
{
try
{
lblStatus.Text="";
int rowPos = dataGrid1.CurrentCell.RowNumber;
string currentNumber = e.ProposedValue as string;
int number =Int32.Parse(currentNumber);
if((number < 1)||(number >9))
{
string errorMessage=
"Number should be between 1 and 9";
e.Row.SetColumnError(e.Column,errorMessage);
}
else
{
int col =e.Column.Ordinal;
bool answerChanged =
_newGame.CheckForAnswerChange(rowPos,col,number);
if(answerChanged)
{
lblStatus.Text="You can't change the answer";
e.ProposedValue = e.Row[e.Column];
}else if(_newGame.CheckForDuplicate(rowPos,col,number)){
e.Row.SetColumnError(e.Column,"Number is Duplicate");
}else
{
e.Row.ClearErrors();
bool answerComplete= IsSolutionComplete();
if(answerComplete)
{
lblStatus.Text= "Great!!! You have done it";
}
}
}
}
catch(Exception ex)
{
e.Row.SetColumnError(e.Column,
"Enter valid Number between 1 & 9");
}
}
上面的代码首先使用读取单元格的当前行位置(The above code first reads the current row position of the cell using the) datagrid1.CurrentCell.RowNumber
属性.事件参数属性中提供了用户输入的单元格值(property. The user entered value for the cell is available in the event argument property) e.ProposedValue
.将此属性转换为数字,然后通过调用执行所有数据验证(. This property is converted to a number and then all data validations are performed by calling) CheckForAnswerChangd()
和(and) CheckForDuplicate()
的方法(methods of the) Sudoku
类.处理程序还通过调用来检查解决方案是否完整(class. The handler also checks whether the solution is complete by invoking) IsSolutionComplete()
方法.的(method. The) label
控件用于显示验证状态,如果解决方案完成,则会显示相应的消息.可以通过设置错误信息来设置(control is used to show the status of validations and an appropriate message is shown if the solution is complete. The error message can be set by setting the) e.Row.SetcolumnError
属性.(property.)
我用过(I have used the) timer
控件以显示答案几秒钟,然后重新显示问题.(control to show the answer for a few seconds and then to redisplay the problem.)
新功能(New Enhancements)
根据读者的建议,我对该程序进行了以下改进:(Based on the suggestions from readers, I have amended the program with the following enhancements:)
- 每个迷你3X3矩阵的边框,以使其与其他迷你集区分开.(Border for each mini 3X3 matrix, to distinguish it clearly from other mini-sets.)
- 答案点现在是只读的.(Answer spots are now read-only.)
- 编辑特定单元格以输入1到9之间的数字时的微调文本控件.(Spinner text control when you edit a particular cell to enter a number between 1 and 9.)
- 加载和保存游戏的功能.这将使用户能够在以后的时间继续游戏.(Load and Save facility for the game. This will enable the user to continue the game at a later time.)
迷你3x3矩阵的边框(Border for Mini 3x3 Matrix)
由于一个(Since a) DataGrid
用于视图时,可以自定义每个单元格的网格样式,也可以使用(is used for the view, either the grid style of each cell can be customized or the border of the) Datagrid
可以更改.要为每个迷你集提供边框,我们需要覆盖(can be changed. To provide border for each mini-set, we need to override the) paint
的方法(method of) DataGrid
.以下代码段显示了重载方法中的代码:(. The following code snippet shows the code in overloaded method:)
/// DataGrid Paint overridden to paint border
private void DataGrid1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Override this handler to do custom painting.
Point currentPoint = new Point(0,0);
Size size = new Size(PREFERRED_COLUMN_WIDTH*3,
PREFERRED_ROW_HEIGHT*3);
Pen myPen = new Pen(Color.Red,3);
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
currentPoint.X = i*PREFERRED_ROW_HEIGHT*3;
currentPoint.Y = j*PREFERRED_ROW_HEIGHT*3;
Rectangle rect = new Rectangle(currentPoint,size);
e.Graphics.DrawRectangle(myPen,rect);
}
}
}
DataGridSpinnerColumn(DataGridSpinnerColumn)
上面提到的功能2和3是通过实现自定义来提供的(Features 2 and 3 mentioned above are provided by implementing a custom) DataGrid
柱.一个习俗(column. A custom) DataGrid
列需要从(column needs to be derived from) DataGridColumnStyle
,这是(, which is an) abstract
类. Microsoft .NET Framework提供了两种从此类派生的自定义列.一个是(class. Microsoft .NET Framework provides two types of custom columns which are derived from this class. One is) DataGridTextBoxColumn
另一个是(and the other is) DataGridBoolColumn
.我已经实施了(. I have implemented a) DataGridSpinnerColumn
上课(class for) DataGridSpinnerColumn
它是从(and it is derived from) DataGridTextBoxColumn
.这样做是为了重用此类所实现的基本属性和方法,并增强此自定义列所需的某些方法.我使用了垂直滚动条控件,它将用作微调控件.(. This is done to reuse the basic properties and methods implemented by this class and to enhance some of its methods required for this custom column. I have used vertical scrollbar control which will act as spinner control.)
主要要求是,当我们编辑(The main requirement is that when we edit the) cell
,它应显示自定义微调器列,以便用户可以使用滚动按钮来更改(, it should display the custom spinner column so that the user can use scroll buttons to change the) cell
价值,当焦点转移到另一个(value and when the focus goes to the other) cell
,则该值应显示在(, then the value should be displayed in the) cell
.另外,答案点应该是只读的.由于此功能,自定义列不是通用的,不能像在其他应用程序中一样使用.这是因为显示微调框控件是通过覆盖edit方法完成的,而该方法又会检查是否(. Also, the answer spots should be read only. Because of this feature, the custom column is not general purpose and cannot be used as is in other applications. This is because showing the spinner control is done by overriding the edit method which in turn checks whether the) cell
编辑中就是答案.如果单元格不包含答案点,则仅显示微调控件.下面给出了edit方法的代码段.(under edit is the answer spot. If the cell does not contain the answer spot, then only the spinner control is displayed. The code snippet for the edit method is given below.)
有关更多信息(For more information on) DataGridColumnStyles
,请参阅MSDN文档.(, refer to the MSDN documentation.)
// On edit, add scroll event handler, and display combobox
protected override void Edit(System.Windows.Forms.CurrencyManager
source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly,
string instantText, bool cellIsVisible)
{
//Call base method which is important else
//edit will not function properly
base.Edit(source, rowNum, bounds, readOnly,
instantText, cellIsVisible);
// Check if cell is not empty
if(this.TextBox.Text.TrimEnd()!="")
{
// Get Column position
int dataValue = Int32.Parse(this.TextBox.Text);
int pos = this.MappingName.LastIndexOf("col");
if(pos > -1)
{
string colIndex = this.MappingName.Substring(pos+3);
int colPos = Int32.Parse(colIndex);
// Check whether it is answer spot.
_answerPostion=_game.CheckIfAnswerPosition(rowNum,
colPos,dataValue);
}
}
else
{
_answerPostion =false;
}
if (!readOnly && cellIsVisible)
{
// Save current row in the DataGrid and currency manager
// associated with the data source for the DataGrid
this._currentRow = rowNum;
this.cm = source;
if(!_answerPostion)
{
// Make parent of scrollbar same as parent.
this.vsBar.Parent = this.TextBox.Parent;
Rectangle rect =
this.DataGridTableStyle.DataGrid.GetCurrentCellBounds();
//Place this control to right.
this.vsBar.Location =
new Point(rect.Right-this.SpinnerWidth,rect.Top);
this.vsBar.Size =
new Size(this.SpinnerWidth,this.TextBox.Height);
// Make the scrollbar visible and place on top textbox control
this.vsBar.Show();
// As textbox control also there let us bring this to front.
this.vsBar.BringToFront();
this.vsBar.Show();
// this.TextBox.Text= this.vsBar.Value.ToString();
this.TextBox.ReadOnly=true;
//Set text color properties different as we are editing cell.
this.TextBox.BackColor = Color.Blue;
this.TextBox.ForeColor=Color.White;
}
else
{
this.TextBox.ReadOnly=true;
this.TextBox.BackColor=Color.White;
this.TextBox.ForeColor =Color.Black;
}
}
}
请参阅源代码以此类的完整实现.(Please refer to the source code for full implementation of this class.)
加载并保存(Load and Save)
我已经重构了(I have re-factored the) sudoku
类提供此功能.属性(class to provide this facility. The properties) ProblemSet
和(and) AnswerSet
现在被替换为(are now replaced by the) GameSet
属性.(property.)
这将使我们能够一次获得所有数据集(This will enable us to get all sets of data in a single) DataSet
.我用过(. I have used) ReadXml()
和(and) WritXml()
的方法(methods of the) DataSet
用于实现加载和保存功能的类.另外,我已经暴露了(class for implementing the load and save functionalities. Also, I have exposed the) InitialiseGameSet()
初始化的所有数据成员的方法(method to initialize all the data members of) sudoku
上课一次(class once the) DataSet
从XML文件加载.该代码很简单,因此这里不作描述.(is loaded from the XML file. This code is easy and hence not described here.)
结论(Conclusion)
您已经看到使用Microsoft .NET的功能在C#中实现此游戏非常容易.您可以使用顶部提供的链接下载完整的源代码.我希望您会喜欢本文以及此游戏的新增强功能.如果您有任何建议或困难,请在下面的评论部分中留下注释.祝好运 !!!(You have seen that how easy it is to implement this game in C# using the power of Microsoft .NET. You can download the full source code using the link provided at the top. l hope you will enjoy this article and the new enhancements to this game. If you have any suggestions or difficulties, then please leave a note in the comments section below. Good luck !!!)
历史(History)
- 25(25)日(th)2005年12月:初始版本(December, 2005: Initial version)
执照(License)
本文没有附带任何明确的许可,但可能在文章文本或下载文件本身中包含使用条款.如有疑问,请通过下面的讨论区与作者联系.(This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.) 可以找到作者可能使用的许可证列表(A list of licenses authors might use can be found) 这里(here) .(.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# .NET Windows Visual-Studio Dev 新闻 翻译