Registriert seit: 13. Feb 2010
33 Beiträge
|
Sudoku: Probleme beim Löschen
1. Mär 2010, 16:20
Hallo,
mein Sudoku ist jetzt schon 'relativ' weit, er löst die meisten Sudokus (Nutzung von NakedSingles sowie HiddenSingles der Zeile&Spalte).
Es ist nicht möglich, in eine bereits beschriebene Zelle eine Zahl rein zu schreiben, stattdessen wird der Zellinhalt mittels Entf oder Backspace gelöscht.
Mein Ansatz dabei ist, dass erstmal die gelöschte Zahl zu den FeldMengen der Zellen der gleichen Zelle, Spalte, Block hinzugefügt werden. Es ist jedoch auch möglich, dass eine Zahl nicht in einer Zelle stehen darf (=kein Element der FeldMenge),weil es diese Zahl bereits in der selben Spalte und der selben Zeile steht. Würde man die Zahl aus der Zeile löschen, so würde in der FeldMenge der betroffenen Zelle die Zahl stehen, obwohl die Zahl in der selben Spalte steht, was nicht regelkonform ist.
Daher wird so getan, als ob alle Zahlen neu eingegeben werden und dadurch die betreffenden FeldMengen reduziert werden.
Fall 1: Alle vorgegebenen Zahlen werden fehlerfrei eingegeben --> Programm löst das Sudoku
Fall 2: Alle vorgegebenen Zahlen werden eingegeben, jedoch muss man einmal eine Zahl (die regelkonform ist) löschen und stattdessen eine andere Zahl eingeben-->Programm macht Murks, pro Block bleiben 2-3 Zellen zurück, die entweder eine leere FeldMenge haben oder eine FeldMenge mit 2 Zahlen haben -->Warum?
Fall 3:Alle vorgegebenen Zahlen werden eingegeben, einmal löscht man eine Zahl und gibt noch einmal genau dieselbe ein-->Programm löst Sudoku
Ich habe überlegt und überlegt, komme jedoch einfach nicht auf den Fehler
Hier mal der Quelltext, vielleicht findet ihr den Fehler
Button1 ist der Knopf den man drückt, um das Sudoku zu lösen, die Entfernungsprozedur steht in OnKeyUP
Delphi-Quellcode:
procedure TForm4.FormCreate(Sender: TObject);
var Spalte,Zeile:integer;
begin
For Zeile:=0 to 8 do
For Spalte:=0 to 8 do
begin
StringGrid1.cells[Spalte,Zeile]:=' '; //normalerweise steht in jedem Feld '', so aber ' ' ; wichtig für Speicherung
FeldMenge[Spalte,Zeile]:=[1,2,3,4,5,6,7,8,9]; //Zu Beginn kann in jedem Feld jeder Zahl zwischen 1 und 9 stehen
end;
end;
procedure TForm4.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin
If (Key in ['1'..'9']) and (StringGrid1.Cells[StringGrid1.col,StringGrid1.row]=' ') //wenn die EIngabe eine Zahl ist und im angeklickten Feld nicht schon eine Zahl steht
then
If StrToInt(Key) in FeldMenge[StringGrid1.col,StringGrid1.row] //wenn die Eingabe regelkonform ist
then
begin
StringGrid1.Cells[StringGrid1.col,StringGrid1.row]:=Key; //im angeklickten Feld die Zahl reinschreiben
FeldMenge[StringGrid1.col,StringGrid1.row]:=[]; //da das Feld eine Zahl enthält, kann dort keine andere Zahl mehr eingesetzt werden
RestMengeNeuZuOrdnen(strtoint(Key),StringGrid1.col,StringGrid1.row); //die eingegebene Zahl wird aus den FeldMengen der Zellen im gleichen Block sowie in der gleichen Spalte oder Zelle gelöscht
end
else
begin //Fehlermeldung, dass Eingabe nicht regelkonform ist
Form5.Top:=Mouse.CursorPos.y-85;
Form5.Left:=Mouse.CursorPos.x-210; //damit die Fehlermeldung so auftaucht, dass die Maus bereits auf dem Fehlermeldung-wegklicken-Button
Form5.Show; //Fehlermeldung anzeigen
beep; //Fehlergeräusch machen
end;
end;
procedure TForm4.StringGrid1Click(Sender: TObject);
var RestMengeDerZelle:Zahlen;
Spalte,Zeile,Zahl:integer;
begin
//ist eine Hilfsfunkion währen der Erstellzeit, damit wird die FeldMenge der angeklickten Zelle in einem zweiten StringGrid gezeigt
RestMengeDerZelle:=FeldMenge[StringGrid1.col,StringGrid1.row];
For Spalte:=0 to 2 do
FOr Zeile:=0 to 2 do
begin
Zahl:=Spalte+1+Zeile*3;
If Zahl in RestMengeDerZelle
then
Stringgrid2.Cells[Spalte,Zeile]:=inttostr(Zahl)
else Stringgrid2.Cells[Spalte,Zeile]:=' ';
end;
end;
procedure TForm4.RestMengeNeuZuordnen(EingegebeneZahl,Spalte,Zeile:integer);
var untersuchteZeile,untersuchteSpalte,x,y,xAdd,yAdd:integer;
begin
//Die Zahl aus allen FeldMengen der gleichen Spalte löschen
For untersuchteZeile:=0 to 8 do
FeldMenge[Spalte,untersuchteZeile]:=FeldMenge[Spalte,untersuchteZeile]-[EingegebeneZahl];
//Die Zahl aus allen FeldMengen der gleichen Zeile löschen
For untersuchteSpalte:=0 to 8 do
FeldMenge[untersuchteSpalte,Zeile]:=FeldMenge[untersuchteSpalte,Zeile]-[EingegebeneZahl];
//Die Zahl aus allen FeldMengen des gleichen Blocks löschen
Block.x:=Spalte div 3; //dient der Bestimmung des Blocks, in dem die FeldMengen verändert werden sollen
Block.y:=Zeile div 3; //x und y gehen von 0..2, der Block 0,0 ist links oben, der Block 2,2 rechts unten
x:=Block.X*3; //dient der Bestimmung der Koordinaten der Zelle links oben in
y:=Block.Y*3; //dem betreffenden Block
For yAdd:=0 to 2 do
For xAdd:=0 to 2 do
begin
FeldMenge[x+xAdd,y+yAdd]:=FeldMenge[x+xAdd,y+yAdd]-[EingegebeneZahl];
end;
{entspricht
FeldMenge[x,y]:=FeldMenge[x,y]-[EingegebeneZahl];
FeldMenge[x+1,y]:=FeldMenge[x+1,y]-[EingegebeneZahl];
FeldMenge[x+2,y]:=FeldMenge[x+2,y]-[EingegebeneZahl];
FeldMenge[x,y+1]:=FeldMenge[x,y+1]-[EingegebeneZahl];
FeldMenge[x+1,y+1]:=FeldMenge[x+1,y+1]-[EingegebeneZahl];
FeldMenge[x+2,y+1]:=FeldMenge[x+2,y+1]-[EingegebeneZahl];
FeldMenge[x,y+2]:=FeldMenge[x,y+2]-[EingegebeneZahl];
FeldMenge[x+1,y+2]:=FeldMenge[x+1,y+2]-[EingegebeneZahl];
FeldMenge[x+2,y+2]:=FeldMenge[x+2,y+2]-[EingegebeneZahl];
die obere Variante ist aber eleganter}
end;
procedure TForm4.Button1Click(Sender: TObject);
begin //wenn der Knopf gedrückt wird, löse das Sudoku
NakedSingles;
HiddenSingles;
RestRaten;
end;
procedure TForm4.NakedSingles;
var Zahl,Spalte,Zeile:integer;
anders:boolean;
begin
anders:=false;
For Spalte:=0 to 8 do
For Zeile:=0 to 8 do
For Zahl:=1 to 9 do
if FeldMenge[Spalte,Zeile]=[Zahl] //wenn in der FeldMenge einer Zelle nur eine Zahl steht, so muss diese Zahl darin stehen
then
begin
StringGrid1.cells[Spalte,Zeile]:=inttostr(Zahl); //im betroffenen Feld die Zahl reinschreiben
FeldMenge[Spalte,Zeile]:=[]; //da das Feld eine Zahl enthält, kann dort keine andere Zahl mehr eingesetzt werden
RestMengeNeuZuOrdnen(Zahl,Spalte,Zeile); //die eingegebene Zahl wird aus den FeldMengen der Zellen im gleichen Block sowie in der gleichen Spalte oder Zelle gelöscht
anders:=true;
end;
If anders then Button1CLick(Form4);
//durch "anders" wird nach dem Durchlaufen der Schleifen die Löseprozedur noch einmal aufgerufen, da bei einmaligen Durchlauf von NakedSingles nur max. eine Zelle gelöst werden kann
//wenn sich durch die Prozedur jedoch nichts ändert (-->anders bleibt false), dann muss die Löseprozedur nicht noch einmal aufgerufen werden; man kann in ihr einen Schritt weiter gehen (-->HiddenSingles)
end;
procedure TForm4.HiddenSingles;
var Zahl,Spalte,Zeile,UntersuchteZeile,UntersuchteSpalte:integer;
RestMenge:Zahlen;
anders:boolean;
begin
anders:=false; //Einsatz von anders genauso wie in NakedSingles
//Zeilenweise
For Zeile:=0 to 8 do
For Spalte:=0 to 8 do
begin
RestMenge:=FeldMenge[Spalte,Zeile];
For UntersuchteSpalte:=0 to 8 do
If Spalte<>UntersuchteSpalte //damit die FeldMenge der Zelle der RestMenge nicht abgezogen wird, würde immer zu RestMenge=[] führen
then RestMenge:=RestMenge-FeldMenge[UntersuchteSpalte,Zeile];
For Zahl:=1 to 9 do
If RestMenge=[Zahl]
then
//Es wird untersucht, ob in einer Zeile oder Spalte eine bestimmte Zahl nur in genau einer Zelle vorkommt; denn wenn das zutrifft, so muss diese Zahl in dieser Zelle stehen
//dazu wird von der FeldMenge einer Zelle die FeldMengen aller anderen Zellen der gleichen Spalte oder des gleichen Blocks abgezogen; wenn nur eine Zahl übrigbleibt, dann deswegen, weil sie nie abgezogen wurde
//diese Zahl steht also in keiner anderen FeldMengen
begin
StringGrid1.cells[Spalte,Zeile]:=inttostr(Zahl);
FeldMenge[Spalte,Zeile]:=[]; //da das Feld eine Zahl enthält, kann dort keine andere Zahl mehr eingesetzt werden
RestMengeNeuZuOrdnen(Zahl,Spalte,Zeile); //die eingegebene Zahl wird aus den FeldMengen der Zellen im gleichen Block sowie in der gleichen Spalte oder Zelle gelöscht
anders:=true;
end;
end;
//Spaltenweise
For Zeile:=0 to 8 do
For Spalte:=0 to 8 do
begin
RestMenge:=FeldMenge[Spalte,Zeile];
For UntersuchteZeile:=0 to 8 do
If Spalte<>UntersuchteZeile //damit die FeldMenge der Zelle der RestMenge nicht abgezogen wird, führt immer zu RestMenge=[]
then RestMenge:=RestMenge-FeldMenge[Spalte,UntersuchteZeile];
For Zahl:=1 to 9 do
If RestMenge=[Zahl]
then
begin
StringGrid1.cells[Spalte,Zeile]:=inttostr(Zahl);
FeldMenge[Spalte,Zeile]:=[];
RestMengeNeuZuOrdnen(Zahl,Spalte,Zeile);
anders:=true
end;
end;
If anders then Button1CLick(Form4);
end;
procedure TForm4.RestRaten;
var Spalte,Zeile,Zahl:integer;
ZellInhalt:string;
begin
//Baustelle
end;
procedure TForm4.StringGrid1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var Spalte,Zeile,Zahl,xAdd,yAdd,x,y:integer;
begin
If ((Key=vk_delete) or (Key=vk_Back)) //wenn Backspace oder Entfernen gedrückt wurde
and (StringGrid1.Cells[StringGrid1.col,StringGrid1.row]<>' ')
then
begin
Zahl:=strtoint(StringGrid1.Cells[StringGrid1.col,StringGrid1.row]);
StringGrid1.Cells[StringGrid1.col,StringGrid1.row]:=' '; //dann lösche den Zellinhalt
FeldMenge[StringGrid1.col,StringGrid1.row]:=[1,2,3,4,5,6,7,8,9]; //in dieser Zelle kann erstmal wieder jede Zahl stehen, die tatsächliche FeldMenge wird gleich bestimmt
//der folgende Block bewirkt, dass die gelöschte Zahl zu den FeldMengen der Zellen der
//gleichen Zelle, Spalte, Block hinzugefügt werden
For Zeile:=0 to 8 do
FeldMenge[StringGrid1.col,Zeile]:=FeldMenge[StringGrid1.col,Zeile]+[Zahl];
For Spalte:=0 to 8 do
FeldMenge[Spalte,StringGrid1.row]:=FeldMenge[Spalte,StringGrid1.row]+[Zahl];
Block.x:=StringGrid1.col div 3; //dient der Bestimmung des Blocks, in dem die FeldMengen verändert werden sollen
Block.y:=StringGrid1.row div 3; //x und y gehen von 0..2, der Block 0,0 ist links oben, der Block 2,2 rechts unten
x:=Block.X*3; //dient der Bestimmung der Koordinaten der Zelle links oben in
y:=Block.Y*3; //dem betreffenden Block
For yAdd:=0 to 2 do
For xAdd:=0 to 2 do
FeldMenge[x+xAdd,y+yAdd]:=FeldMenge[x+xAdd,y+yAdd]+[Zahl];
//es ist jedoch auch möglich, dass eine Zahl nicht in einer Zelle stehen darf (=kein Element der FeldMenge)
//weil es diese Zahl bereits in der selben Spalte und der selben Zeile steht
//Würde man die Zahl aus der Zeile löschen, so würde in der FeldMenge der betroffenen
//Zelle die Zahl stehen, obwohl die Zahl in der selben Spalte steht, was nicht regelkonform ist
//daher wird so getan, als ob alle Zahlen neu eingegeben werden und dadurch die betreffenden FeldMengen reduziert werden
For Zeile:=0 to 8 do
For Spalte:=0 to 8 do
If StringGrid1.Cells[Spalte,Zeile]<>' '
then
begin
Zahl:=strtoint(StringGrid1.Cells[Spalte,Zeile]);
RestMengeNeuZuOrdnen(Zahl,Spalte,Zeile);
end;
end;
end;
|