mirror of
https://github.com/wassname/cardsforscience.git
synced 2026-06-27 16:14:52 +08:00
Simulate rules
This commit is contained in:
Vendored
+3
-1
File diff suppressed because one or more lines are too long
+4
-5
@@ -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
@@ -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">×</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">×</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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user