Boa noite galera, tenho que fazer um trabalho pra faculdade, um joguinho (jogo do 15). Ele é um jogo que você deve organizar os números de 1 a 15 movendo os blocos. Eu consegui fazer ele porém acho que não pode só dar random nos números, porque tive um problema, as vezes não da pra organizar. Fica ex: 1,2,3,4,5,6,8,7 se fosse de 1 a 9. Não importa como tente sempre fica dois números trocados. Não sei como resolver isso…
Pelo que eu entendi, você precisa entender melhor das regras do jogo e criar uma série de condições para a ordem desses valores gerados para que não tenha a possibilidade de gerar um jogo impossível como acima.
Você não pode simplesmente embaralhar um array de peças para implementar este jogo, se fizer isso, você pode acabar embaralhando as peças de forma que não seja possível encontrar a solução para o mesmo.
Você tem que fazer um algoritmo de embaralhamento que faça os mesmos passos que um humano faria para embaralhar: movendo somente as peças adjacentes ao espaço vazio, criando assim um novo espaço vazio com outras peças adjacentes.
Neste link aqui tem um exemplo em JavaScript e o cara também disponibilizou os fontes no GitHub, talvez seja útil.
staroviski está certo.
O que você deve fazer é povoar o tabuleiro com as peças no lugar correto e depois embaralhar.
Para fins de comparação, observe o método organizar na classe tabuleiro, bem como o restante da codificação a seguir:
package crisis;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Crisis extends JFrame {
private final Tabuleiro tab;
public Crisis(int tabuleiro, int objetivo) throws HeadlessException {
tab = new Tabuleiro(tabuleiro, objetivo);
if (tab.getRaiz() > 0) {
setLayout(new GridLayout(tab.getRaiz(), tab.getRaiz(), 10, 10));
tab.getPecas().forEach((peca) -> {
movimentoPecas(peca);
add(peca.getLabel());
});
setSize(tab.getRaiz() * 100, tab.getRaiz() * 100);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
} else {
JOptionPane.showMessageDialog(null, "Um número informado não possui raiz quadrada inteira");
}
}
private void movimentoPecas(Peca peca) {
peca.getLabel().addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mouseReleased(MouseEvent me) {
int indexPeca = tab.getPecas().indexOf(peca);
boolean movimentar = peca.movimentar(tab.getPecas().get(tab.getPos()));
tab.setPos(movimentar ? indexPeca : tab.getPos());
tab.getPecas().stream().forEach(Peca::colorir);
quadradoMagico();
}
@Override
public void mouseEntered(MouseEvent me) {
peca.getLabel().setText(peca.getNum() + "");
peca.getLabel().setBorder(BorderFactory.createDashedBorder(peca.isMove() ? new Color(0, 200, 0) : new Color(200, 50, 0), 10, 5, 1, false));
}
@Override
public void mouseExited(MouseEvent me) {
peca.getLabel().setText(peca.getNum() == tab.getPecas().size() ? "" : peca.getNum() + "");
peca.getLabel().setBorder(BorderFactory.createLineBorder(peca.isMove() ? new Color(0, 200, 0) : new Color(200, 50, 0), 10));
}
});
}
private void quadradoMagico() {
ArrayList<int[]> quadradoMagico = new ArrayList<>();
quadradoMagico.add(tab.matrizObliquo(true));
quadradoMagico.add(tab.matrizObliquo(false));
Arrays.asList(tab.matrizHorizonal()).stream().forEach((linha) -> {
quadradoMagico.add(linha);
});
Arrays.asList(tab.matrizTansposta()).stream().forEach((linha) -> {
quadradoMagico.add(linha);
});
boolean concluir = true;
for (int[] linha : quadradoMagico) {
if (!soma(linha, 0)) {
concluir = false;
break;
}
}
if (concluir) {
JOptionPane.showMessageDialog(null, "Objetivo alcançado: " + tab.getObjetivo() + " em todas as linhas do tabuleiro");
}
}
private boolean soma(int[] linha, int soma) {
for (int lin : linha) {
soma += lin;
}
return soma == tab.getObjetivo();
}
@SuppressWarnings("ResultOfObjectAllocationIgnored")
public static void main(String[] args) {
new Crisis(9, 15);
}
}
package crisis;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JOptionPane;
public class Tabuleiro {
private final List<Peca> pecas = new ArrayList<>();
private final int objetivo;
private int pos;
public Tabuleiro(int raiz, int objetivo) {
ordenar(raiz);
this.objetivo = objetivo;
}
//é melhor ordenar para depois embaralhar
private void ordenar(int quantidadeCasas) {
switch (quantidadeCasas) {
case 9:
Collections.addAll(pecas,
new Peca(0, 0, 8, true), new Peca(0, 1, 1, true), new Peca(0, 2, 6, true),
new Peca(1, 0, 3, true), new Peca(1, 1, 5, true), new Peca(1, 2, 7, true),
new Peca(2, 0, 4, true), new Peca(2, 1, 9, false), new Peca(2, 2, 2, true)
);
pos = 7;//posição do maior valor na coleção é identificado na adição acima
break;
default:
JOptionPane.showMessageDialog(null, "Ainda não implementado");
}
embaralhar();
Collections.unmodifiableCollection(pecas);
}
private void embaralhar() {
int move = pecas.size() * 20;
int peca;
Random r = new Random();
while (move-- > 0) {
peca = r.nextInt(pecas.size());
pos = pecas.get(peca).movimentar(pecas.get(pos)) ? peca : pos;
}
pecas.stream().forEach(Peca::colorir);
}
//retorna o lado do tabuleiro
public int getRaiz() {
return (int) Math.sqrt(pecas.size());
}
public List<Peca> getPecas() {
return pecas;
}
public int getPos() {
return pos;
}
public void setPos(int pos) {
this.pos = pos;
}
//retorna a os valores das peças contidos na coleçao de forma matricial
public int[][] matrizHorizonal() {
int[][] retorno = new int[getRaiz()][getRaiz()];
Peca peca;
for (int i = 0; i < getRaiz(); i++) {
for (int j = 0; j < getRaiz(); j++) {
if ((peca = compare(i, j)) != null) {
retorno[i][j] = peca.getNum();
}
}
}
return retorno;
}
//retorna os valores das peças contidos na coleção sendo que sua organizaçao se dará em uma matriz transposta
public int[][] matrizTansposta() {
/*para a finalidade de transpor a matriz, basta inverter o index da linha
* com o da coluna, neste caso desnecessário pois a matriz é [] quadrada
*/
int[][] retorno = new int[/*coluna*/getRaiz()][/*linha*/getRaiz()];
Peca peca;
for (int lin = 0; lin < retorno.length; lin++) {
for (int col = 0; col < retorno[lin].length; col++) {
if ((peca = compare(col, lin)) != null) {
retorno[lin][col] = peca.getNum();
}
}
}
return retorno;
}
//percorre as inclinações da matriz, uma está à esquerda e a outra à direita
public int[] matrizObliquo(boolean isEsquerdo) {
int[] retorno = new int[getRaiz()];
Peca peca;
int j = getRaiz();
for (int i = 0; i < 10; i++) {
j = isEsquerdo ? i : --j;
if ((peca = compare(i, j)) != null) {
retorno[i] = peca.getNum();
}
}
return retorno;
}
//pelas coordenadas verifica consegue retornar a peças coincidente
private Peca compare(int i, int j) {
for (Peca peca : pecas) {
if (peca.getCoordX() == i && peca.getCoordY() == j) {
return peca;
}
}
return null;
}
//a pontuação a ser atingida
public int getObjetivo() {
return objetivo;
}
}
package crisis;
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
public class Peca {
private int coordX, coordY;
private boolean move;
private int num;
private final JLabel label;
public Peca(int coordX, int coordY, int num, boolean move) {
this.coordX = coordX;
this.coordY = coordY;
this.move = move;
this.num = num;
label = new JLabel(move ? num + "" : "");
configurarPeca();
}
private void configurarPeca() {
label.setFont(new Font(Font.SERIF, Font.BOLD, 60));
label.setHorizontalAlignment(JLabel.CENTER);
colorir();
}
protected final void colorir() {
label.setForeground(move ? Color.BLUE : Color.RED);
label.setBorder(BorderFactory.createLineBorder(move ? new Color(0, 200, 0) : new Color(200, 50, 0), 10));
}
public final int getCoordX() {
return coordX;
}
public final void setCoordX(int coordX) {
this.coordX = coordX;
}
public final int getCoordY() {
return coordY;
}
public final void setCoordY(int coordY) {
this.coordY = coordY;
}
public final boolean isMove() {
return move;
}
/**
* @param outra outro objeto do tipo Peca com o qual ocorrerá uma interação
* @return true caso o movimento seja possível dentro do esperado
* @see Peca#proximaDe
*/
public final boolean movimentar(Peca outra) {
if (this.move && !outra.move && this.proximaDe(outra)) {
//trocando a posição das peças
int n = num;
label.setText(outra.label.getText());
this.num = outra.num;
this.move = false;
outra.move = true;
outra.num = n;
outra.label.setText(outra.move ? n + "" : "");
label.setText(this.move ? n + "" : "");
return true;
}
return false;
}
//retorna o módulo de um número, não confundir com o resto da divisão
private int modulo(int num) {
return num > 0 ? num : -num;
}
/*
* Matriz linha x coluna que serve como modelo de análise
* 00 01 02
* 10 11 12
* 20 21 22
* Para que o movimento seja possível as peças que vão interagir
* devem estar a 1 (uma) casa de distância conforme movimento semelhante à torre do xadrez
*/
private boolean proximaDe(Peca outra) {
return (this.coordX - outra.coordX == 0 && modulo(this.coordY - outra.coordY) == 1)
|| modulo(this.coordX - outra.coordX) == 1 && this.coordY - outra.coordY == 0;
}
public final int getNum() {
return num;
}
public final JLabel getLabel() {
return label;
}
}