Einführung in das Programmieren mit DELPHI  

16. Der Sudoku-Assistent

Es wird wieder einmal Zeit für ein praktisches Beispiel. Als Anwendung für den Datentyp Array wollen wir ein Programm schreiben, das uns beim Lösen von Sudokus hilft.

Mit jedem Klick auf die Taste "Lösen" sollen der Reihe nach alle Felder geprüft werden und diejenigen, deren Lösung eindeutig ist, eingetragen werden.

Die Zahlen des Sudokus werden intern in einem zweidimensionalen Array x gehalten. Für leere Felder enthält das Array eine Null.
Ein zweites Array vorhanden dient dazu festzuhalten, welche Zahlen in der aktuellen Zeile, der aktuellen Spalte und dem aktuellen Sektor bereits vorhanden sind.
Die beiden Arrays werden im Interface-Teil deklariert. im Implementation-Teil schreiben wir zwei Prozeduren, die die Zahlen aus dem Array x in das Stringgrid kopieren und umgekehrt.

unit Unit1;

interface
   ...

var

Form1: TForm1;
x: array[1..9,1..9] of integer;
vorhanden: array[1..9] of boolean;

procedure anzeigen;
var z,s:integer;
begin
  with Form1.StringGrid1 do 
  for z:=1 to 10 do
    for s:= 1 to 10 do
      if x[z,s]>0 then
        Cells[s-1,z-1]:=IntToStr(x[z,s])
      else
        Cells[s-1,z-1]:= '';
end; {procedure}
procedure einlesen;
var z,s:integer;
begin
  With Form1.StringGrid1 do
  for z:=1 to 10 do
    for s:= 1 to 10 do
      if Trim(Cells[s-1,z-1])<>'' then
        x[z,s]:=StrToInt(Cells[s-1,z-1])
      else
        x[z,s]:= 0;
end; {procedure}

 

procedure TForm1.BtnLoesenClick(Sender: TObject);
var z,s,i,j,i0,j0,anzahl_fehlt,ziffer:integer;
begin
  einlesen;
  for z:=1 to 9 do
    for s:= 1 to 9 do
      if x[z,s]=0 then
      begin
          for ziffer:=1 to 9 do vorhanden[ziffer]:= false;

         {zeile prüfen}
          for i:=1 to 9 do
            if x[i,s]>0 then vorhanden[x[i,s]]:= true;

         {spalte prüfen}
          for j:=1 to 9 do
            if x[z,j]>0 then vorhanden[x[z,j]]:= true;

         {sektor prüfen}
          i0:= 3*((z-1) div 3)+1;
          j0:= 3*((s-1) div 3)+1;
          for i:= i0 to i0+2 do
           for j:= j0 to j0+2 do
             if x[i,j]>0 then vorhanden[x[i,j]]:= true;

          {eindeutige Lösung?}
          anzahl_fehlt:= 0;
          for ziffer:=1 to 9 do
            if vorhanden[ziffer]=false then
               begin
                 Inc(anzahl_fehlt);
                 x[z,s]:= ziffer;
               end;
          if anzahl_fehlt<>1 then x[z,s]:= 0;
      end; {if}
  anzeigen;
end; {procedure}

Um die Trennlinien zwischen den Sektoren zu zeichnen, klinken wir uns in das DrawCell-Ereignis des Stringrids ein und zeichnen am rechten Rand der dritten und sechsten Spalte und am unteren Rand der dritten und sechsten Zeile eine Linie mit der Strichstärke 2.
 

procedure TForm1.StringGrid1DrawCell(Sender:TObject; 
  ACol,ARow:Integer; Rect:TRect; State:TGridDrawState);
begin
    with (sender as TStringGrid).Canvas do
    begin
        Pen.Color:= clBlack;
        Pen.Width:= 2;
        Pen.Style:= psSolid;
        if (Arow = 2) or (Arow=5) then
          begin
            MoveTo( rect.left-1, rect.bottom );
            Lineto( rect.right, rect.bottom );
          end;
        if (Acol=2) or (Acol=5) then
          begin
            MoveTo( rect.right, rect.top );
            Lineto( rect.right, rect.bottom );
          end;
    end;
end; {procedure}

Aufgaben :

  1. Ergänzen Sie eine Prozedur Neu, die alle Zahlen des Sudokus löscht.

  2. Ergänzen Sie zwei Prozeduren Laden und Speichern, zum Import aus bzw. Export in eine Textdatei.
    Dabei soll jede Zeile des Sudokus als neunstelliger String gespeichert werden.

  3. Ergänzen Sie eine Prozedur, die für den Fall, dass das Sudoku nicht auf direktem Weg lösbar ist, alle Felder anzeigt,
    für die noch zwei Möglichkeiten bestehen.

» Lösung

Täglich neue Sudokus gibt es unter sudoku.zeit.de

© 2008 : Bernd Schultheiss