minesweeper.md 6.65 KB

Recommended JavaScript reading

There is a comprehensive description of JavaScript in the JS notes. It's quite long, so I will point out sections that may be helpful on each project. If you're curious about other parts of JS, you're welcome to read the other sections, or ask me to add more sections to the document.

If you don't have much experience programming in JavaScript or are a bit rusty, I recommend these sections as a refresher:

This is a 2-week project to give you more time to familiarize yourself with JavaScript.

Minesweeper

Goals

  • Get practice using callbacks to handle click events and timer events
  • See how state can be shared between callbacks
  • Learn the basics of how JS interacts with a web page

The game

Minesweeper is a classic Windows puzzle game. There is a rectangular grid with a known number of mines hidden throughout the grid. Clicking on a square reveals whether it is a mine. If it is a mine, you blow up and lose the game. If the square is empty, then it reveals the number of mines in neighboring squares. The objective is to click all the squares that don't have mines without clicking a mine. It is also possible to "flag" a square as a mine: this puts a flag icon on the square and prevents it from being accidentally clicked.

This video shows a playthrough of an easy grid (9 by 9 with 10 mines).

Your task

Implement Minesweeper in browser-side JavaScript.

Your game should have the following features, which are shown in the video linked above.

  • Generates random grids of any height and width with any number of mines
  • Clicking on a square without a mine displays the number of mines in the 8 neighboring squares (up, down, left, right, and diagonally)
  • Clicking on a square with 0 neighboring mines automatically reveals the numbers in the neighboring squares. You should add a small time delay before revealing the neighbors to simulate a ripple effect across large regions of 0s. To verify that your ripple visits the squares efficiently, make sure it looks smooth even on large grids, e.g. 30 x 30 with 10 mines!
  • Clicking on a square with a mine ends the game and displays a loss message (you can use alert() for this). Clicking and flagging are disabled when the game is over.
  • Revealing all the non-mine squares ends the game and displays a win message
  • Right-clicking a square toggles its flag status if it hasn't been left-clicked yet. When a square is flagged, it shows a flag icon and left-clicking on it is disabled.
  • The first click is guaranteed not to hit a mine. Note that this requires the mines to be placed after the first click.
  • Clicking "Play" should create a new grid if a game was in progress

Options if you want to go further:

  • Implement a timer that shows how long the game has been running. The timer should stop when the game is over. You may want to familiarize yourself with the JavaScript Date API.
  • If a square is shift-clicked when it is showing a number and that many neighboring mines are flagged, reveal all its unflagged neighbors. This feature makes playing the real Minesweeper much faster. If, for example, only 2 flags are placed around a square with 3 neighboring mines, do nothing. If there are the right number of flags but any of them are misplaced, the player should blow up and lose the game. You can use the shiftKey property of a click event to check if the shift key was held during the click.

Interacting with the DOM

JavaScript has a large set of standard library functions for manipulating HTML elements on the current page. These object-oriented APIs are collectively called the "Document Object Model".

Here is a minimal subset of APIs you will likely find useful for this project:

  • Querying
    • document.getElementById(id) gets the HTML element whose ID is set to id
    • element.children gets the array of element's child elements
    • input.value gets the (string) value of an input element
  • Modifying elements
    • document.createElement(type) makes a new HTML element of type type (e.g. 'div' or 'td'). The element initially exists only as a JavaScript object, but it can be added to the page:
    • element.appendChild(child) adds child as the last child element of element
    • element.remove() removes element from its parent
    • element.classList represents the set of classes that element has. (In HTML, classes are strings used to identify groups of elements in JavaScript or CSS. Each element can belong to any number of classes.) classList.add(className), classList.remove(className), classList.toggle(className), and classList.contains(className) do what you expect.
  • Event handlers
    • element.addEventListener('click', handler) registers the function handler to be called whenever element is clicked. For example, element.addEventListener('click', () => alert('clicked')) would display the message "clicked" when element is clicked.
    • element.addEventListener('contextmenu', handler) is similar, but for right-clicks instead. You can prevent the normal right-click menu from showing by calling event.preventDefault():
      element.addEventListener('contextmenu', event => {
        event.preventDefault()
        // Handler code...
      })

It is also possible to attach data to HTML elements using DOM APIs, but they are tricky to use. Instead, it is simpler to store the state of the grid using JavaScript objects and arrays.

Codebase

You are provided a small HTML template and a CSS file with styles for the grid. The Minesweeper grid is represented by <table id='grid'></table>, which can be filled with table rows and cells for the grid squares. (HTML tables are created by nesting <tr> row elements inside the table and <td> cell elements inside the rows.) The squares can be styled by adding the class cell to them, as well as additional classes:

  • number-0, ..., number-8: shows that the square has 0, ..., 8 neighbors
  • mine: shows a mine in the square
  • flagged: shows a flag in the square

The HTML and CSS are intentionally minimal, so feel free to improve the styling of the page!