Sudoku: make it possible to preserve the RawData from the ScratchPad and use it in the solving process.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7263 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
lazarus-bart 2020-01-10 11:11:56 +00:00
parent 32ff846113
commit 8d57550d02
4 changed files with 170 additions and 13 deletions

View File

@ -3,6 +3,7 @@ object ScratchForm: TScratchForm
Height = 545 Height = 545
Top = 113 Top = 113
Width = 799 Width = 799
BorderIcons = [biSystemMenu]
Caption = 'ScratchPad' Caption = 'ScratchPad'
ClientHeight = 545 ClientHeight = 545
ClientWidth = 799 ClientWidth = 799
@ -29,14 +30,30 @@ object ScratchForm: TScratchForm
AnchorSideLeft.Control = ScratchGrid AnchorSideLeft.Control = ScratchGrid
AnchorSideTop.Control = ScratchGrid AnchorSideTop.Control = ScratchGrid
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = btnCopyRaw
AnchorSideRight.Side = asrBottom
Left = 16 Left = 16
Height = 25 Height = 25
Top = 306 Top = 306
Width = 253 Width = 279
AutoSize = True AutoSize = True
BorderSpacing.Top = 10 BorderSpacing.Top = 10
Caption = 'Copy values back and close the ScratchPad' Caption = 'Copy only values back and close the ScratchPad'
OnClick = btnCopyClick OnClick = btnCopyClick
TabOrder = 1 TabOrder = 1
end end
object btnCopyRaw: TButton
AnchorSideLeft.Control = btnCopy
AnchorSideTop.Control = btnCopy
AnchorSideTop.Side = asrBottom
Left = 16
Height = 25
Top = 341
Width = 354
AutoSize = True
BorderSpacing.Top = 10
Caption = 'Copy values and possible digits back and close the ScratchPad'
OnClick = btnCopyRawClick
TabOrder = 2
end
end end

View File

@ -14,11 +14,14 @@ type
{ TScratchForm } { TScratchForm }
TCopyValuesEvent = procedure(Sender: TObject; Values: TValues) of Object; TCopyValuesEvent = procedure(Sender: TObject; Values: TValues) of Object;
TCopyRawDataEvent = procedure(Sender: TObject; RawData: TRawGrid) of Object;
TScratchForm = class(TForm) TScratchForm = class(TForm)
btnCopy: TButton; btnCopy: TButton;
btnCopyRaw: TButton;
ScratchGrid: TStringGrid; ScratchGrid: TStringGrid;
procedure btnCopyClick(Sender: TObject); procedure btnCopyClick(Sender: TObject);
procedure btnCopyRawClick(Sender: TObject);
procedure FormActivate(Sender: TObject); procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure ScratchGridClick(Sender: TObject); procedure ScratchGridClick(Sender: TObject);
@ -27,6 +30,8 @@ type
private private
FRawData: TRawGrid; FRawData: TRawGrid;
FOnCopyValues: TCopyValuesEvent; FOnCopyValues: TCopyValuesEvent;
FOnCopyRawData: TCopyRawDataEvent;
procedure GridToRawData(out RawData: TRawGrid);
procedure SetRawData(Data: TRawGrid); procedure SetRawData(Data: TRawGrid);
procedure GridToValues(out Values: TValues); procedure GridToValues(out Values: TValues);
procedure KeepInView; procedure KeepInView;
@ -34,6 +39,7 @@ type
public public
property RawData: TRawGrid write SetRawData; property RawData: TRawGrid write SetRawData;
property OnCopyValues: TCopyValuesEvent read FOnCopyValues write FOnCopyValues; property OnCopyValues: TCopyValuesEvent read FOnCopyValues write FOnCopyValues;
property OnCopyRawData: TCopyRawDataEvent read FOnCopyRawData write FOnCopyRawData;
end; end;
var var
@ -101,7 +107,9 @@ begin
Self.ReAlign; Self.ReAlign;
//ClientHeight := btnCopy.Top + btnCopy.Height + 10; //ClientHeight := btnCopy.Top + btnCopy.Height + 10;
//Above doesn't work: at this time btnCopy.Top still holds designtime value, even when it's top is anchored to the grid //Above doesn't work: at this time btnCopy.Top still holds designtime value, even when it's top is anchored to the grid
ClientHeight := ScratchGrid.Top + ScratchGrid.Height + 10 + btnCopy.Height + 10; ClientHeight := ScratchGrid.Top + ScratchGrid.Height + 10 + btnCopy.Height + 10 + btnCopyRaw.Height + 10;
btnCopy.AutoSize := False;
btnCopy.Width := btnCopyRaw.Width;
//writeln(format('ClientHeight: %d',[ClientHeight])); //writeln(format('ClientHeight: %d',[ClientHeight]));
KeepInView; KeepInView;
end; end;
@ -110,10 +118,26 @@ procedure TScratchForm.btnCopyClick(Sender: TObject);
var var
Values: TValues; Values: TValues;
begin begin
if not Assigned(FOnCopyValues) then Exit; if Assigned(FOnCopyValues) then
GridToValues(Values); begin
FOnCopyValues(Self, Values); GridToValues(Values);
Close; FOnCopyValues(Self, Values);
ModalResult := mrOk;
//Close;
end;
end;
procedure TScratchForm.btnCopyRawClick(Sender: TObject);
var
ARawData: TRawGrid;
begin
if Assigned(FOnCopyRawData) then
begin
GridToRawData(ARawData);
FOnCopyRawData(Self, ARawData);
ModalResult := mrOk;
//Close;
end;
end; end;
procedure TScratchForm.FormCreate(Sender: TObject); procedure TScratchForm.FormCreate(Sender: TObject);
@ -188,6 +212,35 @@ begin
end; end;
end; end;
procedure TScratchForm.GridToRawData(out RawData: TRawGrid);
var
Col, Row: Integer;
ADigit: TDigits;
DigitSet: TDigitSet;
S: String;
begin
for Col := 0 to 8 do
begin
for Row := 0 to 8 do
begin
S := ScratchGrid.Cells[Col, Row];
if TryCellTextToDigit(S, ADigit) then
begin
RawData[Col+1,Row+1].Value := ADigit;
RawData[Col+1,Row+1].DigitsPossible := [];
RawData[Col+1,Row+1].Locked := True;
end
else
begin
DigitSet := StrToDigitSet(S);
RawData[Col+1,Row+1].Value := 0;
RawData[Col+1,Row+1].DigitsPossible := DigitSet;
RawData[Col+1,Row+1].Locked := False;
end;
end;
end;
end;
procedure TScratchForm.GridToValues(out Values: TValues); procedure TScratchForm.GridToValues(out Values: TValues);
var var
Col, Row: Integer; Col, Row: Integer;

View File

@ -62,19 +62,29 @@ type
{ private declarations } { private declarations }
const const
MaxSteps = 50; MaxSteps = 50;
private
//theValues: TValues; //theValues: TValues;
FSolveUsesRawData: Boolean;
FRawData: TRawGrid;
procedure OnCopyBackValues(Sender: TObject; Values: TValues); procedure OnCopyBackValues(Sender: TObject; Values: TValues);
procedure OnCopyBackRawData(Sender: TObject; RawData: TRawGrid);
procedure SetSolveUsesRawData(AValue: Boolean);
function SolveSudoku(out Values: TValues; out RawData: TRawGrid; out Steps: Integer): Boolean; function SolveSudoku(out Values: TValues; out RawData: TRawGrid; out Steps: Integer): Boolean;
function SolveSudoku(var RawData: TRawGrid; out Values: TValues; out Steps: Integer): Boolean;
procedure GridToValues(out Values: TValues); procedure GridToValues(out Values: TValues);
procedure ValuesToGrid(const Values: TValues); procedure ValuesToGrid(const Values: TValues);
procedure RawDataToGrid(const RawData: TRawGrid);
procedure ShowScratchPad(RawData: TRawGrid); procedure ShowScratchPad(RawData: TRawGrid);
procedure LoadSudokuFromFile(const Fn: String); procedure LoadSudokuFromFile(const Fn: String);
procedure SaveSudokuToFile(const Fn: String); procedure SaveSudokuToFile(const Fn: String);
function IsValidSudokuFile(Lines: TStrings): Boolean; function IsValidSudokuFile(Lines: TStrings): Boolean;
procedure LinesToGrid(Lines: TStrings); procedure LinesToGrid(Lines: TStrings);
procedure GridToLines(Lines: TStrings); procedure GridToLines(Lines: TStrings);
procedure EnableEdit;
procedure DisableEdit;
public public
{ public declarations } { public declarations }
property SolveUsesRawData: Boolean read FSolveUsesRawData write SetSolveUsesRawData default False;
end; end;
ESudokuFile = Class(Exception); ESudokuFile = Class(Exception);
@ -96,7 +106,7 @@ const
procedure TForm1.btnEditClick(Sender: TObject); procedure TForm1.btnEditClick(Sender: TObject);
begin begin
SGrid.Options := SGrid.Options + [goEditing]; EnableEdit;
SGrid.SetFocus; SGrid.SetFocus;
end; end;
@ -105,6 +115,7 @@ begin
if OpenDialog.Execute then if OpenDialog.Execute then
try try
LoadSudokuFromFile(OpenDialog.Filename); LoadSudokuFromFile(OpenDialog.Filename);
SolveUsesRawData := False;
except except
on E: Exception do ShowMessage(E.Message); on E: Exception do ShowMessage(E.Message);
end; end;
@ -128,13 +139,15 @@ end;
procedure TForm1.btnSolveClick(Sender: TObject); procedure TForm1.btnSolveClick(Sender: TObject);
var var
Res: Boolean; Res: Boolean;
RawData: TRawGrid;
Values: TValues; Values: TValues;
Steps: Integer; Steps: Integer;
begin begin
SGrid.Options := SGrid.Options - [goEditing]; DisableEdit;
try try
Res := SolveSudoku(Values, RawData, Steps); if not FSolveUsesRawData then
Res := SolveSudoku(Values, FRawData, Steps)
else
Res := SolveSudoku(FRawData, Values, Steps);
ValuesToGrid(Values); ValuesToGrid(Values);
if Res then if Res then
ShowMessage(Format('Sudoku solved in %d steps.', [Steps])) ShowMessage(Format('Sudoku solved in %d steps.', [Steps]))
@ -144,7 +157,7 @@ begin
ShowMessage(Format('Unable to solve sudoku (no progress after step %d).',[Steps-1])) ShowMessage(Format('Unable to solve sudoku (no progress after step %d).',[Steps-1]))
else else
ShowMessage(Format('Unable to completely solve sudoku (tried %d steps).',[Steps])); ShowMessage(Format('Unable to completely solve sudoku (tried %d steps).',[Steps]));
ShowScratchPad(RawData); ShowScratchPad(FRawData);
end; end;
except except
on E: ESudoku do ShowMessage(E.Message); on E: ESudoku do ShowMessage(E.Message);
@ -174,6 +187,7 @@ end;
procedure TForm1.FormCreate(Sender: TObject); procedure TForm1.FormCreate(Sender: TObject);
begin begin
SolveUsesRawData := False;
OpenDialog.Filter := SudokuFileFilter; OpenDialog.Filter := SudokuFileFilter;
SaveDialog.Filter := SudokuFileFilter; SaveDialog.Filter := SudokuFileFilter;
end; end;
@ -231,6 +245,18 @@ begin
end; end;
end; end;
function TForm1.SolveSudoku(var RawData: TRawGrid; out Values: TValues; out Steps: Integer): Boolean;
var
aSudoku: TSudoku;
begin
aSudoku := TSudoku.Create;
try
Result := aSudoku.GiveSolution(RawData, Values, Steps);
finally
aSudoku.Free;
end;
end;
procedure TForm1.GridToValues(out Values: TValues); procedure TForm1.GridToValues(out Values: TValues);
var var
Col, Row: Integer; Col, Row: Integer;
@ -255,6 +281,26 @@ end;
procedure TForm1.OnCopyBackValues(Sender: TObject; Values: TValues); procedure TForm1.OnCopyBackValues(Sender: TObject; Values: TValues);
begin begin
ValuesToGrid(Values); ValuesToGrid(Values);
SolveUsesRawData := False;
end;
procedure TForm1.OnCopyBackRawData(Sender: TObject; RawData: TRawGrid);
begin
FRawData := RawData;
RawDataToGrid(RawData);
SolveUsesRawData := True;
end;
procedure TForm1.SetSolveUsesRawData(AValue: Boolean);
begin
if FSolveUsesRawData = AValue then Exit;
FSolveUsesRawData := AValue;
if FSolveUsesRawData then
DisableEdit
else
EnableEdit;
btnEdit.Enabled := not FSolveUsesRawData;
btnClear.Enabled := not FSolveUsesRawData;
end; end;
@ -275,13 +321,32 @@ begin
end; end;
end; end;
procedure TForm1.RawDataToGrid(const RawData: TRawGrid);
var
Col, Row: Integer;
Ch: Char;
begin
for Col := 0 to 8 do
begin
for Row := 0 to 8 do
begin
Ch := IntToStr(RawData[Col + 1, Row + 1].Value)[1];
if Ch = '0' then
Ch := VisualEmptyChar;
SGrid.Cells[Col, Row] := Ch;
end;
end;
end;
procedure TForm1.ShowScratchPad(RawData: TRawGrid); procedure TForm1.ShowScratchPad(RawData: TRawGrid);
begin begin
ScratchForm.OnCopyValues := @OnCopyBackValues; ScratchForm.OnCopyValues := @OnCopyBackValues;
ScratchForm.OnCopyRawData := @OnCopyBackRawData;
ScratchForm.RawData := RawData; ScratchForm.RawData := RawData;
ScratchForm.ScratchGrid.Options := SGrid.Options - [goEditing]; ScratchForm.ScratchGrid.Options := SGrid.Options - [goEditing];
ScratchForm.Left := Left + Width + 10; ScratchForm.Left := Left + Width + 10;
ScratchForm.Show; if (ScratchForm.ShowModal <> mrOK) then
SolveUsesRawData := False;
end; end;
procedure TForm1.LoadSudokuFromFile(const Fn: String); procedure TForm1.LoadSudokuFromFile(const Fn: String);
@ -396,5 +461,15 @@ begin
end; end;
end; end;
procedure TForm1.EnableEdit;
begin
SGrid.Options := SGrid.Options + [goEditing];
end;
procedure TForm1.DisableEdit;
begin
SGrid.Options := SGrid.Options - [goEditing];
end;
end. end.

View File

@ -58,6 +58,7 @@ type
public public
constructor Create; constructor Create;
function GiveSolution(var Values: TValues; out RawData: TRawGrid; out Steps: Integer): Boolean; function GiveSolution(var Values: TValues; out RawData: TRawGrid; out Steps: Integer): Boolean;
function GiveSolution(var RawData: TRawGrid; out Values: TValues; out Steps: Integer): Boolean;
property MaxSteps: Integer read FMaxSteps write FMaxSteps default 50; property MaxSteps: Integer read FMaxSteps write FMaxSteps default 50;
end; end;
@ -219,6 +220,17 @@ begin
RawData := Grid; RawData := Grid;
end; end;
{
Note: no sanity check on RawData is performed!
}
function TSudoku.GiveSolution(var RawData: TRawGrid; out Values: TValues; out Steps: Integer): Boolean;
begin
Grid := RawData;
Result := Solve(Steps);
RawData := Grid;
Values := GridToValues;
end;
procedure TSudoku.CalculateValues(out IsSolved: Boolean); procedure TSudoku.CalculateValues(out IsSolved: Boolean);
var var
c, r: Integer; c, r: Integer;