Sudoku feladvány megoldása Backtrack algoritmussal

 

Ez a program Sudoku feladványok megoldásra alkalmas. A használt algoritmus a Backtrack. Csak egyetlen megoldást keres, és ha a feladvány megoldható, akkor talál is. Alapértelmezésben ez így helyes is, mert minden feladványnak csak egyetlen megoldása lehetséges.

 

A következő lehetőségeket kínálja a program:

- a megjelenített táblára magunk beírhatjuk a megoldandó feladványt,

- a beírt számokat rögzíthetjük, mely piros színnel jelenik meg,

- a már rögzített számok kézzel sem írhatók át,

- megtehetjük, hogy magunk próbáljuk megoldani a feladványt,

- a kézi megoldás keresése közben a gép folyamatosan ellenőrzi, hogy az épp beírt szám beírható-e, ha igen akkor megmarad a mezőben, ha nem a gép automatikusan törli azt,

- a feladvány megoldását a gépre is bízhatjuk, melyet a Backtrack algoritmus segítségével keres,

- a gépi megoldás közben, ha ezt a Megjelenít jelölőnézettel kérjük, lépésenként megjeleníti a keresési állapotokat,

- a gép kijelzi a keresés kezdő és befejező időpontját,

- ha sikerül a feladványt megoldani, akkor Kész felirat jelenik meg, ha nem akkor Vége,

- a gép maximum 1 millió lépésig keres megoldásokat, és minden ezredik lépésben megjeleníti az aktuális állapotot a képernyőn,

- ha kedvünk van, véletlen feladványt kérhetünk a géptől, de ezek nem igazi Sudoku feladványok lesznek, lehet, hogy meg sem oldhatók, de az is lehet, hogy több megoldása is van, ami ugye nem szabályos.

 

A program indulási képe:

 

 

A futási kép gépi generálás előtt:

 

 

Az elkészült megoldás:

 

 

A program listája:

 

unit USudokuBTr;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, 

  Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ExtCtrls;

Const GAl=3;
      Max=GAl*GAl;
      LMax=1000000;

type
  TfmSudokuBTr = class(TForm)
    lbSudokuBTr: TLabel;
    btKilepes: TButton;
    sgTabla: TStringGrid;
    btStart: TButton;
    tiIdozito: TTimer;
    cbMegjelenit: TCheckBox;
    btTorles: TButton;
    edIndex: TEdit;
    edStart: TEdit;
    edStop: TEdit;
    lbKesz: TLabel;
    edLepes: TEdit;
    btRandom: TButton;
    btRogzit: TButton;
    Procedure Vizsgal;
    Function Utkozik(H: Word): Boolean;
    procedure btKilepesClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure sgTablaDrawCell(Sender: TObject; Col, Row: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure btStartClick(Sender: TObject);
    procedure tiIdozitoTimer(Sender: TObject);
    procedure btTorlesClick(Sender: TObject);
    procedure btRandomClick(Sender: TObject);
    procedure btRogzitClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  fmSudokuBTr: TfmSudokuBTr;
  SB, SC: Array[1..Max*Max] Of Word;
  Lepes: LongInt;
  Keres: Boolean;

implementation

{$R *.dfm}

procedure TfmSudokuBTr.btKilepesClick(Sender: TObject);
begin
  Close;
end;

procedure TfmSudokuBTr.sgTablaDrawCell(Sender: TObject; Col,
  Row: Integer; Rect: TRect; State: TGridDrawState);
begin
  With sgTabla.Canvas.Brush Do
  Begin
    If Not((gdSelected In State) Or (gdFixed In State)) Then
    If Odd(((Col-1) Div GAl)+((Row-1) Div GAl)) Then
    Color:= clAqua Else Color:= clWindow;
    If (gdSelected In State) Then Color:= clYellow;
  End;
  With sgTabla.Canvas.Font Do If SC[(Row-1)*Max+Col]=0 Then
  Color:= clBlack Else Color:= clRed;
  sgTabla.Canvas.TextRect(Rect,Rect.Left+16,Rect.Top+4,
                               sgTabla.Cells[Col,Row]);
  If gdFocused In State Then sgTabla.Canvas.DrawFocusRect(Rect);
end;

procedure TfmSudokuBTr.FormCreate(Sender: TObject);
begin
  Randomize;
  With sgTabla Do
  Begin
    ColCount:= Max+2; RowCount:= Max+2;
    ColWidths[0]:= 0; RowHeights[0]:= 0;
    ColWidths[ColCount-1]:= 0; RowHeights[RowCount-1]:= 0;
    Col:= ColCount-1; Row:= RowCount-1;
  End;
  Keres:= False;
end;

procedure TfmSudokuBTr.btRogzitClick(Sender: TObject);
begin
  SC:= SB; sgTabla.Repaint;
end;

procedure TfmSudokuBTr.btTorlesClick(Sender: TObject);
Var I, J: Word;
begin
  For I:= 1 To Max*Max Do Begin SB[I]:= 0; SC[I]:= 0 End;
  For I:= 1 To Max Do For J:= 1 To Max Do sgTabla.Cells[I,J]:= '';
  edIndex.Text:= ''; edLepes.Text:= '';
  edStart.Text:= ''; edStop.Text:= '';
  lbKesz.Visible:= False;
end;

Procedure TfmSudokuBTr.Vizsgal;
Var Kod: Integer;
Begin
  With sgTabla Do If Cells[Col,Row]<>'' Then
  Begin
    Val(Cells[Col,Row][1],SB[(Row-1)*Max+Col],Kod);
    If (SC[(Row-1)*Max+Col]<>0) And
    (SC[(Row-1)*Max+Col]<>SB[(Row-1)*Max+Col]) Then
    Begin
      SB[(Row-1)*Max+Col]:= SC[(Row-1)*Max+Col];
      Cells[Col,Row]:= IntToStr(SB[(Row-1)*Max+Col]);
    End;
    If Utkozik((Row-1)*Max+Col) Then
    Begin SB[(Row-1)*Max+Col]:= 0; Cells[Col,Row]:= '' End
  End;
End;

procedure TfmSudokuBTr.tiIdozitoTimer(Sender: TObject);
begin
  If Keres Then Exit; Vizsgal;
end;

Function TfmSudokuBTr.Utkozik(H: Word): Boolean;
Var A, B, I, J, K: Word;
    Ut: Boolean;
Begin
  Ut:= False;
  A:= H-((H-1) Mod Max); B:= H+Max-1-((H-1) Mod Max);
  For I:= A To B Do
  If (SB[H]<>0) And (I<>H) And (SB[H]=SB[I]) Then
  Begin Ut:= True; Break End;
  If Not Ut Then
  Begin
    I:= 1+(H-1) Mod Max;
    While I<Max*Max Do
    Begin If (I<>H) And (SB[H]=SB[I]) Then Ut:= True; Inc(I,Max) End;
  End;
  If Not Ut Then
  Begin
    K:= GAl*Max*((H-1) Div (Gal*Max))+GAl*(((H-1) Mod Max) Div GAl)+1;
    For J:= 1 To Gal Do For I:= K+(J-1)*Max To K+(J-1)*Max+Gal-1 Do
    If (I<>H) And (SB[H]=SB[I]) Then Begin Ut:= True; Break End;
  End;
  Utkozik:= Ut;
End;

procedure TfmSudokuBTr.btStartClick(Sender: TObject);
Var I: Word;
begin
  Keres:= True;
  edStart.Text:= TimeToStr(GetTime); edStart.Repaint;
  edStop.Text:= ''; edStop.Repaint;
  SC:= SB; sgTabla.Repaint;
  I:= 1; Lepes:= 0;
  While (I In [1..Max*Max]) And (Lepes<=LMax) Do
  Begin
    Inc(Lepes);
    If Lepes Mod 1000=0 Then
    Begin
      edIndex.Text:= IntToStr(I);
      edLepes.Text:= IntToStr(Lepes);
      RePaint;
    End;
    While SC[I]<>0 Do Inc(I);
    Inc(SB[I]);
    With sgTabla Do
    Begin
      If SB[I]<>0 Then Cells[(I-1) Mod Max+1,(I-1) Div Max+1]:= IntToStr(SB[I])
      Else Cells[(I-1) Mod Max+1,(I-1) Div Max+1]:= '';
      If cbMegjelenit.Checked Then Repaint;
    End;
    If SB[I]>Max Then
    Begin
      SB[I]:= 0;
      With sgTabla Do
      Begin
        Cells[(I-1) Mod Max+1,(I-1) Div Max+1]:= '';
        If cbMegjelenit.Checked Then Repaint;
      End;
      Dec(I); While SC[I]<>0 Do Dec(I);
    End Else
    With sgTabla Do If Not Utkozik(I) Then
    Begin
      If SB[I]<>0 Then
      Cells[(I-1) Mod Max+1,(I-1) Div Max+1]:= IntToStr(SB[I])
      Else Cells[(I-1) Mod Max+1,(I-1) Div Max+1]:= '';
      If cbMegjelenit.Checked Then Repaint;
      Inc(I);
    End;
  End;
  edIndex.Text:= IntToStr(I);
  edLepes.Text:= IntToStr(Lepes);
  Keres:= False;
  edStop.Text:= TimeToStr(GetTime);
  With lbKesz Do
  Begin
    If I=Max*Max+1 Then Caption:= 'Kész' Else Caption:= 'Vége';
    Visible:= True;
  End;
end;

procedure TfmSudokuBTr.btRandomClick(Sender: TObject);
Var I, H: Word;
begin
  btTorlesClick(Sender);
  With sgTabla Do
  For I:= 1 To 60 Do
  Begin
    H:= Random(Max*Max)+1; SB[H]:= Random(Max)+1;
    Col:= ((H-1) Mod Max)+1;
    Row:= ((H-1) Div Max)+1;
    Cells[Col,Row]:= IntToStr(SB[H]);
    Vizsgal;
  End;
end;

end.