Sudoku: implement loading and saving sudoku files (as text).

Include sample.sudoku file that can be loaded and solved.


git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7232 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
lazarus-bart
2020-01-04 19:22:40 +00:00
parent 8dbd92b634
commit a8d9978525
3 changed files with 205 additions and 5 deletions

View File

@ -0,0 +1,9 @@
--1--7--8
-8-2---5-
7-4---6--
--2-86---
5-------2
---35-1--
--9---7-5
-7---3-1-
8--7--3--

View File

@ -1,6 +1,6 @@
object Form1: TForm1
Left = 566
Height = 359
Height = 380
Top = 203
Width = 333
HorzScrollBar.Page = 271
@ -8,9 +8,10 @@ object Form1: TForm1
ActiveControl = btnEdit
BorderStyle = bsDialog
Caption = 'Sudoku Solver'
ClientHeight = 359
ClientHeight = 380
ClientWidth = 333
OnActivate = FormActivate
OnCreate = FormCreate
LCLVersion = '2.1.0.0'
object SGrid: TStringGrid
Left = 16
@ -40,10 +41,13 @@ object Form1: TForm1
TabOrder = 0
end
object btnSolve: TButton
AnchorSideRight.Control = SGrid
AnchorSideRight.Side = asrBottom
Left = 208
Height = 25
Top = 312
Width = 75
Width = 100
Anchors = [akTop, akLeft, akRight]
BorderSpacing.InnerBorder = 2
Caption = 'Solve'
OnClick = btnSolveClick
@ -58,4 +62,34 @@ object Form1: TForm1
OnClick = btnClearClick
TabOrder = 3
end
object btnSave: TButton
Left = 120
Height = 25
Top = 344
Width = 100
Caption = 'Save to file'
OnClick = btnSaveClick
TabOrder = 4
end
object btnLoad: TButton
Left = 16
Height = 25
Top = 344
Width = 100
Caption = 'Load from file'
OnClick = btnLoadClick
TabOrder = 5
end
object OpenDialog: TOpenDialog
Title = 'Open a Sudoku text file'
Filter = 'Sudoku files|*.sudoku|All files|*'
Options = [ofPathMustExist, ofFileMustExist, ofEnableSizing]
left = 264
top = 16
end
object SaveDialog: TSaveDialog
Options = [ofPathMustExist, ofEnableSizing]
left = 208
top = 16
end
end

View File

@ -39,14 +39,21 @@ type
TForm1 = class(TForm)
btnClear: TButton;
btnLoad: TButton;
btnSave: TButton;
btnSolve: TButton;
btnEdit: TButton;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
SGrid: TStringGrid;
procedure btnClearClick(Sender: TObject);
procedure btnEditClick(Sender: TObject);
procedure btnLoadClick(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure btnSolveClick(Sender: TObject);
procedure EditorKeyPress(Sender: TObject; var Key: char);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure SGridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
{%H-}aState: TGridDrawState);
procedure SGridSelectEditor(Sender: TObject; {%H-}aCol, {%H-}aRow: Integer;
@ -56,10 +63,17 @@ type
theValues: TValues;
function SolveSudoku: Boolean;
procedure ShowSolution;
procedure LoadSudokuFromFile(const Fn: String);
procedure SaveSudokuToFile(const Fn: String);
function IsValidSudokuFile(Lines: TStrings): Boolean;
procedure LinesToGrid(Lines: TStrings);
procedure GridToLines(Lines: TStrings);
public
{ public declarations }
end;
ESudokuFile = Class(Exception);
var
Form1: TForm1;
@ -67,6 +81,12 @@ implementation
{$R *.lfm }
const
FileEmptyChar = '-';
VisualEmptyChar = #32;
AllFilesMask = {$ifdef windows}'*.*'{$else}'*'{$endif}; //Window users are used to see '*.*', so I redefined this constant
SudokuFileFilter = 'Sudoku files|*.sudoku|All files|' + AllFilesMask;
{ TForm1 }
procedure TForm1.btnEditClick(Sender: TObject);
@ -75,6 +95,26 @@ begin
SGrid.SetFocus;
end;
procedure TForm1.btnLoadClick(Sender: TObject);
begin
if OpenDialog.Execute then
try
LoadSudokuFromFile(OpenDialog.Filename);
except
on E: Exception do ShowMessage(E.Message);
end;
end;
procedure TForm1.btnSaveClick(Sender: TObject);
begin
if SaveDialog.Execute then
try
SaveSudokuToFile(SaveDialog.Filename);
except
on E: Exception do ShowMessage(E.Message);
end;
end;
procedure TForm1.btnClearClick(Sender: TObject);
begin
SGrid.Clean;
@ -108,6 +148,12 @@ begin
ClientWidth := 2 * SGrid.Left + SGrid.Width;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
OpenDialog.Filter := SudokuFileFilter;
SaveDialog.Filter := SudokuFileFilter;
end;
procedure TForm1.SGridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
aState: TGridDrawState);
@ -183,11 +229,122 @@ begin
begin
Ch := IntToStr(theValues[Col + 1, Row + 1])[1];
if Ch = '0' then
Ch := #32;
Ch := VisualEmptyChar;
SGrid.Cells[Col, Row] := Ch;
end;
end;
end;
procedure TForm1.LoadSudokuFromFile(const Fn: String);
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.LoadFromFile(Fn);
SL.Text := AdjustLineBreaks(SL.Text);
if not IsValidSudokuFile(SL) then
Raise ESudokuFile.Create(Format('File does not seem to be a valid Sudoku file:'^m'"%s"',[Fn]));
LinesToGrid(SL);
finally
SL.Free
end;
end;
procedure TForm1.SaveSudokuToFile(const Fn: String);
var
SL: TStringList;
begin
SL := TStringList.Create;
try
GridToLines(SL);
{$if fpc_fullversion >= 30200}
SL.WriteBom := False;
{$endif}
SL.SaveToFile(Fn);
finally
SL.Free;
end;
end;
{
A valid SudokuFile consists of 9 lines, each line consists of 9 characters.
Only the characters '1'to '9' and spaces and FileEmptyChar ('-') are allowed.
Empty lines and lines starting with '#' (comments) are discarded
Future implementations may allow for adding a comment when saving the file
}
function TForm1.IsValidSudokuFile(Lines: TStrings): Boolean;
var
i: Integer;
S: String;
Ch: Char;
begin
Result := False;
for i := Lines.Count - 1 downto 0 do
begin
S := Lines[i];
if (S = '') or (S[1] = '#') then Lines.Delete(i);
end;
if (Lines.Count <> 9) then Exit;
for i := 0 to Lines.Count - 1 do
begin
S := Lines[i];
if (Length(S) <> 9) then Exit;
for Ch in S do
begin
if not (Ch in [FileEmptyChar, '1'..'9',VisualEmptyChar]) then Exit;
end;
end;
Result := True;
end;
{
Since this should only be called if IsValidSudokuFile retruns True,
We know that all lines consist of 9 chactres exactly and that there are exactly 9 lines in Lines
}
procedure TForm1.LinesToGrid(Lines: TStrings);
var
Row, Col: Integer;
S: String;
Ch: Char;
begin
for Row := 0 to Lines.Count - 1 do
begin
S := Lines[Row];
for Col := 0 to Length(S) - 1 do
begin
Ch := S[Col+1];
if (Ch = FileEmptyChar) then
Ch := VisualEmptyChar;
SGrid.Cells[Col, Row] := Ch;
end;
end;
end;
procedure TForm1.GridToLines(Lines: TStrings);
var
ALine, S: String;
Ch: Char;
Row, Col: Integer;
begin
Lines.Clear;
for Row := 0 to SGrid.RowCount - 1 do
begin
ALine := StringOfChar(FileEmptyChar,9);
for Col := 0 to SGrid.ColCount - 1 do
begin
S := SGrid.Cells[Col, Row];
if (Length(S) >= 1) then
begin
Ch := S[1];
if (Ch = VisualEmptyChar) then
Ch := FileEmptyChar;
ALine[Col+1] := Ch;
end;
end;
Lines.Add(ALine);
end;
end;
end.