Build a Sliding Puzzle Game With JavaScript

CSSCSSBeginner
Practice Now

Introduction

Welcome to this project on building a simple Sliding Puzzle game using JavaScript. By the end of this guide, you'll have a functioning 3x3 sliding puzzle game that you can play in your browser. The game will feature numbered tiles, a timer, and controls to start, pause, and reset the game.

No prior experience is required, but a basic understanding of JavaScript and HTML would be helpful. Let's dive in!

👀 Preview

effect

🎯 Tasks

In this project, you will learn:

  • How to design the game layout in HTML
  • How to write the CSS styles for the game
  • How to initialize game variables in JavaScript
  • How to implement the move function to handle tile movements
  • How to determine possible tile movements
  • How to implement the game timer
  • How to control the game flow with start, pause, and reset functions
  • How to shuffle the tiles randomly at the beginning or when reset
  • How to initialize the game on page load

🏆 Achievements

After completing this project, you will be able to:

  • Design the layout of a game using HTML
  • Style elements using CSS
  • Implement game logic using JavaScript
  • Handle user input and perform actions based on it
  • Manipulate the DOM to update the game state and display information

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL css(("`CSS`")) -.-> css/BasicConceptsGroup(["`Basic Concepts`"]) css(("`CSS`")) -.-> css/BasicStylingGroup(["`Basic Styling`"]) css(("`CSS`")) -.-> css/CoreLayoutGroup(["`Core Layout`"]) css(("`CSS`")) -.-> css/IntermediateStylingGroup(["`Intermediate Styling`"]) css(("`CSS`")) -.-> css/DynamicStylingGroup(["`Dynamic Styling`"]) css(("`CSS`")) -.-> css/CSSPreprocessorsGroup(["`CSS Preprocessors`"]) css(("`CSS`")) -.-> css/CodingStandardsandBestPracticesGroup(["`Coding Standards and Best Practices`"]) html(("`HTML`")) -.-> html/BasicStructureGroup(["`Basic Structure`"]) html(("`HTML`")) -.-> html/TextContentandFormattingGroup(["`Text Content and Formatting`"]) html(("`HTML`")) -.-> html/LayoutandSectioningGroup(["`Layout and Sectioning`"]) javascript(("`JavaScript`")) -.-> javascript/BasicConceptsGroup(["`Basic Concepts`"]) javascript(("`JavaScript`")) -.-> javascript/AdvancedConceptsGroup(["`Advanced Concepts`"]) javascript(("`JavaScript`")) -.-> javascript/DOMManipulationGroup(["`DOM Manipulation`"]) javascript(("`JavaScript`")) -.-> javascript/ToolsandEnvironmentGroup(["`Tools and Environment`"]) javascript(("`JavaScript`")) -.-> javascript/SecurityGroup(["`Security`"]) css/BasicConceptsGroup -.-> css/selectors("`Selectors`") css/BasicStylingGroup -.-> css/colors("`Colors`") css/BasicStylingGroup -.-> css/fonts("`Fonts`") css/BasicStylingGroup -.-> css/text_styling("`Text Styling`") css/CoreLayoutGroup -.-> css/box_model("`Box Model`") css/CoreLayoutGroup -.-> css/margin_and_padding("`Margin and Padding`") css/CoreLayoutGroup -.-> css/borders("`Borders`") css/CoreLayoutGroup -.-> css/width_and_height("`Width and Height`") css/CoreLayoutGroup -.-> css/display_property("`Display Property`") css/CoreLayoutGroup -.-> css/positioning("`Positioning`") css/IntermediateStylingGroup -.-> css/backgrounds("`Backgrounds`") css/DynamicStylingGroup -.-> css/animations("`Animations`") css/DynamicStylingGroup -.-> css/transitions("`Transitions`") css/CSSPreprocessorsGroup -.-> css/nesting("`Nesting`") css/CodingStandardsandBestPracticesGroup -.-> css/comments("`Comments`") html/BasicStructureGroup -.-> html/basic_elems("`Basic Elements`") html/BasicStructureGroup -.-> html/charset("`Character Encoding`") html/BasicStructureGroup -.-> html/lang_decl("`Language Declaration`") html/BasicStructureGroup -.-> html/head_elems("`Head Elements`") html/TextContentandFormattingGroup -.-> html/para_br("`Paragraphs and Line Breaks`") html/LayoutandSectioningGroup -.-> html/doc_flow("`Document Flow Understanding`") css/IntermediateStylingGroup -.-> css/pseudo_classes("`Pseudo-classes`") javascript/BasicConceptsGroup -.-> javascript/variables("`Variables`") javascript/BasicConceptsGroup -.-> javascript/data_types("`Data Types`") javascript/BasicConceptsGroup -.-> javascript/arith_ops("`Arithmetic Operators`") javascript/BasicConceptsGroup -.-> javascript/comp_ops("`Comparison Operators`") javascript/BasicConceptsGroup -.-> javascript/cond_stmts("`Conditional Statements`") javascript/BasicConceptsGroup -.-> javascript/loops("`Loops`") javascript/BasicConceptsGroup -.-> javascript/functions("`Functions`") javascript/BasicConceptsGroup -.-> javascript/str_manip("`String Manipulation`") javascript/BasicConceptsGroup -.-> javascript/array_methods("`Array Methods`") javascript/BasicConceptsGroup -.-> javascript/obj_manip("`Object Manipulation`") javascript/AdvancedConceptsGroup -.-> javascript/async_prog("`Asynchronous Programming`") javascript/DOMManipulationGroup -.-> javascript/dom_select("`DOM Selection`") javascript/DOMManipulationGroup -.-> javascript/dom_manip("`DOM Manipulation`") javascript/ToolsandEnvironmentGroup -.-> javascript/bom("`Browser Object Model`") javascript/SecurityGroup -.-> javascript/xss("`Cross-Site Scripting`") subgraph Lab Skills css/selectors -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/colors -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/fonts -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/text_styling -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/box_model -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/margin_and_padding -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/borders -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/width_and_height -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/display_property -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/positioning -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/backgrounds -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/animations -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/transitions -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/nesting -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/comments -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/basic_elems -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/charset -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/lang_decl -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/head_elems -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/para_br -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} html/doc_flow -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} css/pseudo_classes -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/variables -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/data_types -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/arith_ops -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/comp_ops -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/cond_stmts -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/loops -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/functions -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/str_manip -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/array_methods -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/obj_manip -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/async_prog -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/dom_select -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/dom_manip -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/bom -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} javascript/xss -.-> lab-298926{{"`Build a Sliding Puzzle Game With JavaScript`"}} end

Design the Game Layout in HTML

Design the game's layout in index.html as mentioned in the previous breakdown.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Puzzle</title>
    <link rel="stylesheet" type="text/css" href="puzzle.css" />
    <script type="text/javascript" src="puzzle.js"></script>
  </head>
  <body>
    <div id="container">
      <!-- Outermost DIV, used to contain the inner structure -->
      <div id="game">
        <!-- Game area, which is the large DIV block -->
        <div id="d1" onclick="move(1)">1</div>
        <!-- Small DIVs, which are the 8 small blocks. When clicked, the move() function is executed. The parameter is the displayed number, so we know which block was clicked -->
        <div id="d2" onclick="move(2)">2</div>
        <div id="d3" onclick="move(3)">3</div>
        <div id="d4" onclick="move(4)">4</div>
        <div id="d5" onclick="move(5)">5</div>
        <div id="d6" onclick="move(6)">6</div>
        <div id="d7" onclick="move(7)">7</div>
        <div id="d8" onclick="move(8)">8</div>
      </div>
      <div id="control">
        <!-- Game control area -->
        <p>
          <rowspan id="timeText">Total Time</rowspan>
          <!--  "Total Time" -->
          <rowspan id="timer"></rowspan>
        </p>
        <!-- Display game time area -->
        <p>
          <rowspan id="start" onclick="start()">Start</rowspan>
          <!--  "Start" -->
          <rowspan id="reset" onclick="reset()">Retry</rowspan>
          <!--  "Retry" -->
        </p>
        <!-- Display control button area -->
      </div>
    </div>
  </body>
</html>

Writing the CSS Styles

After completing the layout structure, it's time to beautify our game with CSS styles. During this phase, you have the liberty to customize and design the game to your preference, adding unique touches and details. You could also introduce more decorative elements to enhance the game's aesthetics. However, an important note: if you change the size of the game's main DIV, ensure you adjust the accompanying JavaScript code. We'll discuss this in greater depth shortly.

In puzzle.css, add:

* {
  padding: 0;
  margin: 0;
  border: 0;
}
/* The '*' is a wildcard, removing default styles for all elements because some browsers might add default styles, which could create layout problems. */

body {
  width: 100%;
  height: 100%;
}
/* Setting body height and width to 100%, so it auto-adapts according to browser screen size. */

#container {
  position: relative;
  width: 620px;
  height: 450px;
  margin: 0 auto;
  margin-top: 100px;
  border-radius: 1px;
}
/* This is the DIV that wraps all elements. Set to 620px width and 450px height. This size can be increased, but not decreased. It should be big enough to contain all internal elements. */

#game {
  position: absolute;
  width: 450px;
  height: 450px;
  border-radius: 5px;
  display: inline-block;
  background-color: #ffe171;
  box-shadow: 0 0 10px #ffe171;
}
/* This is the game area DIV. The size is calculated based on the size of the smaller blocks. Here, the blocks are set to 150px by 150px, so the game area is 150px*3, equaling 450px. */

#game div {
  position: absolute;
  width: 149px;
  height: 149px;
  box-shadow: 1px 1px 2px #777;
  background-color: #20a6fa;
  color: white;
  text-align: center;
  font-size: 150px;
  line-height: 150px;
  cursor: pointer;
  -webkit-transition: 0.3s; /* Browser prefix for Chrome compatibility */
  -moz-transition: 0.3s; /* For Firefox */
  -ms-transition: 0.3s; /* For IE */
  -o-transition: 0.3s; /* For Opera */
  transition: 0.3s;
}
/* This sets the size of the small blocks. Positioned as absolute, changes in position won't affect the positions of other elements. The width and height are both 149px. Notably, there's a box-shadow, adding to its overall dimension. The 'transition:0.3s' property makes changes appear as animations, so when the block's position changes, an animation plays automatically. */

#game div:hover {
  color: #ffe171;
}
/* Setting mouse hover animation for the blocks. When the mouse hovers over an element, these properties replace the above ones, and it returns to the original when the mouse moves away. Here, we're changing the font color. */

#control {
  width: 150px;
  height: 450px;
  display: inline-block;
  float: right;
}
/* Control section. 'display:inline-block' allows the element to maintain block properties while not taking up a full line, and 'float:right' positions it on the right side. */

#control rowspan {
  height: 25px;
  font-size: 20px;
  color: #222;
  margin-top: 10px;
}
/* Common styles for buttons in the control area. */

#start {
  display: inline-block;
  font-size: 28px;
  width: 100px;
  height: 28px;
  background-color: #20a6fa;
  color: #ffe171;
  text-shadow: 1px 1px 2px #ffe171;
  border-radius: 5px;
  box-shadow: 2px 2px 5px #4c98f5;
  text-align: center;
  cursor: pointer;
}
/* Styles for the 'Start' button. 'cursor:pointer' makes the cursor change to a hand symbol when hovering over it. */

#reset {
  display: inline-block;
  font-size: 28px;
  width: 100px;
  height: 28px;
  background-color: #20a6fa;
  color: #ffe171;
  text-shadow: 1px 1px 2px #ffe171; /* Font shadow */
  border-radius: 5px; /* Rounded corners */
  box-shadow: 2px 2px 5px #4c98f5; /* Box shadow */
  text-align: center; /* Center text */
  cursor: pointer;
}
/* Styles for the 'Reset' button. */

#d1 {
  left: 0px;
}
#d2 {
  left: 150px;
}
#d3 {
  left: 300px;
}
#d4 {
  top: 150px;
}
#d5 {
  top: 150px;
  left: 150px;
}
#d6 {
  top: 150px;
  left: 300px;
}
#d7 {
  top: 300px;
}
#d8 {
  left: 150px;
  top: 300px;
}
/* Pre-defined positions for each of the blocks in order. */

Having the styles set is a significant step forward. Next, we'd move on to the JavaScript logic to bring our puzzle game to life. It's advised that initial styling be done as per the instructions above. Once the game's functionality is well-understood, you're free to exhibit your creativity and further customize the styles. However, making early uninformed changes could lead to unforeseen glitches.

Initialize Game Variables in JavaScript

Before adding game functionality, initialize essential game state variables.

In puzzle.js, add:

// puzzle.js

var time = 0;
// Save the elapsed time
var pause = true;
// Flag to indicate if the game is paused, true means it's paused
var set_timer;
// Timer function
var d = new Array(10);
// Store the number of the small DIV currently in the large DIV
var d_direct = new Array(
  [0],
  [2, 4], // The large DIV with the number 1 can go to positions 2 and 4
  [1, 3, 5],
  [2, 6],
  [1, 5, 7],
  [2, 4, 6, 8],
  [3, 5, 9],
  [4, 8],
  [5, 7, 9],
  [6, 8]
);
// Save the possible move positions for the large DIVs
var d_posXY = new Array(
  [0],
  [0, 0], // The first number represents left, the second represents top. E.g., the first block's position is left:0px, top:0px
  [150, 0],
  [300, 0],
  [0, 150],
  [150, 150],
  [300, 150],
  [0, 300],
  [150, 300],
  [300, 300]
);
// Position of the large DIVs
d[1] = 1;
d[2] = 2;
d[3] = 3;
d[4] = 4;
d[5] = 5;
d[6] = 6;
d[7] = 7;
d[8] = 8;
d[9] = 0;
// Default arrangement, the ninth large DIV is empty, so it's 0. We use 0 to represent the blank tile.

Implement the Move Function

Create a function that allows tiles to move based on user interaction.

Add to puzzle.js:

// puzzle.js

function move(id) {
  // Move function
  var i = 1;
  for (i = 1; i < 10; ++i) {
    if (d[i] == id) break;
  }
  // This loop finds the position of the small DIV within the large DIV
  var target_d = 0;
  // Save the possible positions for the small DIV, 0 means it can't move
  target_d = whereCanTo(i);
  // Find out where the small DIV can move to. If it returns 0, it means it can't move, otherwise, it returns the target position number
  if (target_d != 0) {
    d[i] = 0;
    // Set the current large DIV number to 0 because the small DIV has moved, so the current large DIV is now empty
    d[target_d] = id;
    // Set the target large DIV to the number of the clicked small DIV
    document.getElementById("d" + id).style.left = d_posXY[target_d][0] + "px";
    document.getElementById("d" + id).style.top = d_posXY[target_d][1] + "px";
    // Finally, set the clicked small DIV's position to the target large DIV's position
  }

  var finish_flag = true;
  // Flag to indicate if the game is completed, true means it's completed
  for (var k = 1; k < 9; ++k) {
    if (d[k] != k) {
      finish_flag = false;
      break;
    }
  }
  // Iterate from 1, check each large DIV number. If they are not in order, the game is not completed.
  if (finish_flag == true) {
    if (!pause) start();
    alert("Congratulations!");
  }
  // If true, the game is completed. If it's not paused, call the pause function and display a success message.
}

Determine Possible Tile Movements

Create a function to determine where a tile can move based on its current position.

Add to puzzle.js:

// puzzle.js

function whereCanTo(cur_div) {
  // Function to determine if a given DIV can move and to which position
  var j = 0;
  var move_flag = false;
  for (j = 0; j < d_direct[cur_div].length; ++j) {
    if (d[d_direct[cur_div][j]] == 0) {
      move_flag = true;
      break;
    }
  }
  if (move_flag == true) {
    return d_direct[cur_div][j];
  } else {
    return 0;
  }
  // If it can move, return the target position number, otherwise return 0
}

Implement the Game Timer

Add a timer function to keep track of elapsed time during the game.

Add to puzzle.js:

// puzzle.js

function timer() {
  // Timer function, executes every second
  time += 1;
  var min = parseInt(time / 60); // Convert seconds to minutes
  var sec = time % 60; // Get the remaining seconds
  document.getElementById("timer").innerHTML =
    min + " minutes " + sec + " seconds";
}

Control the Game Flow

Implement functions to start, pause, or reset the game based on user input.

Add to puzzle.js:

// puzzle.js

function start() {
  // Start or pause the game
  if (pause) {
    document.getElementById("start").innerHTML = "Pause";
    pause = false;
    set_timer = setInterval(timer, 1000);
  } else {
    document.getElementById("start").innerHTML = "Start";
    pause = true;
    clearInterval(set_timer);
  }
}
function reset() {
  // Reset the game
  time = 0;
  random_d();
  if (pause) start();
}

Shuffle the Tiles Randomly

Create a function to randomly shuffle the tiles at the beginning or when reset.

Add to puzzle.js:

// puzzle.js

function random_d() {
  // Randomly shuffle the tiles
  for (var i = 9; i > 1; --i) {
    var to = parseInt(Math.random() * (i - 1) + 1);
    if (d[i] != 0) {
      document.getElementById("d" + d[i]).style.left = d_posXY[to][0] + "px";
      document.getElementById("d" + d[i]).style.top = d_posXY[to][1] + "px";
    }
    if (d[to] != 0) {
      document.getElementById("d" + d[to]).style.left = d_posXY[i][0] + "px";
      document.getElementById("d" + d[to]).style.top = d_posXY[i][1] + "px";
    }
    var tem = d[to];
    d[to] = d[i];
    d[i] = tem;
  }
}

Initialize the Game on Page Load

Lastly, ensure that when the web page is loaded, the game tiles are displayed and ready to play.

Add to puzzle.js:

// puzzle.js

// Initialize the game on page load
window.onload = function () {
  reset();
};

Running the App

  • Open index.html in a web browser.
    open web

  • The effect of the page is as follows:
    effect

Summary

You've now successfully built a Sliding Puzzle game using JavaScript in a structured and organized manner! The step-by-step breakdown should make it easier for learners to understand each component of the game's logic. Remember, this is just a basic version, and there's a lot of room for enhancements and added features. Dive in and make it your own. Happy coding!

Other CSS Tutorials you may like