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
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();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|