Jogo Sudoku Interface Gráfica

Boa tarde,
li vários tópicos mas não consegui achar uma resposta, então resolvi criar um.

Eu desenvolvi um jogo de Sudoku e agora queria fazer a interface gráfica dele.
Eu até consigo criar um “tabuleiro” com 9x9.
O que eu queria saber é como mexer nas bordas, para parecer que são 9 “quadrados” 3x3 e como eu faria para pegar do teclado o número para escrever.
Eu fiz com JButton, tem como eu pedir pro usuário, por exemplo, clicar em uma “casa” vazia e digitar um número no teclado?
Li algumas coisas sobre KeyListener (keyPressed), mas não entendi muito bem.

Se já existir algum tópico igual, peço desculpas, mas juro que procurei e não achei.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Objects;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;

public class Tela extends JFrame {

    static final Border DIVISAO = BorderFactory.createLineBorder(Color.BLACK);
    Botao selecionado;

    public Tela() throws HeadlessException {
        super();
        init();
    }

    private void init() {
        JPanel root = new JPanel(new GridLayout(2, 2));

        for (int i = 0; i < 4; i++) {
            JPanel divisao = new JPanel(new GridLayout(2, 2));
            divisao.setBorder(DIVISAO);
            for (int j = 0; j < 4; j++) {
                divisao.add(new Botao());
            }
            root.add(divisao);
        }

        getContentPane().add(root);

        initKeyListener();

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
    }

    private void initKeyListener() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if (selecionado != null) {
                    selecionado.setText(Objects.toString(e.getKeyChar()));
                }
            }
        });
    }

    static final Border BORDA_NORMAL = BorderFactory.createLineBorder(Color.GRAY);
    static final Border BORDA_SELECIONADO = BorderFactory.createLineBorder(Color.RED);
    static final Color COR_NORMAL = Color.WHITE;
    static final Color COR_SELECIONADO = Color.YELLOW;

    class Botao extends JLabel {

        public Botao() {
            super();
            init();
        }

        private void init() {
            setBackground(COR_NORMAL);
            setBorder(BORDA_NORMAL);
            setPreferredSize(new Dimension(64, 64));
            setOpaque(true);
            Botao esteBotao = this;
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    selecionaBotao(esteBotao);
                }
            });
        }
    }

    void selecionaBotao(Botao botao) {
        // se tem selecionado
        if (selecionado != null) {
            selecionado.setBackground(COR_NORMAL);
            selecionado.setBorder(BORDA_NORMAL);
        }
        // seleciona o novo se não for nulo
        if (botao != null) {
            botao.setBackground(COR_SELECIONADO);
            botao.setBorder(BORDA_SELECIONADO);
        }
        selecionado = botao;
    }

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

Obrigado diego12, vou dar uma olhada com calma no código e qualquer coisa posto aqui de novo.

Muito obrigado mesmo diego12!
Com o código que você postou estou conseguindo o que eu queria!
Vou ver se lembro de postar o código aqui quando terminar!

Tomara que lembre

Juro que pesquisei e tentei até escrever um método novo, mas não consegui.
Tem algum método que me devolva a linha e a coluna do botão selecionado?

Enquanto espero uma resposta, continuo procurando…

tenta usar uma JTable, sei que você pode editar os valores dentro de cada campo e pegar eles
talvez não tenha sigo muito esclarecedor pq ainda sou iniciante em JAVA, mas a ideia é por ai :sweat_smile: :sweat_smile::sweat_smile:

class Botao {
  int linha, coluna; // adicione os atributos
}

Botao[9][9] botões;
JPanel[3][3] blocos;

JPanel tabuleiro = new JPanel(new GridLayout(3,3));
for (int l = 0; l < 3; l++) {
  for (int c = 0; c < 3; c++) {
    blocos[l][c] = new JPanel(new GridLayout(3,3));
    tabuleiro.add(blocos[l][c]);
  }
}

for (int l = 0; l < 9; l++) {
  for (int c = 0; c < 9; c++) {
    botão[l][c] = new Botao(l, c); // passe pelo construtor, a linha e a coluna que o botão estará
    blocos[l / 3][c / 3].add(botao);
  }
}
// obtem linha/coluna
selecionado.linha;
selecionado.coluna;

diego12, mais uma vez, obrigado pela ajuda.
Eu tinha feito uma gambiarra para funcionar.
Fiz 3 arrays (linhas, colunas e grades) e inseria o “name” de cada botão na mão (rsrs) e funcionou.

Peguei o seu código agora e entendi quase tudo.

No segundo “for”:
blocos[l / 3][c / 3].add(botao); //não seria .add(botoes[l][c])?

Como não entendi muito bem, adicionei as variáveis “linha” e “coluna” na classe botão e criei um “set” pra cada uma, então, na hora de criar os botões lá no seu método “init()”, eu já passo a linha e a coluna junto com outros argumentos, como nome, texto e etc…

Obrigado, mais uma vez, pela ajuda.

Quase terminando…

Desse jeito que eu fiz não funciona direito, ele embaralha as linhas e colunas…

Aquilo que postei é um esboço, portanto não vai funcionar. Tente entender a ideia.

Verifique se todos as peças estão n lugar, se forma 9x9, insira o seguinte para testar os índices :

botoes[linha][coluna].setText("" + botoes[linha][coluna].getLinha() + "," + botoes[linha][coluna].getColuna());

Não sei onde vc quer pegar a linha/coluna, mas seria só usar o getLinha / getColuna no botão que vc deseja.

Não sei como vc está fazendo, difícil dizer como fazer sem gambiarra, mas faria + ou -:

// representa o dados
class Tabuleiro {

    int[][] dados;

    public Tabuleiro() {
        dados = new int[9][9];
    }

    public void setNumero(int linha, int coluna, int valor) {
        dados[linha][coluna] = valor;
    }

    public int getNumero(int linha, int coluna) {
        return dados[linha][coluna];
    }
}

// objeto da view
class Botao extends JLabel {

    int linha, coluna;
    Tabuleiro tabuleiro;

    public Botao(Tabuleiro tabuleiro, int l, int c) {
        super();
        init();
        linha = l;
        coluna = c;
        this.tabuleiro = tabuleiro;
        super.setText("" + this.tabuleiro.getNumero(linha, coluna)); // chamaria o da super class
    }

    // sobrescreve para enviar para o tabuleiro
    @Override
    public void setText(String text) {
        super.setText(text);
        int valor = Integer.parseInt(text);
        tabuleiro.setNumero(linha, coluna, valor);
    }
    
    public void setNumero(int n) {
        this.setText(Integer.toString(n));
    }

    private void init() {
        setBackground(COR_NORMAL);
        setBorder(BORDA_NORMAL);
        setPreferredSize(new Dimension(64, 64));
        setOpaque(true);
        Botao esteBotao = this;
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                selecionaBotao(esteBotao);
            }
        });
    }

    public int getLinha() {
        return linha;
    }

    public void setLinha(int linha) {
        this.linha = linha;
    }

    public int getColuna() {
        return coluna;
    }

    public void setColuna(int coluna) {
        this.coluna = coluna;
    }
}

Fala ai diego12,
Eu sei que o que você está postando são só esboços, estou pegando os pedaços de códigos e adaptando no meu jogo.

Agradeço muito pela ajuda que está me dando.

O meu único problema ainda está sendo a organização das linhas e colunas.
Eu consigo pegar as linhas / colunas, o problema é que fica errado.
Fiz o tabuleiro como você deu o exemplo no primeiro post.

	JPanel tabuleiro = new JPanel(new GridLayout(9, 9));
	JPanel grade = new JPanel();

	setSize(500, 500);
	setLocationRelativeTo(null);

	for(i = 0; i < 9; i++) {

		grade.setBorder(BorderFactory.createLineBorder(Color.BLACK));

		for (j = 0; j < 9; j++) {

			botoes[i][j] = new Botao();
			botoes[i][j].setBackground(Color.white);
			botoes[i][j].setForeground(Color.black);

			grade.add(botoes[i][j]);
		}

		tabuleiro.add(grade);
	}

O problema é que assim, o “i” fica sendo a grade ao invés de ser a linha como seria normalmente.

Então, ao invés de imprimir

00 01 02     03 04 05     06 07 08
10 11 12     13 14 15     16 17 18
20 21 22     23 24 25     26 27 28

o código imprime

00 01 02     10 11 12     20 21 22
03 04 05     13 14 15     23 24 25
06 07 08     16 17 18     26 27 28

e assim em diante.
Então, se eu pedir pra ele pegar a linha 0 / coluna 3, no primeiro caso, ele retornaria o 4º número da 1ª fileira, já no segundo caso, ele me retorna o 1º número da segunda fileira.

Acho que consegui explicar direito.
Caso não consiga entender eu tento explicar melhor.

for (j = 0; j < 9; j++) {

	botoes[i][j] = new Botao();
	botoes[i][j].setLinha(i);
	botoes[i][j].setColuna(j);
	botoes[i][j].setBackground(Color.white);
	botoes[i][j].setForeground(Color.black);
	grade.add(botoes[i][j]);
}

Adicione o setLinha/setColuna

Fiz sem os JPanel, usei o setBackground pra botar uma cor diferente em cada grade (com if’s).
Muito obrigado pela sua ajuda diego.

Uma outra dúvida, questão de embelezamento, já que terminei o código.
Eu quero marcar a linha e a coluna do botão clicado pelo usuário.
Eu até consegui fazer, mas não sei como voltar a cor que estava antes, depois que ele receber algum valor ou depois que o usuário clicar em outro lugar. Ele fica com a cor que eu botei depois do clique.
Vi algumas opções, como passar ‘null’ pro setBackground, mas como estou com cores de Background diferentes, não dá pra fazer assim.

Procurei e não achei.

Mais uma vez, muito obrigado pela sua ajuda!

Não sei se funciona, mas se colocar setOcpaque(false), o background não deve aparecer

É uma ideia, mas não sei se faria exatamente o que eu quero.
Mas era só uma coisa pra embelezar o programa.

Muito obrigado por toda a ajuda Diego.

Ainda faltam algumas pequenas coisas.
Quando eu realmente terminar, posto o código aqui.

import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.awt.;
import java.awt.event.
;
import java.util.Objects;
import javax.swing.*;
import java.awt.HeadlessException;
import javax.swing.border.Border;

public class Sudoku2D extends JFrame implements ActionListener {

Container container;
JPanel painel;
ActionEvent evento;
JMenuBar menu;
JMenu jogo, ajuda;
JMenuItem facil, medio, dificil, expert, resolver, limpar, sair, sobre;
Botao selecionado;
Botao bt;
boolean joga = true;
int preenchidos = 0;

static final String vazio = " ";

Random r = new Random();

Botao [][] botoes = new Botao [9][9];

String [][] array = new String [9][9];

ArrayList <String> jogos = new ArrayList <>();

public Sudoku2D() {

	super("SUDOKU");

	setLayout(new GridLayout(9, 9));

	menu = new JMenuBar();

	jogo = new JMenu("Jogo");

	facil = new JMenuItem("Fácil");
	facil.addActionListener(this);
	jogo.add(facil);

	jogo.addSeparator();

	medio = new JMenuItem("Médio");
	medio.addActionListener(this);
	jogo.add(medio);

	jogo.addSeparator();

	dificil = new JMenuItem("Difícil");
	dificil.addActionListener(this);
	jogo.add(dificil);

	jogo.addSeparator();

	expert = new JMenuItem("Expert");
	expert.addActionListener(this);
	jogo.add(expert);

	jogo.addSeparator();

	resolver = new JMenuItem("Resolve Sudoku");
	resolver.addActionListener(this);
	jogo.add(resolver);

	jogo.addSeparator();

	limpar = new JMenuItem("Limpar");
	limpar.addActionListener(this);
	jogo.add(limpar);

	jogo.addSeparator();

	sair = new JMenuItem("Sair");
	sair.addActionListener(this);
	jogo.add(sair);

	ajuda = new JMenu("Ajuda");

	sobre = new JMenuItem("Sobre");
	sobre.addActionListener(this);
	ajuda.add(sobre);

	menu.add(jogo);
	menu.add(ajuda);

	setJMenuBar(menu);

	setVisible(true);
	pack();
}

public void actionPerformed(ActionEvent evento) {

	this.evento = evento;

	if(evento.getSource() == facil) {

		pegaJogo("facil.txt");
		criaTabuleiro();
	}

	if(evento.getSource() == medio) {

		pegaJogo("medio.txt");
		criaTabuleiro();
	}

	if(evento.getSource() == dificil) {

		pegaJogo("dificil.txt");
		criaTabuleiro();
	}

	if(evento.getSource() == expert) {

		pegaJogo("expert.txt");
		criaTabuleiro();
	}

	if(evento.getSource() == resolver) {

		resolveBT();
	}

	if(evento.getSource() == limpar) {

		JOptionPane.showMessageDialog(null, "Opção indisponível...");
	//	limpa();
	}

	else if(evento.getSource() == sair) {

		System.exit(0);
	}

	else if(evento.getSource() == sobre) {

		JOptionPane.showMessageDialog(
			null, "Autor: Ighor Santiago\n"
			+ "\nSudoku, é um jogo baseado na colocação lógica de"
			+ "\nnúmeros. O objetivo do jogo é a colocação de números de"
			+ "\n1 a 9 em cada uma das células vazias numa grade de 9x9,"
			+ "\nconstituída por 3x3 subgrades chamadas regiões."
			+ "\nO quebra-cabeça contém algumas pistas iniciais, que são"
			+ "\nnúmeros inseridos em algumas células, de maneira a permitir" 
			+ "\numa indução ou dedução dos números em células que estejam"
			+ "\nvazias."
			+ "\nCada linha, coluna e região só pode ter um número de cada"
			+ "\num dos 1 a 9. Resolver o problema requer apenas raciocínio"
			+ "\nlógico e algum tempo.");
	}
}

public String [][] pegaJogo(String arquivo) {

	try {

		FileReader arq = new FileReader(arquivo);
		BufferedReader lerArq = new BufferedReader(arq);

		String linha = lerArq.readLine(); // lê a primeira linha

		while (linha != null) {

			jogos.add(linha);
			linha = lerArq.readLine();
		}

		arq.close();
	} catch (IOException e) {

		System.err.printf("Erro na abertura do arquivo");
	}

	int posicao = r.nextInt(jogos.size());

	String jogo = jogos.get(posicao);

	for(int i = 0; i < 9; i++) {

		for(int j = 0; j < 9; j++) {

			int index = ((i * 9) + j);

			String aux = String.valueOf(jogo.substring(index, (index + 1)));

			if(aux.equals("0")) {

				array[i][j] = vazio;
			}
			else {
				array[i][j] = aux;
				preenchidos++;
			}
		}
	}

	return array;
}

String[] imutaveis = new String[preenchidos];

private void criaTabuleiro() {

	int i, j;

	setSize(400, 400);
	setLocationRelativeTo(null);

	for(i = 0; i < 9; i++) {

		for (j = 0; j < 9; j++) {

			botoes[i][j] = new Botao();

			if(i < 3 || i > 5) {

				if(j < 3 || j > 5) {

					botoes[i][j].setBackground(Color.lightGray);
				}
			}

			else if(i > 2 && i < 6) {

				if(j > 2 && j < 6) {

					botoes[i][j].setBackground(Color.lightGray);
				}
			}
			else
				botoes[i][j].setBackground(Color.white);

			add(botoes[i][j]);
		}
	}

	preencheTabuleiro();
	initKeyListener();
	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public void preencheTabuleiro() {

	int x = 0, y = 0;

	//preenche tabuleiro
	for(int i = 0; i < 9; i++) {

		for (int j = 0; j < 9; j++) {

			botoes[i][j].setText(array[i][j]);
			botoes[i][j].setName("bt_" + x);
			botoes[i][j].setToolTipText("bt_" + x);
			botoes[i][j].setLinha(i);
			botoes[i][j].setColuna(j);
			botoes[i][j].setForeground(Color.blue);

			//pega os números que começam no tabuleiro
			if(!botoes[i][j].getText().equals(vazio)) {

				botoes[i][j].setEditavel(false);
				botoes[i][j].setForeground(Color.black);
			}

			x++;
		}
	}
}

public boolean testaNumero(KeyEvent e) {

	int lin = selecionado.getLinha();
	int col = selecionado.getColuna();
	String numero = Objects.toString(e.getKeyChar());

	joga = true;

	if(joga) {

		//checa linha
		for (int i = 0; i < 9; i++) {  

			if(array[lin][i].equals(numero)) {

				JOptionPane.showMessageDialog(null, "Essa linha já tem esse número...");

				joga = false;
			}
		}
	}

	if(joga) {

		//checa coluna
		for (int j = 0; j < 9; j++) {

			if (array[j][col].equals(numero)) {

				JOptionPane.showMessageDialog(null, "Essa coluna já tem esse número...");

				joga = false;
			}
		}
	}

	if(joga) {

		//checa grade
		int linhaCaixa = lin - (lin % 3);
		int colunaCaixa = col - (col % 3);

		for (int x = 0; x < 3; x++) {

			for (int y = 0; y < 3; y++) {

				if (array[linhaCaixa + x][colunaCaixa + y].equals(numero)) {

					JOptionPane.showMessageDialog(null, "Essa grade já tem esse número...");

					joga = false;
				}
			}
		}
	}

	return joga;
}

//verifica o clique do mouse e pega o valor digitado
private void initKeyListener() {

	addKeyListener(new KeyAdapter() {

		@Override
		public void keyReleased(KeyEvent e) {

			if (selecionado != null) {

				if(selecionado.getEditavel()) {

					if(testaNumero(e)) {

						selecionado.setText(Objects.toString(e.getKeyChar()));
					}
				}
				else
					JOptionPane.showMessageDialog(null, "Este número não pode ser alterado...");
			}
		}
	});
}

class Botao extends JLabel {

	int linha;
	int coluna;
	boolean editavel = true;
	ArrayList <String> possibilidades = new ArrayList<>();

	public Botao() {

		super();
		init();
	}

	public int getLinha() { return linha; }
	public void setLinha(int l) { linha = l; }

	public int getColuna() { return coluna; }
	public void setColuna(int c) { coluna = c; }

	public boolean getEditavel() { return editavel; }
	public void setEditavel(boolean e) { editavel = e; }

	public ArrayList getPossibilidades() { return possibilidades; }
	public void setPossibilidades(String p) { possibilidades.add(p); }
	public void zeraPossibilidades() { possibilidades.clear(); }

	private void init() {

		setBackground(Color.WHITE);
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		setPreferredSize(new Dimension(30, 30));
		setHorizontalAlignment(SwingConstants.CENTER);
		setOpaque(true);
		setText("");
		setLinha(0);
		setColuna(0);
		possibilidades.clear();

		Botao esteBotao = this;

		addMouseListener(new MouseAdapter() {

			@Override
			public void mouseReleased(MouseEvent e) {

				selecionaBotao(esteBotao);
			}
		});
	}
}

void selecionaBotao(Botao botao) {

	JLabel clique;

	//se tem selecionado
	if (selecionado != null) {

		selecionado.setBorder(BorderFactory.createLineBorder(Color.black));
	}

	//seleciona o novo se não for nulo
	if (botao != null) {

		botao.setBorder(BorderFactory.createLineBorder(Color.red));
	}

	selecionado = botao;
}

public boolean checa(int lin, int col, int numero) {

	//checa linha
	for (int i = 0; i < 9; i++) {  

		if(botoes[lin][i].getText().equals(String.valueOf(numero))) {

			return false;
		}
	}

	//checa coluna
	for (int j = 0; j < 9; j++) {

		if (botoes[j][col].getText().equals(String.valueOf(numero))) {

			return false;
		}
	}

	//checa grade
	int linhaCaixa = lin - (lin % 3);
	int colunaCaixa = col - (col % 3);

	for (int x = 0; x < 3; x++) {

		for (int y = 0; y < 3; y++) {

			if (botoes[linhaCaixa + x][colunaCaixa + y].getText().equals(String.valueOf(numero))) {

				return false;
			}
		}
	}

	return true;
}

public void pegaPossibilidades() {

	for (int i = 0; i < 9; i++) {

		for (int j = 0; j < 9; j++) {

			botoes[i][j].zeraPossibilidades();

			if (botoes[i][j].getText().equals(vazio)) {

				for (int k = 1; k <= 9; k++) {

					if (checa(i, j, k)) {

						botoes[i][j].setPossibilidades(String.valueOf(k));
					}
				}
			}
		}
	}
}

public boolean resolveBT() { 

	for(int i = 0; i < 9; i++) {

		for(int j = 0; j < 9; j++) {

			if(botoes[i][j].getText().equals(vazio)) {

				for(int k = 1; k <= 9; k++) {

					if(checa(i, j, k)) {

						botoes[i][j].setText(String.valueOf(k));

						if(resolveBT()) { return true; }
						else botoes[i][j].setText(vazio);
					}
				}

				return false;
			}
		}
	}

	return true;
}

/*
public void resolveSudoku() {

	int zeros = 81, numero = 0;
	int n = 0, lin = 0, col = 0;

	String [][] copia = new String [9][9];

	pegaPossibilidades();

	//imprime números possíveis de cada espaço vazio
	for(int g = 0; g < 9; g++) { 

		for(int h = 0; h < 9; h++) {

			int index = ((g * 9) + h);
			if(!botoes[g][h].possibilidades.isEmpty()) {

				System.out.println(index + " - " + botoes[g][h].possibilidades);
			}
		}
	}

	//checa a quantidade de números que faltam
	for(int i = 0; i < 9; i++) {

		for(int j = 0; j < 9; j++) {

// copia[i][j] = botoes[i][j].getText();

			if(!botoes[i][j].getText().equals(vazio)) {

				zeros--;
			}
		}
	}

	int tentativas = 0;

	while(tentativas < 50) {

		//linha
		for(int i = 0; i < 9; i++) {

			//coluna
			for(int j = 0; j < 9; j++) {

				int possiveis = 0;

				if(botoes[i][j].getText().equals(vazio)) {

					//testa de 1 a 9
					for(int k = 1; k <= 9; k++) {

						if(checa(i, j, k)) {

							numero = k;
							possiveis++;
						}
					}

					if(possiveis == 1) {

						botoes[i][j].setText(String.valueOf(numero));
						botoes[i][j].setForeground(Color.red);
						botoes[i][j].setEditavel(false);

// copia[i][j] = String.valueOf(numero);
zeros–;
}

					int linhaCaixa = i - (i % 3);
					int colunaCaixa = j - (j % 3);

					for(int k = 1; k <= 9; k++) {

						int p = 0;

						for (int x = 0; x < 3; x++) {

							for (int y = 0; y < 3; y++) {

								if (botoes[linhaCaixa + x][colunaCaixa + y].getText().equals(String.valueOf(vazio))) {

									if (checa((linhaCaixa + x), (colunaCaixa + y), k)) {

										lin = (linhaCaixa + x);
										col = (colunaCaixa + y);
										p++;
									}
								}
							}
						}

						if(p == 1) {

							botoes[lin][col].setText(String.valueOf(k));
							botoes[lin][col].setForeground(Color.red);
							botoes[lin][col].setEditavel(false);
						}
					}
				}//if vazio
			}//for j
		}//for i

		tentativas++;
	}//while

	if(!resolveBT()) { resolveBT(); }
	
}//resolve

*/
public void limpa() {

	for(int i = 0; i < 9; i++) {

		for(int j = 0; j < 9; j++) {

			botoes[i][j] = new Botao();
		}
	}
}

public static void main(String[] args) {

	new Sudoku2D();
}

}

O jogo ficou assim…
Só não consegui gerar um novo jogo direto, tenho que fechar a janela e rodar o código de novo.
Se puderem me ajudar.
Fora isso, ta funcionando bem.
O método pra resolver o sudoku resolve todos os níveis bem rápido.

Problema resolvido…
manda criar o tabuleiro no construtor…

public Solver() {
...
    criaTabuleiro();
}

e trocar o criaTabuleiro() por preencheTabuleiro() na hora de escolher o nível…

if(evento.getSource() == facil) {

		pegaJogo("facil.txt");
		preencheTabuleiro();
	}

agora pode ficar jogando direto…