Java Puzzle Game Source Code

How To Create Image Puzzle Game in Java NetBeans

How To Create Image Puzzle Game in Java NetBeans


In this Java Tutorial we will see How to Create a picture puzzle game using Java Swing in Netbeans.
This java swing puzzle game is easy to use, player can upload his own images and Select the  difficulty level from multiple levels (4x4 to 8x8 grid).

The Game Features:
- Upload your own images.
Multiple difficulty levels (4x4 to 8x8 grid).
Move counter and timer.
Smooth piece-swapping animations.
- Winning detection and celebration.

How the Game Works:
When you start the game, you'll see a wimdow split into two parts:
- A sidebar with controls and game stats.
- The main puzzle grid area.

You can upload any image, and the game will automatically:
- Resize it to fit the game window.
- Split it into equal pieces based on your chosen difficulty.
- Shuffle the pieces for you to solve.

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.




if you want the source code click on the download button below




Project Source Code:


    /**
     * Initializes the game timer that updates every second
     */
    private void initializeTimer() {
        timer = new Timer(1000, e -> {
            elapsedTime++;                    // Increment elapsed time
            int minutes = elapsedTime / 60;   // Calculate minutes
            int seconds = elapsedTime % 60;   // Calculate seconds
            timerLabel.setText(String.format("Time: %02d:%02d", minutes, seconds));  // Update timer display
        });
    }
    

    /**
     * Creates a panel to display game statistics (moves and time)
     */
    private JPanel createStatsPanel() {
        // Create panel with vertical box layout
        JPanel statsPanel = new JPanel();
        statsPanel.setLayout(new BoxLayout(statsPanel, BoxLayout.Y_AXIS));
        statsPanel.setOpaque(false);
        
        // Add semi-transparent border
        statsPanel.setBorder(BorderFactory.createCompoundBorder(
                new LineBorder(new Color(255, 255, 255, 50), 1, true), 
                new EmptyBorder(15, 15, 15, 15)));
        
        // Add statistics elements
        JLabel statsTitle = createLabel("Game Stats", 20, Font.BOLD);
        statsPanel.add(statsTitle);
        statsPanel.add(Box.createVerticalStrut(10));
        statsPanel.add(movesLabel);
        statsPanel.add(Box.createVerticalStrut(5));
        statsPanel.add(timerLabel);
        
        return statsPanel;
    }
    

    /**
     * Creates a styled label with specified text, font size, and style
     */
    private JLabel createLabel(String text, int fontSize, int fontStyle) {
        JLabel label = new JLabel(text);
        label.setFont(new Font("Arial", fontStyle, fontSize));  // Set font properties
        label.setForeground(Color.WHITE);                       // Set text color
        label.setAlignmentX(Component.CENTER_ALIGNMENT);        // Center align
        return label;
    }
    

    /**
     * Creates a styled button with specified text and background color
     */
    private JButton createButton(String text, Color color) {
        // Create button with custom painting for rounded corners
        JButton button = new JButton(text) {
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setColor(getBackground());
                g2d.fill(new RoundRectangle2D.Float(0,0, getWidth(), getHeight(), 15, 15));
                g2d.dispose();
                super.paintComponent(g);
            }
        };
        
        // Style the button
        button.setAlignmentX(Component.CENTER_ALIGNMENT);
        button.setMaximumSize(new Dimension(240, 50));
        button.setFont(new Font("Arial", Font.BOLD, 16));
        button.setForeground(Color.WHITE);
        button.setBackground(color);
        button.setBorder(new EmptyBorder(10, 15, 10, 15));
        button.setFocusPainted(false);
        button.setContentAreaFilled(false);
        
        // Add hover effect
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent evt) {
                button.setBackground(color.darker());    // Darken on hover
            }
            
            @Override
            public void mouseExited(MouseEvent evt) {
                button.setBackground(color);            // Restore original color
            }
        });
        
        return button;
    }
    

    /**
     * Applies custom styling to the difficulty combo box
     */
    private void styleCombobox(JComboBox<String> combobox) {
        combobox.setMaximumSize(new Dimension(240, 40));
        combobox.setFont(new Font("Arial", Font.PLAIN, 16));
        combobox.setBackground(new Color(236, 240, 241));
        combobox.setForeground(new Color(44, 62, 80));
        combobox.setBorder(BorderFactory.createCompoundBorder(
                new LineBorder(new Color(189, 195, 199), 1), 
                new EmptyBorder(5, 10, 5, 10)
        ));
        
        combobox.setAlignmentX(Component.CENTER_ALIGNMENT);
        
        // Center align items in dropdown
        combobox.setRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList<?> list, Object value, 
                    int index, boolean isSelected, boolean cellHasFocus) {
                JLabel label = (JLabel)super.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
                label.setHorizontalAlignment(JLabel.CENTER);
                return label;
            }
        });
    }
    

    /**
     * Opens file chooser and loads selected image to start puzzle
     */
    private void loadAndStartPuzzle() {
        JFileChooser fileChooser = new JFileChooser();
        // Set filter to only show image files
        fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter(
                "Image Files", ImageIO.getReaderFileSuffixes()));
        
        int returnValue = fileChooser.showOpenDialog(null);
        if(returnValue == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            
            try {
                originalImage = ImageIO.read(selectedFile);                    // Load image
                BufferedImage resizedImage = resizeImage(originalImage, IMAGE_SIZE, IMAGE_SIZE);  // Resize
                resetGame(resizedImage);                                      // Start new game
            } catch (IOException ex) {
                Logger.getLogger(Java_Puzzle_Game.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    

    /**
     * Resizes an image to specified dimensions while maintaining aspect ratio
     */
    private BufferedImage resizeImage(BufferedImage image, int targetWidth, int targetHeight) {
        // Create scaled instance of image
        Image reszedImage = image.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);
        BufferedImage bufferedReszedImage = new BufferedImage(targetWidth, targetHeight, 
                BufferedImage.TYPE_INT_ARGB);
        
        // Draw scaled image
        Graphics2D g2d = (Graphics2D) bufferedReszedImage.createGraphics();
        g2d.drawImage(reszedImage, 0, 0, null);
        g2d.dispose();
        
        return bufferedReszedImage;
    }
    

    /**
     * Resets the game state and creates new puzzle pieces from the provided image
     */
    private void resetGame(BufferedImage image) {
        // Reset game statistics
        timer.stop();
        elapsedTime = 0;
        timerLabel.setText("Timer: 00:00");
        moves = 0;
        movesLabel.setText("Moves: 0");
        
        // Clear existing puzzle pieces
        gridPanel.removeAll();
        labelList.clear();
        correctOrder.clear();
        firstSelectedSlice = null;
        secondSelectedSlice = null;
        
        // Calculate dimensions for puzzle pieces
        int sliceWidth = image.getWidth() / GRID_SIZE;
        int sliceHeight = image.getHeight() / GRID_SIZE;
        List<ImageIcon> imageSlices = new ArrayList<>();
        
        // Create puzzle pieces
        for(int y = 0; y < GRID_SIZE; y++) {
            // Create puzzle pieces from subimages
            for(int x = 0; x < GRID_SIZE; x++) {
                // Extract piece from original image
                BufferedImage slice = image.getSubimage(
                    x * sliceWidth, 
                    y * sliceHeight, 
                    sliceWidth, 
                    sliceHeight
                );
                
                // Create icon from slice and add to lists
                ImageIcon imageIcon = new ImageIcon(
                    slice.getScaledInstance(sliceWidth, sliceHeight, Image.SCALE_SMOOTH)
                );
                imageSlices.add(imageIcon);
                correctOrder.add(imageIcon);
            }
        }
        
        // Randomly shuffle the puzzle pieces
        Collections.shuffle(imageSlices);
        
        // Create and add labels for each puzzle piece
        for(ImageIcon icon : imageSlices) {
            // Create label for puzzle piece
            JLabel sliceLabel = new JLabel(icon);
            sliceLabel.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
            sliceLabel.setHorizontalAlignment(JLabel.CENTER);
            sliceLabel.setVerticalAlignment(JLabel.CENTER);
            
            // Add click listener to handle piece selection
            sliceLabel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    handleSliceClick(sliceLabel);
                }
            });
            
            // Add to tracking lists and grid
            labelList.add(sliceLabel);
            gridPanel.add(sliceLabel);
        }
        
        // Refresh the display
        gridPanel.revalidate();
        gridPanel.repaint();
        
        // Start the game timer
        timer.start();
    }
    

    /**
     * Updates the grid size based on selected difficulty level
     */
    private void setGridSize(String difficulty) {
        // Set grid dimensions based on selection
        switch (difficulty) {
            case "4x4" -> GRID_SIZE = 4;
            case "5x5" -> GRID_SIZE = 5;
            case "6x6" -> GRID_SIZE = 6;
            case "7x7" -> GRID_SIZE = 7;
            case "8x8" -> GRID_SIZE = 9;
        }
        
        // Update grid layout
        gridPanel.setLayout(new GridLayout(GRID_SIZE, GRID_SIZE));
    }
    

    /**
     * Handles click events on puzzle pieces
     */
    private void handleSliceClick(JLabel clickedLabel) {
        if(firstSelectedSlice == null) {
            // First piece selection
            firstSelectedSlice = clickedLabel;
            firstSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(231,76,60), 2));
        }
        else if(secondSelectedSlice == null && clickedLabel != firstSelectedSlice) {
            // Second piece selection
            secondSelectedSlice = clickedLabel;
            secondSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(231,76,60), 2));
            
            // Start swap animation
            swapSlices();
        }
    }
    

    /**
     * Animates the swapping of two puzzle pieces
     */
    private void swapSlices() {
        Timer animationTimer = new Timer(10, null);
        final int animationDuration = 500;  // Animation duration in milliseconds
        final long startTime = System.currentTimeMillis();
        
        // Store initial positions
        Point firstLocation = firstSelectedSlice.getLocation();
        Point secondLocation = secondSelectedSlice.getLocation();
        
        // Animation update handler
        animationTimer.addActionListener((e) -> {
            // Calculate animation progress
            long elapsed = System.currentTimeMillis() - startTime;
            float progress = Math.min(1f, (float) elapsed / animationDuration);
            float easedProgress = easeInOut(progress);  // Apply easing for smooth movement
            
            // Calculate new positions
            int x1 = (int) (firstLocation.x + (secondLocation.x - firstLocation.x) * easedProgress);
            int y1 = (int) (firstLocation.y + (secondLocation.y - firstLocation.y) * easedProgress);
            int x2 = (int) (secondLocation.x + (firstLocation.x - secondLocation.x) * easedProgress);
            int y2 = (int) (secondLocation.y + (firstLocation.y - secondLocation.y) * easedProgress);
            
            // Update piece positions
            firstSelectedSlice.setLocation(x1, y1);
            secondSelectedSlice.setLocation(x2, y2);
            
            // Check if animation is complete
            if(progress >= 1f) {
                animationTimer.stop();
                finalizeSwap();
            }
        });
        
        // Start animation
        animationTimer.start();
    }
    

    /**
     * Easing function for smooth animation
     * Creates acceleration at start and deceleration at end
     */
    private float easeInOut(float t) {
        return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    

    /**
     * Completes the swap operation after animation
     */
    private void finalizeSwap() {
        // Swap the icons between pieces
        Icon tempIcon = firstSelectedSlice.getIcon();
        firstSelectedSlice.setIcon(secondSelectedSlice.getIcon());
        secondSelectedSlice.setIcon(tempIcon);
        
        // Reset pieces to their grid positions
        firstSelectedSlice.setLocation(firstSelectedSlice.getParent().getComponent(
                labelList.indexOf(firstSelectedSlice)).getLocation()
        );
        secondSelectedSlice.setLocation(secondSelectedSlice.getParent().getComponent(
                labelList.indexOf(secondSelectedSlice)).getLocation()
        );
        
        // Reset borders to default
        firstSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
        secondSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
        
        // Clear selection
        firstSelectedSlice = null;
        secondSelectedSlice = null;
        
        // Update move counter
        moves++;
        movesLabel.setText("Moves: " + moves);
        
        // Check if puzzle is solved
        checkIfSolved();
    }
    

    /**
     * Checks if the puzzle has been solved
     */
    private void checkIfSolved() {
        // Compare current arrangement with correct order
        for(int i = 0; i < labelList.size(); i++) {
            if(labelList.get(i).getIcon() != correctOrder.get(i)) {
                return; // Puzzle not solved
            }
        }
        
        // Puzzle is solved
        timer.stop();
        showCongratulationDialog();
    }
    

    /**
     * Shows congratulation dialog when puzzle is solved
     */
    private void showCongratulationDialog() {
        // Create modal dialog
        JDialog dialog = new JDialog(this, "Congratulation!", true);
        dialog.setLayout(new BorderLayout());
        dialog.setSize(300, 200);
        dialog.setLocationRelativeTo(this);
        
        // Create panel with gradient background
        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                
                // Create gradient background
                int w = getWidth();
                int h = getHeight();
                Color color1 = new Color(41, 128, 185);
                Color color2 = new Color(44, 62, 80);
                GradientPaint gp = new GradientPaint(0, 0, color1, w, h, color2);
                g2d.setPaint(gp);
                g2d.fillRect(0, 0, w, h);
            }
        };
        
        // Configure panel layout
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.setBorder(new EmptyBorder(20, 20, 20, 20));
        
        // Add congratulation message and stats
        JLabel congratsLabel = createLabel("Puzzle Solved!", 24, Font.BOLD);
        JLabel statsLabel = createLabel("Moves: " + moves + " | Time: " + formatTime(elapsedTime), 16, Font.PLAIN);
        
        // Add close button
        JButton closeButton = createButton("Close", new Color(231, 76, 60));
        closeButton.addActionListener((ActionEvent e) -> dialog.dispose());
        
        // Add components to panel
        panel.add(congratsLabel);
        panel.add(Box.createVerticalStrut(10));
        panel.add(statsLabel);
        panel.add(Box.createVerticalStrut(20));
        panel.add(closeButton);
        
        // Show dialog
        dialog.add(panel);
        dialog.setVisible(true);
    }
    

    /**
     * Formats time in minutes:seconds format
     */
    private String formatTime(int seconds) {
        int minutes = seconds / 60;
        int remainingSeconds = seconds % 60;
        return String.format("%02d:%02d", minutes, remainingSeconds);
    }
    

    /**
     * Shuffles the puzzle pieces on the grid
     */
    private void shuffleGrid() {
        // Reset game statistics
        timer.stop();
        elapsedTime = 0;
        timerLabel.setText("Timer: 00:00");
        moves = 0;
        movesLabel.setText("Moves: 0");
        
        // Create and shuffle new arrangement
        List<ImageIcon> shuffledIcons = new ArrayList<>(correctOrder);
        Collections.shuffle(shuffledIcons);
        
        // Apply new arrangement to grid
        for(int i = 0; i < labelList.size(); i++) {
            labelList.get(i).setIcon(shuffledIcons.get(i));
        }
        
        // Start timer if game is active
        if(originalImage != null) {
            timer.start();
        }
    }






The Final Result:

Image Puzzle Game in Java NetBeans

Puzzle Game in Java NetBeans

Java Puzzle Game

Java Puzzle Game Source Code

Java Image Puzzle Game Source Code

Java Swing Puzzle Game

Puzzle Game With Source Code In Java Netbeans







if you want the source code click on the download button below




disclaimer: you will get the source code with the database script and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.





How to Split Image Into Multiple Pieces In Java

How to Create an Image Slicer In Java Netbeans

How to Split Image Into Multiple Pieces In Java


In this Java Tutorial we will see How To Slice an image into parts in java netbeans.
This program lets you upload an image and automatically splits it into a 4x4 (you can change it to more or less) grid of smaller images. Perfect for creating puzzles with image.

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.





How to Create an Image Slicer In Java Netbeans



Project Source Code:


package new_tutorials;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 *
 * @author 1BestCsharp
 */
public class ImageSlicer extends  JFrame{

    // Declare a panel to hold the grid of image slices
    private JPanel gridPanel;
     // Constants for window and image properties
    private static final int FRAME_WIDTH = 500;
    private static final int FRAME_HEIGHT = 500;
    // Target size for resizing the image
    private static final int IMAGE_SIZE = 400;
    // 4x4 grid (16 image slices in total)
    private static final int GRID_SIZE = 5; 
    
    public ImageSlicer(){
        // Set up the main window properties
        setTitle("Image Splitter");
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        setLocationRelativeTo(null);
        
        // Create the "Upload and Split Image" button
        JButton uploadButton = new JButton("Upload and Split Image");
        uploadButton.setFocusPainted(false);
        uploadButton.setBackground(new Color(33,150,243));
        uploadButton.setForeground(Color.WHITE);
        uploadButton.setBorder( BorderFactory.createEmptyBorder(10,20,10,20));
        uploadButton.setFont(new Font("Arial", Font.PLAIN, 17));
        uploadButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        
        // Create a panel to hold the grid of image slices (initially empty)
        // Use GridLayout to divide the panel into a 4x4 grid, no gaps between cells
        gridPanel = new JPanel(new GridLayout(GRID_SIZE, GRID_SIZE, 0, 0));
        gridPanel.setBackground(Color.WHITE);
        
        // Add the grid panel inside a scroll pane in case the content exceeds the window size
        JScrollPane scrollPane = new JScrollPane(gridPanel);
        scrollPane.setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        
        // Add an action listener to the button (what happens when it's clicked)
        uploadButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
               
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileFilter(new FileNameExtensionFilter("Images Files", ImageIO.getReaderFileSuffixes()));
                int returnValue = fileChooser.showOpenDialog(null);
                if(returnValue == JFileChooser.APPROVE_OPTION){
                    try {
                        File selectedFile = fileChooser.getSelectedFile();
                        BufferedImage image = ImageIO.read(selectedFile);
                        // Resize the image to the specified size (400x400 pixels)
                        BufferedImage reseizedImage = resizeImage(image, IMAGE_SIZE, IMAGE_SIZE);
                         // Split the resized image into 16 (4x4) slices and display them
                        splitAndDisplay(reseizedImage);
                    } catch (IOException ex) {
                        Logger.getLogger(ImageSlicer.class.getName()).log(Level.SEVERE, null, ex);
                    }   
                }
            }
        });
        
        JPanel panel = new JPanel();
        panel.setBackground(Color.WHITE);
        panel.add(uploadButton);
        
        add(panel, BorderLayout.NORTH);
        add(scrollPane, BorderLayout.CENTER);
    }
    
            
         // Function to resize the image to the target width and height
         private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight){
             Image resizedImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);
             BufferedImage bufferedResizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB);
             Graphics2D g2d = bufferedResizedImage.createGraphics();
             g2d.drawImage(resizedImage, 0, 0, null);
             g2d.dispose();
             return bufferedResizedImage;
         }
        
         
         // Function to split the image into slices and display them in the grid
         private void splitAndDisplay(BufferedImage image){
             int sliceWidth = image.getWidth() / GRID_SIZE;
             int sliceHeight = image.getHeight() / GRID_SIZE;
             
             gridPanel.removeAll();
             
             for(int y = 0; y < GRID_SIZE; y++){
                 for(int x = 0; x < GRID_SIZE; x++){
                     BufferedImage slice = image.getSubimage(x*sliceWidth, y*sliceHeight, sliceWidth, sliceHeight);
                     
                     ImageIcon imageIcon = new ImageIcon(slice);
                     JLabel sliceLabel = new JLabel(imageIcon);
                     sliceLabel.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
                     gridPanel.add(sliceLabel);
                 }
             }
             
             gridPanel.revalidate();
             gridPanel.repaint();
             
         }
         
    
    public static void main(String[] args) {
        new ImageSlicer().setVisible(true);
    }
}

    


The Final Result: