Heathen0042 Heathen0042 - 1 month ago 7
Java Question

Second Iteration of 8 Puzzle is Not Working

This is my first time working with Java Swing, so my program is a bit chaotic to say the least. The goal is to have an 8-puzzle take an image, divide it into 9 sectors, then place 8 of them in a specific order (Drop 1 for movement). The program works fine the first time through. Once it is solved it is supposed to randomize the

JButton
components and allow you to solve again.The buttons Randomize, but do not allow me to move them. Any help is greatly appreciated.

Normally I would post just the code that I think would have the issue, but due to the chaotic nature of the code, I am posting my entire class.

EDIT:
After going through the code line by line, the issue seems to be in the Order() method. If i remove the second "if" statement all together, the program works fine just does not randomize the components. (Same if i use "else" & "else if" for that statement)

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

public class JEightPuzzleFrame extends JFrame implements ActionListener {

JButton[] buttons = new JButton[9];
JButton[][] format = new JButton[3][3];
JPanel puzzle = new JPanel();
JPanel jp = new JPanel();
String path = null;
int h;
int w;

public JEightPuzzleFrame(String Title, String Path) {
super();
path = Path;
add(puzzle);
makeFrame();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(w + 10, h + 10);
setTitle(Title);
setResizable(false);
jp.setVisible(true);
}

public void makeFrame() {
puzzle.setVisible(true);
puzzle.setBounds(100, 100, 612, 519);
makeBtnImg();
puzzle.setLayout(new GridLayout(3, 3));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
puzzle.add(format[i][j]);
if (format[i][j] == buttons[8]) {
puzzle.remove(format[i][j]);
puzzle.add(jp);
}
}
}
}

public void makeBtnImg() {
try {
BufferedImage pic = ImageIO.read(new File(path));
jp.setSize(h, w);
h = pic.getHeight();
w = pic.getWidth();
int count = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
BufferedImage bimg = pic.getSubimage(i * w / 3, j * h / 3, w / 3, h / 3);
if (count < 9) {
buttons[count] = new JButton(new ImageIcon(bimg));
buttons[count].addActionListener(this);
}
count++;
}
}
order(true);
} catch (IOException e) {
}
}

public void order(boolean initial) {

if (initial == true) {
format[0][0] = buttons[8];
format[1][0] = buttons[4];
format[2][0] = buttons[1];

format[0][1] = buttons[0];
format[1][1] = buttons[7];
format[2][1] = buttons[2];

format[0][2] = buttons[3];
format[1][2] = buttons[6];
format[2][2] = buttons[5];
}
if (initial == false) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int rd = (int) Math.random() * (9 - 0);
JButton temp = buttons[rd];
format[i][j] = temp;
}
}
}
refresh();
}

public void refresh() {

for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
puzzle.remove(format[i][j]);
}
}
puzzle.remove(jp);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
puzzle.add(format[i][j]);
if (format[i][j] == buttons[8]) {
puzzle.remove(format[i][j]);
puzzle.add(jp);
}
}
}
getContentPane().validate();
if (format[0][0] == buttons[0] && format[0][1] == buttons[3] && format[0][2] == buttons[6]
&& format[1][0] == buttons[1] && format[1][1] == buttons[4] && format[1][2] == buttons[7]
&& format[2][0] == buttons[2] && format[2][1] == buttons[5] && format[2][2] == buttons[8]) {

JOptionPane.showMessageDialog(null, "YOU WON!");
order(false);
}
}

@Override
public void actionPerformed(ActionEvent e) {
JButton temp;
JButton button = (JButton) e.getSource();
Dimension size = button.getSize();

int buttonX = button.getX();
int buttonY = button.getY();
int buttonPosX = buttonX / size.width;
int buttonPosY = buttonY / size.height;
int leftNeighbor = buttonPosX - 1;
int rightNeighbor = buttonPosX + 1;
int topNeighbor = buttonPosY - 1;
int bottomNeighbor = buttonPosY + 1;
int c = 0;

if (leftNeighbor >= 0 && format[buttonPosY][leftNeighbor] == buttons[8]) {
c = 1;
} else if (rightNeighbor < 3 && format[buttonPosY][rightNeighbor] == buttons[8]) {
c = 2;
} else if (topNeighbor >= 0 && format[topNeighbor][buttonPosX] == buttons[8]) {
c = 3;
} else if (bottomNeighbor <= 2 && format[bottomNeighbor][buttonPosX] == buttons[8]) {
c = 4;
} else {
c = 0;
}

switch (c) {

case 0:
break;

case 1:
temp = format[buttonPosY][buttonPosX];
format[buttonPosY][buttonPosX] = format[buttonPosY][leftNeighbor];
format[buttonPosY][leftNeighbor] = temp;
refresh();
break;

case 2:
temp = format[buttonPosY][buttonPosX];
format[buttonPosY][buttonPosX] = format[buttonPosY][rightNeighbor];
format[buttonPosY][rightNeighbor] = temp;
refresh();
break;

case 3:
temp = format[buttonPosY][buttonPosX];
format[buttonPosY][buttonPosX] = format[topNeighbor][buttonPosX];
format[topNeighbor][buttonPosX] = temp;
refresh();
break;

case 4:
temp = format[buttonPosY][buttonPosX];
format[buttonPosY][buttonPosX] = format[bottomNeighbor][buttonPosX];
format[bottomNeighbor][buttonPosX] = temp;
refresh();
break;
}
}
}

Answer

First of all this code is hard to read for a few reasons:

  1. Variable names are not meaningful. Names like "c" might mean something to you but when others read it we're left to guess at what it might represent.
  2. You're using primitives and classes provided by Java even though it would simplify your code to create custom objects of your own design.
  3. There are no comments in the code

As for your specific question the problem does indeed lie in the order method as you suspected. It's this line:

int rd = (int) Math.random() * (9 - 0);

The casting to int happens before the multiplication so you're always getting a result of 0 here. This causes all the buttons in your format array to be identical.

However, there's a bigger problem. Even if you fix the above line by doing this:

int rd = (int) (Math.random() * (9 - 0));

you will still have problems because you never ensure that you only use each button once. In other words, rd can be 3 in the current loop and then be 3 again in the next loop because it's random - there is no memory of what has and has not been used before. What you need is a mechanism to prevent duplicates so that each button is unique.

So, something like this should do the trick:

if (initial == false) {
            boolean[] isUsed = new boolean[buttons.length];
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    int rd = -1;
                    do{
                        rd = (int) (Math.random() * (9 - 0));
                    }while(isUsed[rd]);
                    isUsed[rd] = true;
                    JButton temp = buttons[rd];
                    format[i][j] = temp;
                }
            }
        }