Swing Sudoku: Laden / Speichern von Zahlen aus/in mehrere JTextFields aus/in eine(r) Textdatei

Diskutiere Sudoku: Laden / Speichern von Zahlen aus/in mehrere JTextFields aus/in eine(r) Textdatei im AWT, Swing, JavaFX & SWT Bereich.
B

balakov52844

Hallo zusammen,

ich befasse mich innerhalb meines Studiums gerade mit der Entwicklung eines Sudoku-Spiels mit einer einfachen GUI.
Die GUI beinhaltet ein JPanel, auf dem durch ein GridLayout 9 x 9 JTextFields angeordnet sind.

Durch zwei Button (innerhalb einer eigenen Klasse) soll die Anwendung in der Lage sein, aus den JTextFields die Zahlen in eine Textdatei zu schreiben oder umgekehrt, aus einer Textdatei Zahlen in die JTextFields laden.

Das Format soll dabei wie folgt aussehen:
Leere Felder werden dabei als "." repräsentiert.

1 . 3 . 5 6 7 8 9
1 2 3 . 5 5 . 8 9
1 2 . . . 6 . 8 9
1 2 . 4 . 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 . 4 . 6 . 8 9
1 2 3 9 5 . . . .
1 . . 4 5 6 7 8 9
1 2 3 4 5 6 . 8 9

Laden:
Ich schaffe es bisher soweit, dass die Zahlen aus der Datei durch die Methode load(Board board)in ein 2D Array geschrieben werden. Dieses kann ich auch in der Konsole ausgeben lassen.
Das Problem: Ich weiß nicht, wie ich das 2D Array in die JTextFields auf meinem Board schieben kann. Diese bleiben natürlich leer. Ich dachte, ich kann einfach Board.sudokuBoard[][].setText(strArr[i][j] + ""); benutzen aber das funktioniert nicht.

Speichern:
Beim Speichern kann ich lediglich ein vorher manuell erstelltes 2D Array in eine Textdatei speichern. Jedoch fehlt mir hier ebenfalls die Verbindung zu meinen JTextFields auf meinem Board. Was ich dort eintrage, kann ich nicht speichern.

Ist hier eine Lösung möglich? Die Beibehaltung der Klassen und Methodenbezeichnungen ist dabei verpflichtend. Ich kann also nicht mehrere Klassen zusammenführen.

Vielen Dank!!

Hauptklasse:
Java:
public final class Sudoku extends JFrame {
    
    public Sudoku() {
 
        setTitle("Sudoku");
        getContentPane().setBackground(Color.WHITE);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        setLayout(new BorderLayout());
        
        JPanel buttonPanel = new ButtonPanel(null);
        this.add(buttonPanel, BorderLayout.SOUTH);
        
        JPanel board = new Board();
        this.getContentPane().add(board, BorderLayout.CENTER);
        

        pack();
    }

 
    public static void main(String[] args) {
        new Sudoku().setVisible(true);
    }
}

Klasse, in der mein Sudokuboard erstellt wird:
Java:
public final class Board extends JPanel {
    
    private JPanel sudokuPane = new JPanel();
    
    private SuTextField sudokuBoard [][] = new SuTextField [9][9];


    public Board() {
 
        setPreferredSize(new Dimension(400, 380));
        
        sudokuPane = new JPanel(new GridLayout(9, 9));
        for(int x = 0; x < 9; x++) {
            for(int y = 0; y < 9; y++) {

                sudokuBoard[x][y] = new SuTextField();
                sudokuPane.add(sudokuBoard[x][y]);
            }
        }
      
        add(sudokuPane);
        setVisible(true);
        setBorder(null);   
    }

Klasse, in der das Speichern und Laden geregelt werden (sollen):
Java:
public class InAndOut {
    
    private SuTextField sudokuBoard [][] = new SuTextField [9][9];
    private Board boardObj = new Board ();
    
    JTextField textField = new JTextField();
    
    
    
    public void load(Board board) throws IOException {
        
        Scanner scanner = new Scanner(new FileReader(new File("easy-sudoku")));
        
        int rows = 9;
        int cols = 9;
        
        String [][] strArr = new String[rows][cols];
        
        for(int i = 0; i < rows ; i++){
            for(int j = 0; j < cols ; j++){
                strArr[i][j] = scanner.next();
            }
        }
        
        scanner.close();
        
        //Ausgabe in der Konsole zum Test
        
        for(int i = 0; i < 9 ; i++){
            for(int j = 0; j < 9 ; j++){
                System.out.print(strArr[i][j] + " ");
            
            }
            
            System.out.println();
        }



    public void save(Board board) throws IOException {

    // Erstellen eines Testfeldes, zur weiteren Überprüfung
    private int [][] testSudoku =
            
        {{0, 3, 4, 6, 7, 8, 9, 1, 2},
         {6, 7, 2, 1, 9, 5, 3, 4, 8},
         {1, 9, 8, 3, 4, 2, 5, 6, 7},
         {8, 5, 9, 7, 6, 1, 4, 2, 3},
         {4, 2, 6, 8, 5, 3, 7, 9, 1},
         {7, 1, 3, 9, 2, 4, 8, 5, 6},
         {9, 6, 1, 5, 3, 7, 2, 8, 4},
         {2, 8, 7, 4, 1, 9, 6, 3, 5},
         {3, 4, 5, 2, 8, 6, 1, 7, 9}};

        try {
            FileWriter writer = new FileWriter("D:/MySudoku.txt", true);

            for (int x = 0; x < 9 ; x++) {
                for (int y = 0; y < 9;  y++) {

                      writer.write(Integer.toString(testSudoku[x][y]));

                }

                writer.write("\r\n");
            }
            writer.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    
    }

In einer weiteren Klasse habe ich den ActionListener dazu implementiert:

Java:
buttonLoad.addActionListener(new ActionListener() {
            public void actionPerformed (ActionEvent h) {
                try {
                    inAndOutObj.load(boardObj);
                
                } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                }
                }
        });


buttonSave.addActionListener(new ActionListener() {
            public void actionPerformed (ActionEvent h) {
            try {
                inAndOutObj.save(boardObj);

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            }
        });
 
J

JustNobody

Also vom Prinzip her hast du es schon fast richtig probiert:
board.sudokuBoard[i][j].setText(strArr[i][j])

Also du musst natürlich die koordinate auch mit angeben.

Aber da sind noch ein paar andere Kleinigkeiten:
Board war deine Klasse. Die Variable ist natürlich nicht static, somit muss es eine Instanz sein (daher nur board).

Ansonsten ist die Variable private, also nicht von außen zugreifbar. Daher wäre es besser, Methoden zu verwenden. Also ein getTextFieldText(row, column) und setTextFieldText(row, column, text).

Dann kannst du beim Laden direkt das setTextField und beim Speichern getTextField aufrufen. Dann würdest du gar kein 2d Array mehr nutzen für die zwischenspwicherung.

Aber das ist erst einmal nur die einfache Variante. Daten und UI sind dabei nicht getrennt. Das Board ist bei dir auch die Oberfläche.
Das könnte man noch sauber trennen. Das ist aber bei Swing nicht so schön ohne Bindings. Die könnte man einfach manuell schreiben. Aber wenn du das haben möchtest, dann würde ich dir das morgen am Computer skizzieren - jetzt auf dem Handy ist das zu viel.
 
B

balakov52844

Hey JustNobody,

vielen Dank für die Rückmeldung! Ich probiere es morgen gleich aus versuche mich an deine Tipps zu halten!
Was du im unteren Abschnitt schreibst, verstehe ich ehrlich gesagt nur so halb, daher will ich dir da nicht noch mehr Zeit klauen, wenn es erst einmal zweitrangig ist.
Ich freue mich erst einmal über den zusätzlichen Ansatzpunkt!
 
B

balakov52844

Guten Morgen,

ich habe nun erst einmal versucht mittels board.sudokuBoard[i][j].setText(strArr[i][j] + " "); die Daten aus dem Array in die JTextFields zu packen.
Ich bekomme hierbei allerdings den Hinweis "The board Board.sudokuBoard is not visible". Also habe ich es wie folgt versucht:

board.getSudokuBoard()[x][y].setText(strArr[x][y] + " ");

Dafür habe ich die folgenden Getter / Setter Methoden in der Klasse Board geschrieben geschrieben:
Java:
    public SuTextField[][] getSudokuBoard() {
        return sudokuBoard;
    }


    public void setSudokuBoard(SuTextField sudokuBoard[][]) {
        this.sudokuBoard = sudokuBoard;
    }
Der Code kompiliert zwar, allerdings ist auf meinem Board in der GUI nichts zu sehen. Es werden keine Werte in den JTextFields angezeigt.
Was übersehe ich hier?

Ich werde es als nächstes so versuchen, dass ich wie angemerkt, getTextField() und setTextField() nutze. Vielleicht bringt mich das ja weiter!
 
B

balakov52844

Hallo,
ich habe nun die beiden Methoden in der Klasse Board erstellt. Laut Institut müssen beide die Parameter int nutzen.
Jetzt bin ich aber überfragt, wie ich weitermachen soll. Ich bräuchte doch den Rückgabewert String für meine JTextFields?
Das lässt mich vermuten, dass ich bei der Initialisierung meines sudokuBoards in der Klasse Board etwas grundlegend falsch gemacht habe?

Hat eventuell jemand einen Hinweis für mich?

Danke!!

Hier die Methoden aus der Klasse Board
(Parameter int ist für beide Methoden vorgegeben und somit nicht zu ändern)
Java:
    public int getCellValue(int x, int y) {
        
        return sudokuBoard[x][y];
  
    }

    public void setCellValue(int x, int y, int v) {
        
          if (0 > v && v > 9) {
                throw new IllegalArgumentException("Der eingetragene Wert " + v + " entspricht nicht den Spielregeln!");
                
          } else {
              sudokuBoard[x][y] = v;
          }
    }
 
J

JustNobody

Du hast ja in erster Linie nur die Zahlen 1-9. Problematisch ist nur der ".", der ja keine Zahl ist.

Schau noch einmal die Vorgaben an, was da ggf. als Wert vorgegeben ist. Du kannst natürlich für den "." einen Wert wie 0 oder -1 nehmen.

Und schon hättest Du statt Strings Zahlen.

Beim speichern / laden musst du dann schauen, wie Du vorgehen sollst. Wenn das Format vorgegeben ist mit dem ".", dann musst du halt vor dem schreiben den Wert prüfen.
Schreiben: Ist das zu schreibende Wert der Wert für das leere Feld, dann schreib den ., sonst schreib die Zahl.
Lesen: Ist der gelesene Wert "." dann nimm den Wert für das leere Feld, sonst parse die Zahl des gelesenen Wertes.
 
B

balakov52844

Okay - in der Theorie hört sich das logisch an!

Also...

Speichern: mein Format soll in der Textdatei 9x9 Zeichen haben, dabei sind zwischen den einzelnen Zeichen Leerzeichen. Ein leeres Feld soll dabei durch "." repräsentiert werden.

Laden: Aus der Textdatei sollen "." dann nicht in die JTextFields geschrieben werden, bzw diese Felder sollen frei bleiben (oder ein Leerzeichen enthalten.

Ich bekomme das aber leider nicht so implementiert, vielleicht stehe ich heute auch nur ein wenig auf dem Schlauch.

Ich habe es jetzt für load() so probiert:

Java:
        Scanner scanner = new Scanner(new FileReader(new File("D:/sample.txt")));
    
        int rows = 9;
        int cols = 9;
    
        String empty = " ";
        String point = ".";
        int z = Integer.parseInt(point);

    
        String[][] strArr = new String[rows][cols];

        for(int x = 0; x < rows ; x++){
            for(int y = 0; y < cols ; y++){
                if (strArr[x][y] == point) {
                   do something with z oder empty
                } else if {
                strArr[x][y] = scanner.next(); }

            }
        }
Natürlich funktioniert das so noch nicht, aber ich komme hier an der Stelle einfach nicht weiter... Macht meine if-Klausel überhaupt Sinn?

Und: Wie setze ich das ganze nun um, dass ich auch die Methoden getCellValue() und setCellValue() nutzen kann?
 
J

JustNobody

Da sind ein paar Punkte, die so nicht Sinn machen.
Was mir so auf Anhieb auffällt:
a) int z = Integer.parseInt(point); point ist "." und das ist natürlich keine Zahl. parseInt wird somit eine Exception werfen.
b) Strings vergleicht man mit equals und nicht mit ==.

Was ich an Code vorgeschlagen hätte, wäre:
Code:
// In der Klasse Konstanten definieren:
public static final String EMPTY_FIELD_SERIALIZATION = ".";
public static final int EMPTY_FIELD_VALUE = 0; // oder -1 oder was auch immer der int Wert eines leeren Feldes sein soll...
public static final int ROW_COUNT = 9;
public static final int COLUMN_COUNT = 9;

public void load(final String filename) {
  Scanner scanner = new Scanner(new FileReader(new File(filename)));
  for(int row = 0; row < ROW_COUNT; row++){
    for(int column = 0; column < COLUMN_COUNT; column++){
      String value = scanner.next(); // Lesen des nächsten Symbols.
      if (value.equals(EMPTY_FIELD_SERIALIZATION)) {  // Ist es ein leeres Feld?
        setCellValue(row, column, EMPTY_FIELD_VALUE);  // Dann setze die Zelle auf den Wert vom leeren feld.
      } else {
        setCellValue(row, column, Integer.parseInt(value)); // Sonst setze die Zelle auf die Zahl aus dem gelesenen Wert,
      }
    }
  }
}
Das wäre so eine Idee, die ich jetzt einmal im Editor des Forums zusammen geschrieben habe. Also ggf. stimmen die { } nicht ganz oder ich habe irgendwo Tippfehler. Aber das wäre so ein einfacher Ansatz. Und ggf. muss man Exceptions behandeln (IOException?). Und Fehlerbehandlung muss man auch überlegen. NumberFormatException bei ungültigen Werten aber ggf. auch mal schauen, wie Scanner reagiert, wenn zu wenig Zeichen in der Datei stehen. Sowas kann man dann ggf. abfangen und behandeln (z.B. eine Fehlermeldung ausgeben als Minimum.)

Die Idee ist halt: Wir haben ein Zeichen, das keine Zahl ist. Wenn wir dieses haben, dann nehmen wir statt dessen einen definierten Wert. Ansonsten nehmen wir einfach den gelesenen Wert und parsen ihn.
 
B

balakov52844

Vielen Dank!! Das ist mir wirklich eine große Hilfe!!

Mein Problem ist jetzt nur, dass wenn load() aufgerufen wird, die Felder vom Sudoku nicht neu gefüllt werden.
Nach der Methode ist durch setCellValue(int row, int column, int value) mein sudokuBoard[x][y] mit den Zahlen gefüllt.
Jetzt muss ich es im letzten Schritt noch schaffen, dass das neu gefüllte sudokuBoard[][] die Zahlen an meine textFields[][] übergibt und sich das board aktualisiert.

Ich hätte es am ehesten mit textField[x][y].setText(sudokuBoard[x][x] + ""); versucht aber leider hat das keine Wirkung. Auch ein repaint() oder revalidate() zeigen keinen Erfolg.

Ich habe probeweise schon ein ganz neues JFrame öffnen lassen aber auch da wird nichts angezeigt.
Ich weiß, dass die Lösung wahrscheinlich sehr naheliegend ist, aber ich bin wirklich kurz vorm Verzweifeln bei dieser Sache.

Hier nocheinmal die dazugehörige Klasse, die das Sudoku-Feld bereitstellt:
Java:
public final class Board extends JPanel {
    
    public static final int SIZE = 9;

    private JPanel sudokuPane = new JPanel();

    private SuTextField textField [][] = new SuTextField [SIZE][SIZE];
        
    public int [][] sudokuBoard;

     

public Board() {

        sudokuBoard = new int [SIZE][SIZE];
       
        setPreferredSize(new Dimension(400, 380));
        
        sudokuPane = new JPanel(new GridLayout(SIZE, SIZE));

        for(int x = 0; x < SIZE; x++) {
            for(int y = 0; y < SIZE; y++) {

                textField[x][y] = new SuTextField();
                sudokuPane.add(textField[x][y]);
                
                textField[x][y].setText(sudokuBoard[x][x] + "");
                
            }
        }
      
        add(sudokuPane);
        setVisible(true);
        setBorder(null);   

    } 

    public void setCellValue(int x, int y, int v) {
        
          if (0 > v && v > 9) {
                throw new IllegalArgumentException("Der eingetragene Wert " + v + " entspricht nicht den Spielregeln!");
                
          } else {
             sudokuBoard[x][y] = v;
    
          }
    }
 
B

balakov52844

Da ich beim Aufrufen von load() nicht weiterkomme, habe ich angefangen save() zu betrachten.
Hier hängt es aber ebenfalls, da auch hier keine Werte aus den JTextFields erkannt werden. Als Ausgabe erhalte ich nur 81x das Zeichen "." in meiner Textdatei, obwohl die JTextFields gefüllt sind.

Ist der Fehler, dass ich eventuell nicht die Methode getCellValue() in meiner Klasse Board nutzen sollte, sondern eher getTextFieldValue() in der Klasse SuTextField? Immerhin werden hier die JTextFields bereitgestellt, die dann in der Klasse Board hinzugefügt werden. Allerdings klappt es so auch nicht - ich stehe weiterhin vor einem Rätsel...

Hier die Methode save(Board board) in der Klasse InAndOut:
Java:
    private SuTextField suObj = new SuTextField();
    
    public static final int ROW_LENGTH = 9;
    public static final int COLUMN_LENGTH = 9;
    public static final String POINT = ".";
    public static final int EMPTY = 0;

public void save(Board board) throws IOException {
        
        try {
            
            FileWriter writer = new FileWriter("D:/MySudoku.txt", true);
            
            for (int x = 0; x < ROW_LENGTH; x++) {
                for (int y = 0; y < COLUMN_LENGTH; y++)
                    
                    if (SuObj.getTextFieldValue() == EMPTY) {
                        
                        writer.write(POINT + " ");
                        
                    } else if (SuObj.geTextFieldValue() != EMPTY) {
                        
                        writer.write(Integer.toString(board[x][y] + " ");

                    }

                }

            writer.write("\r\n");   // write new line
            
            writer.close();
            
        } catch (IOException e) {
            
            e.printStackTrace();
        }
    }

Hier die Methode getTextFieldValue() in der Klasse SuTextField:
Java:
    public int getTextFieldValue() {
        
        int v;
        
        if (1 <= v && v >= 9) {
            
            return v;
            
        } else {
            
            return 0;
        }
    
    }
Vielleicht hat jemand noch einen Tipp für mich!
 
Thema: 

Sudoku: Laden / Speichern von Zahlen aus/in mehrere JTextFields aus/in eine(r) Textdatei

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben