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:
- Running JS code in the browser
- Basic types
- Using variables
-
if
statements andwhile
loops - (Anonymous) functions
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 toid
-
element.children
gets the array ofelement
's child elements -
input.value
gets the (string) value of aninput
element
-
-
Modifying elements
-
document.createElement(type)
makes a new HTML element of typetype
(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)
addschild
as the last child element ofelement
-
element.remove()
removeselement
from its parent -
element.classList
represents the set of classes thatelement
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)
, andclassList.contains(className)
do what you expect.
-
-
Event handlers
-
element.addEventListener('click', handler)
registers the functionhandler
to be called wheneverelement
is clicked. For example,element.addEventListener('click', () => alert('clicked'))
would display the message "clicked" whenelement
is clicked. -
element.addEventListener('contextmenu', handler)
is similar, but for right-clicks instead. You can prevent the normal right-click menu from showing by callingevent.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!