Skip to content

Instantly share code, notes, and snippets.

@thompson4822
Created January 26, 2014 15:55
Show Gist options
  • Save thompson4822/8634757 to your computer and use it in GitHub Desktop.
Save thompson4822/8634757 to your computer and use it in GitHub Desktop.
Simple Memory Game For Lenny
import 'dart:html' as html;
import 'dart:math';
import 'package:stagexl/stagexl.dart';
Stage stage = null;
RenderLoop renderLoop;
/**
* Tracks all of the glyphs and can create new ones on demand.
*/
class GlyphManager {
List<BitmapData> glyphs = [];
/**
* Build a GlyphManager with [TextureAtlas] glyphs
*/
GlyphManager(TextureAtlas glyphAtlas) {
glyphs = glyphAtlas.getBitmapDatas("glyph");
}
/**
* Returns a new glyph based on the glyph atlas [index]
*
* multiple calls with the same [index] will result in different [Bitmap] items.
*/
Bitmap newGlyph(int index) {
var dropShadow = new DropShadowFilter(4, 45, 0x000000, 0.6, 10, 10, 2);
var glyph=glyphs[index];
return new Bitmap(glyph)
..filters = [dropShadow]
..applyCache(0, 0, glyph.width, glyph.height); // IMPORTANT - This is necessary to apply the filters!
}
/// A convenience method for finding the number of glyphs in the atlas
int get length => glyphs.length;
}
/**
* Represents a single tile (rectangular region) with a single glyph.
*/
class Tile extends Sprite {
/// The glyph shown on this tile
Bitmap glyph;
/// The id of the glyph, which can be used to determine if tiles match
int glyphId;
/// Creates a new [glyph] with the given [glyphId]
Tile(var this.glyph, var this.glyphId) {
var gradient = new GraphicsGradient.linear(6, 6, 88, 88);
gradient.addColorStop(0, Color.White);
gradient.addColorStop(1, Color.Bisque);
graphics
..beginPath()
..rectRound(6, 6, 88, 88, 8, 8)
..closePath()
..fillGradient(gradient)
..strokeColor(Color.Black, 1);
glyph
..x = (this.width - glyph.width) / 2
..y = (this.height - glyph.height) / 2
..alpha = 1.0
..addTo(this);
}
/// Is the glyph on this tile in any way visible?
bool get isVisible => glyph.alpha > 0;
/// Toggle the visibility of the glyph between visible and invisible.
void flip() {
var alphaSetting = glyph.alpha == 0 ? 1.0 : 0.0;
var tween = new Tween(glyph, 0.3, TransitionFunction.linear)
..animate.alpha.to(alphaSetting);
renderLoop.juggler.add(tween);
}
}
/**
* Grid manages the tiles shown on the screen
*/
class Grid extends DisplayObjectContainer {
/// The tiles that the grid tracks.
List<Tile> tiles;
/// The number of columns and rows the grid is in size
int columns, rows;
/// The current user selection (one or two tile indices)
List<int> selected = [];
/**
* Returns a new Grid based on the given [GlyphManager] with optional size.
*
* Note that at this time, the columns and rows should be left with their defaults.
* Anything else would not work very well.
*/
Grid(GlyphManager glyphManager, {var this.columns: 5, var this.rows: 4} ) {
var rand = new Random(new DateTime.now().millisecondsSinceEpoch);
List<int> initialList = [];
tiles = new List<Tile>(columns * rows);
// Create a list like [0, 0, 1, 1, 2, 2, ...]
for(int glyphIndex = 0; glyphIndex < glyphManager.length; glyphIndex++) {
initialList.add(glyphIndex);
initialList.add(glyphIndex);
}
// Using the previous list, pick glyphs and populate the tiles
for(int index = 0; index < tiles.length; index++) {
var randomIndex = rand.nextInt(initialList.length);
var glyphId = initialList[randomIndex];
var tile = new Tile(glyphManager.newGlyph(glyphId), glyphId)
// wonky math because of Dart's lack of 2D arrays
..x = (index % columns) * 100
..y = (index / columns).floor() * 100
..onMouseClick.listen((e) => updateSelected(index))
..addTo(this);
tiles[index] = tile;
initialList.removeAt(randomIndex);
}
}
/// Updates the appearance of the currently selected tiles, where the [index] of the last selection is given.
void updateSelected(int index) {
// This check eliminates the cheat that my son found!
if(selected.contains(index) == false) {
selected.add(index);
tiles[index].flip();
if(selected.length == 2) {
var tile1 = tiles[selected[0]];
var tile2 = tiles[selected[1]];
if(tile1.glyphId != tile2.glyphId) {
// Make the following part of a stream!
updateIncorrectGuesses();
var delayedAction = new DelayedCall(() {
tile1.flip();
tile2.flip();
}, 1.0);
renderLoop.juggler.add(delayedAction);
}
else {
// Great place to use a stream!
// var gameWon = tiles.where((tile) => tile.isVisible).length == tiles.length - 1;
// if(gameWon) {
// playAgain();
// }
}
selected = [];
}
}
}
/// Briefly shows what the puzzle will look like when solved.
void showSolved() {
for(int index = 0; index < tiles.length; index++) {
var delayedAction = new DelayedCall(() => tiles[index].flip(), 3.0);
renderLoop.juggler.add(delayedAction);
}
}
}
/// Main entry point
void main() {
setupStage();
var resourceManager = new ResourceManager()
..addTextureAtlas("glyphs", "images/glyphAtlas.json", TextureAtlasFormat.JSONARRAY);
resourceManager.load().then((result) => startGame(resourceManager));
}
/// General purpose method of getting the canvas, stage and render loop ready.
void setupStage() {
// setup the Stage and RenderLoop
var canvas = html.querySelector('#stage');
stage = new Stage('myStage', canvas);
renderLoop = new RenderLoop();
renderLoop.addStage(stage);
}
/// Kicks off the game with the given [ResourceManager]
void startGame(ResourceManager manager) {
var glyphManager = new GlyphManager(manager.getTextureAtlas("glyphs"));
html.querySelector("button")
..onClick.listen((e){
if(stage.numChildren > 0)
stage.removeChildren();
var grid = new Grid(glyphManager)
..addTo(stage);
grid.showSolved();
html.querySelector("#incorrectGuesses").innerHtml="0";
});
}
/// Bumps the number of incorrect guesses shown on the screen up by 1.
void updateIncorrectGuesses() {
var incorrectGuesses = html.querySelector("#incorrectGuesses");
incorrectGuesses.innerHtml = "${int.parse(incorrectGuesses.text) + 1}";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment