You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

424 lines
14 KiB

require([ "dojo/_base/declare", "dojo/dom-construct", "dojo/dom-style", "dojo/on", "dojo/query", "dojo/dom-class", "dojo/_base/lang", "dojo/dom", "dojox/timing" ],
function(declare, domConstruct, domStyle, on, query, domClass, lang, dom) {
declare("fenouiltonic.game100.gamemodel", null, {
_gameGraph: null,
_gameState: [],
_currentVertex: null,
_xmax: null,
_ymax: null,
_game: null,
_gameController: null,
constructor: function() {
},
setGameParameters: function(xmax, ymax, gameGraph) {
if (gameGraph == null) {
gameGraph = this._createDefaultGraph(xmax, ymax);
}
this._gameGraph = gameGraph;
this._xmax = xmax;
this._ymax = ymax;
// Fire notifications
if (this._gameController != null) {
this._gameController.gameParametersChanged();
}
},
_createDefaultGraph: function(xmax, ymax) {
var graph = {};
var mvtx = [2, 3, 2, 0, -2, -3, -2, 0];
var mvty = [2, 0, -2, -3, -2, 0, 2, 3];
for (var x = 0; x < xmax; x++) {
for (var y = 0; y < ymax; y++) {
var edges = [];
for (var m = 0; m < mvtx.length; m++) {
var newx = x + mvtx[m];
var newy = y + mvty[m];
if (newx >= 0 && newy >= 0 && newx < xmax && newy < ymax) { // in game boundaries ?
edges.push(":" + newx + "-" + newy);
}
}
graph[":" + x + "-" + y] = edges;
}
}
return graph;
},
getGameWidth: function () {
return this._xmax;
},
getGameHeight: function () {
return this._ymax;
},
setController: function(controller) {
this._gameController = controller;
},
isStarted: function() {
return this._gameState.length > 0;
},
isWon: function() {
return this._gameState.length == (this._xmax * this._ymax);
},
getCurrentCount: function() {
return this._gameState.length;
},
isLost: function() {
if (! this.isStarted()) {
return false;
}
if (this.isWon()) {
return false;
}
var edges = this.getEdges(this._currentVertex);
var cannotMove = true;
for (var i = 0; i < edges.length; i++) {
var cannotMoveToThisEdge = false;
for (var j = 0; j < this._gameState.length; j++) {
if (this._gameState[j] == edges[i]) {
cannotMoveToThisEdge = true;
break;
}
}
cannotMove = cannotMove && cannotMoveToThisEdge;
}
return cannotMove;
},
setGameState: function(currentVertex, gameState) {
if (currentVertex == null) {
throw "Missing arg 'currentVertex'";
}
this._currentVertex = currentVertex;
this._gameState = typeof gameState !== 'undefined' ? gameState : [];
// Fire notifications
if (this._gameController != null) {
this._gameController.gameStateChanged();
}
},
getGameState: function() {
return this._gameState;
},
startNewGame: function() {
this._currentVertex = null;
this._gameState = [];
// Fire notifications
if (this._gameController != null) {
this._gameController.gameStateChanged();
}
},
undo: function() {
if (this._currentVertex == null) {
return; // Very beginning, nothing to do !
}
this._gameState.pop();
if (this._gameState.length > 0) {
this._currentVertex = this._gameState[this._gameState.length - 1];
} else {
this._currentVertex = null;
}
// Fire notifications
if (this._gameController != null) {
this._gameController.gameStateChanged();
}
},
canMoveTo: function(vertex) {
if (vertex == null) {
throw "Argument 'vertex' cannot be null";
}
try {
vertex = this.ensureValidVertex(vertex);
} catch (e) {
return false;
}
// Cannot move if the edges is already visited
for (var i = 0; i < this._gameState.length; i++) {
if (this._gameState[i] == vertex) {
return false;
}
}
if (this._currentVertex != null) {
// Can move if edge is in the current vertex's edge list
var edges = this.getEdges(this._currentVertex);
for (var i = 0; i < edges.length; i++) {
if (edges[i] == vertex) {
return true;
}
}
return false;
}
return true;
},
moveTo: function(vertex) {
if (! this.canMoveTo(vertex)) {
throw "Cannot move to vertex '" + vertex + "'";
}
this._gameState.push(vertex);
this._currentVertex = vertex;
},
getEdges: function(vertex) {
vertex = this.ensureValidVertex(vertex);
var edges = this._gameGraph[vertex];
return edges;
},
isValidVertex: function(vertex) {
if (vertex == null) {
throw "Argument 'vertex' cannot be null";
}
try {
this.ensureValidVertex(vertex);
} catch (e) {
return false;
}
return true;
},
ensureValidVertex: function(vertex) {
if (vertex == null && this._currentVertex == null) {
throw "Argument 'vertex' is null and there is no current vertex.";
}
var v = vertex != null ? vertex : this._currentVertex;
if (! this._gameGraph.hasOwnProperty(v)) {
throw "There is no vertex by that name '" + v + "'";
}
return v;
},
getCurrentVertex: function() {
return this._currentVertex;
}
});
declare("fenouiltonic.game100.gamecontroller", null, {
_gameModel: null,
_gameBoard: null,
_infoView: null,
constructor: function() {
},
setModel: function(model) {
this._gameModel = model;
},
setBoard: function(board) {
this._gameBoard = board;
},
setInfoView: function(view) {
this._infoView = view;
},
gameParametersChanged: function() {
if (this._gameBoard != null) {
this._gameBoard.createDOM();
// TODO
}
},
gameStateChanged: function() {
if (this._gameBoard != null) {
this._gameBoard.clearDOM();
// TODO
}
if (this._infoView != null) {
this._infoView.setMessage("");
this._infoView.setClock("00:00");
// TODO
}
},
moveTo: function(cell) {
if (this._gameModel.isLost() || this._gameModel.isWon()) {
return false;
}
var allowed = this._gameModel.canMoveTo(cell);
if (! allowed) {
this._infoView.setMessage("You cannot move to that cell");
return false;
}
this._infoView.setMessage("");
this._gameModel.moveTo(cell);
if (this._gameModel.isLost()) {
this._infoView.setMessage("You lost !");
}
if (this._gameModel.isWon()) {
this._infoView.setMessage("You won !");
}
return true;
},
});
declare("fenouiltonic.game100.infoview", null, {
_infoViewRootNode: null,
_messageNode: null,
_clockNode: null,
constructor: function(domNode) {
this._infoViewRootNode = domNode;
this.createDOM();
},
createDOM: function() {
var domNode = this._infoViewRootNode;
// Clear everything
domConstruct.empty(domNode);
// and build the DOM structure
var tableNode = domConstruct.create("table", { 'class': 'game-info' }, domNode);
var tr = domConstruct.create("tr", {}, tableNode);
this._messageNode = domConstruct.create("td", { 'class': "game-message" }, tr);
this._clockNode = domConstruct.create("td", { 'class': "game-clock" }, tr);
},
setMessage: function(str) {
this._messageNode.textContent = str;
},
setClock: function(str) {
this._clockNode.textContent = str;
}
});
declare("fenouiltonic.game100.gameboard", null, {
_gameModel: null,
_gameController: null,
_gameTableNode: null,
_gameBoardRootNode: null,
_displayPossibleCells: false,
constructor: function(domNode) {
this._gameBoardRootNode = domNode;
},
setModel: function(model) {
this._gameModel = model;
},
setDisplayPossibleCells: function(b) {
this._displayPossibleCells = b;
},
setController: function(controller) {
this._gameController = controller;
},
createDOM: function() {
var xmax = this._gameModel.getGameWidth();
var ymax = this._gameModel.getGameHeight();
var domNode = this._gameBoardRootNode;
// Clear everything
domConstruct.empty(domNode);
// and build the DOM structure
this._gameTableNode = domConstruct.create("table", { 'class': 'game-board' }, domNode);
for (var x = 0; x < xmax; x++) {
var tr = domConstruct.create("tr", {}, this._gameTableNode);
for (var y = 0; y < ymax; y++) {
var td = domConstruct.create("td", { 'class': ((x + y) % 2 == 0 ? 'even-cell' : 'odd-cell'),
id: ":" + x + "-" + y }, tr);
on(td, "click", dojo.hitch(this, "_evtClick"));
}
}
// Adjust the table height to be equal to its height
var width = domStyle.get(this._gameTableNode, "width");
domStyle.set(this._gameTableNode, "height", width + "px");
},
clearDOM: function() {
// Clear CSS Classes
query(".active-cell, .used-cell, .possible-cell, .impossible-cell", this._gameTableNode)
.removeClass("active-cell")
.removeClass("used-cell")
.removeClass("possible-cell")
.removeClass("impossible-cell");
// Remove text content
query("td", this._gameTableNode)
.forEach(function (node) {
node.textContent = "";
});
var usedCells = this._gameModel.getGameState();
var cell = null;
for (var i = 0; i < usedCells.length; i++) {
cell = dom.byId(usedCells[i]);
cell.textContent = (i + 1)
domClass.add(cell, "used-cell");
}
if (cell != null) {
domClass.add(cell, "active-cell");
this._displayPossibleCellsIfNeeded();
}
},
_displayPossibleCellsIfNeeded: function() {
if (this._displayPossibleCells) {
var possibleCells = this._gameModel.getEdges();
for (var i = 0; i < possibleCells.length; i++) {
var cell = possibleCells[i];
domClass.add(cell, domClass.contains(cell, "used-cell") ? "impossible-cell" : "possible-cell");
}
}
},
_evtClick: function(evt) {
var cellID = evt.target.id;
var cellNode = evt.target;
var allowed = this._gameController.moveTo(cellID)
if (allowed) {
// Clear CSS Classes
query(".active-cell, .possible-cell, .impossible-cell", this._gameTableNode)
.removeClass("active-cell")
.removeClass("possible-cell")
.removeClass("impossible-cell");
cellNode.textContent = this._gameModel.getCurrentCount();
domClass.add(cellNode, "active-cell");
domClass.add(cellNode, "used-cell");
this._displayPossibleCellsIfNeeded();
}
}
});
});