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