Sudoku, ein japanisches Spiel, bei dem es gilt die Zahlen 1 bis 9
auf einem Spielfeld aus 9 mal 9 Feldern so zu verteilen, dass a)
in jeder Reihe, b) in jeder Spalte und c) in 3 mal 3 Unterfeldern
jeweils jede Zahl nur einmal vorkommt. Klingt einfach, ist es
aber nicht.
Gut, dass wir einen Rechner haben und Visual C# 2005 Express
Edition. Wir wollen ein Programm bauen, dass uns beliebige
Sudokus löst. Der Algorithmus dahinter wird im Laufe des
Bauens klar.
Schritt 1: Am Anfang war das Fenster…
Wir starten Visual C# Express und erzeugen im Menü „Datei“ ein
neues Projekt, Typ Windows-Anwendung und verpassen dem Ding
den Namen Sudoko-Löser und los geht’s.
Zunächst einmal soll der Benutzer seine InitializeGrid fügen wir in die Klasse SudokuControl:
Aufgabe eingeben können, dann mit
einer Schaltfläche den Lösungsvorgang
starten und danach die Lösung sehen.
Also brauchen wir zunächst etwas, das die
Spielfläche darstellt. Dazu Rechtsklick auf
Sudoku-Löser rechts oben in der Projektmappe
und im Menü „Hinzufügen
Benutzersteuerelement“ wählen. Wir
werden nach dem Namen gefragt, SudokuControl
soll das Baby heißen.
| Schritt 2: Der Arbeiter
Unser Control sieht noch sehr langweilig aus, daher gehen wir in
die Vollen: Rechte Maustaste auf die graue Fläche und „Code
anzeigen“ wählen. Unser Spielfeld besteht aus 9 mal 9 Textfeldern.
Aber unsere Textfelder können mehr, sie helfen uns bei der Lösung.
Dazu müssen Sie sich merken können, welche möglichen Werte
das Spielfeld hier haben kann. Daher fügen wir direkt vor dem Text
public partial class SudokuControl folgendes ein:
![public class GameBoardCell : TextBox
{
public bool IsBlocked;
protected SudokuControl _Mother;
protected int _Value;
public int Value
{
get { return _Value; }
}
protected List<int> _PossibleValues;
public GameBoardCell(SudokuControl Mother)
{
_Value = 0;
_Mother = Mother;
}
public void SetPossibleValues(List<int> possibles)
{
_PossibleValues = possibles;
_Value = _PossibleValues[0];
_PossibleValues.RemoveAt(0);
this.Text = _Value.ToString();
}
public bool StepNextPossibleEntry()
{
if (IsBlocked == true) return false;
if (_PossibleValues.Count > 0)
{
_Value = _PossibleValues[0];
_PossibleValues.RemoveAt(0);
this.Text = _Value.ToString();
return true;
}
_Value = 0;
return false;
}
protected override void OnTextChanged(EventArgs e)
{
_Value = Int16.Parse(this.Text);
if (_Mother.IsInEditMode == true)
IsBlocked = true;
base.OnTextChanged(e);
}
}](/germany/msdn/aktionen/wirbauenwas/images/sudoku/schritt02_01_code.jpg)
Klicken Sie auf die Abbildung um den Code zu erhalten!
Man sieht, dass wir die Klasse „Textbox“ verwenden und erweitern.
Die neue Klasse kann weitere mögliche Werte verwalten und
kennt einen Modus zum Editieren und einen zum Berechnen.
| Schritt 3: Darstellung
Weiter unten im Text erweitern wir die Klasse SudokuControl, so
dass sie ein 9 mal 9 Felder großes Spielfeld darstellt. Natürlich
wollen wir nicht jedes Feld einzeln malen, das soll der Rechner
erledigen:
![public partial class SudokuControl : UserControl
{
public int nSizeSubBoard;
public int nSizeBoard;
public bool IsInEditMode;
private GameBoardCell[,] GameBoard;
public SudokuControl()
{
nSizeSubBoard = 3; nSizeBoard = 9;
GameBoard = new GameBoardCell[nSizeBoard, nSizeBoard];
IsInEditMode = true;
InitializeComponent();
InitializeGrid();
}
private void InitializeGrid()
{
for (int nZeile = 0; nZeile < nSizeBoard; nZeile++)
for (int nSpalte = 0; nSpalte < nSizeBoard; nSpalte++)
{
GameBoardCell newBox = new GameBoardCell(this);
newBox.Width = this.Width / nSizeBoard;
newBox.Height = this.Height / nSizeBoard;
newBox.Location = new System.Drawing.Point((this.Width / nSizeBoard) * nSpalte, (this.Height / nSizeBoard) * nZeile);
this.Controls.Add(newBox);
GameBoard[nZeile, nSpalte] = newBox;
}
}
}](/germany/msdn/aktionen/wirbauenwas/images/sudoku/schritt03_01_code.jpg)
Komponenten. Doppelklick auf SudokuControl und das Control
liegt in unserem Fenster. Jetzt kann man mit dem grünen Pfeil in
der Leiste oben das Programm starten und sieht sein Spielfeld.
| Schritt 4: Die Aufgabe knacken…Zurück zum SudokuControl.cs Fenster. Direkt unter der Funktion InitializeGrid fügen wir in die Klasse SudokuControl: ![public void Solve()
{
IsInEditMode = false;
Compute(0, 0);
IsInEditMode = true;
}
private bool FindNextCell(ref int Zeile, ref int Spalte)
{
Zeile++;
if (Zeile == nSizeBoard)
{ Spalte++; Zeile = 0; if (Spalte == nSizeBoard) return true; }
return false;
}
private bool Compute(int ActZeile, int ActSpalte)
{
int NextZeile = ActZeile; int NextSpalte = ActSpalte;
bool ValueFound;
List<int> PossibleValues;
if (GameBoard[ActZeile, ActSpalte].IsBlocked == false)
{
PossibleValues = new List<int>();
for (int Value = 1; Value <= nSizeBoard; Value++)
{
ValueFound = false;
//Teste Zeilen
for (int Zeile = 0; Zeile < nSizeBoard && ValueFound == false; Zeile++)
{
if (GameBoard[Zeile, ActSpalte].Value == Value)
ValueFound = true;
}](/germany/msdn/aktionen/wirbauenwas/images/sudoku/schritt04_01_code.jpg)
Klicken Sie auf die Abbildung um den Code zu erhalten! ![//Teste Spalten
for (int Spalte = 0; Spalte < nSizeBoard && ValueFound == false; Spalte++)
{
if (GameBoard[ActZeile, Spalte].Value == Value)
ValueFound = true;
}
//Teste SubGameBoard
int ZeileSubBoardStart = (ActZeile/nSizeSubBoard)*nSizeSubBoard;
for (int Zeile = ZeileSubBoardStart; Zeile < ZeileSubBoardStart + nSizeSubBoard; Zeile++)
{
int SpalteSubBoardStart = (ActSpalte / nSizeSubBoard) *
nSizeSubBoard;
for(int Spalte = SpalteSubBoardStart; Spalte < SpalteSubBoardStart+nSizeSubBoard; Spalte++)
if (GameBoard[Zeile, Spalte].Value == Value)
ValueFound = true;
}
if (ValueFound == false) PossibleValues.Add(Value);
}
if (PossibleValues.Count == 0) return false;
GameBoard[ActZeile, ActSpalte].SetPossibleValues(PossibleValues);
}
if (FindNextCell(ref NextZeile, ref NextSpalte) == true && GameBoard[ActZeile, ActSpalte].Value!=0 )
{
return true; // We are done
}
do
{
if (Compute(NextZeile, NextSpalte) == true) return true;
} while (GameBoard[ActZeile, ActSpalte].StepNextPossibleEntry() == true);
return false;
}
}](/germany/msdn/aktionen/wirbauenwas/images/sudoku/schritt04_02_code.jpg)
Klicken Sie auf die Abbildung um den Code zu erhalten!
Was treiben wir hier? Die Funktion Compute wird aufgerufen und
ermittelt für jede Position auf dem Spielfeld die möglichen Zahlenwerte.
Danach setzt sie einen möglichen Wert auf die Position und
ruft sich selbst für die Nachbarposition auf. Schaffen wir es einen
Wert für alle Zellen zu ermitteln, haben wir es geschafft. Gehen uns
unterwegs die möglichen Zahlen aus, müssen wir einen Schritt
zurück und es mit einem anderen Wert weiter probieren.
Wir gehen zurück zu Form1.cs [Entwurf] und fügen aus der
Toolbox einen Button hinzu. Doppelklick auf den Button und den
Code
sudokuControl1.Solve();
Fertig.
Mit dem grünen Pfeil starten, Sudoku Aufgabe eingeben und lösen
lassen…
|
|