-This is a game with a secret rule. You try to put down the next card and work out the rule by trial and error. The rule is randomised each time and there are hints available. At it's base this is a game of inductive reason and the scientific method.
-
-
-
-Hints:
-
-
-
-This game is based on Eleusis by Robert Abbott and John Golden's Eleusis Express.
-
diff --git a/src/index.js b/src/index.js
index 305e874..8d3c448 100644
--- a/src/index.js
+++ b/src/index.js
@@ -56,4 +56,25 @@ var bootstrap = require("bootstrap");
// var Rules = require("js/rules.js");
// var UI = require("js/ui.js");
// var Game = require("js/game.js");
-var app = require("js/app.js");
+// var app = require("js/app.js");
+
+/** This file exports parts of the app as a library **/
+var clientApp = module.exports = {
+ ObjectStorage: require("js/storage.js"),
+ Helpers: require("js/helpers.js"),
+ Analytics: require("js/analytics.js"),
+ GameObjects: require("js/gameobjects.js"),
+ Rules: require("js/rules.js"),
+ Simulate: require("js/rules/simulate.js"),
+ UI: require("js/ui.js"),
+ Game: require("js/game.js"),
+ app: require("js/app.js"),
+};
+
+// require("html/win.html");
+// require("html/lose.html");
+// require("html/game.html");
+
+// deleteme dev TODO XXX
+require("js/rules/simulate.html");
+console.log('break here for dev');
diff --git a/src/index.webpack b/src/index.webpack
index 0bb910c..7725417 100644
--- a/src/index.webpack
+++ b/src/index.webpack
@@ -48,14 +48,16 @@
diff --git a/src/js/analytics.js b/src/js/analytics.js
index dc6d3e3..1fe0313 100644
--- a/src/js/analytics.js
+++ b/src/js/analytics.js
@@ -49,7 +49,7 @@ var analytics = module.exports =
ga('create', Helpers.analytics,'auto');
ga('set', { 'appName': 'Cards For Science', 'appId': '', 'appVersion': '0.6' });
ga('set', 'anonymizeIp', true);
- ga('send','pageview');
+ // ga('send','pageview');// angulartics
}
diff --git a/src/js/app.js b/src/js/app.js
index 211861e..015d868 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -7,13 +7,14 @@
var jquery = require("jquery");
var jqueryUi = require("jquery-ui");
var jqueryUiTouchPunch = require("jquery-ui-touch-punch");
-var jqueryCookie = require("js-cookie");
+var jsCookie = require("js-cookie");
var angular = require("angular");
var angularDragdrop = require("angular-dragdrop");
var angularAnimate = require("angular-animate");
var angulartics = require('angulartics');
var angularticsGoogleAnalytics = require('angulartics-google-analytics');
+var ngAlertify = require("alertify.js/dist/js/ngAlertify.js");
//app
var ObjectStorage = require("js/storage");
@@ -22,11 +23,12 @@ var GameObjects = require("js/gameobjects");
var analytics = require("js/analytics");
var Game = require("js/game");
var Rules = require("js/rules.js");
+var UI = require("js/ui.js");
var app = (function (Helpers,analytics,Game,Rules) {
Helpers.validateSaveVersion();
- var app = angular.module('cardsForScience', ['ngDragDrop','ngAnimate','angulartics', angularticsGoogleAnalytics]);
+ var app = angular.module('cardsForScience', ['ngDragDrop','ngAnimate','angulartics', angularticsGoogleAnalytics,"ngAlertify"]);
// config
app.config(function ($analyticsProvider) {
@@ -160,7 +162,7 @@ var app = (function (Helpers,analytics,Game,Rules) {
game.lab = lab;
game.allObjects.lab = lab;
var promise = game.load($http, $q);
- game.init();
+ game.reset();
// return promise;
return game;
};
@@ -259,8 +261,8 @@ var app = (function (Helpers,analytics,Game,Rules) {
function TableController($scope, game, lab, $filter) {
var vm = this;
vm.cards = detector.cards;
- vm.rule = '';
- vm.hints = [];
+ vm.ruleInfo = game.ruleInfo;
+ vm.hints = game.hints;
vm.limit = -12;
vm.hintCost = 10;
vm.ruleCost = 300;
@@ -279,8 +281,10 @@ var app = (function (Helpers,analytics,Game,Rules) {
var result = game.onDrop(event, ui, game);
};
vm.revealRule = function () {
- vm.rule = game.rule.describe();
- lab.state.score -= vm.ruleCost;
+ if (vm.ruleInfo.length===0){
+ vm.ruleInfo.push(game.rule.describe());
+ lab.state.score -= vm.ruleCost;
+ };
};
vm.revealHint = function () {
var hint = game.rule.nextHint();
@@ -294,61 +298,17 @@ var app = (function (Helpers,analytics,Game,Rules) {
app.controller('TableController', TableController);
- function LabController($interval, game, lab) {
- // todo give workers instead of game
- var vm = this;
- vm.lab = lab;
- vm.showDetectorInfo = function () {
- if (!vm._detectorInfo) {
- vm._detectorInfo = Helpers.loadFile('html/detector.html');
- }
- UI.showModal('Detector', vm._detectorInfo);
- };
- };
- LabController.$inject = ['$interval', 'game', 'lab'];
- app.controller('LabController', LabController);
-
-
- function RulesController($scope, game, lab) {
+ function RulesController($scope, game, lab,$analytics,alertify) {
var vm = this;
// present just a few hypothesis
- var rules = Rules.rules.map(function (r) {
- return angular.copy(r);
+
+ // emit event track (with category and label properties for GA)
+ $analytics.eventTrack('rule', {
+ category: 'rule', label: game.rule.describe()
});
- rules = _.sampleSize(rules, 2);
- if (!_.find(rules, {
- description: game.rule.description
- })) {
- var rule = angular.copy(game.rule);
- rule.setOptions({}); // reset the value of this copy to not give away;
- rules.push(rule);
- } else {
- rules.push(angular.copy(_.sample(Rules.rules)));
- }
- vm.rules = _.shuffle(rules);
- // or present them without options?
- var rules2 = Rules.rules.map(function (r) {
- var rule = angular.copy(r);
- rule.randomize();
- return rule;
- });
- rules2 = _.sampleSize(rules, 4);
- // add the real rule and a couple of variations
- var rule = angular.copy(game.rule);
- rules2.push(rule);
- var rule = angular.copy(game.rule);
- rule.randomize();
- rules2.push(rule);
- var rule = angular.copy(game.rule);
- rule.randomize();
- rules2.push(rule);
-
- // clean and add to controller
- rules2 = _.uniq(rules2);
- rules2 = _.shuffle(rules2);
- vm.rules2 = rules2;
+ vm.hypotheses = game.hypotheses;
vm.upgrades = game.upgrades;
vm.isVisible = function (upgrade) {
@@ -357,15 +317,10 @@ var app = (function (Helpers,analytics,Game,Rules) {
vm.isAvailable = function (upgrade) {
return upgrade.isAvailable(lab, game.allObjects);
};
- vm.upgrade = function (upgrade) {
- if (upgrade.buy(lab, game.allObjects)) {
- UI.showUpdateValue("#update-funding", upgrade.cost);
- }
- };
/** return a class based on none right or wrong guessed **/
vm.isGuessed = function(rule){
- if (rule.state.guessed===true) return 'bg-success';
- else if (rule.state.guessed===false) return 'bg-danger';
+ if (rule.guessed===true) return 'bg-success';
+ else if (rule.guessed===false) return 'bg-danger';
else return '';
};
@@ -377,17 +332,47 @@ var app = (function (Helpers,analytics,Game,Rules) {
if (sameRule && sameOpts) {
// right!
lab.state.score += 200;
+ lab.state.rulesGuessed.push({
+ key: game.rule.key,
+ options: game.rule.options,
+ description: game.rule.describe(),
+ });
if (!rule.state) rule.state={};
- rule.state.guessed=true;
+ rule.guessed=true;
+
+ alertify.alert(
+ 'You have won using inductive logic!
Play again?',
+ function(event){
+ event.preventDefault();
+ // ObjectStorage.clear();
+ // $window.location.reload(true); /// reloads are better for ads?
+ game.reset();
+ // since this confirmation was away from the dom we need to
+ // manually refresh
+ $scope.$apply();
+ },function(){}
+ );
} else {
lab.state.score -= 200;
- rule.state.guessed=false;
+ rule.guessed=false;
+ lab.state.rulesFailed.push({
+ key: rule.key,
+ options: rule.options,
+ description: rule.describe(),
+ });
}
-
console.log('guess', arguments);
+
+
+ // vm.winDialouge = function () {
+ // if (!vm._winDialouge) {
+ // vm._winDialouge = Helpers.loadFile('html/win.html');
+ // }
+ // UI.showModal('Win', vm._winDialouge);
+ // };
};
};
- RulesController.$inject = ['$scope', 'game', 'lab'];
+ RulesController.$inject = ['$scope', 'game', 'lab','$analytics','alertify'];
app.controller('RulesController', RulesController);
function AchievementsController($scope, game, lab) {
@@ -402,35 +387,83 @@ var app = (function (Helpers,analytics,Game,Rules) {
AchievementsController.$inject = ['$scope', 'game', 'lab'];
app.controller('AchievementsController', AchievementsController);
- function SaveController($scope, $interval, $window, game, lab) {
+ function SaveController($scope, $interval, $window, alertify, game, lab) {
var vm = this;
game.lastSaved = new Date().getTime();
vm.lastSaved = game.lastSaved;
+;
+
vm.saveNow = function () {
var saveTime = new Date().getTime();
lab.state.time += saveTime - game.lastSaved;
game.save();
game.lastSaved = saveTime;
vm.lastSaved = game.lastSaved;
+
+ // if (lab.state.score<0){
+ // alertify.alert(
+ // 'Your score is below zero, so you lost :( Want to try a different rule?',
+ // function(event){
+ // event.preventDefault();
+ // // ObjectStorage.clear();
+ // // $window.location.reload(true); /// reloads are better for ads?
+ // game.reset();
+ // // since this confirmation was away from the dom we need to
+ // // manually refresh
+ // $scope.$apply();
+ // },function(){}
+ // );
+ // }
+
};
vm.restart = function () {
- if ($window.confirm(
- 'Do you really want to restart the game? All progress will be lost.'
- )) {
- ObjectStorage.clear();
- $window.location.reload(true);
- }
+ console.log('restart');
+ alertify.confirm(
+ 'Do you really want to restart the game? All progress will be lost.',
+ function(event){
+ event.preventDefault();
+ // ObjectStorage.clear();
+ // $window.location.reload(true); /// reloads are better for ads?
+ game.reset();
+ // since this confirmation was away from the dom we need to
+ // manually refresh
+ $scope.$apply();
+ },function(){}
+ );
};
$interval(vm.saveNow, 10000);
};
- SaveController.$inject = ['$scope', '$interval', '$window', 'game', 'lab'];
+ SaveController.$inject = ['$scope', '$interval', '$window', 'alertify', 'game', 'lab'];
app.controller('SaveController', SaveController);
- function StatsController($scope, lab) {
+ function StatsController($scope, lab,game,alertify) {
var vm = this;
vm.lab = lab;
+ vm.lost=false;
+
+
+ $scope.$watch('lc.lab.state.score', function (newValue, oldValue) {
+ if (newValue<0&&!vm.lost){
+ vm.lost=true;
+ alertify.alert(
+ '
You lost :(
Play again?
P.S. The rule was:
"'+game.rule.describe()+'"',
+ function(event){
+ event.preventDefault();
+ // ObjectStorage.clear();
+ // $window.location.reload(true); /// reloads are better for ads?
+ game.reset();
+ // since this confirmation was away from the dom we need to
+ // manually refresh
+ $scope.$apply();
+ vm.lost=false;
+
+ },function(){}
+ );
+ };
+ });
+
};
- StatsController.$inject = ['$scope', 'lab'];
+ StatsController.$inject = ['$scope', 'lab','game','alertify'];
app.controller('StatsController', StatsController);
analytics.init();
diff --git a/src/js/game.js b/src/js/game.js
index b21afb9..33a3c09 100644
--- a/src/js/game.js
+++ b/src/js/game.js
@@ -7,6 +7,7 @@ var GameObjects = require("js/gameobjects.js");
var Rules = require("js/rules.js");
var cards = require("json/cards.json");
var achievements = require("json/achievements.json");
+var ruleSimulations = require("json/simulations.json");
var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,cards,achievements) {
'use strict';
@@ -21,11 +22,13 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
// lab: this.lab
};
this.loaded = false;
- this.rules = Rules.rules;
- this.rule=undefined;
+ this.hypotheses = [];
this.lastCards= [];
+ this.hints=[];
+ this.ruleInfo=[];
this.incorrectCards= [];
+ this.rule=undefined;
this.rules=Rules.rules;
this.Rule=Rules.Rule;
};
@@ -36,56 +39,9 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
return;
}
- // I know synchronous requests are bad as they will block the browser.
- // However, I don't see any other reasonable way to do this in order to
- // make it work with Angular. If you know a way, let me know, and I'll
- // give you a beer. - Kevin
this.cards = cards; //Helpers.loadFile('json/cards.json');
this.achievements = require("json/achievements.json"); //Helpers.loadFile('./json/achievements.json');
- // function successCallback(response) {
- // return angular.fromJson(response.data);
- // }
- //
- // function errorCallback(response) {
- // return console.error('Could not get url', response.statusText, response);
- // }
- // return $q.all(
- // $http.get('json/cards.json', {
- // transformResponse: angular.fromJson
- // })
- // .then(function (response) {
- // return self.cards = response.data;
- // }),
- // $http.get('json/workers.json', {
- // transformResponse: angular.fromJson
- // })
- // .then(function (response) {
- // return self.workers = response.data;
- // }),
- // $http.get('json/upgrades.json', {
- // transformResponse: angular.fromJson
- // })
- // .then(
- // function (response) {
- // return self.upgrades = response.data;
- // }),
- // $http.get('json/achievements.json', {
- // transformResponse: angular.fromJson
- // })
- // .then(
- // function (response) {
- // return self.achievements = response.data;
- // }),
- // $http.get('json/keywords.json', {
- // transformResponse: angular.fromJson
- // })
- // .then(
- // function (response) {
- // return self.keywords = response.data;
- // })
- // ).then(function () {
-
// Turn JSON files into actual game objects and fill map of all objects
var makeGameObject = function (type, object) {
// It's okay to define this function here since load is only called
@@ -102,54 +58,126 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
function (a) {
return makeGameObject(GameObjects.Achievement, a);
});
- // Load states from local store
- for (var key in self.allObjects) {
- var o = self.allObjects[key];
- o.loadState(ObjectStorage.load(key));
- }
+
// put cards in extended array with utility methods
self.Card = new GameObjects.Cards();
self.Card.push.apply(self.Card, self.cards);
self.cards = self.Card;
+ // add rules to load and save states
+ self.rules.map(function(o){
+ self.allObjects[o.key] = o;
+ });
+
+ // TODO save and load lastCards and incorrectCards
+
+ // Load states from local store
+ for (var key in self.allObjects) {
+ var o = self.allObjects[key];
+ o.loadState(ObjectStorage.load(key));
+ }
+
self.loaded = true;
return self;
};
- Game.prototype.init = function () {
+ Game.prototype.reset = function () {
// setup game
- this.rule = _.sample(Rules.rules);
- this.rule.randomize();
+ this.rule = this.newRule();
- // check if we have any cards in hand
- // var totalElements = _(self.cards).map('state.amount').sum();
- // if (totalElements<1) self.dealHand();
this.dealHand();
// deal first card
// TODO make sure these follow rule
+
+ // deal new initial cards
+ this.lastCards.splice(0,this.lastCards.length);
this.lastCards.push.apply(this.lastCards,_.sampleSize(this.cards,3));
- for (var i = 0; i < this.lastCards.length; i++) {
- this.incorrectCards[i]=[];
- }
+
+ this.ruleInfo.splice(0,this.ruleInfo.length);
+ this.hints.splice(0,this.hints.length);
+
+ // empty incorrect cards
+ this.incorrectCards.splice(0,this.incorrectCards.length);
+ this.incorrectCards.push([]);
+ this.incorrectCards.push([]);
+ this.incorrectCards.push([]);
// reset score
this.lab.state.score = 200;
- return self;
+
+ // new set of hypothes
+ this.hypotheses=this.genHypotheses();
+
+ return this;
+ };
+
+ Game.prototype.genHypotheses = function () {
+ // get some hypotheses
+ var hypo=[];
+
+ // a random 2, 2 variations of each
+ for (var i = 0; i < 3; i++) {
+ var rule = _.sample(this.rules);
+ rule = angular.copy(rule);
+ rule.randomize();
+ hypo.push(rule);
+ rule = angular.copy(rule);
+ rule.randomize();
+ hypo.push(rule);
+ }
+
+ // add the real rule
+ var rule = angular.copy(this.rule);
+ hypo.push(rule);
+
+ // and a variation of the real rule
+ var rule = angular.copy(this.rule);
+ rule.randomize();
+ hypo.push(rule);
+
+ // clean and remember
+ hypo = _.uniq(hypo);
+ hypo = _.shuffle(hypo);
+
+ // empty old ones
+ this.hypotheses.splice(0,this.hypotheses.length);
+ // put in new ones
+ for (var i = 0; i < hypo.length; i++) {
+ this.hypotheses.push(hypo[i]);
+ }
+ return this.hypotheses;
+ };
+
+ Game.prototype.newRule = function () {
+ var okRules = _.filter(ruleSimulations,function(s){
+ return s.ratioRight>0.1&&s.ratioRight<0.6;
+ });
+ // choose and ok rule
+ var ruleConfig = _.sample(okRules);
+ // now find the rule and set these options
+ var rule = _.find(this.rules,{key:ruleConfig.key});
+ var options = ruleConfig.options;
+ if (typeof options==="string") options = JSON.parse(options);
+ rule.setOptions(options);
+ return this.rule = rule;
};
Game.prototype.dealHand = function (n) {
n=n||12;
- var hand=_.sampleSize(this.cards,n);
+
+ var sample=_.sampleSize(this.cards,n);
+
+ // empty all cards
this.cards.map(function(card){
card.state.amount=0;
});
- for (var i = 0; i < hand.length; i++) {
- var card = hand[i];
+ // now increase the value of our sample
+ for (var i = 0; i < sample.length; i++) {
+ var card = sample[i];
this.cards.get(card.key).state.amount++;
}
-
};
Game.prototype.onClick = function (event, ui) {
var self=this;
diff --git a/src/js/gameobjects.js b/src/js/gameobjects.js
index be71cfa..44854b7 100644
--- a/src/js/gameobjects.js
+++ b/src/js/gameobjects.js
@@ -29,7 +29,7 @@ var GameObjects = module.exports = (function () {
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
- }
+ };
/** @class Lab
*/
@@ -254,109 +254,6 @@ var GameObjects = module.exports = (function () {
return this.state.amount -= 1;
};
- /** @class Worker
- * Implement an auto-clicker in the game.
- */
- var Worker = function (obj) {
- GameObject.apply(this, [obj]);
- this.state.hired = 0;
- };
-
- Worker.prototype = Object.create(GameObject.prototype);
-
- Worker.prototype.constructor = Worker;
-
- Worker.prototype.isVisible = function (lab) {
- if (!lab) {
- return false;
- }
- return this.state.hired > 0 ||
- lab.state.money >= this.state.cost * GLOBAL_VISIBILITY_THRESHOLD;
- };
-
- Worker.prototype.isAvailable = function (lab) {
- if (!lab) {
- return false;
- }
- return lab.state.money >= this.state.cost;
- };
-
- Worker.prototype.hire = function (lab) {
- if (lab && lab.buy(this.state.cost)) {
- this.state.hired++;
- var cost = this.state.cost;
- this.state.cost = Math.floor(cost * this.cost_increase);
- return cost;
- }
- return -1; // not enough money
- };
-
- Worker.prototype.getTotal =
- function () {
- return this.state.hired * this.state.rate;
- };
-
- /** @class Upgrade
- */
- var Upgrade = function (obj) {
- GameObject.apply(this, [obj]);
- this.state.visible = false;
- this.state.used = false;
- };
-
- Upgrade.prototype = Object.create(GameObject.prototype);
-
- Upgrade.prototype.constructor = Upgrade;
-
- Upgrade.prototype.meetsRequirements = function (allObjects) {
- if (!allObjects) {
- return false;
- }
- for (var i = 0; i < this.requirements.length; i++) {
- var req = this.requirements[i];
- if (allObjects[req.key].state[req.property] < req.threshold) {
- return false;
- }
- }
- return true;
- };
-
- Upgrade.prototype.isAvailable = function (lab, allObjects) {
- if (!lab || !allObjects) {
- return false;
- }
- return !this.state.used && lab.state.money >= this.cost &&
- this.meetsRequirements(allObjects);
- };
-
- Upgrade.prototype.isVisible = function (lab, allObjects) {
- if (!lab || !allObjects) {
- return false;
- }
- if (!this.state.used &&
- (this.state.visible ||
- lab.state.money >= this.cost * GLOBAL_VISIBILITY_THRESHOLD &&
- this.meetsRequirements(allObjects))) {
- this._visible = true;
- return true;
- }
- return false;
- };
-
- Upgrade.prototype.buy = function (lab, allObjects) {
- if (lab && allObjects && !this.state.used && lab.buy(this.cost)) {
- for (var i = 0; i < this.targets.length; i++) {
- var t = this.targets[i];
- allObjects[t.key].state[t.property] *= this.factor || 1;
- allObjects[t.key].state[t.property] += this.constant || 0;
- }
- this.state.used = true; // How about actually REMOVING used upgrades?
- this.state.visible = false;
- return this.cost;
- }
- return -1;
- };
-
/** @class Achievement
*/
@@ -394,8 +291,6 @@ var GameObjects = module.exports = (function () {
return {
Lab: Lab,
Card: Card,
- Worker: Worker,
- Upgrade: Upgrade,
Achievement: Achievement,
Cards: Cards
};
diff --git a/src/js/rules.js b/src/js/rules.js
index 44c61e6..bf874c2 100644
--- a/src/js/rules.js
+++ b/src/js/rules.js
@@ -5,7 +5,7 @@
if (!module) module=function(){};
var chai;
if (!chai){
- chair = require("chai");
+ chai = require("chai");
}
/**
@@ -16,11 +16,41 @@ if (!chai){
var Rules = module.exports = (function functionName(_,chai) {
+ /** Generate all combination of arguments from array using functional programming instead of recursive
+ * @param {Object} opts - An array or arrays with keys describing options [['Ben','Jade','Darren'],['Smith','Miller']]
+ * @returns {Array} - An array of arrays e.g. [['Ben','Smith'],[..]]
+ **/
+ function cartesianProductOf(opts) {
+ if (arguments.length>1) opts=arguments;
+ return _.reduce(opts, function(a, b) {
+ return _.flatten(_.map(a, function(x) {
+ return _.map(b, function(y) {
+ return x.concat([y]);
+ });
+ }), true);
+ }, [ [] ]);
+ };
+
+
+ /** Generate all combination of arguments from objects using functional programming instead of recursive
+ * @param {Object} opts - An object or arrays with keys describing options {firstName:['Ben','Jade','Darren'],lastName:['Smith','Miller']}
+ * @returns {Array} - An array of objects e.g. [{firstName:'Ben',LastName:'Smith'},{..]
+ **/
+ function cartesianProductObj(optObj){
+ var opts = _.values(optObj);
+ var keys = _.keys(optObj);
+ var combs = cartesianProductOf(opts);
+ return _.map(combs,function(comb){
+ return _.zipObject(keys,comb);
+ });
+ }
+
/**
* Rule prototype
+ * @param {String} key A short key
* @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 {Function} ruleFunc A function returning a true or a failure message or a assertion 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
@@ -30,23 +60,44 @@ var Rules = module.exports = (function functionName(_,chai) {
* `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) {
+ var Rule = function (key, description, ruleFunc, optionDefaults, optionDesc, hintTmpls) {
+
+ // check inputs
if (!(typeof (description) === 'string' || description instanceof String) || !description.length)
- throw new TypeError('description should be a non empty string');
+ 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.key=key;
this.ruleFunc = ruleFunc;
this.optionDesc = optionDesc || {};
this.options = this.optionDefaults = optionDefaults || {};
this.hintTmpls = hintTmpls || [];
// init
- this.state = {};
- this.hintsUsed = 0;
+ this.state = {
+ hintsUsed: 0,
+ wrongCards: 0,
+ rightCards: 0,
+ wrongGuesses: 0,
+ rightGuesses: 0,
+ };
+ // used for template rendering
this.otherOptions={nth:this.nth,lastn:this.lastn};
+ // render hint templates
this.hints = this.genHints();
+ // work out all option combos, only takes a second
+ this.optionsPossible=this.genOptions();
+ };
+ Rule.prototype.genOptions = function () {
+ var possibleVals={};
+ for (var prop in this.optionDesc) {
+ if (this.optionDesc.hasOwnProperty(prop)) {
+ possibleVals[prop]=this.optionDesc[prop].possibleVals;
+ }
+ }
+ return this.optionsPossible=cartesianProductObj(possibleVals);
};
Rule.prototype.setOptions = function (options) {
this.options = _.defaults(options, this.optionDefaults);
@@ -98,56 +149,6 @@ var Rules = module.exports = (function functionName(_,chai) {
Rule.prototype.describe = function () {
return this.describeVariation(this.options);
};
- // Rule.prototype.describeHtml = function (arguments) {
- // // we want template params where each options is replace with a select box
- // var tmplParams = {};
- //
- // for (var option in this.optionDesc) {
- // if (this.optionDesc.hasOwnProperty(option)) {
- // var vals = this.optionDesc[option].possibleVals;
- // if (vals) {
- // s = '\n';
- // tmplParams[option] = s;
- // } else {
- // tmplParams[option] = option;
- // }
- // }
- // }
- // return _.template(this.description)(tmplParams);
- // };
- /** Generate angular template for this rule **/
- // Rule.prototype.describeNg = function (arguments) {
- //
- // // first put option name into this option template
- // // _.templateSettings.interpolate = /<%=([\s\S]+?)%>/g;
- // optionTmpl = '' +
- // '\n';
- //
- // // we want template params where each options is replace with a select box
- // var tmplParams = {};
- //
- // for (var option in this.optionDesc) {
- // if (this.optionDesc.hasOwnProperty(option)) {
- // var vals = this.optionDesc[option].possibleVals;
- // if (vals) {
- // tmplParams[option] = _.template(optionTmpl)({
- // option: option
- // });
- // } else {
- // tmplParams[option] = option;
- // }
- // }
- // }
- //
- // // now in description replace params with select box
- // return _.template(this.description)(tmplParams);
- // };
/** Compile description using options to express rule**/
Rule.prototype.describeVariation = function (options) {
var compiled = _.template(this.description);
@@ -191,15 +192,15 @@ var Rules = module.exports = (function functionName(_,chai) {
};
/** Get next hint **/
Rule.prototype.nextHint = function () {
- var hint = this.hints[this.hintsUsed];
- this.hintsUsed++;
+ var hint = this.hints[this.state.hintsUsed];
+ this.state.hintsUsed++;
return hint || "";
};
/** Generate an automatic hint from params **/
Rule.prototype.genHints = function () {
// first manual hints
- this.hintsUsed = 0;
+ this.state.hintsUsed = 0;
var hints = [];
// compile hints for each unused property
@@ -226,7 +227,7 @@ var Rules = module.exports = (function functionName(_,chai) {
for (var optionUnused in unusedOptions) {
if (unusedOptions.hasOwnProperty(optionUnused)) {
- // find hint templats that take this unused option
+ // find hint templates that take this unused option
for (var i = 0; i < this.hintTmpls.length; i++) {
var tmpl = this.hintTmpls[i];
if (tmpl.indexOf(optionUnused) >= 0) {
@@ -250,8 +251,8 @@ var Rules = module.exports = (function functionName(_,chai) {
}
// 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).");
+ hints.push("That's all the hints. But this is a game of inductive logic so try to disprove rules instead of proving them.");
+ hints.push("No more hints you cheeky blighter!");
// and finally remove duplicate hints
hints = _.uniq(hints);
@@ -299,6 +300,11 @@ var Rules = module.exports = (function functionName(_,chai) {
}
};
+ Rule.prototype.loadState =
+ function (state) {
+ $.extend(this.state, state);
+ };
+
// Now defined actual rules
var rules = [];
@@ -307,6 +313,7 @@ var Rules = module.exports = (function functionName(_,chai) {
// 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(
+ "d4a2f61a-0fb9-4eb6-9258-3e14ae4cd512",
"Next card must not have the same <%= property %> as the <%= lastn(n) %> card",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - this.options.n];
@@ -336,10 +343,10 @@ var Rules = module.exports = (function functionName(_,chai) {
]
),
new Rule(
+ "2b70bd6a-8bdd-49d4-bec7-bfb9de090e78",
"If <%= lastn(n) %> cards value was between <%= min %> and <%= max %> play a card that isn't and vice versa.",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - this.options.n];
- var property = this.options.property;
var lastWasbetween = options.min < lastNCard.value && lastNCard.value < options.max;
if (lastWasbetween)
return chai.expect(card)
@@ -382,6 +389,7 @@ var Rules = module.exports = (function functionName(_,chai) {
]
),
new Rule(
+ "3e12d713-335a-42e7-81ed-7064442f628a",
"Play a card with a value <%= min %> to <%= max %> higher than the value of the <%= lastn(n) %> card. The numbers wrap around once they reach the max",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - this.options.n];
@@ -426,6 +434,7 @@ var Rules = module.exports = (function functionName(_,chai) {
new Rule(
+ "99b1d408-000d-41e6-8a0c-886a5d21d06f",
"If the <%= lastn(n) %> card is an even-valued card, play a <%= evenColor %> card. Otherwise play the other color",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - this.options.n];
@@ -465,6 +474,7 @@ var Rules = module.exports = (function functionName(_,chai) {
),
new Rule(
+ "4014c459-a7a7-4a49-98f5-302a1b7b89e7",
"Play a card that has the same <%= property %> or color as the <%= lastn(n) %> card but not both.",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - options.n];
@@ -495,6 +505,7 @@ var Rules = module.exports = (function functionName(_,chai) {
),
new Rule(
+ "793d93cb-ca09-4cda-8781-6fce02edf09c",
"If the <%= lastn(n) %> card's number is higher than <%= min %>, change <%= property %>, and if lower, keep it the same.",
function (card, lastCards, allCards, options) {
var lastNCard = lastCards[lastCards.length - options.n];
@@ -540,6 +551,7 @@ var Rules = module.exports = (function functionName(_,chai) {
]
),
new Rule(
+ "f4fba793-f886-4db8-9853-240002bb112e",
"If the <%= lastn(n) %> card was a <%= property %> card, play a higher value card otherwise lower.",
//TODO what if we have a high card? we can only go higher
function (card, lastCards, allCards, options) {
diff --git a/src/js/rules/chaiVerbose.js b/src/js/rules/chaiVerbose.js
deleted file mode 100644
index 0011723..0000000
--- a/src/js/rules/chaiVerbose.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/** Extend chai to be used in defining rules **/
-
-// might want to use chair string for startswith etc
-// chai-properties for muliple properties
-// chai-adhoc for easier additions https://github.com/AnAppAMonth/chai-adhoc
-var chai = (function functionName(chai) {
-
- // wrap every chainer to add message to chain? that would be interesting
-
- // card method that set's name of card?
-
- // wrap or overide property chain to add to name
-
- // and methods to compare to Nth last card's property
-
- // and what about more complex ones like mod and odd and prime
-
-
-})(chai);
diff --git a/src/js/ui.js b/src/js/ui.js
index 441308a..8ed4857 100644
--- a/src/js/ui.js
+++ b/src/js/ui.js
@@ -89,7 +89,7 @@ var UI = module.exports = (function (FastClick,Cookies) {
if (typeof Cookies.get('cookielaw') === 'undefined') {
var alert = '
';
alert += '';
- alert += ' Particle Clicker uses local storage to store your current progress.';
+ alert += ' Cards for science uses local storage to store your current progress.';
alert += '