Advertisement
LegendSujay2019

Puzzle game in Java (Game Of Fifteen)

Dec 30th, 2020
736
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.52 KB | None | 0 0
  1. import java.awt.BorderLayout;
  2. import java.awt.Color;
  3. import java.awt.Dimension;
  4. import java.awt.Font;
  5. import java.awt.FontMetrics;
  6. import java.awt.Graphics;
  7. import java.awt.Graphics2D;
  8. import java.awt.RenderingHints;
  9. import java.awt.event.MouseAdapter;
  10. import java.awt.event.MouseEvent;
  11. import java.util.Random;
  12.  
  13. import javax.swing.JFrame;
  14. import javax.swing.JPanel;
  15. import javax.swing.SwingUtilities;
  16.  
  17. // We are going to create a Game of 15 Puzzle with Java 8 and Swing
  18. // If you have some questions, feel free to ue comments ;)
  19. public class GameOfFifteen extends JPanel { // our grid will be drawn in a dedicated Panel
  20.  
  21.   // Size of our Game of Fifteen instance
  22.   private int size;
  23.   // Number of tiles
  24.   private int nbTiles;
  25.   // Grid UI Dimension
  26.   private int dimension;
  27.   // Foreground Color
  28.   private static final Color FOREGROUND_COLOR = new Color(239, 83, 80); // we use arbitrary color
  29.   // Random object to shuffle tiles
  30.   private static final Random RANDOM = new Random();
  31.   // Storing the tiles in a 1D Array of integers
  32.   private int[] tiles;
  33.   // Size of tile on UI
  34.   private int tileSize;
  35.   // Position of the blank tile
  36.   private int blankPos;
  37.   // Margin for the grid on the frame
  38.   private int margin;
  39.   // Grid UI Size
  40.   private int gridSize;
  41.   private boolean gameOver; // true if game over, false otherwise
  42.  
  43.   public GameOfFifteen(int size, int dim, int mar) {
  44.     this.size = size;
  45.     dimension = dim;
  46.     margin = mar;
  47.    
  48.     // init tiles
  49.     nbTiles = size * size - 1; // -1 because we don't count blank tile
  50.     tiles = new int[size * size];
  51.    
  52.     // calculate grid size and tile size
  53.     gridSize = (dim - 2 * margin);
  54.     tileSize = gridSize / size;
  55.    
  56.     setPreferredSize(new Dimension(dimension, dimension + margin));
  57.     setBackground(Color.WHITE);
  58.     setForeground(FOREGROUND_COLOR);
  59.     setFont(new Font("SansSerif", Font.BOLD, 60));
  60.    
  61.     gameOver = true;
  62.    
  63.     addMouseListener(new MouseAdapter() {
  64.       @Override
  65.       public void mousePressed(MouseEvent e) {
  66.         // used to let users to interact on the grid by clicking
  67.         // it's time to implement interaction with users to move tiles to solve the game !
  68.         if (gameOver) {
  69.           newGame();
  70.         } else {
  71.           // get position of the click
  72.           int ex = e.getX() - margin;
  73.           int ey = e.getY() - margin;
  74.          
  75.           // click in the grid ?
  76.           if (ex < 0 || ex > gridSize  || ey < 0  || ey > gridSize)
  77.             return;
  78.          
  79.           // get position in the grid
  80.           int c1 = ex / tileSize;
  81.           int r1 = ey / tileSize;
  82.          
  83.           // get position of the blank cell
  84.           int c2 = blankPos % size;
  85.           int r2 = blankPos / size;
  86.          
  87.           // we convert in the 1D coord
  88.           int clickPos = r1 * size + c1;
  89.          
  90.           int dir = 0;
  91.          
  92.           // we search direction for multiple tile moves at once
  93.           if (c1 == c2  &&  Math.abs(r1 - r2) > 0)
  94.             dir = (r1 - r2) > 0 ? size : -size;
  95.           else if (r1 == r2 && Math.abs(c1 - c2) > 0)
  96.             dir = (c1 - c2) > 0 ? 1 : -1;
  97.            
  98.           if (dir != 0) {
  99.             // we move tiles in the direction
  100.             do {
  101.               int newBlankPos = blankPos + dir;
  102.               tiles[blankPos] = tiles[newBlankPos];
  103.               blankPos = newBlankPos;
  104.             } while(blankPos != clickPos);
  105.            
  106.             tiles[blankPos] = 0;
  107.           }
  108.          
  109.           // we check if game is solved
  110.           gameOver = isSolved();
  111.         }
  112.        
  113.         // we repaint panel
  114.         repaint();
  115.       }
  116.     });
  117.    
  118.     newGame();
  119.   }
  120.  
  121.   private void newGame() {
  122.     do {
  123.       reset(); // reset in intial state
  124.       shuffle(); // shuffle
  125.     } while(!isSolvable()); // make it until grid be solvable
  126.    
  127.     gameOver = false;
  128.   }
  129.  
  130.   private void reset() {
  131.     for (int i = 0; i < tiles.length; i++) {
  132.       tiles[i] = (i + 1) % tiles.length;
  133.     }
  134.    
  135.     // we set blank cell at the last
  136.     blankPos = tiles.length - 1;
  137.   }
  138.  
  139.   private void shuffle() {
  140.     // don't include the blank tile in the shuffle, leave in the solved position
  141.     int n = nbTiles;
  142.    
  143.     while (n > 1) {
  144.       int r = RANDOM.nextInt(n--);
  145.       int tmp = tiles[r];
  146.       tiles[r] = tiles[n];
  147.       tiles[n] = tmp;
  148.     }
  149.   }
  150.  
  151.   // Only half permutations o the puzzle are solvable
  152.   // Whenever a tile is preceded by a tile with higher value it counts
  153.   // as an inversion. In our case, with the blank tile in the solved position,
  154.   // the number of inversions must be even for the puzzle to be solvable
  155.   private boolean isSolvable() {
  156.     int countInversions = 0;
  157.    
  158.     for (int i = 0; i < nbTiles; i++) {
  159.       for (int j = 0; j < i; j++) {
  160.         if (tiles[j] > tiles[i])
  161.           countInversions++;
  162.       }
  163.     }
  164.    
  165.     return countInversions % 2 == 0;
  166.   }
  167.  
  168.   private boolean isSolved() {
  169.     if (tiles[tiles.length - 1] != 0) // if blank tile is not in the solved position ==> not solved
  170.       return false;
  171.    
  172.     for (int i = nbTiles - 1; i >= 0; i--) {
  173.       if (tiles[i] != i + 1)
  174.         return false;      
  175.     }
  176.    
  177.     return true;
  178.   }
  179.  
  180.   private void drawGrid(Graphics2D g) {
  181.     for (int i = 0; i < tiles.length; i++) {
  182.       // we convert 1D coords to 2D coords given the size of the 2D Array
  183.       int r = i / size;
  184.       int c = i % size;
  185.       // we convert in coords on the UI
  186.       int x = margin + c * tileSize;
  187.       int y = margin + r * tileSize;
  188.      
  189.       // check special case for blank tile
  190.       if(tiles[i] == 0) {
  191.         if (gameOver) {
  192.           g.setColor(FOREGROUND_COLOR);
  193.           drawCenteredString(g, "\u2713", x, y);
  194.         }
  195.        
  196.         continue;
  197.       }
  198.      
  199.       // for other tiles
  200.       g.setColor(getForeground());
  201.       g.fillRoundRect(x, y, tileSize, tileSize, 25, 25);
  202.       g.setColor(Color.BLACK);
  203.       g.drawRoundRect(x, y, tileSize, tileSize, 25, 25);
  204.       g.setColor(Color.WHITE);
  205.      
  206.       drawCenteredString(g, String.valueOf(tiles[i]), x , y);
  207.     }
  208.   }
  209.  
  210.   private void drawStartMessage(Graphics2D g) {
  211.     if (gameOver) {
  212.       g.setFont(getFont().deriveFont(Font.BOLD, 18));
  213.       g.setColor(FOREGROUND_COLOR);
  214.       String s = "Click to start new game";
  215.       g.drawString(s, (getWidth() - g.getFontMetrics().stringWidth(s)) / 2,
  216.           getHeight() - margin);
  217.     }
  218.   }
  219.  
  220.   private void drawCenteredString(Graphics2D g, String s, int x, int y) {
  221.     // center string s for the given tile (x,y)
  222.     FontMetrics fm = g.getFontMetrics();
  223.     int asc = fm.getAscent();
  224.     int desc = fm.getDescent();
  225.     g.drawString(s,  x + (tileSize - fm.stringWidth(s)) / 2,
  226.         y + (asc + (tileSize - (asc + desc)) / 2));
  227.   }
  228.  
  229.   @Override
  230.   protected void paintComponent(Graphics g) {
  231.     super.paintComponent(g);
  232.     Graphics2D g2D = (Graphics2D) g;
  233.     g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  234.     drawGrid(g2D);
  235.     drawStartMessage(g2D);
  236.   }
  237.  
  238.   public static void main(String[] args) {
  239.     SwingUtilities.invokeLater(() -> {
  240.       JFrame frame = new JFrame();
  241.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  242.       frame.setTitle("Game of Fifteen");
  243.       frame.setResizable(false);
  244.       frame.add(new GameOfFifteen(4, 550, 30), BorderLayout.CENTER);
  245.       frame.pack();
  246.       // center on the screen
  247.       frame.setLocationRelativeTo(null);
  248.       frame.setVisible(true);
  249.     });
  250.   }
  251.  
  252.  
  253. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement