diff --git a/applications/sudoku/scratchpad.lfm b/applications/sudoku/scratchpad.lfm index b21e9ca30..d572a284e 100644 --- a/applications/sudoku/scratchpad.lfm +++ b/applications/sudoku/scratchpad.lfm @@ -3,6 +3,7 @@ object ScratchForm: TScratchForm Height = 545 Top = 113 Width = 799 + BorderIcons = [biSystemMenu] Caption = 'ScratchPad' ClientHeight = 545 ClientWidth = 799 @@ -29,14 +30,30 @@ object ScratchForm: TScratchForm AnchorSideLeft.Control = ScratchGrid AnchorSideTop.Control = ScratchGrid AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCopyRaw + AnchorSideRight.Side = asrBottom Left = 16 Height = 25 Top = 306 - Width = 253 + Width = 279 AutoSize = True BorderSpacing.Top = 10 - Caption = 'Copy values back and close the ScratchPad' + Caption = 'Copy only values back and close the ScratchPad' OnClick = btnCopyClick TabOrder = 1 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 diff --git a/applications/sudoku/scratchpad.pas b/applications/sudoku/scratchpad.pas index f7e413940..9b6676b28 100644 --- a/applications/sudoku/scratchpad.pas +++ b/applications/sudoku/scratchpad.pas @@ -14,11 +14,14 @@ type { TScratchForm } TCopyValuesEvent = procedure(Sender: TObject; Values: TValues) of Object; + TCopyRawDataEvent = procedure(Sender: TObject; RawData: TRawGrid) of Object; TScratchForm = class(TForm) btnCopy: TButton; + btnCopyRaw: TButton; ScratchGrid: TStringGrid; procedure btnCopyClick(Sender: TObject); + procedure btnCopyRawClick(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ScratchGridClick(Sender: TObject); @@ -27,6 +30,8 @@ type private FRawData: TRawGrid; FOnCopyValues: TCopyValuesEvent; + FOnCopyRawData: TCopyRawDataEvent; + procedure GridToRawData(out RawData: TRawGrid); procedure SetRawData(Data: TRawGrid); procedure GridToValues(out Values: TValues); procedure KeepInView; @@ -34,6 +39,7 @@ type public property RawData: TRawGrid write SetRawData; property OnCopyValues: TCopyValuesEvent read FOnCopyValues write FOnCopyValues; + property OnCopyRawData: TCopyRawDataEvent read FOnCopyRawData write FOnCopyRawData; end; var @@ -101,7 +107,9 @@ begin Self.ReAlign; //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 - 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])); KeepInView; end; @@ -110,10 +118,26 @@ procedure TScratchForm.btnCopyClick(Sender: TObject); var Values: TValues; begin - if not Assigned(FOnCopyValues) then Exit; - GridToValues(Values); - FOnCopyValues(Self, Values); - Close; + if Assigned(FOnCopyValues) then + begin + GridToValues(Values); + 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; procedure TScratchForm.FormCreate(Sender: TObject); @@ -188,6 +212,35 @@ begin 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); var Col, Row: Integer; diff --git a/applications/sudoku/sudokumain.pas b/applications/sudoku/sudokumain.pas index 350ede3c1..a58843793 100644 --- a/applications/sudoku/sudokumain.pas +++ b/applications/sudoku/sudokumain.pas @@ -62,19 +62,29 @@ type { private declarations } const MaxSteps = 50; + private //theValues: TValues; + FSolveUsesRawData: Boolean; + FRawData: TRawGrid; 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(var RawData: TRawGrid; out Values: TValues; out Steps: Integer): Boolean; procedure GridToValues(out Values: TValues); procedure ValuesToGrid(const Values: TValues); + procedure RawDataToGrid(const RawData: TRawGrid); procedure ShowScratchPad(RawData: TRawGrid); procedure LoadSudokuFromFile(const Fn: String); procedure SaveSudokuToFile(const Fn: String); function IsValidSudokuFile(Lines: TStrings): Boolean; procedure LinesToGrid(Lines: TStrings); procedure GridToLines(Lines: TStrings); + procedure EnableEdit; + procedure DisableEdit; public { public declarations } + property SolveUsesRawData: Boolean read FSolveUsesRawData write SetSolveUsesRawData default False; end; ESudokuFile = Class(Exception); @@ -96,7 +106,7 @@ const procedure TForm1.btnEditClick(Sender: TObject); begin - SGrid.Options := SGrid.Options + [goEditing]; + EnableEdit; SGrid.SetFocus; end; @@ -105,6 +115,7 @@ begin if OpenDialog.Execute then try LoadSudokuFromFile(OpenDialog.Filename); + SolveUsesRawData := False; except on E: Exception do ShowMessage(E.Message); end; @@ -128,13 +139,15 @@ end; procedure TForm1.btnSolveClick(Sender: TObject); var Res: Boolean; - RawData: TRawGrid; Values: TValues; Steps: Integer; begin - SGrid.Options := SGrid.Options - [goEditing]; + DisableEdit; try - Res := SolveSudoku(Values, RawData, Steps); + if not FSolveUsesRawData then + Res := SolveSudoku(Values, FRawData, Steps) + else + Res := SolveSudoku(FRawData, Values, Steps); ValuesToGrid(Values); if Res then 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])) else ShowMessage(Format('Unable to completely solve sudoku (tried %d steps).',[Steps])); - ShowScratchPad(RawData); + ShowScratchPad(FRawData); end; except on E: ESudoku do ShowMessage(E.Message); @@ -174,6 +187,7 @@ end; procedure TForm1.FormCreate(Sender: TObject); begin + SolveUsesRawData := False; OpenDialog.Filter := SudokuFileFilter; SaveDialog.Filter := SudokuFileFilter; end; @@ -231,6 +245,18 @@ begin 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); var Col, Row: Integer; @@ -255,6 +281,26 @@ end; procedure TForm1.OnCopyBackValues(Sender: TObject; Values: TValues); begin 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; @@ -275,13 +321,32 @@ begin 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); begin ScratchForm.OnCopyValues := @OnCopyBackValues; + ScratchForm.OnCopyRawData := @OnCopyBackRawData; ScratchForm.RawData := RawData; ScratchForm.ScratchGrid.Options := SGrid.Options - [goEditing]; ScratchForm.Left := Left + Width + 10; - ScratchForm.Show; + if (ScratchForm.ShowModal <> mrOK) then + SolveUsesRawData := False; end; procedure TForm1.LoadSudokuFromFile(const Fn: String); @@ -396,5 +461,15 @@ begin end; end; +procedure TForm1.EnableEdit; +begin + SGrid.Options := SGrid.Options + [goEditing]; +end; + +procedure TForm1.DisableEdit; +begin + SGrid.Options := SGrid.Options - [goEditing]; +end; + end. diff --git a/applications/sudoku/sudokutype.pas b/applications/sudoku/sudokutype.pas index 3cbf6bd7b..3b10cf6f8 100644 --- a/applications/sudoku/sudokutype.pas +++ b/applications/sudoku/sudokutype.pas @@ -58,6 +58,7 @@ type public constructor Create; 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; end; @@ -219,6 +220,17 @@ begin RawData := Grid; 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); var c, r: Integer;