/jse-fall15-7

JS-evening fall 2015 Homework, week 7

Primary LanguageJavaScriptMIT LicenseMIT

Homework 7

Due Wed. Sept.2

###Memory Game

This week you'll be writing an interactive browser game of Memory, in which some set of cards are arranged face down on a board and a player turns them over looking for pairs.

The game's implementation will be divided into three independent modules which work together: Memory Cards, MemoryGame, and MemoryGUI. Each should be a constructor packaged as an IIFE in a separate file, memory-cards.js, memory-game.js, and memory-gui.js. Each constructor can implement its instance methods and data in either of two ways: 1) storing instance data in public properties which are accessed through shared prototype methods, or 2) using instance-specific methods which access private data variables through closure.

The body of your application will be a single HTML file which imports the three modules and ties them together in a main file.

1) MemoryCards [10%]

The MemoryCards module specifies the game's content: the values and pairings of the memory cards. You may use one of the three simple examples provided in cardset-example.js, or you may write your own. You might choose to adapt your earlier playing-card module or write a new one for a completely different system of cards.

Your MemoryCards constructor needs to build only one instance, an object representing a set of cards rather than an individual card. The MemoryCards instance should have the following methods:

  • values(): return an array of all the card values in the set. Each value could be either an object or a primitive, depending on how you choose to implement your cards.

  • match(valA,valB): given card values valA and valB (both of which should be found in values()), return true if they match as a pair, or false otherwise.

  • display(val): given card value val, return a string which represents that card. If your card values are already strings, this method could merely return val, but if your card values are objects, you'll need to generate a string version (e.g. the name() or shortName() of playing-card objects)

For example, if you decide to use regular playing cards in your game, your values() could be a set of 52 card objects generated by your Card constructor. In that case, an appropriate match() function might return true if two cards are the same rank and same color, creating 26 matching pairs, and display() might return the card's name.

2) MemoryGame [10%]

The MemoryGame module (a.k.a. the "Model") represents the rules and status of a game (i.e. this positions of the cards and which ones have been matched). It has an API (command-line interface) but otherwise has no commitment to any graphical representation nor any particular set of cards. The game's "board" will be a single row of cards which are identified by a single position number (the GUI is what displays it as a 2D grid).

Each call to the constructor MemoryGame(cardset) will construct one game instance using the cards represented in cardset, an instance of MemoryCards. Each MemoryGame instance must keep track of whether and where any card is face up, where any matching cards have been removed, and where unmatched cards remain. It will have these methods:

  • reset() replaces all removed cards, reshuffles the entire board, and rebuilds the board face-down.

  • faceupWhere() returns the position (a number) of the one face-up card (if any), otherwise returns false.

  • faceupValue() returns the raw value of the one face-up card (if any), otherwise false.

  • remaining() returns an array of the positions of all cards still on the board, including faceupWhere() if any.

  • lift(where) attempts to lift a card. If there is a face-down card at position where (a single number), return its display value (by calling cardset.display(val) callback on the card's raw value); otherwise return false. If there is not currently a face-up card, leave this card face-up. If there's already a face-up card, do one of the following:

    • If this card and the face-up card match (according to cardset.match()), remove both from the board. If all pairs are removed from the board, you win the game.

    • If there is no match, leave both cards in place and turn them face down.

It should be possible (though inconvenient) to play an entire game through the console, one lift() call at a time.

This module has been written for you! You will need to decipher how it works and decide how to integrate your GUI with it.

3) MemoryGUI [80%]

The GUI module (a.k.a. the "View") knows nothing about the logical structure or rules of the game, but it knows how to draw it in the browser and how to initiate action with clicks. The GUI includes both a .js and a .css file which work together and share the same assumptions about the interface.

The following steps will help guide you through the development of the GUI, but you don't need to provide answers along the way. You may develop your GUI by any process you like, as long as it has the API (i.e. set of public methods) outlined in the template file.


a) In the GUI's IIFE, write a constructor MemoryGUI(container, game).
The first parameter container should be either a DOM element or the id of one, and tells the GUI instance where in the document to render the interface. The second parameter game should be an instance of MemoryGame; the GUI instance will remember that object as its game model, which represents the game's status.

When the GUI instance is created, it can call game.size() to decide how many cards to draw. Arrange the cards in an approximately-square grid of cells. You many use a <table> or a series of <div>s. Each cell should have a unique id which maps onto a number, the position of the corresponding card in your game model. Place the grid within container. The html file template defines a <div> labeled 'memorygame' which could be used as the GUI's container.

The GUI instance should immediately render the game's starting state, with all cards face down. But any updates will be driven by the game; the GUI just waits for game to call its various rendering methods.


b) Make each grid cell clickable. When a cell with an id like 'idN' (for some number N) is clicked, it should call game.lift(N), which attempts a change to the game state. If the lift is successful (which game decides), game will call one or more of gui's rendering methods (see part d below) to update the display.

Before you implement the rendering updates below, debug your clicking mechanism by providing a "dummy" game which merely reports any lift() calls from gui.


c) Each card on the board can be in one of three states: face-down, face-up, and matched (possibly removed or hidden, depending on how you want your interface to look). For each state, define a CSS class to display a card in that state. The body of each card can be either the grid cell itself or another element within it.


d) Give your GUI instance three rendering methods, one for each state above:

  • show(where,displayString): turn one card (at position where) face-up immediately. The display value of that card (according to the game model) will be provided as parameter displayString.

  • hideSoon(whereArray): turn some cards (at positions listed in whereArray) face-down, but only after some time (or triggering event) decided by your GUI. (Hint: you might want to use window.setTimeout).

  • removeSoon(whereArray): turn some cards (at positions listed in whereArray) into the matched state, but only after a delay decided by your GUI.


e) Write another method of GUI:

  • reset(): set the state of all cards to face-down, restoring the initial display.

Then add a Reset button to your interface, implemented and styled any way you like. When pushed, it should reset both GUI and game (by calling game.reset()).

At this point, you should be able to debug your GUI by manually simulating the game from the console, driving the GUI through its four public methods (reset,show,hideSoon, and removeSoon).


f) Once your GUI is debugged, you can replace the dummy game with a real game instance. You should now be able to play the game through the GUI interface.


g) Improve whatever card set you've been using, either the simple ones from the template or a custom set you've written. Be as creative as you like. For the card values, you can use strings, numbers, or objects (which are matched according to some common property and converted to a string using the cardset's display() method). If you want your GUI to display images instead of strings, the display() method could return an image URL for the GUI to render.

Have fun!