AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Sudoku: Probleme beim Löschen

Ein Thema von Salazriel · begonnen am 1. Mär 2010 · letzter Beitrag vom 2. Mär 2010
Antwort Antwort
Salazriel

Registriert seit: 13. Feb 2010
33 Beiträge
 
#1

Sudoku: Probleme beim Löschen

  Alt 1. Mär 2010, 15: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;
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#2

Re: Sudoku: Probleme beim Löschen

  Alt 1. Mär 2010, 16:07
Wenn man eine Zahl löscht, muss das Array FeldMenge komplett neu berechnet werden.
  Mit Zitat antworten Zitat
Salazriel

Registriert seit: 13. Feb 2010
33 Beiträge
 
#3

Re: Sudoku: Probleme beim Löschen

  Alt 2. Mär 2010, 09:38
Ehrlich gesagt war das auch mein erster Ansatz

Delphi-Quellcode:
procedure TForm4.StringGrid1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var Spalte,Zeile,Zahl,xAdd,yAdd,x,y:integer;
    Sudoku:string;
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
  StringGrid1.Cells[StringGrid1.col,StringGrid1.row]:=' '; //dann lösche den Zellinhalt
  For Zeile:=0 to 8 do
    For Spalte:=0 to 8 do
      FeldMenge[Spalte,Zeile]:=[1,2,3,4,5,6,7,8,9]; //FeldMengen neu füllen
  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); //FeldMengen reduzieren
        end;
  end;
end;
Das Problem: es funktioniert genauso nicht -.-

Aber ich habe imzwischen eine Lösung des Problems gefunden: Zum Speichern einer Sudoku-Datei wandle ich das Sudoku in einen String um und zum Laden wandle ich den String in das Sudoku um

Delphi-Quellcode:
function TForm4.SudokuInListeSpeichern:String;
var Spalte,Zeile:integer;
    Datei:String;
begin
For Zeile:=0 to 8 do
  For Spalte:=0 to 8 do
    Datei:=Datei+StringGrid1.cells[Spalte,Zeile];
Result:=Datei;
end;

procedure TForm4.SudokuAusListeLaden(Datei:string);
var Spalte,Zeile,i:integer;
begin
FormCreate(Form4);
For i:=1 to 81 do //die gespeicherte Datei besteht aus 81 Zeichen
  begin
  Spalte:=(i-1) mod 9;
  Zeile:=(i-1) div 9; // i-1 da Zellen/Spalten von 0..8
  StringGrid1.Cells[Spalte,Zeile]:=Datei[i];
  If Datei[i] in ['1'..'9']
  then
    begin
    FeldMenge[Spalte,Zeile]:=[];
    RestMengeNeuZuordnen(strtoint(Datei[i]),Spalte,Zeile);
    end;
  end;
end;
Und das funktioniert seltsamerweise, obwohl der Unterschied eigentlich nur im Löschen und Neuschreiben aller Zahlen liegt

Aber gestern Nacht kam mir ein wahrer Geistesblitz, als ich mit was völlig anderem beschäftigt war

Delphi-Quellcode:
procedure TForm4.StringGrid1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var Spalte,Zeile,Zahl,xAdd,yAdd,x,y:integer;
    Sudoku:string;
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
  Sudoku:=SudokuInListeSpeichern;
  Sudoku[StringGrid1.Row*9+StringGrid1.Col+1]:=' ';
  SudokuAusListeLaden(Sudoku);
end;
Das ist wahrscheinlich nicht die schnellste Methode, aber es funktioniert schonmal
Obwohl ich mich wirklich frage, warum die ersten 2 Ansätze fehlerhaft sind...
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#4

Re: Sudoku: Probleme beim Löschen

  Alt 2. Mär 2010, 11:11
Eine Ursache für dein Problem ist, das Oberfläche und Datenhaltung/verarbeitung nicht getrennt sind.
Wenn du im KeyUp auf den Inhalt der Zelle zugreifst, hat der sich noch lange nicht geändert.
Das Grid zeigt zum Bearbeiten ein Editfeld an, dessen Inhalt erst nach OnSetEditValue in die Zelle übernommen wird.
Deshalb wurde RestMengeNeuZuOrdnen immer noch der alte Wert übergeben.
  Mit Zitat antworten Zitat
Salazriel

Registriert seit: 13. Feb 2010
33 Beiträge
 
#5

Re: Sudoku: Probleme beim Löschen

  Alt 2. Mär 2010, 18:58
Aber ich schreibe doch ganz anders in die Zellen rein, mittels KeyPress.
Dieses SetEditText wird nach der OH auch nur ausgelöst, wenn bei Options GoEditing=true ist, was bei mir nicht ist.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:17 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz