Homework #7

Threes Dice Game (Client Side JavaScript) - Due Wednesday, April 11th Thursday, April 12th, by 11PM

Overview

Goals / Topics Covered

You'll be using the following concepts:

  • manipulating the DOM
  • setting DOM element attributes
  • handling events with addEventListener

Description

Threes is a dice game where the goal is to get the lowest score (with each die face counting for that value, with the exception of threes, which count as 0). It's played with 5 dice. In our version there are two players, the user and the computer.

  1. A player starts off buy rolling 5 dice.
  2. The player then chooses 1 or more dice to pin (that is, to save and count towards their score).
  3. Once at least 1 die is pinned, the player rolls the remaining dice …
  4. The player then chooses 1 or more dice to pin.
  5. This process repeats until the player either pins all of the remaining dice, or there's only 1 die left, and the player is forced to pin that die.
  6. Once all of a player's dice are pinned, the values of all of the rolls are added to determine a player's score.
    • 3's count as 0
    • for example, if the pinned dice were: 1, 4, 1, 2, 3
    • … then the score would be: 8
  7. The other player then repeats steps 1 - 6 to get their score.
  8. The player with the lower score wins.

You will be making an online version of this game where all of the game logic is client side JavaScript. Here's an example of what your game may look like:

Submission Process

You will be given access to a private repository on GitHub. The final version of your assignment should be in GitHub

  • Push your changes to the homework repository on GitHub.

Make at Least 4 Commits

  • Commit multiple times throughout your development process.
  • Make at least 4 separate commits

Threes Dice Game Requirements

Required Features

Use the following markup

  1. Start a new express project that uses express-static.
    • the root directory should contain package.json, your eslint config, .gitignore, etc.
    • the src directory should contain app.js (serving on 3000), public, etc.
  2. (there's no need to create any route handlers for this homework, you can do the whole thing with static files)
  3. In your public folder, create an index.html file.
  4. Add the following code to your index.html:
    <!doctype html>
    <html>
    <head>
      <title>A Little Threesy</title>
      <script src="game.js"></script>
      <link rel="stylesheet" href="base.css" type="text/css" media="screen" title="no title" charset="utf-8">
    </head>
    <body>
      <div id="content">
     <h1>A Little Threesy</h1> 
     <div id="intro">
       Starting Dice Values (leave empty for random):
       <input type="text" id="diceValues" name="diceValues">
       <button>Go!</button>
    
     </div>
       <div id="game" class="hidden">
     </div>
    
     <div id="error-message" class="overlay">
       <div class="modal">
         <p></p>
         <button class="closeButton">Ok. Got it!</button>
       </div>
     </div>
      </div><!--close id="content"-->
    </body>
    </head>
    </html>
    
  5. You are not allowed to use any additional markup; you must generate any additional elements you'll need with JavaScript
  6. All of your JavaScript should go in your external JavaScript file.
  7. … and, of course, all of your CSS should go in your external CSS file.

Only show title screen and form on page load

  • Only show the content in the div with id intro
    • Make sure the overlay div and the game div are not displayed
    • Hint:
      • Make the appropriate CSS rules
      • Use JavaScript's someElementObj.classList's add, remove, and contains to add and remove classes so that you can control which CSS rules are active
  • If the user clicks on the button, then start the game (see next requirement for instructions)
  • Here's what the interaction should look like (you won't have any real content on the next page yet, though):

Pressing "Go" reveals the game screen and sets predefined dice roll results

  • Use addEventListener to allow the button on the title screen to be pressed
    • It should lead to the next screen, which will be contained within the div with id, game
    • Check out the slides on events
    • Along with mdn's documentation on addEventListener, click, and DOMContentLoaded
    • Remember, you'll need to put all of your DOM dependant JavaScript in a DOMContentLoaded listener
    • And, of course, you'll need to add a click event listener for your button
    • document.querySelector will also be very useful - see the docs or slides!
    • Create and apply the appropriate classes to get rid of the title screen (do this with styles, there's no need to remove the element) and show the game screen
  • Note that there's a form field in the title screen
    • This field will allow the player to set the results of the dice rolls in the game (it's kind of like cheating / using loaded dice, but it's really for making it easier to test!)!
    • If the player enters a value in this field, then the dice rolls will be set to the sequence inputted
    • The input should be a comma separated list of numbers, for example 1,2,3,4,5,6,7,8,9
      • the first 5 rolls should be 1,2,3,4,5
      • the next 5 rolls should be 6,7,8,9 plus a random 5th number
    • No validation is required (assume that the user puts in valid input or no input)
    • You can retrieve the user input from the text field by using the value property on the form element that contains the user input - see the mdn docs on value under HTML Input Element
    • If there's nothing in the field, then dice rolls should be random
    • To implement this behavior, see below …
  • Create a function or object that generates dice rolls
    • You'll use this any time a roll is needed for the player or the computer
    • Again, dice rolls should initially be random …
    • However, the function or object should be configurable so that it can draw numbers from the list of numbers entered
    • (Use whatever mechanism you like to do this - perhaps storing the list in a closure or in a property in the function or constructor… or just a plain old global)
    • Note that the first several numbers will be exhausted by the computer's rolls
    • Once the list of specified numbers is exhausted, random numbers should be generated again
  • Here's an example of how it may work (again, the game screen will be blank for now, but when it's implemented, it should function like this):

Generate DOM elements for 5 dice and 3 buttons

  • Create DOM elements to represent 5 dice
    • The elements should start off with no text
    • Hint: you may find it helpful to create a containing element that holds all 5 dice
    • Hint: to lay out all 5 dice adjacent to each other and still maintain a width and height, you can use …
      • display: inline-block
      • a table
      • or float your elements
    • Hint: depending on your layout and positioning, it may be useful to have a vertical-align: top; in the containing element to keep the dice aligned in the same row once you start adding text nodes to them
  • Create three buttons, Start, Roll, and Pin
    • Set Roll and Pin to disabled by adding a disabled attribute
    • For example:<button disabled>My Button</button>
    • Hint: To do this in JavaScript, use either someElementObject.disabled = true; or someElementObject.setAttribute("disabled", "disabled");
  • The generated dice and buttons should look something like this:

Pressing "Start" Generates a score for the computer and shows your score

  • Add an event listener to the Start button so that it when it's clicked it:
    1. Generates a set of pinned dice for the computer
    2. Shows the player's score
    3. Allows the player to start rolling
  • Generate a set of pinned dice (and consequently a score) for the computer
    • The computer will:
      1. roll 5 dice, and pick the lowest out of the 5 to pin
      2. roll the remaining 4 dice, and pick the lowest out of the 4 to pin
      3. roll the remaining 3 dice, and pick the lowest out of the 3 to pin
      4. … and continue to roll remaining dice and choose the single lowest dice to pin until all 5 dice are pinned
    • You can test this by setting the initial form input to 5,5,5,5,5,4,4,4,4,3,3,3,2,2,1
      • (the computer's dice should be 5, 4, 3, 2, and 1)
    • Display the result as Computer Score: 5 + 4 + 3 + 2 + 1 (where the numbers are the values of the pinned dice)
    • This text should go below the title, but above the player's score
  • Display the player's score (which is 0 for now) below the computer's score, but above the dice
  • Here's what the two scores should look like:
  • Finally, disable the Start button and enable the Roll button
    • Hint: To do this in JavaScript, use either someElementObject.disabled = false; or someElementObject.removeAttribute("disabled");

Rolling dice

  • When the roll button is pressed…
  • Assign a random number between 1 and 6 to each unpinned die
    • You'll have to keep track of which dice have been saved / pinned versus dice that have not been pinned
    • Hint: There are many ways to do this:
      • Simply use global variables, or variables that are accessible to all of your listener callback functions
      • You can also base pinned and unpinned on the attributes of the elements themselves (for example, each pinned die could have a data-pinned attribute or could have a class, pinned)
      • Or any other design or architecture that meets the requirements
  • Display that number in each DOM element
  • After you roll, the Roll button should be disabled
  • The pin button should be enabled
  • You can do this by using someElementObject.removeAttribute('disabled');
  • See an example roll below…

Selecting dice to pin

  • After rolling, a player must pin at least one die
  • First, the player must select the dice that they would like to pin
  • They do this by clicking on the actual displayed dice
    • Clicking on a die selects it for pinning (when the Pin button is pressed) and changes the die's appearance (you can make this whatever you like - such as changing the background color, adding a differently styled border, etc.)
    • Note that dice that have already been pinned should not be clickable (they'll be a different style; in the sample program, pinned dice are dark gray)
    • Hovering over the dice should also have an effect on styling (in the examples, hovering over dice show a green background)
    • The example below shows a die selected for pinning being given a background color of light gray…
  • Clicking on a die again deselects it for pinning
    • A user can deselect a die simply by clicking on the die again
    • Note that this reverts its style
    • Hint: someElementObject.classList.toggle(someClassName) is helpful when dealing with flipping back-and-forth between one state and another
    • See below for an example of selecting and then deselecting (toggling)
  • Selecting the dice to pin doesn't actually pin the dice… you'll have to press the pin button to do that (see next step!)

Pressing the pin button

  • Once a player has chosen the dice to pin, they can finalize their choices by clicking the Pin button
    • At least one die must be selected to pin for the Pin button to work
    • Pinned dice can no longer be rolled
    • …And they can't be chosen to be pinned again (they're already pinned!)
  • Clicking on the Pin button will change the styles on the selected dice
    • When the Pin button is pressed, the dice that were not set to be pinned become blank (see image below)
    • The dice that were selected to be pinned receive a different style (in the examples shown, the background is dark gray)
  • After the Pin button is pressed, the player's score should be re-calculated and updated based on the newly pinned dice
  • Again, if there are still unpinned dice after pinning, then they become blank (no number)
  • Everything together should look like:

Determine the winner

Once all of the dice have been pinned…

  1. Disable both the Pin and Roll buttons
  2. Compare the computer's score with the user's score
  3. The player with the lower score wins
  4. Add text to indicate of the user won, lost or tied
  5. The text should be styled differently depending on the game outcome… for example, in the screen captures for the solution's version…
    • win text is green
    • lose text is red
    • tie text is blue
  6. See the example below of a user losing a game:

Validation and error messages

There are two interactions that the user shouldn't be able to do:

  1. Click on the Pin button before the user has selected dice to pin
  2. Select a die to pin before a they've rolled (that is, click on the die when it's blank)

If either state occurs, create an overlay on the screen with the appropriate error message. When they click on the OK button (or even better anywhere, in the overlay), the overlay should disappear.

  • implement this using plain CSS and/or JavaScript (no JQuery or bootstrap)
  • Hint: the overlay should be position:fixed with a height and width of 100%
  • Hint: the overlay's z-index should be a high number (100?) so that it appears on top of everything else
  • Hint: to center content within the overlay, margin: auto and a specific width is helpful

Optional Features (Extra Credit)

Implement any of the following features

(10 points) Restart game

  • when game ends, add a restart button along the bottom row of buttons
  • the restart button resets the game so that:
    • the computer generates a new score
    • the player starts with 5 unpinned dice
    • Start and Pin are disabled, but Roll is enabled

(5 points) Add actual dice faces to the game

  • instead of numbers, add an actual face to each die
  • (the computer's pinned dice can remain as numbers, though)