Added rules and tests for them

This commit is contained in:
2016-02-26 14:21:01 +08:00
parent a82d04af1a
commit 1ceecc417d
13 changed files with 801 additions and 340 deletions
+29 -6
View File
@@ -303,17 +303,20 @@ h1 br {
padding-top: 50px; padding-top: 50px;
} }
.test-tube { .test-tube {
border-left: 5px solid #949494; /*border-left: 5px solid #949494;
border-right: 5px solid #949494; border-right: 5px solid #949494;
border-bottom: 5px solid #949494; border-bottom: 5px solid #949494;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;*/
border: 5px solid #949494;
border-radius: 10px;
} }
/* science-click custom css */ /* science-click custom css */
.panel-body.large { .panel-body.large {
min-height: 500px; min-height: 600px;
} }
.ui-draggable { .ui-draggable {
@@ -338,13 +341,19 @@ h1 br {
.white-badge { .white-badge {
color: black; color: black;
background-color: transparent; background-color: white;
border-radius: 2px;
padding: 2px;
z-index: 100;
position: relative; position: relative;
font-weight: bold; border: 1px black solid;
/*font-weight: bold;*/
top: -1em; top: -1em;
left: 2em; left: 2.5em;
} }
/* https://github.com/mbostock/d3/blob/master/lib/colorbrewer/colorbrewer.css */ /* https://github.com/mbostock/d3/blob/master/lib/colorbrewer/colorbrewer.css */
.Paired .q0-12{fill:rgb(166,206,227)} .Paired .q0-12{fill:rgb(166,206,227)}
.Paired .q1-12{fill:rgb(31,120,180)} .Paired .q1-12{fill:rgb(31,120,180)}
@@ -395,3 +404,17 @@ h1 br {
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
} }
.ui-grid-filter-select {
background: white;
font-size: 22px;
}
#card-deck{
font-size: 75px;
}
.ui-grid-cell-contents{
font-size: 22px;
}
.ui-grid-viewport .ui-grid-cell-contents{
font-size: 22px;
}
+13 -6
View File
@@ -96,7 +96,7 @@
id="{{r.key}}" id="{{r.key}}"
class="{{r.key}} element element-store {{ r.state.interesting ? 'blink' : '' }} {{r.color}} {{r.state.amount>0 ? ' ui-draggable': 'empty'}}" class="{{r.key}} element element-store {{ r.state.interesting ? 'blink' : '' }} {{r.color}} {{r.state.amount>0 ? ' ui-draggable': 'empty'}}"
data-element="{{r.key}}" data-element="{{r.key}}"
data-drag="true" data-drag="rc.isAvailable(r)"
jqyoui-draggable="{containment:'offset'}" jqyoui-draggable="{containment:'offset'}"
data-jqyoui-options="{{rc.dragOptions}}" data-jqyoui-options="{{rc.dragOptions}}"
data-hashkey={{r.$$hashKey}} data-hashkey={{r.$$hashKey}}
@@ -150,9 +150,10 @@
> >
Your detector. Click on it to generate events. Your detector. Click on it to generate events.
</canvas> </canvas>
<canvas id="detector-flame" width="300" height="300" class="prevent-select center-block " ng-click="dc.toggleFlameFuel()"> <span id="card-deck" title="deal more cards" ng-click="dc.click()" class="prevent-select">🂠</span>
<!-- <canvas id="detector-flame" width="300" height="300" class="prevent-select center-block " ng-click="dc.toggleFlameFuel()">
Your bunsen burner. Click it to turn on and off. Your bunsen burner. Click it to turn on and off.
</canvas> </canvas> -->
<button id="clearall" class="btn btn-default" ng-click="dc.clearAll()"><i class="fa fa-refresh"></i></button> <button id="clearall" class="btn btn-default" ng-click="dc.clearAll()"><i class="fa fa-refresh"></i></button>
</div> </div>
</div> </div>
@@ -346,8 +347,14 @@
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js"></script>
<script src="bower_components/chai/chai.js"></script>
<script src="bower_components/should/should.js"></script>
<script src="bower_components/expect/index.js"></script>
<script src="bower_components/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular.js"></script>
<script src="bower_components/angular-dragdrop/src/angular-dragdrop.js"></script> <script src="bower_components/angular-dragdrop/src/angular-dragdrop.js"></script>
<script src="bower_components/angular-animate/angular-animate.js"></script>
<script src="js/external/retina.js"></script> <script src="js/external/retina.js"></script>
<script src="js/external/fastclick.js"></script> <script src="js/external/fastclick.js"></script>
@@ -359,11 +366,11 @@
<script src="js/helpers.js"></script> <script src="js/helpers.js"></script>
<script src="js/analytics.js"></script> <script src="js/analytics.js"></script>
<script src="js/gameobjects.js"></script> <script src="js/gameobjects.js"></script>
<script src="js/rules.js"></script>
<script src="js/detector/flame.js"></script> <!-- <script src="js/detector/flame.js"></script>
<script src="js/detector/bubblr.js"></script> <script src="js/detector/bubblr.js"></script> -->
<script src="js/detector/event.js"></script>
<script src="js/detector/detector.js"></script> <script src="js/detector/detector.js"></script>
<script src="js/ui.js"></script> <script src="js/ui.js"></script>
+16 -9
View File
@@ -14,12 +14,12 @@ var app = (function () {
// var elements = Helpers.loadFile('json/elements.json'); // var elements = Helpers.loadFile('json/elements.json');
// elements = elements.map( // elements = elements.map(
// function (r) { // function (r) {
// return new GameObjects.ElementStore(r); // return new GameObjects.Card(r);
// }); // });
// // put in extended array with helper methods // // put in extended array with helper methods
// elementStore = new GameObjects.ElementStores(); // Card = new GameObjects.Cards();
// elementStore.push.apply(elementStore,elements); // Card.push.apply(Card,elements);
// return elementStore; // return Card;
// }); // });
@@ -107,10 +107,10 @@ var app = (function () {
var draggable = angular.element(ui.draggable); var draggable = angular.element(ui.draggable);
var key = draggable.data('element'); var key = draggable.data('element');
if (!draggable.hasClass('element-store')) { if (!draggable.hasClass('element-store')) {
var elementStore = vm.elements.get(key); var Card = vm.elements.get(key);
var i = _.findIndex(vm.elements,{$$hashKey:draggable.data('hashkey')}); var i = _.findIndex(vm.elements,{$$hashKey:draggable.data('hashkey')});
detector.elements.splice(i, 1); detector.elements.splice(i, 1);
elementStore.state.amount += 1; Card.state.amount += 1;
} }
}; };
}; };
@@ -150,8 +150,8 @@ var app = (function () {
return false; return false;
}; };
vm.toggleFlameFuel = function () { vm.toggleFlameFuel = function () {
console.log('toggleFlameFuel'); // console.log('toggleFlameFuel');
detector.flamer.toggleFuel(); // detector.flamer.toggleFuel();
}; };
vm.clearAll = function () { vm.clearAll = function () {
detector.clearAll(game); detector.clearAll(game);
@@ -200,13 +200,20 @@ var app = (function () {
enableFiltering: true, enableFiltering: true,
columnDefs: [{ columnDefs: [{
field: 'inputs', field: 'inputs',
filter: {}, filter: {
type: 'select',
selectOptions: _(game.elements).filter({state:{discovered:true}}).map(function(e){return {value:e.key,label:e.key};}).value(),
},
visible: true visible: true
}, { }, {
field: 'reactants', field: 'reactants',
visible: false visible: false
}, { }, {
field: 'results', field: 'results',
filter: {
type: 'select',
selectOptions: _(game.elements).filter({state:{discovered:true}}).map(function(e){return {value:e.key,label:e.key};}).value(),
},
visible: true, visible: true,
sort: { sort: {
direction: 'asc' direction: 'asc'
+22 -47
View File
@@ -4,26 +4,7 @@ var Detector = function(){
return { return {
core: elements: new GameObjects.Cards(),
{
// canvas: null,
// ctx: null
},
//
// events:
// {
// canvas: null,
// ctx: null,
// list: [],
// },
flame:
{
// canvas: null,
// ctx: null
},
elements: new GameObjects.ElementStores(),
visible: true, visible: true,
@@ -36,21 +17,10 @@ var Detector = function(){
bubblr: undefined, bubblr: undefined,
init: function(baseSize,element) init: function()
{ {
// get canvas // this.initBubbles();
// this.core.canvas = document.getElementById('detector-core'); // this.initFlame();
// if (!this.core.canvas) {
// this.core.canvas=$('<canvas id="detector-core"></canvas>');
// $(element).append(this.core.canvas);
// }
// this.core.ctx = this.core.canvas.getContext('2d');
//
// this.flame.canvas = document.getElementById('detector-flame');
// this.flame.ctx = this.flame.canvas.getContext('2d');
this.initBubbles();
this.initFlame();
}, },
initBubbles: function(){ initBubbles: function(){
@@ -72,10 +42,10 @@ var Detector = function(){
this.bubblr = bubblrElem.data('plugin_bubblr'); this.bubblr = bubblrElem.data('plugin_bubblr');
}, },
initFlame: function(){ // initFlame: function(){
var flameElem = $('#detector-flame').flame(); // var flameElem = $('#detector-flame').flame();
this.flamer = flameElem.data('plugin_flame') // this.flamer = flameElem.data('plugin_flame')
}, // },
bindOnResize: function(){ bindOnResize: function(){
// HACK // HACK
@@ -114,19 +84,19 @@ var Detector = function(){
/** When a user clicks the detector **/ /** When a user clicks the detector **/
addEvent: function() addEvent: function()
{ {
this.bubblr.bubble(); // bubble for 500ms, TODO make one bubble // this.bubblr.bubble(); // bubble for 500ms, TODO make one bubble
}, },
/** When a worker clicks the detector **/ /** When a worker clicks the detector **/
addEventExternal: function(numWorkers) addEventExternal: function(numWorkers)
{ {
this.bubblr.genBubbles(numWorkers); // this.bubblr.genBubbles(numWorkers);
}, },
/** Draw current events **/ /** Draw current events **/
draw: function(duration) draw: function(duration)
{ {
this.bubblr.bubble(); // this.bubblr.bubble();
}, },
/** Clear an element back to element Store **/ /** Clear an element back to element Store **/
@@ -196,9 +166,14 @@ var Detector = function(){
if (newElement && intersectingElements.indexOf(newElement)===-1) intersectingElements.push(newElement); if (newElement && intersectingElements.indexOf(newElement)===-1) intersectingElements.push(newElement);
var observation = this.experiment({inputs:intersectingElements,location:$draggable.offset()},game); var uniqueElems = _(intersectingElements).map('key').uniq().value().length;
console.log('intersectingElements', intersectingElements.length, observation); if (intersectingElements.length==2 && uniqueElems>1){
return observation; var observation = this.experiment({inputs:intersectingElements,location:$draggable.offset()},game);
console.log('intersectingElements', intersectingElements.length, observation);
return observation;
} else {
return;
}
}, },
@@ -209,7 +184,7 @@ var Detector = function(){
var inputKeys = inputs.map(function(e){return e.key;}); var inputKeys = inputs.map(function(e){return e.key;});
inputKeys.sort(); // this makes reaction be independant of order inputKeys.sort(); // this makes reaction be independant of order
var result = game.rules[inputKeys]; var result = game.testRules(inputKeys);
if (result) { if (result) {
this.reaction(inputs,result.reactants,result.results, options.location, game); this.reaction(inputs,result.reactants,result.results, options.location, game);
} else { } else {
@@ -232,7 +207,7 @@ var Detector = function(){
// get the uuid from inputs // get the uuid from inputs
var ingredient = inputs.filter(function(e){return e.key===reactants[i];})[0]; var ingredient = inputs.filter(function(e){return e.key===reactants[i];})[0];
var j = _.findIndex(this.elements,{uuid:ingredient.uuid}); var j = _.findIndex(this.elements,{uuid:ingredient.uuid});
var removed = this.elements.slice(j,1); var removed = this.elements.splice(j,1);
} }
// TODO use angular effects to remove in puff of fade // TODO use angular effects to remove in puff of fade
@@ -259,7 +234,7 @@ var Detector = function(){
} }
// effects // effects
this.bubblr.bubble(); // this.bubblr.bubble();
}, },
}; };
-70
View File
@@ -1,70 +0,0 @@
/** Define and draw to canvas particle events, used in detector **/
/**
* Define and draw to canvas particle events, used in detector
* @param {Object} type Object with name 'electron'||'jet'||'muon'
* @param {Number} count Number of particles in this event
* @param {Boolean} external Wether even was caused by player or worker
*/
function ParticleEvent(type, count, external)
{
this.work = typeof external !== 'undefined' ? external : false;
this.type = type;
this.length = 0;
this.radius = 0;
this.direction = 0;
this.sign = (Math.random() - 0.5 >= 0) ? 1 : -1;
this.alpha = this.work ? 0.5 : 1;
this.count = count;
switch (this.type.name)
{
case 'electron':
this.length = detector.radius.siliconSpace * detector.ratio + Math.round((detector.radius.ecal * detector.ratio + 10 - detector.radius.siliconSpace * detector.ratio) * Math.random());
this.direction = Math.random() * Math.PI * 2;
this.radius = 20 + Math.round((100 - 20) * Math.random());
break;
case 'jet':
this.length = detector.radius.ecal * detector.ratio + Math.round((detector.radius.mucal * detector.ratio - detector.radius.ecal * detector.ratio) * Math.random());
this.direction = Math.random() * Math.PI * 2;
this.radius = 40 + Math.round((200 - 40) * Math.random());
break;
case 'muon':
this.length = detector.radius.mucal * detector.ratio + 3 * detector.radius.mucalDark * detector.ratio + Math.round((4 * detector.radius.mucalLight * detector.ratio + 2 * detector.radius.mucalDark * detector.ratio) * Math.random());
this.direction = Math.random() * Math.PI * 2;
this.radius = 200 + Math.round((600 - 200) * Math.random());
break;
}
this.draw(16, true);
};
ParticleEvent.prototype.draw = function(duration, init)
{
init = typeof init !== 'undefined' ? init : false;
var ctx = detector.events.ctx;
var cx = detector.width / 2;
var cy = detector.height / 2;
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.strokeStyle = this.type.color;
ctx.fillStyle = this.type.color;
ctx.lineWidth = 2;
ctx.translate(cx, cy);
ctx.rotate(this.direction);
ctx.translate(-cx, -cy);
ctx.beginPath();
ctx.arc(cx + this.length / 2, cy + this.sign * Math.round(Math.sqrt(Math.abs(this.radius * this.radius - this.length * this.length / 4))), this.radius, - this.sign * Math.PI / 2 - Math.asin(this.length / (2 * this.radius)), - this.sign * Math.PI / 2 + Math.asin(this.length / (2 * this.radius)), false);
ctx.stroke();
ctx.restore();
if (!init) {
this.alpha -= 0.03 / 16 * duration;
}
};
+175 -80
View File
@@ -76,103 +76,198 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
// }) // })
// ).then(function () { // ).then(function () {
// Turn JSON files into actual game objects and fill map of all objects // Turn JSON files into actual game objects and fill map of all objects
var makeGameObject = function (type, object) { var makeGameObject = function (type, object) {
// It's okay to define this function here since load is only called // It's okay to define this function here since load is only called
// once anyway... // once anyway...
var o = new type(object); var o = new type(object);
self.allObjects[o.key] = o; self.allObjects[o.key] = o;
return o; return o;
}; };
self.elements = self.elements.slice(0, 20).map( self.elements = self.elements.map(
function (r) { function (r) {
return makeGameObject(GameObjects.ElementStore, r); return makeGameObject(GameObjects.Card, r);
}); });
self.workers = self.workers.map( self.workers = self.workers.map(
function (w) { function (w) {
return makeGameObject(GameObjects.Worker, w); return makeGameObject(GameObjects.Worker, w);
}); });
self.upgrades = self.upgrades.map( self.upgrades = self.upgrades.map(
function (u) { function (u) {
return makeGameObject(GameObjects.Upgrade, u); return makeGameObject(GameObjects.Upgrade, u);
}); });
self.achievements = self.achievements.map( self.achievements = self.achievements.map(
function (a) { function (a) {
return makeGameObject(GameObjects.Achievement, a); return makeGameObject(GameObjects.Achievement, a);
}); });
// Load states from local store // Load states from local store
for (var key in self.allObjects) { for (var key in self.allObjects) {
var o = self.allObjects[key]; var o = self.allObjects[key];
o.loadState(ObjectStorage.load(key)); o.loadState(ObjectStorage.load(key));
} }
// put elements in extended array with utility methods // put elements in extended array with utility methods
self.elementStore = new GameObjects.ElementStores(); self.Card = new GameObjects.Cards();
self.elementStore.push.apply(self.elementStore, self.elements); self.Card.push.apply(self.Card, self.elements);
self.elements = self.elementStore; self.elements = self.Card;
// var totalElements = _(self.elements).map('state.amount').sum();
// if (totalElements<1) self.initialElements();
self.rules = self.generateRules();
self.loaded = true; self.rules = self.generateRules();
return self;
self.loaded = true;
return self;
// }); // });
}; };
Game.prototype.initialElements = function () {
// if we are making new rules we will also reset element stores
this.elements.map(function (element) {
element.state.amount = 0;
element.state.discovered = false;
element.state.interesting = false;
});
// but give us a random 4 number cards of one suite
var startSuit = _.sample(["Spades", "Hearts", "Diamonds", "Clubs"]);
this.elements.map(function (element) {
if (element.number && element.suit === startSuit) {
element.state.amount = 5;
element.state.discovered = true;
}
});
console.log('Set initial cards');
return startSuit;
}
/** Generate rules between runes **/ /** Generate rules between runes **/
Game.prototype.generateRules = function () { Game.prototype.generateRules = function () {
var rules = ObjectStorage['rules'];
if (!rules) {
var elements = this.elements;
// generate rules and store them with a hash of inredients
// the rule will be an object with reactants, catalysts, conditions, results
// todo make a strcture that's more like a tree with progression etc
// todo add duds, misleading ones, explosions wildcards
// todo make this theory based?
// todo simulation
var rules = {};
for (var k = 0; k < this.elements.length * 20; k++) {
// make a rules
var rule = {
reactants: [],
catalysts: [],
conditions: [],
results: [],
inputs: []
}
var numOfIngredients = 2 + Math.round(Math.random() * 2);
for (var i = 0; i < numOfIngredients; i++) {
var j = Math.round(Math.random() * (elements.length - 1));
rule.reactants.push(elements[j].key);
}
if (Math.random() < 0.1) { /**
var j = Math.round(Math.random() * (elements.length - 1)); * Make the values sequential for given card names
rule.catalysts.push(elements[j].key); * @param {Array} names - String names e.g. ['King','Queen']
* @param {Object} rules - Rules
* @return {Object} - modified Rules
*/
function orderValues(names, rules, reverse) {
var namedCards = _.filter(elements, function (c) {
return names.indexOf(c.name) > -1;
});
var uNamedCardVals = _.uniq(_.map(namedCards, 'value'));
var maxVal = _.max(uNamedCardVals);
var initialValue = rules.value[uNamedCardVals[0]];
for (var i = 0; i < uNamedCardVals.length; i++) {
var v = uNamedCardVals[i];
if (reverse)
rules.value[v] = initialValue + maxVal - v;
else
rules.value[v] = initialValue + v;
}
return rules;
}
var rules = ObjectStorage.load('rules');
if (!rules) {
var startSuit = this.initialElements();
var elements = this.elements;
var attrs = ["key", "name", "value", "suit", "color", "royal", "face", "number"];
var rules = {};
// first lets apply random values to each attribute
for (var i = 0; i < attrs.length; i++) {
var attr = attrs[i];
rules[attr] = {};
var uniqVals = _.uniq(_.map(elements, attr));
for (var j = 0; j < uniqVals.length; j++) {
var v = uniqVals[j];
// E.g. rules.suit.black=4, rules.name.Queen=10 etc
rules[attr][v] = Math.round(Math.random() * 104);
} }
var numOfresults = Math.round(Math.random() * 3);
for (var i = 0; i < numOfresults; i++) {
var j = Math.round(Math.random() * (elements.length - 1));
rule.results.push(elements[j].key);
}
rule.inputs = [].concat(rule.reactants, rule.catalysts)
rule.reactants.sort()
rule.results.sort()
rule.catalysts.sort()
rule.inputs.sort();
// index byhash of sorted array of reactants
rules[rule.inputs] = rule
} }
// now lets overide some attrbutes with a bit more order
// number cards should be in sequence as should face cards
// _.uniq(_.map(_.filter(cards,{face:'true'}),'value'))
var royals = ["Jack", "Knight", "Queen", "King"];
var numbers = ["Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"];
// lets randomly add the ace to the top or bottom
if (Math.random() > 0.5) {
royals.push("Ace");
} else {
numbers.push("Ace");
}
orderValues(royals, rules);
orderValues(numbers, rules);
// and make the startingSuit 0 for a simpler set of initial rules
rules.suit[startSuit] = 0;
// and finally get the joker a change for a negative
rules.value[16] = Math.random(Math.random() * 20 - 10);
console.log('Reset and made new rules');
} }
ObjectStorage['rules'] = rules; ObjectStorage.save('rules', rules);
return rules; return rules;
}, },
Game.prototype.save = function () { /**
// Save every object's state to local storage * Test the rules
for (var key in this.allObjects) { * @param {array} inputKeys - e.g ["🂤", "🂤"]
ObjectStorage.save(key, this.allObjects[key].state); * @return {Object} rule with {inputs,reactants,results}
*/
Game.prototype.testRules = function (inputKeys) {
var self = this;
var attrs = ["value", "suit"]; // attrs we use, possible expand later
// work out total for this combo
var total = 0;
for (var i = 0; i < inputKeys.length; i++) {
var key = inputKeys[i];
var element = _.find(self.elements, {
key: key
});
for (var j = 0; j < attrs.length; j++) {
var attr = attrs[j];
// add appropriate value for this card's attribute
// e.g. total+=rules.suit["spade]
total += self.rules[attr][element[attr]];
}
} }
// you keep you results and thus get more cards unless the total is
// <5
var maxValue = 14; //=_(self.elements).map('value').map(function(v){return parseInt(v);}).max()
var divisor = Math.round(maxValue * 3); // ~66% chance of dud if they don't understand rules
var value = total % maxValue;
var resultElem = _.find(self.elements, {
value: value
});
var reactants = resultElem ? inputKeys : [];
var results = resultElem ? [resultElem.key] : [];
// TODO add penalty for duds? like more cards
return {
results: results,
inputs: inputKeys,
conditions: [],
catalysts: [],
reactants: reactants
};
}; };
Game.prototype.save = function () {
// Save every object's state to local storage
for (var key in this.allObjects) {
ObjectStorage.save(key, this.allObjects[key].state);
}
};
return Game; return Game;
}(Helpers, GameObjects, ObjectStorage)); }(Helpers, GameObjects, ObjectStorage));
+42 -60
View File
@@ -98,7 +98,13 @@ var GameObjects = (function () {
obsText[k] = observation[k].sort().join(''); obsText[k] = observation[k].sort().join('');
} }
} }
this.state.observations.push(obsText); obsText.amount=1;
// check if an obs with all the attributes matching (extra attribs are ok)
var index = _.findIndex(this.state.observations,obsText);
if (index>-1)
this.state.observations[index].amount+=1;
else
this.state.observations.push(obsText);
}; };
@@ -111,50 +117,50 @@ var GameObjects = (function () {
return false; return false;
}; };
var ElementStores = function (obj) { var Cards = function (obj) {
this.push.apply(this, obj); this.push.apply(this, obj);
}; };
ElementStores.prototype = Object.create(Array.prototype); Cards.prototype = Object.create(Array.prototype);
ElementStores.prototype.constructor = Array.constructor; Cards.prototype.constructor = Array.constructor;
ElementStores.prototype.pushAll = function (items) { Cards.prototype.pushAll = function (items) {
this.push.apply(this, items); this.push.apply(this, items);
}; };
/** Add a random element or specify it's key **/ /** Add a random element or specify it's key **/
ElementStores.prototype.addToStore = function (element) { Cards.prototype.addToStore = function (element) {
if (element) this.get(element); if (element) this.get(element);
if (!element) element = this.select(); if (!element) element = this.select();
return element.state.amount += 1; return element.state.amount += 1;
}; };
/** Add a random discovered element or specify it's key **/ /** Add a random discovered element or specify it's key **/
ElementStores.prototype.addKnownToStore = function (element) { Cards.prototype.addKnownToStore = function (element) {
var discovered = this.filter(function (e) { var discovered = this.filter(function (e) {
return e.state.discovered; return e.state.discovered;
}); });
discovered = new GameObjects.ElementStores(discovered); discovered = new GameObjects.Cards(discovered);
if (element) discovered.get(element); if (element) discovered.get(element);
if (!element) element = discovered.select(); if (!element) element = discovered.select();
return element.state.amount += 1; return element.state.amount += 1;
}; };
/** Select random element from store **/ /** Select random element from store **/
ElementStores.prototype.select = function () { Cards.prototype.select = function () {
var i = Math.round((this.length - 1) * Math.random()); var i = Math.round((this.length - 1) * Math.random());
return this[i]; return this[i];
}; };
/** Get element by key **/ /** Get element by key **/
ElementStores.prototype.get = function (key) { Cards.prototype.get = function (key) {
return this.filter(function (e) { return this.filter(function (e) {
return e.key === key; return e.key === key;
})[0]; })[0];
}; };
/** Get element by hashid **/ /** Get element by hashid **/
ElementStores.prototype.getByHashKey = function (hashKey) { Cards.prototype.getByHashKey = function (hashKey) {
if (hashKey === undefined) { if (hashKey === undefined) {
console.warn('GetByHashKey given an undefined hashkey', hashKey) console.warn('GetByHashKey given an undefined hashkey', hashKey)
return; return;
@@ -172,66 +178,42 @@ var GameObjects = (function () {
} }
}; };
/** filter by e.g. {uuid:'34hgyh454'} **/ /** @class Card
// ElementStores.prototype.filterBy = function (filter) {
// return this.filter(function (e) {
// var match = true;
// for (var k in filter) {
// if (filter.hasOwnProperty(k)) {
// match = match && (e[k] === filter[k]);
// };
// }
// return match;
// });
// };
//
// /** Get indexes matching filter e.g. {uuid:'34hgyh454'} **/
// ElementStores.prototype.findIndex = function (filter) {
// var self = this;
// return this.filterBy(function (e) {
// var match = true;
// for (var k in filter) {
// if (filter.hasOwnProperty(k)) {
// match = match && (e[k] === filter[k]);
// };
// }
// return match;
// })
// .map(function (e) {
// return self.indexOf(e);
// });
// };
/** @class ElementStore
*/ */
var ElementStore = function (obj) { var Card = function (obj) {
// load from localStorage by obj.key
GameObject.apply(this, [obj]); GameObject.apply(this, [obj]);
this.state.amount = Math.round(Math.random() * 2);
this.state.discovered = Math.random() < 0.1; // apply defaults to undefined values
this.state.interesting = Math.random() < 0.1; this.state = _.defaults(this.state,{
this.state.color = Math.round(Math.random() * 11); amount: 0,
this.uuid = this.guid(); discovered: false,
interesting: false,
});
// generate uuid
this.uuid = this.uuid || this.guid();
}; };
ElementStore.prototype = Object.create(GameObject.prototype); Card.prototype = Object.create(GameObject.prototype);
ElementStore.prototype.constructor = ElementStore; Card.prototype.constructor = Card;
ElementStore.prototype.isVisible = function (lab) { Card.prototype.isVisible = function (lab) {
if (!lab) { if (!lab) {
return false; return false;
} }
return this.state.discovered; return this.state.discovered;
}; };
ElementStore.prototype.isAvailable = function (lab) { Card.prototype.isAvailable = function (lab) {
if (!lab) { if (!lab) {
return false; return false;
} }
return this.state.amount > 0; return this.state.amount > 0;
}; };
ElementStore.prototype.research = function (lab) { Card.prototype.research = function (lab) {
if (lab && lab.research(this.state.cost, this.state.reputation)) { if (lab && lab.research(this.state.cost, this.state.reputation)) {
this.state.level++; this.state.level++;
if (this.state.info_levels.length > 0 && if (this.state.info_levels.length > 0 &&
@@ -246,7 +228,7 @@ var GameObjects = (function () {
return -1; return -1;
}; };
ElementStore.prototype.getInfo = function () { Card.prototype.getInfo = function () {
if (!this._info) { if (!this._info) {
this._info = Helpers.loadFile(this.info); this._info = Helpers.loadFile(this.info);
} }
@@ -254,16 +236,16 @@ var GameObjects = (function () {
return this._info; return this._info;
}; };
/** Create a new element for the test tube from this ElementStore **/ /** Create a new element for the test tube from this Card **/
ElementStore.prototype.spawn = function () { Card.prototype.spawn = function () {
var element = angular.copy(this); var element = angular.copy(this);
element.uuid = element.guid(); element.uuid = element.guid();
element.state = undefined; element.state = undefined;
this.state.amount -= 1; // this.state.amount -= 1;
return element; return element;
}; };
ElementStore.prototype.decreaseStore = function () { Card.prototype.decreaseStore = function () {
return this.state.amount -= 1; return this.state.amount -= 1;
}; };
@@ -406,10 +388,10 @@ var GameObjects = (function () {
// Expose classes in module. // Expose classes in module.
return { return {
Lab: Lab, Lab: Lab,
ElementStore: ElementStore, Card: Card,
Worker: Worker, Worker: Worker,
Upgrade: Upgrade, Upgrade: Upgrade,
Achievement: Achievement, Achievement: Achievement,
ElementStores: ElementStores Cards: Cards
}; };
}()); }());
+248
View File
@@ -0,0 +1,248 @@
/**
* Rules objects for game
* @param {[type]} function functionName( [description]
* @return {[type]} [description]
*/
var Rules = (function functionName(_) {
/**
* Rule prototype
* @param {String} description - Description of the rule using angular templating from options
* @param {Function} ruleFunc - A function returning a true or a failure
* message or a error
* @param {Object} options - Options for the rule will be given on test and description rendering
*/
/**
* Rule prototype
* @param {String} description Description of the rule using angular templating from options
* @param {Function} ruleFunc A function returning a true or a failure message or a error
* @param {Object} optionDefaults Default options for the rule e.g. {n:1, parameter:'color'}.
* @param {Object} optionDesc Object with object for each option
* @param {Array} optionDesc[].possibleVals Array of possible values or empty if they are to many to be enumerated
* @param {String} optionDesc[].description Description of this option e.g. 'Property to compare to last card'
* @param {String} optionDesc[].type Type for this option e.g. 'String'
* @param {Array} hintTmpls Array of hint templates. Each should only contain one option e.g.
* `The rule uses {{parameter}}` and for every parameter you can
* use `The rule doesn't use {{parameterUnused}}` to have each unused param substituted in.
*/
var Rule = function (description, ruleFunc, optionDefaults, optionDesc,hintTmpls) {
if (!(typeof(description)==='string'||description instanceof String)|| !description.length)
throw new TypeError('description should be a non empty string');
this.description = description;
if (!(ruleFunc instanceof Function))
throw new TypeError('ruleFunc should be a function');
this.ruleFunc = ruleFunc;
this.optionDesc = optionDesc||{};
this.options = this.optionDefaults = optionDefaults||{};
this.hintTmpls=hintTmpls||[];
// use angular and mustache templating
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
// init
this.hintsUsed = 0;
this.hints=this.genHints();
};
Rule.prototype.setOptions = function (options) {
this.options = _.defaults(options, this.optionDefaults);
this.hints=this.genHints();
return this.options;
};
/** Tests the rule and returns true, false, or an AssertionError **/
Rule.prototype.testAndTell = function (card, lastCards, allCards) {
chai.expect(card).to.be.a('object');
chai.expect(lastCards).to.be.a('array');
chai.expect(allCards).to.be.a('array');
var result;
var reason;
try {
result = this.ruleFunc(card, lastCards, allCards, this.options);
if (result instanceof chai.Assertion){
reason=result;
result=true;
}
} catch (e) {
if (e instanceof chai.AssertionError){
result = false;
reason=e;
} else {throw(e);}
}
return {result:result,reason:reason};
};
Rule.prototype.test = function (card, lastCards, allCards) {
var res = this.testAndTell(card,lastCards,allCards);
return res.result;
};
/** Describe rule using set options **/
Rule.prototype.describe = function () {
return this.describeVariation(this.options);
};
/** Compile description using options to express rule**/
Rule.prototype.describeVariation = function (options) {
var compiled = _.template(this.description);
return compiled(_.defaults(options,this.options));
};
/** Return options string for humans **/
Rule.prototype.describeOptions = function () {
var s = "";
// explicit python style
var template = '{{option}} \n\tDescription: {{description}}\n\tType {{type}} \n\tValues: current:{{current}}, default:{{defaultVal}}, all:[{{possibleVals}}]\n ';
// jsdoc style I think
// var template = '{{type}}\t[{{option}}={{defaultVal}}]\t{{description}}. ({{current}})[{{possibleVals}}]\n';
for (var option in this.optionDesc) {
if (this.optionDesc.hasOwnProperty(option)) {
var tmplParams = _.defaults({option:option,defaultVal:this.optionDefaults[option],current:this.options[option]},this.optionDesc[option]);
s+=_.template(template)(tmplParams);
}
}
return s;
};
/** Describe all variations on the default options **/
Rule.prototype.describeVariations = function () {
var s="";
for (var option in this.optionDesc) {
if (this.optionDesc.hasOwnProperty(option)) {
var vals = this.optionDesc[option].possibleVals;
for (var i = 0; i < vals.length; i++) {
var options={};
options[option]=vals[i];
s+=this.describeVariation(options)+'\n';
}
}
}
return s;
};
/** Get next hint **/
Rule.prototype.nextHint = function () {
var hint = this.hints[this.hintsUsed];
this.hintsUsed++;
return hint||"";
};
/** Generate an automatic hint from params **/
Rule.prototype.genHints = function () {
// first manual hints
this.hintsUsed=0;
var hints = [];
// compile hints for each unused property
// this onl handles one unused property per hint
// get all unused options
var unusedOptions = {};
for (var option in this.optionDesc) {
if (this.optionDesc.hasOwnProperty(option)) {
// for each option find any unused options
var posVals = this.optionDesc[option].possibleVals;
if (posVals){
var optionUnused = _.difference(posVals,[this.options[option]]);
unusedOptions[option+'Unused']=optionUnused;
}
}
}
// copy options
var tmplParams = _.extend({},this.options);
// make hint from unused options if there are hint templates for them
for (var optionUnused in unusedOptions) {
if (unusedOptions.hasOwnProperty(optionUnused)) {
// find hint templats that take this unused option
for (var i = 0; i < this.hintTmpls.length; i++) {
var tmpl = this.hintTmpls[i];
if (tmpl.indexOf(optionUnused)>=0){
var tmplCmp = _.template(tmpl);
var unused = unusedOptions[optionUnused];
// generate a hint for each unsed options
for (var j = 0; j < unused.length; j++) {
tmplParams[optionUnused]=unused[j];
var hint = tmplCmp(tmplParams);
hints.push(hint);
}
}
}
}
}
// now make other hints for used params
for (var i = 0; i < this.hintTmpls.length; i++) {
var hint = _.template(this.hintTmpls[i])(tmplParams);
hints.push(hint);
}
// on on a last hint
hints.push("That's all the hints for this rule. But don't forget to check for hints for the overall game. This is a game of inductive logic to try to disprove rules.");
hints.push("You cheeky blighter. Feel free to restart the game to get a new rule if this one is no fun (it will happen).");
// and finally remove duplicate hints
hints=_.uniq(hints);
return hints;
};
/** How many combination of this rule **/
Rule.prototype.combinations = function (arguments) {
var pos = _.map(this.optionDesc,'possibleVals');
var c=0;
for (var i = 0; i < pos.length; i++) {
c*pos[i].length;
}
return c;
};
// TODO estimate hardness perhaps through simulation, combinatorics or hints
// Now defined actual rules
var rules = [];
rules.push(
// here is a example rule where the card must have differen't color etc from the last card
// but it's abstracted to allow variations
new Rule(
"Next card must not have the same {{property}} as the last {{n}}'th card",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[this.options.n-1];
var property = this.options.property;
return chai.expect(card)
.to.have.property(property)
.not.equal(lastNCard[property]);
},
{
property: 'color',
n: 1
},
{
property: {
description: 'The property to compare in last and current card',
possibleVals: ['color','face','number','value','suit'],
type: 'String'
},
n: {
description: 'Number for how many cards back. Start counting at 1',
possibleVals: [1,2,3],
type: 'Number'
}
},
[
'This rule does not involve {{propertyUnused}}',
// 'This rule does not involve the {{nUnused}}th last card',
'This rule does involve {{property}}',
'This rule does involve the {{n}}th last card',
]
)
);
return {
Rule: Rule,
rules: rules,
};
})(_);
+5
View File
@@ -9,6 +9,11 @@ var UI = (function () {
FastClick.attach(document.body); FastClick.attach(document.body);
}); });
// $('.prevent-select').on('mousedown', function(e) {
// e.preventDefault();
// });
/** Show a bootstrap modal with dynamic content e.g. background info **/ /** Show a bootstrap modal with dynamic content e.g. background info **/
var showModal = function(title, text, level) { var showModal = function(title, text, level) {
var $modal = $('#infoBox'); var $modal = $('#infoBox');
+59 -59
View File
@@ -1,59 +1,59 @@
[{"key": "🂡","name":"Ace","value":"1","suite":"Spades","color":"Black","royal":"false","face":"false","number":"false"}, [{"key": "🂡","name":"Ace","value":1,"suit":"Spades","color":"Black","royal":false,"face":false,"number":false},
{"key": "🂢","name":"Two","value":"2","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂢","name":"Two","value":2,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂣","name":"Three","value":"3","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂣","name":"Three","value":3,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂤","name":"Four","value":"4","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂤","name":"Four","value":4,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂥","name":"Five","value":"5","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂥","name":"Five","value":5,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂦","name":"Six","value":"6","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂦","name":"Six","value":6,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂧","name":"Seven","value":"7","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂧","name":"Seven","value":7,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂨","name":"Eight","value":"8","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂨","name":"Eight","value":8,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂩","name":"Nine","value":"9","suite":"Spades","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🂩","name":"Nine","value":9,"suit":"Spades","color":"Black","royal":false,"face":false,"number":true},
{"key": "🂪","name":"Ten","value":"10","suite":"Spades","color":"Black","royal":"false","face":"true","number":"true"}, {"key": "🂪","name":"Ten","value":10,"suit":"Spades","color":"Black","royal":false,"face":true,"number":true},
{"key": "🂫","name":"Jack","value":"11","suite":"Spades","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🂫","name":"Jack","value":11,"suit":"Spades","color":"Black","royal":true,"face":true,"number":false},
{"key": "🂬","name":"Knight","value":"12","suite":"Spades","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🂬","name":"Knight","value":12,"suit":"Spades","color":"Black","royal":true,"face":true,"number":false},
{"key": "🂭","name":"Queen","value":"13","suite":"Spades","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🂭","name":"Queen","value":13,"suit":"Spades","color":"Black","royal":true,"face":true,"number":false},
{"key": "🂮","name":"King","value":"14","suite":"Spades","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🂮","name":"King","value":14,"suit":"Spades","color":"Black","royal":true,"face":true,"number":false},
{"key": "🂱","name":"Ace","value":"1","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"false"}, {"key": "🂱","name":"Ace","value":1,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":false},
{"key": "🂲","name":"Two","value":"2","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂲","name":"Two","value":2,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂳","name":"Three","value":"3","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂳","name":"Three","value":3,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂴","name":"Four","value":"4","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂴","name":"Four","value":4,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂵","name":"Five","value":"5","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂵","name":"Five","value":5,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂶","name":"Six","value":"6","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂶","name":"Six","value":6,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂷","name":"Seven","value":"7","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂷","name":"Seven","value":7,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂸","name":"Eight","value":"8","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂸","name":"Eight","value":8,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂹","name":"Nine","value":"9","suite":"Hearts","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🂹","name":"Nine","value":9,"suit":"Hearts","color":"Red","royal":false,"face":false,"number":true},
{"key": "🂺","name":"Ten","value":"10","suite":"Hearts","color":"Red","royal":"false","face":"true","number":"true"}, {"key": "🂺","name":"Ten","value":10,"suit":"Hearts","color":"Red","royal":false,"face":true,"number":true},
{"key": "🂻","name":"Jack","value":"11","suite":"Hearts","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🂻","name":"Jack","value":11,"suit":"Hearts","color":"Red","royal":true,"face":true,"number":false},
{"key": "🂼","name":"Knight","value":"12","suite":"Hearts","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🂼","name":"Knight","value":12,"suit":"Hearts","color":"Red","royal":true,"face":true,"number":false},
{"key": "🂽","name":"Queen","value":"13","suite":"Hearts","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🂽","name":"Queen","value":13,"suit":"Hearts","color":"Red","royal":true,"face":true,"number":false},
{"key": "🂾","name":"King","value":"14","suite":"Hearts","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🂾","name":"King","value":14,"suit":"Hearts","color":"Red","royal":true,"face":true,"number":false},
{"key": "🃁","name":"Ace","value":"1","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"false"}, {"key": "🃁","name":"Ace","value":1,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":false},
{"key": "🃂","name":"Two","value":"2","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃂","name":"Two","value":2,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃃","name":"Three","value":"3","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃃","name":"Three","value":3,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃄","name":"Four","value":"4","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃄","name":"Four","value":4,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃅","name":"Five","value":"5","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃅","name":"Five","value":5,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃆","name":"Six","value":"6","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃆","name":"Six","value":6,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃇","name":"Seven","value":"7","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃇","name":"Seven","value":7,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃈","name":"Eight","value":"8","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃈","name":"Eight","value":8,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃉","name":"Nine","value":"9","suite":"Diamonds","color":"Red","royal":"false","face":"false","number":"true"}, {"key": "🃉","name":"Nine","value":9,"suit":"Diamonds","color":"Red","royal":false,"face":false,"number":true},
{"key": "🃊","name":"Ten","value":"10","suite":"Diamonds","color":"Red","royal":"false","face":"true","number":"true"}, {"key": "🃊","name":"Ten","value":10,"suit":"Diamonds","color":"Red","royal":false,"face":true,"number":true},
{"key": "🃋","name":"Jack","value":"11","suite":"Diamonds","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🃋","name":"Jack","value":11,"suit":"Diamonds","color":"Red","royal":true,"face":true,"number":false},
{"key": "🃌","name":"Knight","value":"12","suite":"Diamonds","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🃌","name":"Knight","value":12,"suit":"Diamonds","color":"Red","royal":true,"face":true,"number":false},
{"key": "🃍","name":"Queen","value":"13","suite":"Diamonds","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🃍","name":"Queen","value":13,"suit":"Diamonds","color":"Red","royal":true,"face":true,"number":false},
{"key": "🃎","name":"King","value":"14","suite":"Diamonds","color":"Red","royal":"true","face":"true","number":"false"}, {"key": "🃎","name":"King","value":14,"suit":"Diamonds","color":"Red","royal":true,"face":true,"number":false},
{"key": "🃑","name":"Ace","value":"1","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"false"}, {"key": "🃑","name":"Ace","value":1,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":false},
{"key": "🃒","name":"Two","value":"2","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃒","name":"Two","value":2,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃓","name":"Three","value":"3","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃓","name":"Three","value":3,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃔","name":"Four","value":"4","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃔","name":"Four","value":4,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃕","name":"Five","value":"5","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃕","name":"Five","value":5,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃖","name":"Six","value":"6","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃖","name":"Six","value":6,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃗","name":"Seven","value":"7","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃗","name":"Seven","value":7,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃘","name":"Eight","value":"8","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃘","name":"Eight","value":8,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃙","name":"Nine","value":"9","suite":"Clubs","color":"Black","royal":"false","face":"false","number":"true"}, {"key": "🃙","name":"Nine","value":9,"suit":"Clubs","color":"Black","royal":false,"face":false,"number":true},
{"key": "🃚","name":"Ten","value":"10","suite":"Clubs","color":"Black","royal":"false","face":"true","number":"true"}, {"key": "🃚","name":"Ten","value":10,"suit":"Clubs","color":"Black","royal":false,"face":true,"number":true},
{"key": "🃛","name":"Jack","value":"11","suite":"Clubs","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🃛","name":"Jack","value":11,"suit":"Clubs","color":"Black","royal":true,"face":true,"number":false},
{"key": "🃜","name":"Knight","value":"12","suite":"Clubs","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🃜","name":"Knight","value":12,"suit":"Clubs","color":"Black","royal":true,"face":true,"number":false},
{"key": "🃝","name":"Queen","value":"13","suite":"Clubs","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🃝","name":"Queen","value":13,"suit":"Clubs","color":"Black","royal":true,"face":true,"number":false},
{"key": "🃞","name":"King","value":"14","suite":"Clubs","color":"Black","royal":"true","face":"true","number":"false"}, {"key": "🃞","name":"King","value":14,"suit":"Clubs","color":"Black","royal":true,"face":true,"number":false},
{"key": "🂠","name":"Playing Card","value":"15","suite":"Black","color":"Black","royal":"false","face":"false","number":"false"}, {"key": "🂠","name":"Playing Card","value":15,"suit":"Black","color":"Black","royal":false,"face":false,"number":false},
{"key": "🃏","name":"Joker","value":"16","suite":"Black","color":"Black","royal":"false","face":"true","number":"false"}, {"key": "🃏","name":"Joker","value":16,"suit":"Black","color":"Black","royal":false,"face":true,"number":false},
{"key": "🃟","name":"Joker","value":"16","suite":"Red","color":"Red","royal":"false","face":"true","number":"false"}] {"key": "🃟","name":"Joker","value":16,"suit":"Red","color":"Red","royal":false,"face":true,"number":false}]
+83
View File
@@ -0,0 +1,83 @@
[
{"key":"ᚠ"},
{"key":"ᚡ"},
{"key":"ᚢ"},
{"key":"ᚣ"},
{"key":"ᚤ"},
{"key":"ᚥ"},
{"key":"ᚦ"},
{"key":"ᚧ"},
{"key":"ᚨ"},
{"key":"ᚩ"},
{"key":"ᚪ"},
{"key":"ᚫ"},
{"key":"ᚬ"},
{"key":"ᚭ"},
{"key":"ᚮ"},
{"key":"ᚯ"},
{"key":"ᚰ"},
{"key":"ᚱ"},
{"key":""},
{"key":"ᚳ"},
{"key":"ᚴ"},
{"key":"ᚵ"},
{"key":"ᚶ"},
{"key":""},
{"key":"ᚸ"},
{"key":"ᚹ"},
{"key":"ᚺ"},
{"key":"ᚻ"},
{"key":"ᚼ"},
{"key":"ᚽ"},
{"key":"ᚾ"},
{"key":"ᚿ"},
{"key":"ᛀ"},
{"key":""},
{"key":"ᛂ"},
{"key":"ᛃ"},
{"key":"ᛄ"},
{"key":"ᛅ"},
{"key":"ᛆ"},
{"key":"ᛇ"},
{"key":"ᛈ"},
{"key":"ᛉ"},
{"key":"ᛊ"},
{"key":"ᛋ"},
{"key":""},
{"key":"ᛍ"},
{"key":"ᛎ"},
{"key":"ᛏ"},
{"key":"ᛐ"},
{"key":"ᛑ"},
{"key":"ᛒ"},
{"key":"ᛓ"},
{"key":"ᛔ"},
{"key":""},
{"key":""},
{"key":"ᛗ"},
{"key":"ᛘ"},
{"key":"ᛙ"},
{"key":"ᛚ"},
{"key":"ᛛ"},
{"key":"ᛜ"},
{"key":"ᛝ"},
{"key":"ᛞ"},
{"key":"ᛟ"},
{"key":"ᛠ"},
{"key":"ᛡ"},
{"key":"ᛢ"},
{"key":"ᛣ"},
{"key":"ᛤ"},
{"key":"ᛥ"},
{"key":"ᛦ"},
{"key":"ᛧ"},
{"key":"ᛨ"},
{"key":"ᛩ"},
{"key":"ᛪ"},
{"key":"᛫"},
{"key":""},
{"key":""},
{"key":"ᛮ"},
{"key":"ᛯ"},
{"key":"ᛰ"}
]
+7 -3
View File
@@ -53,7 +53,10 @@ module.exports = function (config) {
// dependencies // dependencies
'bower_components/jquery/dist/jquery.js', 'bower_components/jquery/dist/jquery.js',
'bower_components/jquery-ui/jquery-ui.js', 'bower_components/jquery-ui/jquery-ui.js',
'bower_components/bootstrap/dist/js/bootstrap.js', 'bower_components/bootstrap/dist/js/bootstrap.js',
'bower_components/angular/angular.js', 'bower_components/angular/angular.js',
'bower_components/angular-route/angular-route.js', 'bower_components/angular-route/angular-route.js',
'bower_components/angular-resource/angular-resource.js', 'bower_components/angular-resource/angular-resource.js',
@@ -61,7 +64,10 @@ module.exports = function (config) {
'bower_components/angular-mocks/angular-mocks.js', 'bower_components/angular-mocks/angular-mocks.js',
'bower_components/angular-dragdrop/src/angular-dragdrop.js', 'bower_components/angular-dragdrop/src/angular-dragdrop.js',
'bower_components/angular-ui-grid/ui-grid.js', 'bower_components/angular-ui-grid/ui-grid.js',
'bower_components/lodash/dist/lodash.js', 'bower_components/lodash/dist/lodash.js',
'bower_components/chai/chai.js',
'js/external/*.js', 'js/external/*.js',
// fixtures // fixtures
@@ -88,9 +94,7 @@ module.exports = function (config) {
'js/helpers.js', 'js/helpers.js',
'js/analytics.js', 'js/analytics.js',
'js/gameobjects.js', 'js/gameobjects.js',
'js/detector/flame.js', 'js/rules.js',
'js/detector/bubblr.js',
'js/detector/event.js',
'js/detector/detector.js', 'js/detector/detector.js',
'js/ui.js', 'js/ui.js',
'js/game.js', 'js/game.js',
+102
View File
@@ -0,0 +1,102 @@
'use strict';
/* jasmine specs for filters go here */
var allCards;
beforeEach(function(){
allCards = Helpers.loadFile('json/elements.json');
});
describe('Rules', function () {
var card, lastCards;
beforeEach(
module('Rules')
);
beforeEach(function(){
lastCards = _.sampleSize(allCards,4);
card = _.sample(allCards);
});
describe('Rule', function () {
it('should test false on false', function () {
var rule = new Rules.Rule('1',function(){return false;},{},{},[]);
var res = rule.test(card,lastCards,allCards);
expect(res).toBe(false);
});
it('should test false on assertion error', function () {
var rule = new Rules.Rule('2',function(){return chai.expect(1).to.equal(0);},{},{},[]);
var res = rule.test(card,lastCards,allCards);
expect(res).toBe(false);
});
it('should test true on assertion', function () {
var rule = new Rules.Rule('3',function(){return chai.expect(1).to.equal(1);},{},{},[]);
var res = rule.test(card,lastCards,allCards);
expect(res).toBe(true);
});
it('should test true on true', function () {
var rule = new Rules.Rule('4',function(){return true;},{},{},[]);
var res = rule.test(card,lastCards,allCards);
expect(res).toBe(true);
});
it('should throw test on error', function () {
var rule = new Rules.Rule('5',function(){throw new Error('test');return true;},{},{},[]);
expect(function(){
rule.test(card,lastCards,allCards);
}).toThrow();
});
});
Rules.rules.forEach(function(rule){
describe('rule', function () {
it('should test', function () {
var res = rule.test(card,lastCards,allCards);
Boolean(res);
});
it('should describe itself', function () {
var desc = rule.describe();
expect(typeof desc).toBe('string');
expect(desc).not.toContain(/[{}]+/);
});
it('should describeOptions', function () {
var desc = rule.describeOptions();
expect(typeof desc).toBe('string');
expect(desc).not.toContain(/[{}]+/);
});
it('should describeVariations', function () {
var desc = rule.describeVariations();
expect(typeof desc).toBe('string');
expect(desc).not.toContain(/[{}]+/);
});
it('should genHints', function () {
var hints = rule.genHints();
expect(hints instanceof Array).toBe(true);
expect(hints.length).toBeGreaterThan(0);
expect(hints.join('')).not.toContain(/[{}]+/);
});
it('should set options', function () {
var opts1 = rule.options;
var opts = rule.setOptions({a24tgsdgtg43t5fd:1});
expect(typeof opts).toBe('object');
expect(opts).not.toEqual(opts1);
});
// now check each rule permutation
var options=Object.keys(rule.optionDesc);
options.forEach(function(option){
var vals = rule.optionDesc[option].possibleVals;
for (var i = 0; i < vals.length; i++) {
it('should test with option: '+option+' = '+vals[i],function(){
var options={};
options[option]=vals[i];
rule.setOptions(options);
var res = rule.test(card,lastCards,allCards);
Boolean(res);
});
}
});
});
});
});