Simulate rules

This commit is contained in:
2016-03-01 07:54:33 +08:00
parent f94973c43c
commit 436d4619db
8 changed files with 281 additions and 84 deletions
+3 -1
View File
File diff suppressed because one or more lines are too long
+4 -5
View File
@@ -65,16 +65,15 @@ var clientApp = module.exports = {
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"),
simulate: require("json/simulations.json"),
cards: require("json/cards.json"),
// acheivements: require("json/achievements.json"),
};
// require("html/win.html");
// require("html/lose.html");
// require("html/game.html");
require("js/rules/simulate.html");
// deleteme dev TODO XXX
require("js/rules/simulate.html");
console.log('break here for dev');
+59 -19
View File
@@ -42,7 +42,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand pc-icon" href="#" data-toggle="modal" data-target="#myModal"> Cards For Science</a>
<a class="navbar-brand pc-icon" href="#" data-toggle="modal" data-target="#myModal"> Cards For Science - Beta</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
@@ -58,6 +58,7 @@
<!-- </ul> -->
<!-- </li> -->
<li><a href="#" data-toggle="modal" data-target="#cardModal">🂠 Game</a></li>
<li><a href="#" data-toggle="modal" data-target="#myModal"><i class="fa fa-users"></i> About</a></li>
</ul>
</div>
@@ -270,24 +271,6 @@
</div>
</div> -->
<div class="modal fade" id="win-modal" tabindex="-1" role="dialog" aria-labelledby="win-label" aria-hidden="true" ng-controller="RulesController">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="stats-label"><i class="fa fa-bar-chart"></i> Statistics</h4>
</div>
<div class="modal-body">
<ul class="list-group">
<li class="list-group-item"><i class="fa fa-location-arrow"></i> : <strong>{{ lab.state.clicks | niceNumber }}</strong></li>
<li class="list-group-item"><i class="fa fa-database"></i> : <strong>{{ lab.state.dataSpent | niceNumber }}</strong> used / <strong>{{ lab.state.dataCollected | niceNumber }}</strong> collected</li>
<li class="list-group-item"><i class="fa fa-money"></i> : <strong>{{ lab.state.moneySpent | niceNumber }}</strong> spent / <strong>{{ lab.state.moneyCollected | niceNumber }}</strong> gathered</li>
</ul>
</div>
</div>
</div>
</div>
<!-- <div class="modal fade" id="stats-modal" tabindex="-1" role="dialog" aria-labelledby="stats-label" aria-hidden="true" ng-controller="StatsController">
<div class="modal-dialog">
<div class="modal-content">
@@ -334,6 +317,63 @@
<li><a href="https://github.com/wassname">wassname</a></li>
</ul>
<p>Feel free to get in touch with via GitHub or by sending a message on<br>cardsforscience at wassname.org</a>.</p>
</div>
</div>
</div>
</div>
<div class="modal fade" id="cardModal" tabindex="-1" role="dialog" aria-labelledby="cardModelLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="cardModelLabel">Game</h4>
</div>
<div class="modal-body">
<h5><strong>Rules</strong></h5>
<ul>
<li>Guess the rule correctly to win the game</li>
<li>The rule determines which card can be put down next</li>
<li>Prove or disprove rules by testing next cards</li>
<li>But putting down an incorrect card decreases your score by 2 and gives you 2 new cards.</li>
<li>Empty your hand to increase your score.</li>
<li>Every game will have a random rule chosen from around 300 rules</li>
<li>Hints cost you 10 points.</li>
<li>At zero points you lose the game.</li>
</ul>
<p>
<h5><strong>Hints:</strong></h5>
<ul>
<li>Try to disprove rules, those which remain must be the truth</li>
<li>Don't be afraid to use hints</li>
<li>If this rule is too hard restart the game to get a new rule</li>
</ul>
<p>
<h5><strong>Cards and values</strong> </h5>
<div ng-controller="CardController as ec">
<table class="table">
<thead>
<tr>
<th ng-repeat='key in ["key", "name", "value", "suit", "color", "royal", "face", "number"]'>
{{key}}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="card in ec.cards">
<!-- <th>
{{card.key}}
</th> -->
<td ng-repeat='key in ["key", "name", "value", "suit", "color", "royal", "face", "number"]'>
{{card[key]|boolToTick}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
+67 -54
View File
@@ -98,49 +98,49 @@ var app = (function (Helpers,analytics,Game,Rules) {
* Directive to render a rule and bind it's option with select boxes
* This expects ng-model="rule" as an attribute
*/
function cfsRule($compile) {
return {
link: function (scope, element, attrs) {
var rule = scope.$eval(attrs.ngModel);
// first generate a select box for each option (using lodash templating)
_.templateSettings.interpolate = /<%=([\s\S]+?)%>/g;
var optionTmpl = '' +
'<select \n' +
' name="<%=option%>" \n' +
' convert-to-number="{{rule.optionDesc.<%=option%>.type===\'Number\'}}" \n' +
' ng-model="rule.options.<%=option%>" \n' +
' class="form-control input-sm" \n' +
' ng-options="v for v in rule.optionDesc.<%=option%>.possibleVals track by v" \n' +
'>\n' +
'</select>\n';
var tmplParams = _.defaults({},rule.options,rule.otherOptions);
for (var option in rule.optionDesc) {
if (rule.optionDesc.hasOwnProperty(option)) {
var vals = rule.optionDesc[option].possibleVals;
if (vals) {
tmplParams[option] = _.template(optionTmpl)({
option: option
});
} else {
// if there are no options replace '{{color}}' with 'color'
tmplParams[option] = option;
}
}
}
// now put each select box into description
// replace '{{color}}' with '<select name="color"...'
var template = _.template(rule.description)(tmplParams);
// now compile with angular
element.html(template).show();
$compile(element.contents())(scope);
}
};
};
cfsRule.$inject = ['$compile'];
app.directive('cfsRule', cfsRule);
// function cfsRule($compile) {
// return {
// link: function (scope, element, attrs) {
// var rule = scope.$eval(attrs.ngModel);
//
// // first generate a select box for each option (using lodash templating)
// _.templateSettings.interpolate = /<%=([\s\S]+?)%>/g;
// var optionTmpl = '' +
// '<select \n' +
// ' name="<%=option%>" \n' +
// ' convert-to-number="{{rule.optionDesc.<%=option%>.type===\'Number\'}}" \n' +
// ' ng-model="rule.options.<%=option%>" \n' +
// ' class="form-control input-sm" \n' +
// ' ng-options="v for v in rule.optionDesc.<%=option%>.possibleVals track by v" \n' +
// '>\n' +
// '</select>\n';
//
// var tmplParams = _.defaults({},rule.options,rule.otherOptions);
// for (var option in rule.optionDesc) {
// if (rule.optionDesc.hasOwnProperty(option)) {
// var vals = rule.optionDesc[option].possibleVals;
// if (vals) {
// tmplParams[option] = _.template(optionTmpl)({
// option: option
// });
// } else {
// // if there are no options replace '{{color}}' with 'color'
// tmplParams[option] = option;
// }
// }
// }
// // now put each select box into description
// // replace '{{color}}' with '<select name="color"...'
// var template = _.template(rule.description)(tmplParams);
//
// // now compile with angular
// element.html(template).show();
// $compile(element.contents())(scope);
// }
// };
// };
// cfsRule.$inject = ['$compile'];
// app.directive('cfsRule', cfsRule);
// factories to provide services. They serve shared game objects
@@ -182,13 +182,24 @@ var app = (function (Helpers,analytics,Game,Rules) {
niceNumber.$inject = ['$filter'];
app.filter('niceNumber', niceNumber);
function transpose($filter) {
/** transpose data for a table **/
// function transpose($filter) {
// return function (input) {
// return _.zip(input);
// };
// };
// transpose.$inject = ['$filter'];
// app.filter('transpose', transpose);
function boolToTick($filter) {
return function (input) {
return _.zip(input);
if (input===true) return '✓';
else if (input===false) return '❌';
else return input;
};
};
transpose.$inject = ['$filter'];
app.filter('transpose', transpose);
boolToTick.$inject = ['$filter'];
app.filter('boolToTick', boolToTick);
function niceTime($filter) {
return Helpers.formatTime;
@@ -421,13 +432,15 @@ var app = (function (Helpers,analytics,Game,Rules) {
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();
alertify.alert('Restarting. <p> P.S. The rule was: <p>"'+game.rule.describe(),function(){
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(){}
);
};
+21 -5
View File
@@ -88,12 +88,25 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
this.dealHand();
// deal first card
// TODO make sure these follow rule
// deal new initial cards
// deal new initial cards that follow the rule
this.lastCards.splice(0,this.lastCards.length);
this.lastCards.push.apply(this.lastCards,_.sampleSize(this.cards,3));
this.lastCards.push(_.sample(this.cards));
for (var i = 0; i < 52; i++) {
if (this.lastCards.length>2) break; // stop here
var card = _.sample(this.cards);
var res;
try{
res = this.rule.test(card,this.lastCards,this.cards);
} catch(e){
// in case of an error just add a random card
// this is probobly because it is looking back 2 or 3 cards
// yet we only have 1
this.lastCards.push(_.sample(this.cards));
}
if (res) this.lastCards.push(_.sample(this.cards));
}
if (this.lastCards.length<3) console.error('Could not deal cards for rule',this.rule.key,this.rule.options);
// this.lastCards.push.apply(this.lastCards,_.sampleSize(this.cards,3));
this.ruleInfo.splice(0,this.ruleInfo.length);
this.hints.splice(0,this.hints.length);
@@ -151,9 +164,12 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
};
Game.prototype.newRule = function () {
// FIXME
var okRules = _.filter(ruleSimulations,function(s){
return s.ratioRight>0.1&&s.ratioRight<0.6;
});
// var okRules = this.rules;
// _.map(this.rules,function(r){return r.randomize();});
// choose and ok rule
var ruleConfig = _.sample(okRules);
// now find the rule and set these options
+60
View File
@@ -190,6 +190,7 @@ var Rules = module.exports = (function functionName(_,chai) {
}
return s;
};
/** Get next hint **/
Rule.prototype.nextHint = function () {
var hint = this.hints[this.state.hintsUsed];
@@ -304,7 +305,66 @@ var Rules = module.exports = (function functionName(_,chai) {
function (state) {
$.extend(this.state, state);
};
Rule.prototype.simulateOne = function (options,cards,n) {
if (!n) n=52*2;
var t0=new Date();
var rights=[];
var wrongs=[];
var errors=[];
var lastCards = _.sampleSize(cards,3);
this.setOptions(options);
for (var i = 0; i < n; i++) {
var res=undefined;
var error=undefined;
var card = _.sample(cards);
try{
var res = this.testAndTell(card,lastCards,cards);
} catch(e){
error=e;
}
if (error){
errors.push(error.message);
} else if (res.result){
rights.push(res.result);
} else {
if (res.reason&&res.reason.message){
wrongs.push(res.reason.message);
} else if (res.reason){
wrongs.push(res.reason);
} else {
wrongs.push(res);
}
}
}
var ratioRight = rights.length/(rights.length+wrongs.length);
var ok = ratioRight>0.1&&ratioRight<0.66;
return {
wrongs:wrongs,
wrong:wrongs.length,
right:rights.length,
rights:rights,
error:errors.length,
errors:errors,
key:this.key,
description:this.describe(),
options:this.options,
n:n,
time:new Date()-t0,
ok:ok,
ratioRight:_.round(ratioRight,4),
};
};
Rule.prototype.simulate = function (cards) {
var results = [];
for (var i = 0; i < this.optionsPossible.length; i++) {
var res = this.simulateOne(this.optionsPossible[i],cards);
results.push(res);
}
return results;
};
// Now defined actual rules
var rules = [];
+66
View File
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simulate rules</title>
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> -->
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="../../../clientApp.css">
</head>
<body>
<h1>Simulate rules</h1>
<textarea rows="10" style="width: 1000px; height: 400px" id="summary"></textarea>
<div id="export"></div>
<table id="results" class="table">
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.min.js"></script>
<!-- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> -->
<script src="../../../clientApp.bundle.js"></script>
<script>
var results = [];
for (var i = 0; i < clientApp.Rules.rules.length; i++) {
var rule = clientApp.Rules.rules[i];
var res = rule.simulate(clientApp.cards);
results.push.apply(results,res);
}
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(results));
$('<a class="btn btn-default" href="data:' + data + '" download="data.json">export table as JSON</a>').appendTo('#export');
results=_.map(results,function(r){
r.options=JSON.stringify(r.options);
return r;
})
// just get keys we want and in order
var keys = [ "n","time", "ratioRight","ok", "right", "wrong", "error", "key", "options", "rule", "description","rights","wrongs","errors", ]
var table=$('#results');
var ts='';
ts+='<thead><tr><th>'+keys.join('</th><th>')+'</th></tr></thead><tbody>';
for (var i = 0; i < results.length; i++) {
// order them into an array
var result = _.map(keys,function(key){return results[i][key];});
// make table row
ts+='<tr><td>'+result.join('</td><td>')+'</td></tr>';
}
ts+='</tbody>';
table.append(ts);
$('#results').dataTable();
// var summaryJson = JSON.stringify(simulation.summarize(),null,4);
// $('#summary').text(summaryJson);
</script>
</head>
<body>
</html>
File diff suppressed because one or more lines are too long