mirror of
https://github.com/wassname/cardsforscience.git
synced 2026-06-27 16:14:52 +08:00
Made it work on mobiles - kindof
This commit is contained in:
@@ -1,7 +1,69 @@
|
||||
# Particle Clicker
|
||||
# Cards for science
|
||||
|
||||
An addictive incremental game that teaches players the history of high energy particle physics.
|
||||
Cards For Science is a card game where you work out the secret rule to determine which cards can be played. This game is based on Eleusis by Robert Abbott and John Golden's Eleusis Express.
|
||||
|
||||
Developed during the 2014 CERN Webfest over a weekend.
|
||||
<img src="./docs/screenshots.png"></img>
|
||||
|
||||
Visit [http://cern.ch/particle-clicker](http://cern.ch/particle-clicker) to play the game.
|
||||
## Install
|
||||
|
||||
`npm i`
|
||||
|
||||
## Serving
|
||||
|
||||
`npm start`
|
||||
|
||||
## Deploy
|
||||
|
||||
To run a producton bundle with webpack:
|
||||
|
||||
- `npm run dist`
|
||||
|
||||
Then to send to amazon s3 bucket:
|
||||
|
||||
- `gulp s3`
|
||||
|
||||
Note this depends on your amazon credentials and bucket being setup up in untracked file ./secrets/aws-credentials.json.
|
||||
For more refer to the [docs](https://www.npmjs.com/package/gulp-awspublish).
|
||||
|
||||
I've been hosting it on an amazon bucket at cardsforscience.com.
|
||||
|
||||
## Testing
|
||||
|
||||
`npm test`
|
||||
|
||||
## Adding rules
|
||||
|
||||
Add rules to rules.js as a new rule object. Read the jsdoc comment for the rule object for params.
|
||||
|
||||
## Simulating
|
||||
|
||||
After adding a rule go to /src/js/rules/simulate.html and click "export table as JSON". Make this the contents of simulations.json. This tells the game which rule options are balanced.
|
||||
|
||||
## Developing
|
||||
|
||||
- `index.webpack` Html is from
|
||||
- app.js is the main angular app
|
||||
- game.js is the main game app, provided as a servie from angular 1
|
||||
- cards.json are the cards and thier properties
|
||||
- simulations - give balalance to the rules
|
||||
- rules.js has the rules and thier classes
|
||||
- ui.js is mostly left over
|
||||
- helpers.js is mostly left over
|
||||
- analytics is outdates and can be replaces with angulartics
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] acheivements
|
||||
- [ ] analystics (I want to know how long they spend on each rule, and how many guesses)
|
||||
- [ ] more rules, we have ~9 rules with ~300 variations
|
||||
- [ ] every black card must be follower by a card with a face
|
||||
- [ ] "Alternate between cards which have closed loops in their number or letter designations (e.g. 4, Q), and cards which don't (e.g. 2, K)".
|
||||
- [ ] Play a sequence of suits from clubs to spades and back repeating the suit with clubs and spades, thusly, C, D, H, S, S, H, D, C, C, etc.
|
||||
- [ ] Play a progressive pattern of alternating red and black cards, such that first one black and one red card alternate, then two black and 2 red, then 3 black and 3 red, then repeat the pattern.
|
||||
- [ ] Cards are played consecutively upward or downward by 1, except skipping all the prime numbers (3,5,7,11,13). When 1 or 12 is reached the pattern reverses with the next card up or down. This Universe may start on any card except a prime number.
|
||||
- [ ] The sum of the card played plus the last card played must total a number that can be divided by 3 evenly. Thus totals of 3, 6, 9, 12, 15, 18, 21, & 24 are allowed. (For example, if the first card turned is an 8, correct cards are 1, 4, 7, 10, 13). The starting card may be anything but a 3, 6, 9, or 12, and that means these 4 values will never be played.
|
||||
- [ ] Each card played must be lower in value than the previous card, unless the card is 3 or less (3, 2, or 1), then the next card is that card's value plus 10 (3+10=13, 2+10=12, 1+10=11).
|
||||
- [ ] prompts
|
||||
- [ ] when all cards are gone
|
||||
- [ ] when starting
|
||||
- [ ] when all hints are gone
|
||||
|
||||
@@ -6,11 +6,6 @@ var gutil = require('gulp-util')
|
||||
|
||||
var webpack = require('webpack-stream');
|
||||
|
||||
// var source = require('vinyl-source-stream');
|
||||
// var buffer = require('vinyl-buffer');
|
||||
// var del = require('del');
|
||||
// var globby = require('globby');
|
||||
|
||||
var concurrent = require("concurrent-transform");
|
||||
var rename = require('gulp-rename');
|
||||
var awspublish = require('gulp-awspublish');
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "CardsForScience",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"description": "Cards For Science",
|
||||
"keywords": [
|
||||
"cards",
|
||||
|
||||
+13
-11
@@ -9,6 +9,8 @@
|
||||
<meta property="og:type" content="game">
|
||||
<meta property="og:description" content="A card game where you work out the secret rule to determine which cards can be played next.">
|
||||
<meta property="og:image" content="assets/favicon.png">
|
||||
<meta name="msvalidate.01" content="36C4A686262A56C0F7978C7A90F26B1A" />
|
||||
<meta name="google-site-verification" content="SF3w4G37_C-mMW_XxVVRNhe1BdkyTjQwQfo9D_G0VbE" />
|
||||
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="assets/apple-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="assets/apple-icon-60x60.png">
|
||||
@@ -67,7 +69,7 @@
|
||||
|
||||
<div id="main-content" class="container-fluid Paired">
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-md-3s col-lg-2 col-no-padding visible-md-block visible-lg-block">
|
||||
<div class="col-md-3 col-lg-2 col-sm-4 col-no-padding">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
@@ -81,7 +83,7 @@
|
||||
<div id="handContent">
|
||||
<div class="row" ng-cloak>
|
||||
|
||||
<div class="col-xs-2 col-no-padding " ng-repeat="r in rc.cards|orderBy:'key'" ng-show="rc.isAvailable(r)">
|
||||
<div class="col-md-2 col-sm-3 col-no-padding " ng-repeat="r in rc.cards|orderBy:'key'" ng-show="rc.isAvailable(r)">
|
||||
<span ng-show="rc.isAvailable(r)"
|
||||
id="{{r.key}}"
|
||||
class="{{r.key}} card cards-store {{ r.state.interesting ? 'blink' : '' }} {{r.color}} {{rc.isAvailable(r) ? ' ui-draggable': 'empty'}}"
|
||||
@@ -103,10 +105,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="column-lab" class="col-xs-6 col-md-6 col-md-6s col-lg-7 col-no-padding-xs" ng-controller="StatsController as lc">
|
||||
<div id="column-lab" class="col-md-6 col-sm-8 col-lg-7 col-no-padding-xs" ng-controller="StatsController as lc">
|
||||
|
||||
<div class="row status" ng-cloak>
|
||||
<div class="col-xs-12 text-center col-no-padding-xs">
|
||||
<div class="col-sm-12 text-center col-no-padding-xs">
|
||||
|
||||
<div class="{{lc.lab.state.score<0?'bg-danger':''}} animate-increase animate-decrease" ><strong>Score</strong><br>{{ lc.lab.state.score | niceNumber }}</div>
|
||||
<div class="update-value " ng-model="lc.lab.state.score" cfs-score-change id="update-data"></div>
|
||||
@@ -129,7 +131,7 @@
|
||||
>
|
||||
<div class="main-line card-line" id="lastCards">
|
||||
<div class="row" >
|
||||
<div class="col-xs-1 col-no-padding add-from-top" ng-repeat="r in dc.lastCards | limitTo: -10" ng-cloak>
|
||||
<div class="col-md-1 col-sm-1 col-no-padding add-from-top" ng-repeat="r in dc.lastCards | limitTo: -10" ng-cloak>
|
||||
<span
|
||||
class="{{r.key}} card {{r.color}}"
|
||||
data-cards="{{r.key}}"
|
||||
@@ -143,7 +145,7 @@
|
||||
<div class=""><strong>Incorrect cards:</strong><br></div>
|
||||
<div class="side-line card-line short-lines-nup" id="incorrectCards">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-no-padding" ng-repeat="rr in dc.incorrectCards | limitTo: -10" ng-cloak>
|
||||
<div class="col-md-1 col-sm-1 col-no-padding" ng-repeat="rr in dc.incorrectCards | limitTo: -10" ng-cloak>
|
||||
<div class="row">
|
||||
<div ng-repeat="r in rr| limitTo: -7" class="col-md-12 col-no-padding add-from-right ">
|
||||
<span
|
||||
@@ -192,8 +194,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 col-md-3s col-lg-2 col-no-padding visible-md-block visible-lg-block">
|
||||
<div class="panel panel-default hidden-xs hidden-sm">
|
||||
<div class="col-md-3 col-lg-2 col-sm-6 col-no-padding">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<i class="fa fa-wrench"></i> Winning: guess which rule
|
||||
@@ -217,7 +219,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="column-tabs" class="col-xs-6 visible-xs-block visible-sm-block col-no-padding">
|
||||
<!-- <div id="column-tabs" class="col-xs-6 visible-xs-block visible-sm-block col-no-padding">
|
||||
<ul id="TabList" class="nav nav-tabs" role="tablist">
|
||||
<li class="active"><a href="#cards" role="tab" data-toggle="tab"><i class="fa fa-cogs"></i> Elements </a></li>
|
||||
<li><a href="#hr" role="tab" data-toggle="tab"><i class="fa fa-users"></i> HR</a></li>
|
||||
@@ -234,7 +236,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -308,7 +310,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h4>About</h4>
|
||||
<p class="small">Version: 0.6</p>
|
||||
<p class="small">Version: 0.7</p>
|
||||
|
||||
<p class="small bg-warning">This is a pre-release. <p>Cards For Science is a card game where you work out the secret rule to determine which cards can be played. This game is based on <a href="http://www.logicmazes.com/games/eleusis/">Eleusis</a> by Robert Abbott and John Golden's <a href="http://www.logicmazes.com/games/eleusis/express.html">Eleusis</a> Express.
|
||||
|
||||
|
||||
+2
-2
@@ -432,7 +432,7 @@ var app = (function (Helpers,analytics,Game,Rules) {
|
||||
alertify.confirm(
|
||||
'Do you really want to restart the game? All progress will be lost.',
|
||||
function(event){
|
||||
alertify.alert('Restarting. <p> P.S. The rule was: <p>"'+game.rule.describe(),function(){
|
||||
alertify.alert('Restarting. <p> The rule was: <p>"'+game.rule.describe(),function(){
|
||||
event.preventDefault();
|
||||
// ObjectStorage.clear();
|
||||
// $window.location.reload(true); /// reloads are better for ads?
|
||||
@@ -459,7 +459,7 @@ var app = (function (Helpers,analytics,Game,Rules) {
|
||||
if (newValue<0&&!vm.lost){
|
||||
vm.lost=true;
|
||||
alertify.alert(
|
||||
'<p>You lost :( <p> Play again? <p><p>P.S. The rule was: <p>"'+game.rule.describe()+'"',
|
||||
'<p>You lost :( Play again? <p><p> The rule was: <p>"'+game.rule.describe()+'"',
|
||||
function(event){
|
||||
event.preventDefault();
|
||||
// ObjectStorage.clear();
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ var Game = module.exports =(function (Helpers, GameObjects, ObjectStorage,Rules,
|
||||
var hypo=[];
|
||||
|
||||
// a random 2, 2 variations of each
|
||||
for (var i = 0; i < 3; i++) {
|
||||
for (var i = 0; i < 1; i++) {
|
||||
var rule = _.sample(this.rules);
|
||||
rule = angular.copy(rule);
|
||||
rule.randomize();
|
||||
|
||||
+41
-14
@@ -21,13 +21,34 @@ var Rules = module.exports = (function functionName(_,chai) {
|
||||
this.rules=rules;
|
||||
};
|
||||
Simulation.prototype.run = function () {
|
||||
var allPromises = [];
|
||||
var allres = [];
|
||||
for (var i = 0; i < this.rules.length; i++) {
|
||||
var rule = this.rules[i];
|
||||
var promises = rule.simulate();
|
||||
allPromises.push(promises);
|
||||
var res = rule.simulate(this.cards);
|
||||
allres.push.apply(allres,res);
|
||||
}
|
||||
return Promise.all(allPromises).then(function(data){return _.concat(data);});
|
||||
this.results=allres;
|
||||
return allres;
|
||||
};
|
||||
Simulation.prototype.summarize = function () {
|
||||
|
||||
var summarize={};
|
||||
var props = _.keys(this.results[0]);
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var property=props[i];
|
||||
var vals = _.map(results,property);
|
||||
summarize[property]={
|
||||
min: _.min(vals),
|
||||
max: _.max(vals),
|
||||
n: vals.length,
|
||||
count: _.filter(vals,Boolean).length,
|
||||
hist: _.countBy(vals),
|
||||
mean: _.mean(vals),
|
||||
nTruthy: _.filter(vals,Boolean).length,
|
||||
nFalsey: _.filter(vals,function(v){return !Boolean(v);}).length,
|
||||
};
|
||||
}
|
||||
return summarize;
|
||||
};
|
||||
|
||||
|
||||
@@ -328,9 +349,14 @@ var Rules = module.exports = (function functionName(_,chai) {
|
||||
var rights=[];
|
||||
var wrongs=[];
|
||||
var errors=[];
|
||||
var lastCards = _.sampleSize(cards,3);
|
||||
self.setOptions(options);
|
||||
|
||||
|
||||
// simulate once
|
||||
for (var i = 0; i < n; i++) {
|
||||
// random last cards each time
|
||||
var lastCards = _.sampleSize(cards,3);
|
||||
|
||||
var res=undefined;
|
||||
var error=undefined;
|
||||
var card = _.sample(cards);
|
||||
@@ -338,7 +364,7 @@ var Rules = module.exports = (function functionName(_,chai) {
|
||||
try{
|
||||
var res = self.testAndTell(card,lastCards,cards);
|
||||
} catch(e){
|
||||
console.error('simulateOne',err);
|
||||
console.error('simulateOne',e);
|
||||
error=e;
|
||||
}
|
||||
|
||||
@@ -356,23 +382,24 @@ var Rules = module.exports = (function functionName(_,chai) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ratioRight = rights.length/(rights.length+wrongs.length);
|
||||
if (isNaN(ratioRight)) ratioRight = 0;
|
||||
var ok = ratioRight>0.1&&ratioRight<0.66;
|
||||
return {
|
||||
wrongs:wrongs,
|
||||
wrongs:_.uniq(wrongs),
|
||||
wrong:wrongs.length,
|
||||
right:rights.length,
|
||||
rights:rights,
|
||||
rights:_.uniq(rights),
|
||||
error:errors.length,
|
||||
errors:errors,
|
||||
errors:_.uniq(errors),
|
||||
key:self.key,
|
||||
description:self.describe(),
|
||||
options:self.options,
|
||||
n:n,
|
||||
time:new Date()-t0,
|
||||
ok:ok,
|
||||
ratioRight:_.round(ratioRight,4),
|
||||
ratioRight:_.round(ratioRight,2),
|
||||
|
||||
};
|
||||
};
|
||||
@@ -589,7 +616,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.",
|
||||
"If the <%= lastn(n) %> card's number is higher than <%= min %>, change <%= property %> from it, and if lower, keep it the same.",
|
||||
function (card, lastCards, allCards, options) {
|
||||
var lastNCard = lastCards[lastCards.length - options.n];
|
||||
var lastWasHigher = lastNCard.value > options.min;
|
||||
@@ -635,16 +662,16 @@ 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. But any card is OK if the <%= lastn(n) %> card was an Ace, King to Joker.",
|
||||
"If the <%= lastn(n) %> card was a <%= property %> card, play a higher value card otherwise lower. But any card is OK if the <%= lastn(n) %> card was an Ace,2, or King to Joker.",
|
||||
function (card, lastCards, allCards, options) {
|
||||
var lastNCard = lastCards[lastCards.length - options.n];
|
||||
var value = lastNCard.value;
|
||||
var lastHadProperty = lastNCard[options.property];
|
||||
|
||||
// reverse on card of these values
|
||||
var reverseOn=[1,14,15,16];
|
||||
var reverseOn=[1,2,14,15,16];
|
||||
if (reverseOn.indexOf(value)>-1) return true;
|
||||
|
||||
|
||||
if (lastHadProperty) {
|
||||
return chai.expect(card)
|
||||
.to.have.property('value')
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<body>
|
||||
<h1>Simulate rules</h1>
|
||||
<textarea rows="10" style="width: 1000px; height: 400px" id="summary"></textarea>
|
||||
<textarea rows="3" style="width: 1000px; height: 100px" id="oks"></textarea>
|
||||
<div id="export"></div>
|
||||
<table id="results" class="table">
|
||||
|
||||
@@ -25,12 +26,18 @@
|
||||
<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 simulation = new clientApp.Rules.Simulation(clientApp.Rules.rules,clientApp.cards);
|
||||
var results=simulation.run();
|
||||
var summary = simulation.summarize()
|
||||
// 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);
|
||||
// }
|
||||
|
||||
$('#oks').text('ok: '+_.map(results,'ok').reduce(_.add)+'/'+results.length);
|
||||
|
||||
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');
|
||||
@@ -56,8 +63,8 @@
|
||||
table.append(ts);
|
||||
$('#results').dataTable();
|
||||
|
||||
// var summaryJson = JSON.stringify(simulation.summarize(),null,4);
|
||||
// $('#summary').text(summaryJson);
|
||||
var summaryJson = JSON.stringify(summary,null,4);
|
||||
$('#summary').text(summaryJson);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user