commit
23121ef730
4 changed files with 5091 additions and 0 deletions
@ -0,0 +1,424 @@ |
|||||
|
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(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,110 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, user-scalable=no"> |
||||
|
<script data-dojo-config="async: 1" |
||||
|
src="//ajax.googleapis.com/ajax/libs/dojo/1.10.10/dojo/dojo.js"></script> |
||||
|
<link rel="stylesheet" type="text/css" href="theme.css"> |
||||
|
<title>100</title> |
||||
|
<script type="text/javascript" src="core.js"></script> |
||||
|
<script type="text/javascript"> |
||||
|
var gameModel = null; |
||||
|
var gameBoard = null; |
||||
|
var gameController = null; |
||||
|
|
||||
|
function newgame() { |
||||
|
gameModel.startNewGame(); |
||||
|
} |
||||
|
|
||||
|
var currentVertex = null; |
||||
|
var gameState = []; |
||||
|
var savedXmax = 5; |
||||
|
var savedYmax = 5; |
||||
|
function load() { |
||||
|
var xmax = gameModel.getGameWidth(); |
||||
|
var ymax = gameModel.getGameHeight(); |
||||
|
if (xmax != savedXmax || ymax != savedYmax) { |
||||
|
gameModel.setGameParameters(savedXmax, savedYmax); |
||||
|
} |
||||
|
gameModel.setGameState(currentVertex, gameState); |
||||
|
} |
||||
|
|
||||
|
var displayPossibleCells = true; |
||||
|
function toggledisplay() { |
||||
|
gameBoard.setDisplayPossibleCells(! displayPossibleCells); |
||||
|
gameController.gameStateChanged(); |
||||
|
displayPossibleCells = !displayPossibleCells; |
||||
|
} |
||||
|
|
||||
|
function undo() { |
||||
|
gameModel.undo(); |
||||
|
} |
||||
|
|
||||
|
function setgame(x, y) { |
||||
|
gameModel.setGameParameters(x, y); |
||||
|
gameModel.startNewGame(); |
||||
|
} |
||||
|
|
||||
|
function save() { |
||||
|
currentVertex = gameModel.getCurrentVertex(); |
||||
|
gameState = gameModel.getGameState(); |
||||
|
savedXmax = gameModel.getGameWidth(); |
||||
|
savedYmax = gameModel.getGameHeight(); |
||||
|
} |
||||
|
|
||||
|
require(["dojo/ready", "dojo" ], |
||||
|
function(ready, dojo) { |
||||
|
ready(function () { |
||||
|
var boardNode = dojo.byId("gameboard-placeholder"); |
||||
|
gameBoard = new fenouiltonic.game100.gameboard(boardNode); |
||||
|
|
||||
|
var infoNode = dojo.byId("gameinfo-placeholder"); |
||||
|
var infoView = new fenouiltonic.game100.infoview(infoNode); |
||||
|
infoView.createDOM(); |
||||
|
|
||||
|
gameController = new fenouiltonic.game100.gamecontroller(); |
||||
|
gameModel = new fenouiltonic.game100.gamemodel(); |
||||
|
gameModel.setController(gameController); |
||||
|
gameController.setModel(gameModel); |
||||
|
gameController.setBoard(gameBoard); |
||||
|
gameController.setInfoView(infoView); |
||||
|
gameBoard.setController(gameController); |
||||
|
gameBoard.setModel(gameModel); |
||||
|
gameBoard.setDisplayPossibleCells(true); |
||||
|
|
||||
|
var xmax = 5; |
||||
|
var ymax = 5; |
||||
|
gameModel.setGameParameters(xmax, ymax); |
||||
|
gameModel.startNewGame(); |
||||
|
}); |
||||
|
} |
||||
|
); |
||||
|
</script> |
||||
|
</head> |
||||
|
<body> |
||||
|
<table> |
||||
|
<tr> |
||||
|
<td style="width: 300px"> |
||||
|
<img src="logo.svg" width="300"> |
||||
|
<div id="gameinfo-placeholder"></div> |
||||
|
<div id="gameboard-placeholder"></div> |
||||
|
</td> |
||||
|
<td> |
||||
|
<input type="submit" onClick="setgame(5, 5)" value="5x5"><br/> |
||||
|
<input type="submit" onClick="setgame(6, 6)" value="6x6"><br/> |
||||
|
<input type="submit" onClick="setgame(7, 7)" value="7x7"><br/> |
||||
|
<input type="submit" onClick="setgame(8, 8)" value="8x8"><br/> |
||||
|
<input type="submit" onClick="setgame(9, 9)" value="9x9"><br/> |
||||
|
<input type="submit" onClick="setgame(10, 10)" value="10x10"><br/> |
||||
|
<input type="submit" onClick="newgame()" value="newgame"><br/> |
||||
|
<input type="submit" onClick="load()" value="load"><br/> |
||||
|
<input type="submit" onClick="save()" value="save"><br/> |
||||
|
<input type="submit" onClick="undo()" value="undo"><br/> |
||||
|
<label><input type="checkbox" onChange="toggledisplay()" checked="1" value="display possible moves">display possible moves</label> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</body> |
||||
|
</html> |
||||
|
|
||||
|
After Width: | Height: | Size: 394 KiB |
@ -0,0 +1,69 @@ |
|||||
|
table.game-board td.even-cell { |
||||
|
background-color: rgb(6, 42, 58); |
||||
|
} |
||||
|
|
||||
|
table.game-board td.odd-cell { |
||||
|
background-color: rgb(6, 42, 58); |
||||
|
} |
||||
|
|
||||
|
table.game-board td.odd-cell.used-cell, table.game-board td.even-cell.used-cell { |
||||
|
background-color: rgb(127, 71, 221); |
||||
|
} |
||||
|
|
||||
|
table.game-board td.odd-cell.possible-cell, table.game-board td.even-cell.possible-cell { |
||||
|
background-color: rgb(140, 198, 63); |
||||
|
} |
||||
|
|
||||
|
table.game-board td.odd-cell.impossible-cell, table.game-board td.even-cell.impossible-cell { |
||||
|
} |
||||
|
|
||||
|
table.game-board td.odd-cell.active-cell, table.game-board td.even-cell.active-cell { |
||||
|
background-color: rgb(0, 169, 157); |
||||
|
} |
||||
|
|
||||
|
table.game-board td.head-col, table.game-board td.head-row, table.game-board td.top-left { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
#message { |
||||
|
width: 80%; |
||||
|
} |
||||
|
|
||||
|
#clock { |
||||
|
width: 20%; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
font-family: "Bauhaus 93",Arial,Helvetica,sans-serif; |
||||
|
background-color: rgb(0, 27, 39); |
||||
|
text-align: center; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
width: 80%; |
||||
|
} |
||||
|
|
||||
|
table.game-info, table.game-board { |
||||
|
border: 1px solid rgb(0, 27, 39); |
||||
|
table-layout: fixed; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
table td { |
||||
|
padding: 0; |
||||
|
overflow: hidden; |
||||
|
text-align: center; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
|
||||
|
table.game-board td { |
||||
|
width: 10%; |
||||
|
height: 10%; |
||||
|
} |
||||
|
|
||||
|
table.game-info td { |
||||
|
background-color: rgb(6, 42, 58); |
||||
|
} |
||||
|
|
||||
Loading…
Reference in new issue