mirror of
https://github.com/wassname/cardsforscience.git
synced 2026-07-26 13:17:19 +08:00
Made into next card game
This commit is contained in:
+6
-3
@@ -10,8 +10,7 @@
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
"test"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "1.4.x",
|
||||
@@ -22,6 +21,10 @@
|
||||
"angular-resource": "1.4.x",
|
||||
"angular-animate": "1.4.x",
|
||||
"angular-dragdrop": "~1.0.13",
|
||||
"lodash": "~4.5.1"
|
||||
"lodash": "~4.5.1",
|
||||
"chai": "~3.5.0",
|
||||
"angular-ui-grid": "~3.1.1",
|
||||
"jquery-ui": "~1.11.4",
|
||||
"font-awesome": "~4.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
+48
-20
@@ -30,20 +30,6 @@ h1 br {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#detector-flame{
|
||||
position: relative;
|
||||
/* So it looks like the flame it touching the test-tube */
|
||||
top: -60px;
|
||||
}
|
||||
|
||||
#detector-core {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#detector-events {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#detector-info {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
@@ -140,9 +126,9 @@ h1 br {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
/*.panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
.media-list-no-margin {
|
||||
margin-top: -15px;
|
||||
@@ -209,8 +195,8 @@ h1 br {
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
#detector-holder {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
/*margin-left: -15px;
|
||||
margin-right: -15px;*/
|
||||
}
|
||||
|
||||
.status {
|
||||
@@ -250,11 +236,11 @@ h1 br {
|
||||
|
||||
@media screen and (min-width: 992px) and (max-width: 1199px){
|
||||
.col-md-3s {
|
||||
width:20%;
|
||||
/*width:20%;*/
|
||||
}
|
||||
|
||||
.col-md-5s {
|
||||
width:40%;
|
||||
/*width:40%;*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,3 +404,45 @@ h1 br {
|
||||
.ui-grid-viewport .ui-grid-cell-contents{
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.card-panel {
|
||||
min-height: 130px;
|
||||
min-width: 100px;
|
||||
/*border: 1px solid black;*/
|
||||
/*border-radius: 4px;*/
|
||||
/*padding: 5px;*/
|
||||
/*margin: 15px;*/
|
||||
/*font-size: 20px;*/
|
||||
}
|
||||
.short-lines {
|
||||
line-height: 3em;
|
||||
}
|
||||
.main-line{
|
||||
border: 3px green solid;
|
||||
border-radius: 5px;
|
||||
/*margin: 10px;*/
|
||||
}
|
||||
.side-line {
|
||||
/*padding-top: 10px;*/
|
||||
/*margin: 10px;*/
|
||||
border:2px red solid;
|
||||
border-radius: 5px;
|
||||
min-height: 200px;
|
||||
}
|
||||
.card-line{
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.left-list ul{
|
||||
padding-left: 5px;
|
||||
}
|
||||
.hypotheses select{
|
||||
height:20px;
|
||||
padding:1px;
|
||||
|
||||
}
|
||||
.guess-correct{
|
||||
|
||||
}
|
||||
.guess-wrong{
|
||||
|
||||
}
|
||||
|
||||
+135
-115
@@ -11,11 +11,10 @@
|
||||
<meta property="og:image" content="https://avatars2.githubusercontent.com/u/8345868">
|
||||
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/angular-ui-grid/ui-grid.min.css">
|
||||
<link rel="stylesheet" href="css/ui-grid.css">
|
||||
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
|
||||
<link rel="icon" type="image/png" href="assets/favicon.png" />
|
||||
@@ -23,14 +22,7 @@
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="assets/mobile/icon.png">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
@@ -74,27 +66,26 @@
|
||||
</nav>
|
||||
|
||||
<div id="main-content" class="container-fluid col-no-padding 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="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<i class="fa fa-cogs"></i> Elements
|
||||
<i class="fa fa-cogs"></i> Hand
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="panel-body large" id="elementLarge"
|
||||
data-drop="true"
|
||||
jqyoui-droppable="{onDrop: 'rc.onDrop'}"
|
||||
data-jqyoui-options="{{rc.dropOptions}}"
|
||||
ng-controller="ElementController as rc"
|
||||
>
|
||||
<div id="elementContent">
|
||||
<div class="row" ng-cloak>
|
||||
|
||||
<div class="col-md-4" ng-repeat="r in rc.elements">
|
||||
<span ng-show="rc.isVisible(r)"
|
||||
<div class="col-md-4" ng-repeat="r in rc.elements" ng-show="rc.isAvailable(r)">
|
||||
<span ng-show="rc.isAvailable(r)"
|
||||
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}} {{rc.isAvailable(r) ? ' ui-draggable': 'empty'}}"
|
||||
ng-click="rc.onClick(r)"
|
||||
data-element="{{r.key}}"
|
||||
data-drag="rc.isAvailable(r)"
|
||||
jqyoui-draggable="{containment:'offset'}"
|
||||
@@ -103,7 +94,7 @@
|
||||
>
|
||||
{{r.key}}
|
||||
</span>
|
||||
<span ng-show="rc.isVisible(r)" ng-show="r.state.amount > 0" class="{{r.state.amount>0 ? '': 'empty'}} white-badge ng-binding">{{r.state.amount}}</span>
|
||||
<span ng-show="r.state.amount > 1" class="{{rc.isAvailable(r) ? '': 'empty'}} white-badge ng-binding">{{r.state.amount}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,111 +103,142 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="column-lab" class="col-xs-6 col-md-5 col-md-5s col-lg-6 col-no-padding-xs" ng-controller="LabController as lc">
|
||||
<input class="hidden-xs" id="labname" value="{{ lc.lab.state.name }}" ng-model="lc.lab.state.name" ng-cloak>
|
||||
<div id="column-lab" class="col-xs-6 col-md-6 col-md-6s col-lg-7 col-no-padding-xs" ng-controller="LabController as lc">
|
||||
|
||||
<!-- <input class="hidden-xs" id="labname" value="{{ lc.lab.state.name }}" ng-model="lc.lab.state.name" ng-cloak> -->
|
||||
<div class="row status" ng-cloak>
|
||||
<div class="col-xs-12 text-center col-no-padding-xs">
|
||||
<strong>Score</strong><br>
|
||||
{{ lc.lab.state.score | niceNumber }}
|
||||
<div class="update-value" id="update-data"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<hr class="hidden-xs">
|
||||
<div id="detector-holder">
|
||||
|
||||
<div id="detector" ng-controller="DetectorController as dc">
|
||||
|
||||
|
||||
<div id="detector-element-container" ng-cloak>
|
||||
<div ng-repeat="r in dc.elements">
|
||||
<span
|
||||
class="{{r.key}} element {{r.color}} ui-draggable"
|
||||
data-element="{{r.key}}"
|
||||
data-drag="true"
|
||||
jqyoui-draggable="{containment:'offset'}"
|
||||
data-jqyoui-options="{{rc.dragOptions}}"
|
||||
aria-disabled="false"
|
||||
style="position: fixed; z-index: 9999; left: {{r.state.left}}px; top: {{r.state.top}}px;"
|
||||
data-hashkey={{r.$$hashKey}}
|
||||
>
|
||||
{{r.key}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<canvas
|
||||
ng-click="dc.click()"
|
||||
id="detector-core"
|
||||
width="400"
|
||||
height="400"
|
||||
class="test-tube prevent-select center-block"
|
||||
data-drop="true"
|
||||
<div id="detector-element-container"
|
||||
ng-cloak
|
||||
jqyoui-droppable="{onDrop: 'dc.onDrop'}"
|
||||
data-jqyoui-options="{{dc.dropOptions}}"
|
||||
data-drop="true"
|
||||
>
|
||||
Your detector. Click on it to generate events.
|
||||
</canvas>
|
||||
<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.
|
||||
</canvas> -->
|
||||
<button id="clearall" class="btn btn-default" ng-click="dc.clearAll()"><i class="fa fa-refresh"></i></button>
|
||||
<div class="main-line card-line" id="lastCards">
|
||||
<div class="row" >
|
||||
<div class="col-md-1" ng-repeat="r in dc.lastCards | limitTo: -10" ng-cloak>
|
||||
<span
|
||||
class="{{r.key}} element {{r.color}}"
|
||||
data-element="{{r.key}}"
|
||||
data-hashkey={{r.$$hashKey}}
|
||||
>
|
||||
{{r.key}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-line card-line short-lines" id="incorrectCards">
|
||||
<div class="row">
|
||||
<div ng-repeat="rr in dc.incorrectCards | limitTo: -10" class="col-md-1" ng-cloak>
|
||||
<div ng-repeat="r in rr| limitTo: -10" class="row">
|
||||
<div class="col-md-12">
|
||||
<span
|
||||
class="{{r.key}} element {{r.color}}"
|
||||
data-element="{{r.key}}"
|
||||
data-hashkey={{r.$$hashKey}}
|
||||
>
|
||||
{{r.key}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="hints">
|
||||
<div ng-click="dc.revealHint()" class="col-md-6">
|
||||
<div class="panel panel-default left-list card-panel">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Hint</h3>
|
||||
<div ng-cloak> (-{{dc.hintCost}} score)</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul>
|
||||
<li ng-repeat="h in dc.hints" ng-cloak><i>{{h}}</i></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rule" ng-click="dc.revealRule()" class="col-md-6">
|
||||
<div class="panel panel-default left-list card-panel">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Rule</h3>
|
||||
<div ng-cloak>(-{{dc.ruleCost}} score)</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<i ng-cloak>{{dc.rule}}</i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <span id="card-deck" title="deal more cards" ng-click="dc.click()" class="prevent-select">🂠</span> -->
|
||||
<!-- <button id="clearall" class="btn btn-default" ng-click="dc.clearAll()"><i class="fa fa-refresh"></i></button> -->
|
||||
</div>
|
||||
</div>
|
||||
<button id="detector-info" class="btn btn-info" ng-click="lc.showDetectorInfo()"><i class="fa fa-info"></i></button>
|
||||
<button id="detector-info" type="button" class="btn btn-info" ng-click="lc.showDetectorInfo()"><i class="fa fa-info"></i></button>
|
||||
|
||||
<div class="row status" ng-cloak>
|
||||
<!-- <div class="row status" ng-cloak>
|
||||
<div class="col-xs-4 text-center col-no-padding-xs">
|
||||
<strong>Data</strong><br>
|
||||
{{ lc.lab.state.data | niceNumber }}
|
||||
<strong>Score</strong><br>
|
||||
{{ lc.lab.state.score | niceNumber }}
|
||||
<div class="update-value" id="update-data"></div>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center col-no-padding-xs">
|
||||
<strong>Reputation</strong><br>
|
||||
{{ lc.lab.state.reputation | niceNumber }}
|
||||
<strong>Rank</strong><br>
|
||||
{{ lc.lab.state.rank | niceNumber }}
|
||||
<div class="update-value" id="update-reputation"></div>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center col-no-padding-xs">
|
||||
<strong>Funding</strong><br>
|
||||
{{ lc.lab.state.money | currency }}
|
||||
<strong>High Score</strong><br>
|
||||
{{ lc.lab.state.highScore | niceNumber }}
|
||||
<div class="update-value" id="update-funding"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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 panel-stick">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<i class="fa fa-users"></i> Observations
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body scrollable large obervations-panel-body" id="hrLarge">
|
||||
|
||||
|
||||
|
||||
<div id="hrContent" ng-controller="ObservationsController as oc">
|
||||
<div id="observations-grid" ui-grid="oc.gridOptions" class="observations-grid"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</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="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<i class="fa fa-wrench"></i> Upgrades
|
||||
<i class="fa fa-wrench"></i> Guess the rule
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body scrollable large" id="upgradesLarge">
|
||||
<div id="upgradesContent" ng-controller="UpgradesController as uc">
|
||||
<ul class="media-list media-list-no-margin" ng-cloak>
|
||||
<li class="media" ng-show="false"></li>
|
||||
<li class="media" ng-repeat="u in uc.upgrades" ng-show="uc.isVisible(u)">
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading"><i class="fa {{ u.icon }} media-object"></i> {{ u.name }}</h4>
|
||||
<p>{{ u.description }}</p>
|
||||
<p class="small">{{ u.effect }}</p>
|
||||
<button class="btn btn-primary" ng-disabled="!uc.isAvailable(u)" ng-click="uc.upgrade(u)"><i class="fa fa-money"></i> <small>{{ u.cost | currency }}</small></button>
|
||||
</div>
|
||||
<div id="upgradesContent" class="left-list" ng-controller="UpgradesController as uc">
|
||||
<!-- <label>Search: <input ng-model="searchText"></label> -->
|
||||
<!-- <ul class="form-inline hypotheses" ng-cloak>
|
||||
<li ng-repeat="rule in uc.rules|filter:searchText">
|
||||
<div cfs-rule="rule" index="$index" ng-model="uc.rules[$index]"></div>
|
||||
<button ng-click="uc.guess($event,rule)" class="btn btn-sm btn-default" type="submit">Guess</button>
|
||||
</li>
|
||||
</ul>
|
||||
</ul> -->
|
||||
|
||||
<form class="form-inline hypotheses" ng-submit="uc.guess($event,uc.rules2[uc.guessedIndex])">
|
||||
<div class="radio" ng-repeat="rule in uc.rules2">
|
||||
<label class="{{uc.isGuessed(rule)}}" ng-cloak>
|
||||
<input type="radio" ng-model="uc.guessedIndex" name="ruleRadios" id="ruleRadios-{{$index}}" value="{{$index}}" class="btn btn-sm btn-default">
|
||||
</input>
|
||||
{{rule.describe()}}
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-default" type="submit">Guess</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -241,6 +263,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="infoBox" tabindex="-1" role="dialog" aria-labelledby="infoBoxLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
@@ -339,40 +362,37 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="bower_components/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="bower_components/jquery-ui/jquery-ui.js"></script>
|
||||
<script type="text/javascript" src="js/external/jquery.ui.touch-punch.min.js"></script>
|
||||
<script type="text/javascript" src="js/external/jquery.cookie-1.4.1.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
|
||||
<script src="js/external/jquery.ui.touch-punch.min.js"></script>
|
||||
<script src="js/external/jquery.cookie-1.4.1.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular-dragdrop/src/angular-dragdrop.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular-animate/angular-animate.js"></script>
|
||||
|
||||
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js"></script>
|
||||
<script type="text/javascript" src="js/external/retina.js"></script>
|
||||
<script type="text/javascript" src="js/external/fastclick.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 type="text/javascript" src="bower_components/chai/chai.js"></script>
|
||||
<script type="text/javascript" src="bower_components/lodash/dist/lodash.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular-ui-grid/ui-grid.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-animate/angular-animate.js"></script>
|
||||
|
||||
<script src="js/external/retina.js"></script>
|
||||
<script src="js/external/fastclick.js"></script>
|
||||
|
||||
<script src="bower_components/lodash/dist/lodash.js"></script>
|
||||
<script src="bower_components/angular-ui-grid/ui-grid.js"></script>
|
||||
<!-- Google Analytics -->
|
||||
<!-- <script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
</script> -->
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
<script src="js/storage.js"></script>
|
||||
<script src="js/helpers.js"></script>
|
||||
<script src="js/analytics.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/bubblr.js"></script> -->
|
||||
<script src="js/detector/detector.js"></script>
|
||||
|
||||
<script src="js/ui.js"></script>
|
||||
<script src="js/game.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
|
||||
@@ -8,6 +8,80 @@ var app = (function () {
|
||||
var app = angular.module('scienceAlchemy', ['ngDragDrop', 'ui.grid']);
|
||||
|
||||
// directives
|
||||
/**
|
||||
* Provides an easy way to toggle a checkboxes indeterminate property
|
||||
*
|
||||
* @example <input type="checkbox" ui-indeterminate="isUnkown">
|
||||
*/
|
||||
// app.directive('uiIndeterminate', [
|
||||
// function () {
|
||||
//
|
||||
// return {
|
||||
// compile: function (tElm, tAttrs) {
|
||||
// if (!tAttrs.type || tAttrs.type.toLowerCase() !== 'checkbox') {
|
||||
// return angular.noop;
|
||||
// }
|
||||
//
|
||||
// return function ($scope, elm, attrs) {
|
||||
// $scope.$watch(attrs.uiIndeterminate, function (newVal) {
|
||||
// elm[0].indeterminate = !!newVal;
|
||||
// });
|
||||
// };
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
// ]);
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
scope.$eval("$index");
|
||||
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
|
||||
// app.factory('elements', function () {
|
||||
@@ -25,12 +99,14 @@ var app = (function () {
|
||||
|
||||
function game($http, $q, lab) {
|
||||
var game = new Game();
|
||||
game.lab=lab;
|
||||
game.lab = lab;
|
||||
game.allObjects.lab = lab;
|
||||
var promise = game.load($http, $q);
|
||||
game.init();
|
||||
// return promise;
|
||||
return game;
|
||||
};
|
||||
game.$inject = ['$http', '$q'];
|
||||
game.$inject = ['$http', '$q', 'lab'];
|
||||
app.factory('game', game);
|
||||
|
||||
function lab() {
|
||||
@@ -39,21 +115,21 @@ var app = (function () {
|
||||
};
|
||||
app.factory('lab', lab);
|
||||
|
||||
function detector() {
|
||||
var detector = new Detector();
|
||||
detector.init(400);
|
||||
return detector;
|
||||
}
|
||||
app.factory('detector', detector);
|
||||
|
||||
// add helpers as filters
|
||||
|
||||
function niceNumber($filter) {
|
||||
return Helpers.formatNumberPostfix;
|
||||
};
|
||||
niceNumber.$inject = ['$filter'];
|
||||
app.filter('niceNumber', niceNumber);
|
||||
|
||||
function transpose($filter) {
|
||||
return function (input) {
|
||||
return _.zip(input);
|
||||
};
|
||||
};
|
||||
transpose.$inject = ['$filter'];
|
||||
app.filter('transpose', transpose);
|
||||
|
||||
function niceTime($filter) {
|
||||
return Helpers.formatTime;
|
||||
};
|
||||
@@ -81,19 +157,17 @@ var app = (function () {
|
||||
|
||||
// controllers
|
||||
app.controller('ElementController', ElementController);
|
||||
ElementController.$inject = ['$scope', '$compile', 'game', 'detector', 'lab'];
|
||||
ElementController.$inject = ['$scope', '$compile', 'game', 'lab'];
|
||||
|
||||
function ElementController($scope, $compile, game, detector, lab) {
|
||||
function ElementController($scope, $compile, game, lab) {
|
||||
var vm = this;
|
||||
vm.dragOptions = {
|
||||
revert: true, //"invalid",
|
||||
zIndex: 100,
|
||||
// helper: "clone", // drags a clone
|
||||
// opacity: 0.75,
|
||||
// start: vm.onRuneDrop.bind(vs),
|
||||
// stop: vm.onRuneDrop.bind(vs),
|
||||
cancel: false,
|
||||
// containment:false
|
||||
};
|
||||
vm.onClick = function (card) {
|
||||
game.play(card);
|
||||
};
|
||||
vm.elements = game.elements;
|
||||
vm.isVisible = function (item) {
|
||||
@@ -102,23 +176,19 @@ var app = (function () {
|
||||
vm.isAvailable = function (item) {
|
||||
return item.isAvailable(lab);
|
||||
};
|
||||
vm.onDrop = function (event, ui) {
|
||||
// store the dropped element
|
||||
var draggable = angular.element(ui.draggable);
|
||||
var key = draggable.data('element');
|
||||
if (!draggable.hasClass('element-store')) {
|
||||
var Card = vm.elements.get(key);
|
||||
var i = _.findIndex(vm.elements,{$$hashKey:draggable.data('hashkey')});
|
||||
detector.elements.splice(i, 1);
|
||||
Card.state.amount += 1;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function DetectorController($scope, game, detector, lab) {
|
||||
function DetectorController($scope, game, lab, $filter) {
|
||||
var vm = this;
|
||||
vm.elements = detector.elements;
|
||||
vm.rule = '';
|
||||
vm.hints = [];
|
||||
vm.limit = -12;
|
||||
vm.hintCost = 10;
|
||||
vm.ruleCost = 300;
|
||||
vm.lastCards = game.lastCards;
|
||||
vm.incorrectCards = game.incorrectCards;
|
||||
vm.dropOptions = {
|
||||
// accept: ".rune",
|
||||
addClasses: true,
|
||||
@@ -127,41 +197,26 @@ var app = (function () {
|
||||
activeClass: "ui-state-hover",
|
||||
hoverClass: "ui-state-active",
|
||||
};
|
||||
vm.dragOptions = {
|
||||
revert: "invalid",
|
||||
zIndex: 100,
|
||||
// helper: "clone", // drags a clone
|
||||
// opacity: 0.75,
|
||||
// start: this.onRuneDrop.bind(this),
|
||||
// stop: this.onRuneDrop.bind(this),
|
||||
cancel: false,
|
||||
// containment:false
|
||||
};
|
||||
vm.onDrop = function (event, ui) {
|
||||
var result = detector.onDrop(event, ui, game);
|
||||
if (result)
|
||||
lab.observe(result);
|
||||
var result = game.onDrop(event, ui, game);
|
||||
};
|
||||
vm.click = function () {
|
||||
lab.clickDetector();
|
||||
detector.addEvent();
|
||||
UI.showUpdateValue("#update-data", lab.state.detector);
|
||||
game.elements.addKnownToStore();
|
||||
return false;
|
||||
vm.revealRule = function () {
|
||||
vm.rule = game.rule.describe();
|
||||
lab.state.score -= vm.ruleCost;
|
||||
};
|
||||
vm.toggleFlameFuel = function () {
|
||||
// console.log('toggleFlameFuel');
|
||||
// detector.flamer.toggleFuel();
|
||||
};
|
||||
vm.clearAll = function () {
|
||||
detector.clearAll(game);
|
||||
vm.revealHint = function () {
|
||||
var hint = game.rule.nextHint();
|
||||
if (hint) {
|
||||
vm.hints.push(hint);
|
||||
lab.state.score -= vm.hintCost;
|
||||
}
|
||||
};
|
||||
};
|
||||
DetectorController.$inject = ['$scope', 'game', 'detector', 'lab'];
|
||||
DetectorController.$inject = ['$scope', 'game', 'lab', '$filter'];
|
||||
app.controller('DetectorController', DetectorController);
|
||||
|
||||
|
||||
function LabController($interval, game, detector, lab) {
|
||||
function LabController($interval, game, lab) {
|
||||
// todo give workers instead of game
|
||||
var vm = this;
|
||||
vm.lab = lab;
|
||||
@@ -171,68 +226,52 @@ var app = (function () {
|
||||
}
|
||||
UI.showModal('Detector', vm._detectorInfo);
|
||||
};
|
||||
$interval(function () { // one tick
|
||||
var grant = lab.getGrant();
|
||||
UI.showUpdateValue("#update-funding", grant);
|
||||
var sum = 0;
|
||||
for (var i = 0; i < game.workers.length; i++) {
|
||||
sum += game.workers[i].state.hired * game.workers[i].state.rate;
|
||||
}
|
||||
if (sum > 0) {
|
||||
lab.acquireData(sum);
|
||||
UI.showUpdateValue("#update-data", sum);
|
||||
detector.addEventExternal(game.workers.map(function (w) {
|
||||
return w.state.hired;
|
||||
}).reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0));
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
LabController.$inject = ['$interval', 'game', 'detector', 'lab'];
|
||||
LabController.$inject = ['$interval', 'game', 'lab'];
|
||||
app.controller('LabController', LabController);
|
||||
|
||||
|
||||
function ObservationsController($scope, game, lab) {
|
||||
var vm = this;
|
||||
vm.observations = lab.state.observations;
|
||||
vm.gridOptions = {
|
||||
enableFiltering: true,
|
||||
columnDefs: [{
|
||||
field: 'inputs',
|
||||
filter: {
|
||||
type: 'select',
|
||||
selectOptions: _(game.elements).filter({state:{discovered:true}}).map(function(e){return {value:e.key,label:e.key};}).value(),
|
||||
},
|
||||
visible: true
|
||||
}, {
|
||||
field: 'reactants',
|
||||
visible: false
|
||||
}, {
|
||||
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,
|
||||
sort: {
|
||||
direction: 'asc'
|
||||
}
|
||||
}, {
|
||||
field: 'catalysts',
|
||||
visible: false
|
||||
}, {
|
||||
field: 'conditions',
|
||||
visible: false
|
||||
}, ],
|
||||
data: vm.observations
|
||||
};
|
||||
};
|
||||
ObservationsController.$inject = ['$scope', 'game', 'lab'];
|
||||
app.controller('ObservationsController', ObservationsController);
|
||||
|
||||
function UpgradesController($scope, game, lab) {
|
||||
var vm = this;
|
||||
// present just a few hypothesis
|
||||
var rules = Rules.rules.map(function (r) {
|
||||
return angular.copy(r);
|
||||
});
|
||||
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.upgrades = game.upgrades;
|
||||
vm.isVisible = function (upgrade) {
|
||||
return upgrade.isVisible(lab, game.allObjects);
|
||||
@@ -245,6 +284,30 @@ var app = (function () {
|
||||
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';
|
||||
else return '';
|
||||
|
||||
};
|
||||
vm.guess = function (e, rule) {
|
||||
var params = angular.element(e.target).parent('form').serializeArray();
|
||||
|
||||
var sameRule = rule.description == game.rule.description;
|
||||
var sameOpts = angular.equals(rule.options, game.rule.options);
|
||||
if (sameRule && sameOpts) {
|
||||
// right!
|
||||
lab.state.score += 200;
|
||||
if (!rule.state) rule.state={};
|
||||
rule.state.guessed=true;
|
||||
} else {
|
||||
lab.state.score -= 200;
|
||||
rule.state.guessed=false;
|
||||
}
|
||||
|
||||
console.log('guess', arguments);
|
||||
};
|
||||
};
|
||||
UpgradesController.$inject = ['$scope', 'game', 'lab'];
|
||||
app.controller('UpgradesController', UpgradesController);
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
Bubblr v1.0 - http://mikeyhogarth.wordpress.com/2012/04/15/bubblr-jquery-bubbles-plugin/
|
||||
Copyright (c) 2012 Mikey Hogarth
|
||||
template based on original jquery lightweight plugin biolerplate: @ajpiano, @addyosmani
|
||||
This plugin available for use in all personal or commercial projects under both MIT and GPL licenses.
|
||||
*/
|
||||
|
||||
(function ($, window, document, undefined) {
|
||||
|
||||
var pluginName = "bubblr";
|
||||
|
||||
/** default options **/
|
||||
var defaults = {
|
||||
numberOfBubbles: 10,
|
||||
backgroundColor: "transparent",
|
||||
bubbleColor: "White",
|
||||
bubbleOpacity: 0.7,
|
||||
bubbleMinSize: 1,
|
||||
bubbleMaxSize: 2,
|
||||
bubbleMaxSpeed: 2,
|
||||
bubbleMinSpeed: 1,
|
||||
animationSpeed: 10
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the bubblr
|
||||
* @param {dom element} element - dom canvas element
|
||||
* @param {object} options - options see Bubblr._defaults for options
|
||||
*/
|
||||
function Bubblr(element, options) {
|
||||
this.element = element;
|
||||
this.options = $.extend({}, defaults, options);
|
||||
this._defaults = defaults;
|
||||
this.state = {
|
||||
bubbles: [],
|
||||
started: false,
|
||||
globalTick: 0,
|
||||
genBubbles: false,
|
||||
}
|
||||
this.init();
|
||||
this.start();
|
||||
}
|
||||
|
||||
Bubblr.prototype.init = function () {
|
||||
|
||||
if (this.element.getContext) {
|
||||
this.ctx = this.element.getContext("2d");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$canvas = $(this.element);
|
||||
|
||||
this.width = $canvas.width();
|
||||
this.height = $canvas.height();
|
||||
|
||||
$canvas.css("background-color", this.options.backgroundColor)
|
||||
|
||||
// initial bubbles
|
||||
for (i = 0; i < this.options.numberOfBubbles; i++) {
|
||||
this.state.bubbles[i] = this.generateBubble();
|
||||
}
|
||||
for (i in this.state.bubbles) {
|
||||
this.drawBubble(this.state.bubbles[i]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Recenter on resize,
|
||||
* bind this to $(window).on('resize',bubblr.onResize.bind(bubblr);) if needed
|
||||
**/
|
||||
Bubblr.prototype.onResize = function () {
|
||||
this.clear();
|
||||
this.width = $(this.element).outerWidth();
|
||||
this.height = $(this.element).outerWidth();
|
||||
};
|
||||
|
||||
/** Start animation loop if not started **/
|
||||
Bubblr.prototype.start = function () {
|
||||
if (!this.state.started) {
|
||||
this.state.started = true;
|
||||
this.animationLoop();
|
||||
}
|
||||
};
|
||||
|
||||
/** Stop animation loop **/
|
||||
Bubblr.prototype.stop = function () {
|
||||
this.state.started = false;
|
||||
this.clear();
|
||||
};
|
||||
|
||||
/** Animation loop run Bubblr.stop and start control this **/
|
||||
Bubblr.prototype.animationLoop = function () {
|
||||
window.requestAnimFrame(this.animationLoop.bind(this));
|
||||
this.state.globalTick++;
|
||||
if (this.state.started) this.animate();
|
||||
};
|
||||
|
||||
Bubblr.prototype.animate = function (arguments) {
|
||||
this.clear();
|
||||
for (i in this.state.bubbles) {
|
||||
this.update(i, this.state.bubbles[i]);
|
||||
this.draw(i, this.state.bubbles[i]);
|
||||
}
|
||||
|
||||
// clear bubbles where y<0 (pop em)
|
||||
var toRemove = this.state.bubbles.findIndex(function (b) {
|
||||
return b.y < 0;
|
||||
});
|
||||
for (var i = 0; i < toRemove.length; i++) {
|
||||
var bubble = this.state.bubbles.splice(toRemove[i], 1)[0];
|
||||
//bubble.pop();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Bubblr.prototype.clear = function () {
|
||||
this.ctx.globalAlpha = 1;
|
||||
this.ctx.fillStyle = this.options.backgroundColor;
|
||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
|
||||
Bubblr.prototype.draw = function (index, bubble) {
|
||||
this.ctx.globalAlpha = this.options.bubbleOpacity;
|
||||
this.drawBubble(bubble);
|
||||
};
|
||||
|
||||
/** Update bubble position **/
|
||||
Bubblr.prototype.update = function (index, bubble) {
|
||||
bubble.y = bubble.y - bubble.speed
|
||||
|
||||
if (bubble.y < 0 && this.state.continuous)
|
||||
// reset to bottom if continuous running is on
|
||||
this.state.bubbles[index] = this.generateBubble(true);
|
||||
else {}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a single bubble
|
||||
* @param {object} options - options for the bubble
|
||||
* @param {Number} options.x - Starting x position. Defaults to a random
|
||||
* location.
|
||||
* @param {Number} options.y - starting y level. Default to the bottom or
|
||||
* null for random position, a number sets a
|
||||
* specific level. Note the top is 0, while the
|
||||
* bottom is `-$(element).height()`
|
||||
*/
|
||||
Bubblr.prototype.bubble = function (options) {
|
||||
this.state.bubbles.push(this.generateBubble(options));
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to generate multiple bubble
|
||||
* @param {[type]} amount - amount of bubbles to create
|
||||
* @param {[type]} options - see Bubblr.bubble for description
|
||||
*/
|
||||
Bubblr.prototype.genBubbles = function (amount, options) {
|
||||
for (var i = 0; i < amount; i++) {
|
||||
this.state.bubbles.push(this.generateBubble(options));
|
||||
}
|
||||
}
|
||||
|
||||
Bubblr.prototype.drawBubble = function (bubble) {
|
||||
this.ctx.fillStyle = this.options.bubbleColor;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(bubble.x, bubble.y, bubble.radius, 0, Math.PI * 2, true);
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
};
|
||||
|
||||
Bubblr.prototype.generateBubble = function (options) {
|
||||
options = options || {};
|
||||
var bubble = new Object();
|
||||
|
||||
|
||||
if (options.x === undefined || options.x === null)
|
||||
bubble.x = this.randomize(1, this.width);
|
||||
else
|
||||
bubble.x = options.x
|
||||
|
||||
if (options.y === undefined)
|
||||
bubble.y = this.height;
|
||||
else if (options.y === null)
|
||||
bubble.y = this.randomize(1, this.height);
|
||||
else
|
||||
bubble.y = options.y
|
||||
|
||||
if (options.radius)
|
||||
bubble.radius = options.radius
|
||||
else
|
||||
bubble.radius = this.randomize(this.options.bubbleMinSize, this.options.bubbleMaxSize);
|
||||
|
||||
if (options.speed)
|
||||
bubble.speed = options.speed;
|
||||
else
|
||||
bubble.speed = this.randomize(this.options.bubbleMinSpeed, this.options.bubbleMaxSpeed);
|
||||
|
||||
return bubble;
|
||||
};
|
||||
|
||||
Bubblr.prototype.randomize = function (min, max) {
|
||||
return Math.floor((Math.random() * max) + min);
|
||||
};
|
||||
|
||||
// $.flame will create or return the bubblr element
|
||||
$.fn[pluginName] = function (options) {
|
||||
return this.each(function () {
|
||||
if (!$.data(this, 'plugin_' + pluginName)) {
|
||||
$.data(this, 'plugin_' + pluginName,
|
||||
new Bubblr(this, options));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})(jQuery, window, document);
|
||||
|
||||
// shim for requestAnimFrame
|
||||
window.requestAnimFrame = (function () {
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function ( /* function */ callback, /* DOMElement */ element) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})();
|
||||
+8
-122
@@ -5,6 +5,8 @@ var Detector = function(){
|
||||
|
||||
return {
|
||||
elements: new GameObjects.Cards(),
|
||||
lastCards: new GameObjects.Cards(),
|
||||
incorrectCards: new GameObjects.Cards(),
|
||||
|
||||
visible: true,
|
||||
|
||||
@@ -17,70 +19,12 @@ var Detector = function(){
|
||||
|
||||
bubblr: undefined,
|
||||
|
||||
init: function()
|
||||
init: function(game)
|
||||
{
|
||||
// this.initBubbles();
|
||||
// this.initFlame();
|
||||
// deal first card
|
||||
this.lastCards.push(_.sample(game.elements));
|
||||
},
|
||||
|
||||
initBubbles: function(){
|
||||
// init the test tube animation
|
||||
var bubblrElem = $('#detector-core').bubblr({
|
||||
backgroundColor: '#e8e8e8',
|
||||
bubbleColor: "#bfbfbf",
|
||||
bubbleMinSize: 3,
|
||||
bubbleMaxSize: 5,
|
||||
bubbleMaxSpeed: 2,
|
||||
bubbleMinSpeed: 1,
|
||||
animationSpeed: 20,
|
||||
// bubbleXposMultiplier allows us to position bubbles on the x-axis
|
||||
bubbleXposMultiplier: 2,
|
||||
// bubbleYpopLimit determines how far in pixels from the canvas's
|
||||
// top edge bubbles will disappear
|
||||
bubbleYpopLimit: 10
|
||||
});
|
||||
this.bubblr = bubblrElem.data('plugin_bubblr');
|
||||
},
|
||||
|
||||
// initFlame: function(){
|
||||
// var flameElem = $('#detector-flame').flame();
|
||||
// this.flamer = flameElem.data('plugin_flame')
|
||||
// },
|
||||
|
||||
bindOnResize: function(){
|
||||
// HACK
|
||||
$(window).on('resize',this.onResize.bind(this));
|
||||
},
|
||||
|
||||
// onResize: function(){
|
||||
// // TODO, do one, schedule a check and then prevent firing until then
|
||||
// if ($(window).width() >= 1200) {
|
||||
// if (this.width != 500) {
|
||||
// $('#detector').width(500).height(500);
|
||||
// this.init(500);
|
||||
// }
|
||||
// } else if ($(window).width() < 768 && $(window).height() - 90 < 300) {
|
||||
// var newWidth = $(window).width() - Math.max($(window).width() - ($(window).height() - 90 + 10), 300) - 10;
|
||||
// if (this.width != newWidth) {
|
||||
// $('#detector').width(newWidth).height(newWidth);
|
||||
// this.init(newWidth);
|
||||
// }
|
||||
// } else if ($(window).width() < 992) {
|
||||
// if (this.width != 300) {
|
||||
// $('#detector').width(300).height(300);
|
||||
// this.init(300);
|
||||
// }
|
||||
// } else {
|
||||
// if (this.width != 400) {
|
||||
// $('#detector').width(400).height(400);
|
||||
// this.init(400);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.bubblr.onResize();
|
||||
// this.flamer.onResize();
|
||||
// },
|
||||
|
||||
/** When a user clicks the detector **/
|
||||
addEvent: function()
|
||||
{
|
||||
@@ -99,19 +43,6 @@ var Detector = function(){
|
||||
// this.bubblr.bubble();
|
||||
},
|
||||
|
||||
/** Clear an element back to element Store **/
|
||||
storeElementBy: function(qObject,game){
|
||||
var i = _.findIndex(this.elements,qObject);
|
||||
var removedElement = this.elements.splice(i,1)[0];
|
||||
return game.elements.get(removedElement.key).state.amount+=1;
|
||||
},
|
||||
|
||||
clearAll: function(game){
|
||||
var hashKeys = this.elements.map(function(e){return e.$$hashKey;});
|
||||
for (var i = 0; i < hashKeys.length; i++) {
|
||||
this.storeElementBy({'$$hashKey': hashKeys[i]}, game);
|
||||
}
|
||||
},
|
||||
|
||||
onDrop: function(event, ui, game){
|
||||
var self=this;
|
||||
@@ -122,60 +53,15 @@ var Detector = function(){
|
||||
// if the dragger came from the elements panels, clone it to here
|
||||
var newElement;
|
||||
if ($draggable.hasClass('element-store')){
|
||||
|
||||
var elementStore = game.elements.filter(function(e){return e.key==$draggable.data('element');})[0];
|
||||
elementStore.state.amount-=1;
|
||||
newElement = angular.copy(elementStore);
|
||||
newElement.state.top=$draggable.offset().top;
|
||||
newElement.state.left=$draggable.offset().left;
|
||||
this.elements.push(newElement);
|
||||
}
|
||||
|
||||
|
||||
// get everything intersecting the drop
|
||||
var draggableTop = $draggable.offset().top;
|
||||
var draggableHeight = $draggable.height();
|
||||
var draggableBottom = draggableTop + draggableHeight;
|
||||
var draggableLeft = $draggable.offset().left;
|
||||
var draggableWidth = $draggable.height();
|
||||
var draggableRight = draggableLeft + draggableWidth;
|
||||
var detectorDOMElems = angular.element('#detector').find('.element').not('.element-store'); // replace with detector.elements
|
||||
var intersectingDOMElems = detectorDOMElems.filter( function() {
|
||||
var $elem = angular.element(this);
|
||||
var top = $elem.offset().top;
|
||||
var height = $elem.height();
|
||||
var bottom = top + height;
|
||||
|
||||
var left = $elem.offset().left;
|
||||
var width = $elem.width();
|
||||
var right = left + width;
|
||||
|
||||
var isCoveredByDraggable = top <= draggableBottom && bottom >= draggableTop
|
||||
&& left <= draggableRight && right >= draggableLeft;
|
||||
return isCoveredByDraggable;
|
||||
});
|
||||
|
||||
// We have the DOM's in the area (maybe missing the newly dropped one)
|
||||
// var droppedModel = game.elements.getByHashKey($draggable.data('hashkey'));
|
||||
var intersectingElements = intersectingDOMElems.toArray().map(function(input){
|
||||
return self.elements.getByHashKey(angular.element(input).data('hashkey'));
|
||||
});
|
||||
|
||||
// make sure we include the dropped element as in some cases
|
||||
// it wont have DOM object yet so it wont be picked up as intersecting
|
||||
if (newElement && intersectingElements.indexOf(newElement)===-1) intersectingElements.push(newElement);
|
||||
|
||||
|
||||
var uniqueElems = _(intersectingElements).map('key').uniq().value().length;
|
||||
if (intersectingElements.length==2 && uniqueElems>1){
|
||||
var observation = this.experiment({inputs:intersectingElements,location:$draggable.offset()},game);
|
||||
console.log('intersectingElements', intersectingElements.length, observation);
|
||||
return observation;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var observation = game.test
|
||||
console.log('intersectingElements', intersectingElements.length, observation);
|
||||
return observation;
|
||||
},
|
||||
|
||||
/** Run an experiment depending on reactants and conditions **/
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
|
||||
/**
|
||||
* @name Flame
|
||||
* @description jquery plugin to generate flame on a canvas element..
|
||||
* Based on http://codepen.io/jackrugile/pen/Jbnpv
|
||||
* @copyright (c) 2015 wassname
|
||||
* @license: MIT
|
||||
*/
|
||||
;(function ( $, window, document, undefined ) {
|
||||
var pluginName = "flame";
|
||||
|
||||
var defaults = {
|
||||
hueRange: 50,
|
||||
};
|
||||
|
||||
var defaultState = {
|
||||
parts: [],
|
||||
partCount: 200,
|
||||
partsFull: false,
|
||||
on: true,
|
||||
rendering: false,
|
||||
globalTick: 0,
|
||||
width: null,
|
||||
height: null,
|
||||
}
|
||||
|
||||
var Part = function(flame){
|
||||
this.flame=flame;
|
||||
this.ctx=this.flame.ctx;
|
||||
this.reset();
|
||||
};
|
||||
|
||||
|
||||
Part.prototype.reset = function(){
|
||||
this.startRadius = this.rand(1, 25);
|
||||
this.radius = this.startRadius;
|
||||
this.width = this.flame.state.width;
|
||||
this.height = this.flame.state.height;
|
||||
this.x = this.width/2 + (this.rand(0, 6) - 3);
|
||||
this.y = 250;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.hue = this.rand(this.flame.state.globalTick - this.flame.options.hueRange, this.flame.state.globalTick + this.flame.options.hueRange);
|
||||
this.saturation = this.rand(50, 100);
|
||||
this.lightness = this.rand(20, 70);
|
||||
this.startAlpha = this.rand(1, 10) / 100;
|
||||
this.alpha = this.startAlpha;
|
||||
this.decayRate = 0.1;
|
||||
this.startLife = 7;
|
||||
this.life = this.startLife;
|
||||
this.lineWidth = this.rand(1, 3);
|
||||
}
|
||||
|
||||
Part.prototype.update = function(){
|
||||
this.vx += (this.rand(0, 200) - 100) / 1500;
|
||||
this.vy -= this.life/50;
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
this.alpha = this.startAlpha * (this.life / this.startLife);
|
||||
this.radius = this.startRadius * (this.life / this.startLife);
|
||||
this.life -= this.decayRate;
|
||||
if(
|
||||
this.x > this.width + this.radius ||
|
||||
this.x < -this.radius ||
|
||||
this.y > this.height + this.radius ||
|
||||
this.y < -this.radius ||
|
||||
this.life <= this.decayRate
|
||||
){
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
|
||||
Part.prototype.render = function(){
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
|
||||
this.ctx.fillStyle = this.ctx.strokeStyle = 'hsla('+this.hue+', '+this.saturation+'%, '+this.lightness+'%, '+this.alpha+')';
|
||||
this.ctx.lineWidth = this.lineWidth;
|
||||
this.ctx.fill();
|
||||
this.ctx.stroke();
|
||||
};
|
||||
|
||||
Part.prototype.rand = function(min, max){
|
||||
return Math.floor( (Math.random() * (max - min + 1) ) + min);
|
||||
};
|
||||
|
||||
var Flame = function(element, options){
|
||||
this.element = element;
|
||||
this.options = $.extend( {}, defaults, options) ;
|
||||
this._defaults = defaults;
|
||||
this.init();
|
||||
};
|
||||
|
||||
Flame.prototype.init = function (arguments) {
|
||||
if(this.element.getContext) {
|
||||
this.ctx = this.element.getContext("2d");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (this.state) this.stop();
|
||||
this.state = $.extend( {parts: [],partsFull: false,globalTick: 0,}, defaultState, this.state);
|
||||
// this.state = {
|
||||
// parts: [],
|
||||
// partCount: 200,
|
||||
// partsFull: false,
|
||||
// on: true,
|
||||
// rendering: false,
|
||||
// globalTick: 0,
|
||||
// width: null,
|
||||
// height: null,
|
||||
// }
|
||||
this.state.width = $(this.element).outerWidth();
|
||||
this.state.height = $(this.element).outerWidth();
|
||||
this.start();
|
||||
}
|
||||
|
||||
/** If needed: $(window).on('resize',this.onResize.bind(this) **/
|
||||
Flame.prototype.onResize = function (arguments) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
Flame.prototype.createParts = function(){
|
||||
if(this.state.parts.length > this.state.partCount){
|
||||
this.state.partsFull = true;
|
||||
} else {
|
||||
this.state.parts.push(new Part(this));
|
||||
}
|
||||
};
|
||||
|
||||
Flame.prototype.updateParts = function(){
|
||||
var i = this.state.parts.length;
|
||||
while(i--){
|
||||
this.state.parts[i].update();
|
||||
}
|
||||
};
|
||||
|
||||
Flame.prototype.renderParts = function(){
|
||||
var i = this.state.parts.length;
|
||||
while(i--){
|
||||
this.state.parts[i].render();
|
||||
}
|
||||
};
|
||||
|
||||
Flame.prototype.clear = function (arguments) {
|
||||
this.ctx.globalAlpha = 1;
|
||||
this.ctx.fillStyle = 'hsla(0, 0%, 0%, .0)';
|
||||
this.ctx.clearRect(0,0,this.state.width, this.state.height);
|
||||
}
|
||||
|
||||
/** Fades the previous flames into opacity to product a blur **/
|
||||
Flame.prototype.fade = function(){
|
||||
this.ctx.globalCompositeOperation = 'destination-out';
|
||||
this.ctx.fillStyle = 'hsla(0, 0%, 0%, .3)';
|
||||
this.ctx.fillRect(0, 0, this.state.width, this.state.height);
|
||||
this.ctx.globalCompositeOperation = 'lighter';
|
||||
};
|
||||
|
||||
Flame.prototype.draw = function (arguments) {
|
||||
this.fade();
|
||||
if (this.state.on){
|
||||
this.createParts();
|
||||
} else if (this.state.parts.length>1){
|
||||
this.state.parts.pop();
|
||||
}
|
||||
this.updateParts();
|
||||
this.renderParts();
|
||||
}
|
||||
|
||||
Flame.prototype.start = function (arguments) {
|
||||
if (!this.state.rendering){
|
||||
this.state.rendering=true
|
||||
this.animate();
|
||||
return this
|
||||
} else {return;}
|
||||
}
|
||||
|
||||
Flame.prototype.stop = function (arguments) {
|
||||
this.state.rendering=false;
|
||||
this.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Toggle fuel and the flame will burn down or up again **/
|
||||
Flame.prototype.toggleFuel = function (state) {
|
||||
if (state===undefined)
|
||||
this.state.on=!this.state.on;
|
||||
else
|
||||
this.state.on=state;
|
||||
return this.state.on;
|
||||
}
|
||||
|
||||
Flame.prototype.animate = function(){
|
||||
window.requestAnimFrame(this.animate.bind(this),this.element);
|
||||
this.state.globalTick++;
|
||||
if (this.state.rendering) this.draw();
|
||||
};
|
||||
|
||||
$.fn[pluginName] = function ( options ) {
|
||||
return this.each(function () {
|
||||
if (!$.data(this, 'plugin_' + pluginName)) {
|
||||
$.data(this, 'plugin_' + pluginName,
|
||||
new Flame( this, options ));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// shim for requestAnimFrame
|
||||
window.requestAnimFrame = (function(){
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(/* function */ callback, /* DOMElement */ element){
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
})( jQuery, window, document );
|
||||
+77
-154
@@ -5,7 +5,7 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
|
||||
'use strict';
|
||||
|
||||
var Game = function () {
|
||||
// this.lab = new GameObjects.Lab();
|
||||
this.lab = null;
|
||||
this.elements = null;
|
||||
this.workers = null;
|
||||
this.upgrades = null;
|
||||
@@ -14,7 +14,11 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
|
||||
// lab: this.lab
|
||||
};
|
||||
this.loaded = false;
|
||||
this.rules = null;
|
||||
this.rules = Rules.rules;
|
||||
this.rule=undefined;
|
||||
|
||||
this.lastCards= [];
|
||||
this.incorrectCards= [];
|
||||
};
|
||||
|
||||
Game.prototype.load = function ($http, $q) {
|
||||
@@ -28,10 +32,7 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
|
||||
// make it work with Angular. If you know a way, let me know, and I'll
|
||||
// give you a beer. - Kevin
|
||||
this.elements = Helpers.loadFile('json/elements.json');
|
||||
this.workers = Helpers.loadFile('/json/workers.json');
|
||||
this.upgrades = Helpers.loadFile('./json/upgrades.json');
|
||||
this.achievements = Helpers.loadFile('./json/achievements.json');
|
||||
this.keywords = Helpers.loadFile('./json/keywords.json');
|
||||
|
||||
// function successCallback(response) {
|
||||
// return angular.fromJson(response.data);
|
||||
@@ -88,14 +89,6 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
|
||||
function (r) {
|
||||
return makeGameObject(GameObjects.Card, r);
|
||||
});
|
||||
self.workers = self.workers.map(
|
||||
function (w) {
|
||||
return makeGameObject(GameObjects.Worker, w);
|
||||
});
|
||||
self.upgrades = self.upgrades.map(
|
||||
function (u) {
|
||||
return makeGameObject(GameObjects.Upgrade, u);
|
||||
});
|
||||
self.achievements = self.achievements.map(
|
||||
function (a) {
|
||||
return makeGameObject(GameObjects.Achievement, a);
|
||||
@@ -110,158 +103,88 @@ var Game = (function (Helpers, GameObjects, ObjectStorage) {
|
||||
self.Card = new GameObjects.Cards();
|
||||
self.Card.push.apply(self.Card, self.elements);
|
||||
self.elements = self.Card;
|
||||
// var totalElements = _(self.elements).map('state.amount').sum();
|
||||
// if (totalElements<1) self.initialElements();
|
||||
|
||||
|
||||
self.rules = self.generateRules();
|
||||
|
||||
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;
|
||||
Game.prototype.init = function () {
|
||||
// setup game
|
||||
this.rule = _.sample(Rules.rules);
|
||||
this.rule.randomize();
|
||||
|
||||
// check if we have any cards in hand
|
||||
// var totalElements = _(self.elements).map('state.amount').sum();
|
||||
// if (totalElements<1) self.dealHand();
|
||||
this.dealHand();
|
||||
|
||||
// deal first card
|
||||
// TODO make sure these follow rule
|
||||
this.lastCards.push.apply(this.lastCards,_.sampleSize(this.elements,3));
|
||||
for (var i = 0; i < this.lastCards.length; i++) {
|
||||
this.incorrectCards[i]=[];
|
||||
}
|
||||
|
||||
// reset score
|
||||
this.lab.state.score = 200;
|
||||
return self;
|
||||
};
|
||||
|
||||
Game.prototype.dealHand = function (n) {
|
||||
n=n||12;
|
||||
var hand=_.sampleSize(this.elements,n);
|
||||
this.elements.map(function(card){
|
||||
card.state.amount=0;
|
||||
});
|
||||
// but give us a random 4 number cards of one suite
|
||||
var startSuit = _.sample(["Spades", "Hearts", "Diamonds", "Clubs"]);
|
||||
for (var i = 0; i < hand.length; i++) {
|
||||
var card = hand[i];
|
||||
this.elements.get(card.key).state.amount++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
Game.prototype.onClick = function (event, ui) {
|
||||
var self=this;
|
||||
console.debug('onClick',arguments);
|
||||
|
||||
/** Generate rules between runes **/
|
||||
Game.prototype.generateRules = function () {
|
||||
var cardType = angular.element(ui.draggable).data('element');
|
||||
var card = _.find(this.elements,{key:cardType});
|
||||
return this.play(card);
|
||||
};
|
||||
Game.prototype.onDrop = function (event, ui) {
|
||||
var self=this;
|
||||
console.debug('onDrop',arguments);
|
||||
|
||||
/**
|
||||
* Make the values sequential for given card names
|
||||
* @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 cardType = angular.element(ui.draggable).data('element');
|
||||
var card = _.find(this.elements,{key:cardType});
|
||||
return this.play(card);
|
||||
};
|
||||
Game.prototype.play = function (card) {
|
||||
var self=this;
|
||||
card.state.amount-=1;
|
||||
|
||||
var rules = ObjectStorage.load('rules');
|
||||
if (!rules) {
|
||||
var turn = this.lastCards.length-1;
|
||||
var correct = this.test(card);
|
||||
if (correct){
|
||||
if(!this.incorrectCards[turn]) this.incorrectCards[turn]=[];
|
||||
this.lastCards.push(angular.copy(card));
|
||||
if(!this.incorrectCards[turn+1]) this.incorrectCards[turn+1]=[];
|
||||
this.lab.state.score+=1;
|
||||
} else {
|
||||
// add incorrect one to sidelines
|
||||
if (!this.incorrectCards[turn]) this.incorrectCards[turn]=[];
|
||||
this.incorrectCards[turn].push(angular.copy(card));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.save('rules', rules);
|
||||
return rules;
|
||||
},
|
||||
/**
|
||||
* Test the rules
|
||||
* @param {array} inputKeys - e.g ["🂤", "🂤"]
|
||||
* @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
|
||||
};
|
||||
};
|
||||
// deal 2 random cards
|
||||
_.sample(this.elements).state.amount+=1;
|
||||
_.sample(this.elements).state.amount+=1;
|
||||
this.lab.state.score-=1;
|
||||
}
|
||||
return correct;
|
||||
};
|
||||
/** Test the rule **/
|
||||
Game.prototype.test = function (card) {
|
||||
return this.rule.test(card,this.lastCards,this.elements);
|
||||
};
|
||||
Game.prototype.save = function () {
|
||||
// Save every object's state to local storage
|
||||
for (var key in this.allObjects) {
|
||||
|
||||
+6
-1
@@ -37,7 +37,7 @@ var GameObjects = (function () {
|
||||
GameObject.apply(this, [{
|
||||
key: 'lab',
|
||||
state: {
|
||||
name: 'Click here to give your lab an awesome name!',
|
||||
name: 'Write your name here',
|
||||
detector: 1,
|
||||
factor: 5,
|
||||
data: 0,
|
||||
@@ -50,6 +50,11 @@ var GameObjects = (function () {
|
||||
dataSpent: 0,
|
||||
time: 0,
|
||||
observations: [],
|
||||
score: 0,
|
||||
highScore: 0,
|
||||
hints: 0,
|
||||
rulesGuessed: [],
|
||||
rulesFailed: [],
|
||||
}
|
||||
}]);
|
||||
|
||||
|
||||
+405
-67
@@ -5,13 +5,7 @@
|
||||
*/
|
||||
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
|
||||
@@ -23,93 +17,162 @@ var Rules = (function functionName(_) {
|
||||
* @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.
|
||||
* `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)
|
||||
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');
|
||||
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;
|
||||
this.optionDesc = optionDesc || {};
|
||||
this.options = this.optionDefaults = optionDefaults || {};
|
||||
this.hintTmpls = hintTmpls || [];
|
||||
|
||||
// init
|
||||
this.state = {};
|
||||
this.hintsUsed = 0;
|
||||
this.hints=this.genHints();
|
||||
this.otherOptions={nth:this.nth,lastn:this.lastn};
|
||||
this.hints = this.genHints();
|
||||
};
|
||||
Rule.prototype.setOptions = function (options) {
|
||||
this.options = _.defaults(options, this.optionDefaults);
|
||||
this.hints=this.genHints();
|
||||
this.hints = this.genHints();
|
||||
return this.options;
|
||||
};
|
||||
Rule.prototype.randomize = function (options) {
|
||||
var options = {};
|
||||
for (var option in this.optionDesc) {
|
||||
if (this.optionDesc.hasOwnProperty(option)) {
|
||||
var vals = this.optionDesc[option].possibleVals;
|
||||
options[option] = _.sample(vals);
|
||||
}
|
||||
}
|
||||
return this.setOptions(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');
|
||||
// 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;
|
||||
if (result instanceof chai.Assertion) {
|
||||
reason = result;
|
||||
result = true;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof chai.AssertionError){
|
||||
if (e instanceof chai.AssertionError) {
|
||||
result = false;
|
||||
reason=e;
|
||||
} else {throw(e);}
|
||||
reason = e;
|
||||
} else {
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
return {result:result,reason:reason};
|
||||
return {
|
||||
result: result,
|
||||
reason: reason
|
||||
};
|
||||
};
|
||||
Rule.prototype.test = function (card, lastCards, allCards) {
|
||||
var res = this.testAndTell(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);
|
||||
};
|
||||
// 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 = '<select name="' + option + '" ng-model="rule.options.' + option + '" class="form-control input-sm">\n';
|
||||
// for (var i = 0; i < vals.length; i++) {
|
||||
// s += '<option value="' + vals[i] + '">' + vals[i] + '</option>\n';
|
||||
// }
|
||||
// s += '</select>\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 = '' +
|
||||
// '<select name="<%=option%>" type="number" ng-model="rule.options.<%=option%>" class="form-control input-sm">\n' +
|
||||
// '<option ng-repeat="v in rule.optionDesc.<%=option%>.possibleVals" value="{{v}}">{{v}}</option>\n' +
|
||||
// '</select>\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);
|
||||
return compiled(_.defaults(options,this.options));
|
||||
return compiled(_.defaults(options, this.options, this.otherOptions));
|
||||
};
|
||||
/** 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 ';
|
||||
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';
|
||||
// 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);
|
||||
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="";
|
||||
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';
|
||||
var options = {};
|
||||
options[option] = vals[i];
|
||||
var v = this.describeVariation(options) + '\n';
|
||||
s.push(v);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,13 +183,13 @@ var Rules = (function functionName(_) {
|
||||
Rule.prototype.nextHint = function () {
|
||||
var hint = this.hints[this.hintsUsed];
|
||||
this.hintsUsed++;
|
||||
return hint||"";
|
||||
return hint || "";
|
||||
|
||||
};
|
||||
/** Generate an automatic hint from params **/
|
||||
Rule.prototype.genHints = function () {
|
||||
// first manual hints
|
||||
this.hintsUsed=0;
|
||||
this.hintsUsed = 0;
|
||||
var hints = [];
|
||||
|
||||
// compile hints for each unused property
|
||||
@@ -138,15 +201,15 @@ var Rules = (function functionName(_) {
|
||||
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;
|
||||
if (posVals) {
|
||||
var optionUnused = _.difference(posVals, [this.options[option]]);
|
||||
unusedOptions[option + 'Unused'] = optionUnused;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy options
|
||||
var tmplParams = _.extend({},this.options);
|
||||
var tmplParams = _.defaults({}, this.options, this.otherOptions);
|
||||
|
||||
|
||||
// make hint from unused options if there are hint templates for them
|
||||
@@ -156,12 +219,12 @@ var Rules = (function functionName(_) {
|
||||
// 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){
|
||||
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];
|
||||
tmplParams[optionUnused] = unused[j];
|
||||
var hint = tmplCmp(tmplParams);
|
||||
hints.push(hint);
|
||||
}
|
||||
@@ -181,22 +244,51 @@ var Rules = (function functionName(_) {
|
||||
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);
|
||||
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;
|
||||
var pos = _.map(this.optionDesc, 'possibleVals');
|
||||
var c = 0;
|
||||
for (var i = 0; i < pos.length; i++) {
|
||||
c*pos[i].length;
|
||||
c * pos[i].length;
|
||||
}
|
||||
return c;
|
||||
};
|
||||
// TODO estimate hardness perhaps through simulation, combinatorics or hints
|
||||
|
||||
/** Helper to format numbers **/
|
||||
Rule.prototype.nth = function (d) {
|
||||
if (d > 3 && d < 21) return 'th'; // thanks kennebec
|
||||
switch (d % 10) {
|
||||
case 1:
|
||||
return "st";
|
||||
case 2:
|
||||
return "nd";
|
||||
case 3:
|
||||
return "rd";
|
||||
default:
|
||||
return "th";
|
||||
}
|
||||
};
|
||||
/** Helper to format numbers **/
|
||||
Rule.prototype.lastn = function (d) {
|
||||
if (d > 3 && d < 21) return d+'th to last'; // thanks kennebec
|
||||
switch (d % 10) {
|
||||
case 1:
|
||||
return d+" st to last";
|
||||
case 2:
|
||||
return d+" nd to last";
|
||||
case 3:
|
||||
return d+" rd to last";
|
||||
default:
|
||||
return d+" th to last";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Now defined actual rules
|
||||
var rules = [];
|
||||
@@ -205,40 +297,286 @@ var Rules = (function functionName(_) {
|
||||
// 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",
|
||||
"Next card must not have the same <%= property %> as the <%= lastn(n) %> card",
|
||||
function (card, lastCards, allCards, options) {
|
||||
var lastNCard = lastCards[this.options.n-1];
|
||||
var lastNCard = lastCards[lastCards.length - this.options.n];
|
||||
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'],
|
||||
possibleVals: ['color', 'face', 'number', 'value', 'suit'],
|
||||
type: 'String'
|
||||
},
|
||||
n: {
|
||||
description: 'Number for how many cards back. Start counting at 1',
|
||||
possibleVals: [1,2,3],
|
||||
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',
|
||||
}, [
|
||||
'This rule does not involve <%= propertyUnused %>',
|
||||
'This rule does involve <%= property %>',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
'This rule does not involve the <%= nUnused %><%=nth(n)%> last card', // only indirectly
|
||||
]
|
||||
),
|
||||
new Rule(
|
||||
"If last <%= n %><%=nth(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)
|
||||
.to.have.property('value')
|
||||
.not.within(options.min, options.max);
|
||||
else
|
||||
return chai.expect(card)
|
||||
.to.have.property('value')
|
||||
.within(options.min, options.max);
|
||||
}, {
|
||||
n: 1,
|
||||
min: 0,
|
||||
max: 7
|
||||
}, {
|
||||
min: {
|
||||
description: 'The min bounds to compare value to',
|
||||
possibleVals: [0, 1, 2, 3, 4, 5],
|
||||
type: 'Number'
|
||||
},
|
||||
max: {
|
||||
description: 'The max bounds to compare value to',
|
||||
possibleVals: [7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
type: 'Number'
|
||||
},
|
||||
n: {
|
||||
description: 'Number for how many cards back. Start counting at 1',
|
||||
possibleVals: [1, 2, 3],
|
||||
type: 'Number'
|
||||
}
|
||||
}, [
|
||||
'This rule does not involve color',
|
||||
'This rule does not involve if it\'s a face card',
|
||||
'This rule does not involve royalty',
|
||||
'This rule does not involve color',
|
||||
'This rule does not involve suit',
|
||||
'This rule does not involve the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does involve value',
|
||||
'This rule does involve a value cuttoff',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
),
|
||||
new Rule(
|
||||
"Play a card with a value <%= min %> to <%= max %> higher than the value of the last <%=n%><%=nth(n)%> card. The numbers wrap around once they reach the max",
|
||||
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)
|
||||
.to.have.property('value')
|
||||
.not.within(options.min, options.max);
|
||||
else
|
||||
return chai.expect(card)
|
||||
.to.have.property('value')
|
||||
.within(options.min, options.max);
|
||||
}, {
|
||||
n: 1,
|
||||
min: 1,
|
||||
max: 4
|
||||
}, {
|
||||
min: {
|
||||
description: 'The min difference to the last nth card',
|
||||
possibleVals: [1, 2, 3],
|
||||
type: 'Number'
|
||||
},
|
||||
max: {
|
||||
description: 'The max difference to the last nth card',
|
||||
possibleVals: [4, 5, 6, 7, 8],
|
||||
type: 'Number'
|
||||
},
|
||||
n: {
|
||||
description: 'Number for how many cards back. Start counting at 1',
|
||||
possibleVals: [1, 2, 3],
|
||||
type: 'Number'
|
||||
}
|
||||
}, [
|
||||
'This rule does not involve color',
|
||||
'This rule does not involve if it\'s a face card',
|
||||
'This rule does not involve royalty',
|
||||
'This rule does not involve color',
|
||||
'This rule does not involve suit',
|
||||
'This rule does not involve the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does involve value',
|
||||
'This rule does involve the difference in value to the last card',
|
||||
'This rule does involve the difference in values that wraps around',
|
||||
'This rule does involve the difference in values of at least <%= min %>',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
),
|
||||
|
||||
|
||||
|
||||
new Rule(
|
||||
"If the last <%= n %><%=nth(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];
|
||||
var lastWasEven = lastNCard % 2 == 0;
|
||||
if (lastWasEven)
|
||||
return chai.expect(card)
|
||||
.to.have.property('color')
|
||||
.equals(options.evenColor);
|
||||
else
|
||||
return chai.expect(card)
|
||||
.to.have.property('color')
|
||||
.not.equals(options.evenColor);
|
||||
}, {
|
||||
n: 1,
|
||||
evenColor: 'red',
|
||||
}, {
|
||||
evenColor: {
|
||||
description: 'The color to play if lastNCard was even',
|
||||
possibleVals: ['red', 'black'],
|
||||
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 if it\'s a face card',
|
||||
'This rule does not involve royalty',
|
||||
'This rule does not involve suit',
|
||||
'This rule does not involve the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does not involve the difference in value to the last card',
|
||||
'This rule does involve color',
|
||||
'This rule does involve value',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
),
|
||||
|
||||
new Rule(
|
||||
"Play a card that has the same <%= property %> or color as the last <%= n %><%=nth(n)%> card but not both.",
|
||||
function (card, lastCards, allCards, options) {
|
||||
var lastNCard = lastCards[lastCards.length - options.n];
|
||||
var matchesColor = lastNCard.color === card.color;
|
||||
var matches2 = lastNCard.suit === lastNCard.suit;
|
||||
return ((matchesColor && !matches2) || (!matchesColor && matches2));
|
||||
}, {
|
||||
n: 1,
|
||||
property: 'suit',
|
||||
}, {
|
||||
property: {
|
||||
description: 'The property to compare to the lastNCard',
|
||||
possibleVals: ['suit', 'value', 'face', 'royal'],
|
||||
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 the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does involve color',
|
||||
'This rule does not involve <%= propertyUnused %>',
|
||||
'This rule does involve <%= property %>',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
),
|
||||
|
||||
new Rule(
|
||||
"If the last <%= n %><%=nth(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];
|
||||
var lastWasHigher = lastNCard.value > options.min;
|
||||
if (lastWasHigher) {
|
||||
return chai.expect(card)
|
||||
.to.have.property(options.property)
|
||||
.not.equals(lastNCard[options.property]);
|
||||
} else {
|
||||
return chai.expect(card)
|
||||
.to.have.property(options.property)
|
||||
.equals(lastNCard[options.property]);
|
||||
}
|
||||
var matches2 = lastNCard.suit === lastNCard.suit;
|
||||
return ((matchesColor && !matches2) || (!matchesColor && matches2));
|
||||
}, {
|
||||
n: 1,
|
||||
min: 7,
|
||||
property: 'suit',
|
||||
}, {
|
||||
property: {
|
||||
description: 'The property to compare to the lastNCard',
|
||||
possibleVals: ['suit', 'face', 'royal', 'color'],
|
||||
type: 'String'
|
||||
},
|
||||
n: {
|
||||
description: 'Number for how many cards back. Start counting at 1',
|
||||
possibleVals: [1, 2, 3],
|
||||
type: 'Number'
|
||||
},
|
||||
min: {
|
||||
description: 'The min difference to the last nth card',
|
||||
possibleVals: [5, 6, 7, 8, 9],
|
||||
type: 'Number'
|
||||
},
|
||||
}, [
|
||||
'This rule does not involve the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does not involve <%= propertyUnused %>',
|
||||
'This rule does involve value',
|
||||
'This rule does a minimum value',
|
||||
'This rule does involve <%= property %>',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
),
|
||||
new Rule(
|
||||
"If the last <%= n %><%=nth(n)%> card was a <%= property %> card, play a higher value card otherwise lower.",
|
||||
function (card, lastCards, allCards, options) {
|
||||
var lastNCard = lastCards[lastCards.length - options.n];
|
||||
var lastHadProperty = lastNCard[options.property];
|
||||
if (lastHadProperty) {
|
||||
return chai.expect(card)
|
||||
.to.have.property('value')
|
||||
.above(lastNCard[options.value]);
|
||||
} else {
|
||||
return chai.expect(card)
|
||||
.to.have.property('value')
|
||||
.lessThan(lastNCard[options.value]);
|
||||
}
|
||||
}, {
|
||||
n: 1,
|
||||
property: 'face',
|
||||
}, {
|
||||
property: {
|
||||
description: 'The property to compare to the lastNCard',
|
||||
possibleVals: ['face', 'royal', 'spade', 'club', 'diamond', 'heart', 'red', 'black'],
|
||||
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 the <%= nUnused %><%=nth(nUnused)%> last card',
|
||||
'This rule does not check for <%= propertyUnused %> cards',
|
||||
'This rule does involve <%= property %>',
|
||||
'This rule wants you to play a higher card in some circumstances',
|
||||
'This rule does involve the <%= n %><%=nth(n)%> last card',
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
||||
return {
|
||||
Rule: Rule,
|
||||
rules: rules,
|
||||
|
||||
+1063
-59
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,37 @@
|
||||
|
||||
/* jasmine specs for directives go here */
|
||||
|
||||
describe('directives', function() {
|
||||
describe('directives', function () {
|
||||
var $compile,
|
||||
$rootScope;
|
||||
|
||||
// Load the myApp module, which contains the directive
|
||||
beforeEach(module('scienceAlchemy'));
|
||||
// beforeEach(module('Rules'));
|
||||
|
||||
// Store references to $rootScope and $compile
|
||||
// so they are available to all tests in this describe block
|
||||
beforeEach(inject(function (_$compile_, _$rootScope_) {
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('Replaces the element with the appropriate content', function () {
|
||||
// define a rule for testing
|
||||
$rootScope.rule = Rules.rules[0];
|
||||
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile('<div cfs-rule="rule" ng-model="rule"></div>')($rootScope);
|
||||
|
||||
// fire all the watches, so the scope expressions will be evaluated
|
||||
// $rootScope.$digest();
|
||||
$rootScope.$apply();
|
||||
|
||||
// Check that the compiled element contains the templated content
|
||||
var html = element.html();
|
||||
expect(html).toContain("<select");
|
||||
expect(html).toContain("<option");
|
||||
expect(html).toContain("Next card must not have the same");
|
||||
});
|
||||
});
|
||||
|
||||
+64
-10
@@ -47,7 +47,7 @@ describe('Rules', function () {
|
||||
});
|
||||
});
|
||||
Rules.rules.forEach(function(rule){
|
||||
describe('rule', function () {
|
||||
describe('rule: "'+rule.describe()+'"', function () {
|
||||
it('should test', function () {
|
||||
var res = rule.test(card,lastCards,allCards);
|
||||
Boolean(res);
|
||||
@@ -64,7 +64,7 @@ describe('Rules', function () {
|
||||
});
|
||||
it('should describeVariations', function () {
|
||||
var desc = rule.describeVariations();
|
||||
expect(typeof desc).toBe('string');
|
||||
expect(desc instanceof Array).toBe(true);
|
||||
expect(desc).not.toContain(/[{}]+/);
|
||||
});
|
||||
it('should genHints', function () {
|
||||
@@ -79,20 +79,34 @@ describe('Rules', function () {
|
||||
expect(typeof opts).toBe('object');
|
||||
expect(opts).not.toEqual(opts1);
|
||||
});
|
||||
it('should randomize', function () {
|
||||
var opts1 = rule.options;
|
||||
var opts = rule.randomize();
|
||||
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);
|
||||
var def = rule.optionDefaults[option];
|
||||
describe('option: '+option, function () {
|
||||
for (var i = 0; i < vals.length; i++) {
|
||||
it('should test with val: '+vals[i],function(){
|
||||
var options={};
|
||||
options[option]=vals[i];
|
||||
rule.setOptions(options);
|
||||
var res = rule.test(card,lastCards,allCards);
|
||||
expect(typeof res).toBe('boolean');
|
||||
});
|
||||
}
|
||||
|
||||
it('default should be in possible vals '+option+' = '+vals[i],function(){
|
||||
expect(vals).toEqual(jasmine.arrayContaining([def]));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -100,3 +114,43 @@ describe('Rules', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rules compilation', function () {
|
||||
var $compile,
|
||||
$rootScope;
|
||||
|
||||
beforeEach(
|
||||
module('scienceAlchemy')
|
||||
);
|
||||
|
||||
// Store references to $rootScope and $compile
|
||||
// so they are available to all tests in this describe block
|
||||
beforeEach(inject(function (_$compile_, _$rootScope_) {
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
Rules.rules.forEach(function(rule){
|
||||
describe('rule: "'+rule.describe()+'"', function () {
|
||||
it('should render using cfs-rule directive', function () {
|
||||
|
||||
// define a rule for testing
|
||||
$rootScope.rule = Rules.rules[0];
|
||||
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile('<div cfs-rule="rule" ng-model="rule"></div>')($rootScope);
|
||||
|
||||
// fire all the watches, so the scope expressions will be evaluated
|
||||
// $rootScope.$digest();
|
||||
$rootScope.$apply();
|
||||
|
||||
// Check that the compiled element contains the templated content
|
||||
var html = element.html();
|
||||
expect(html).toContain("<select");
|
||||
expect(html).toContain("<option");
|
||||
expect(html).toContain("Next card must not have the same");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ describe('services', function () {
|
||||
//load modules
|
||||
beforeEach(module('scienceAlchemy'));
|
||||
|
||||
|
||||
// Test service availability
|
||||
describe('game', function () {
|
||||
it('should exist', inject(function (game) {
|
||||
@@ -12,11 +13,11 @@ describe('services', function () {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('detector', function () {
|
||||
it('should exist', inject(function (detector) {
|
||||
expect(detector).toBeDefined();
|
||||
}));
|
||||
});
|
||||
// describe('detector', function () {
|
||||
// it('should exist', inject(function (detector) {
|
||||
// expect(detector).toBeDefined();
|
||||
// }));
|
||||
// });
|
||||
|
||||
describe('lab', function () {
|
||||
it('should exist', inject(function (lab) {
|
||||
|
||||
Reference in New Issue
Block a user