__all__ = ('ResolutionError', 'Module', 'JSResolver',)
-import sys, re, json
+import sys, re, jsond as json
from itertools import chain, repeat
from collections import defaultdict
from subprocess import Popen, check_output, STDOUT, PIPE
from glob import glob
+from pprint import pprint, pformat
from bunch import *
from path import path
return (before, None, [])
return (before, sep, list(it))
+
+
def canonicalise(query, base=None):
""" query: A module ID (relative or absolute) or filepath
base: Optional. The referring module ID.
def requires(self):
return self.calcRequires('_at_requires') + self._requires
+ def __json_default__(self):
+ return dict( (k,self[k]) for k in 'id file name _requires _at_requires'.split(' ') )
+
def __hash__(self):
return hash(self.id)
class CommonJS(object):
""" Compiles JS modules into browser-safe JS files. """
- @staticmethod
- def discover(files, repos, **options):
- "Discover listed modules and their dependencies."
- cjs = CommonJS(repos, **options)
- queue = [ cjs.lookup(f) for f in files ]
- seen = set()
- while queue:
- mod = queue.pop(0)
- seen.add(mod)
- # print mod, "requirements:", mod.requires
- for query in mod.requires:
- # print mod, "requires", query
- req = cjs.lookup(query, mod)
- if req not in seen:
- queue.append(req)
- if cjs.deps_name:
- cjs.genDepLoader()
- return cjs
-
-
repos = []
modules = {} # id -> Module
_graph = None # id -> set(dependants)
_at_graph = None # id -> set(dependants)
- def __init__(self, repos, out='build', deps_name=None, clean=True, **options):
+ def __init__(self, repos, out='build', save='build', deps_name=None, clean=True, **options):
self.modules = {}
self.options = options
self.deps_name = deps_name
for k, mod in self.modules.iteritems():
yield k, mod
+ def reset(self):
+ self._graph = None
+ self._at_graph = None
+ return self
+
def calcGraph(self, attr):
if self._graph is None:
graph = self._graph = defaultdict(set)
return self.calcGraph('_at_graph')
def tsort(self, graph):
- p = Popen(['tsort'], stderr=STDOUT, stdin=PIPE, stdout=PIPE)
+ p = Popen(['tsort'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE)
deps = '\n'.join( '%s %s' % (id, dep.id) for (id, deps) in graph.iteritems() for dep in deps )
p.stdin.write(deps)
p.stdin.close()
if p.wait() != 0:
raise ResolutionError('Cannot resolve dependencies! Requirements must be acyclic!')
- return p.stdout.read()
+ return p.stdout.read().strip().split('\n')
@property
def dependencies(self):
- deps = (self.tsort(self.atGraph)+self.tsort(self.graph)).strip().split('\n')
- for i, dep in reversed(list(enumerate(deps[:]))):
- try:
- idx = deps.index(dep, 0, i-1)
- if i != idx:
- del deps[idx]
- except ValueError: pass
- return deps
+ atg = self.tsort(self.atGraph)
+ nog = self.tsort(self.graph)
+ atdeps = self.atGraph.keys()
+
+ for dep in atg[:]:
+ if dep not in atdeps:
+ try: atg.remove(dep)
+ except ValueError: pass
+ else:
+ try: nog.remove(dep)
+ except ValueError: pass
+
+ # print '\nAtKeys:\n', pformat(atdeps), '\n'
+ # print '\nAtDeps:\n', pformat(atg), '\n'
+ # print '\nNoKeys:\n', pformat(nog), '\n'
+ return atg+nog
def dumpDependencies(self):
column = Popen(['column', '-s', '\t', '-t'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE)
- mods = self.modules.values()
- for mod in sorted(mods, key=lambda m: len(m.requires), reverse=True):
+ # for mod in sorted(self.modules.values(), key=lambda m: len(m.requires), reverse=True):
+ for mod in sorted(self.modules.values()):
column.stdin.write('%s\t->\t%r\n' % (mod, sorted(mod.requires)))
- # print '%s\t->\t%r' % (mod, sorted(mod.requires))
column.stdin.close()
if column.wait() != 0:
print >> sys.stderr, 'Some sort of error has occurred!'
@property
def scriptTags(self):
return '\n'.join( '<script src="{}" type="text/javascript"></script>'.format(uri) for uri in self.uris )
+
+ def save(self, dir):
+ with (path(dir)/'.modules.json').open('w') as f:
+ json.dump({
+ 'modules' : self.modules,
+ 'ensure' : dict( (id, list(mod.id for mod in mods)) for id,mods in self.atGraph.iteritems() ),
+ 'graph' : dict( (id, list(mod.id for mod in mods)) for id,mods in self.graph.iteritems() ),
+ }, f, indent=4)
+ return self
+
+ @staticmethod
+ def discover(files, repos, **options):
+ "Discover listed modules and their dependencies."
+ cjs = CommonJS(repos, **options)
+
+ # print >> sys.stderr, '\n[ %s ]' % f
+
+ queue = [ cjs.reset().lookup(f) for f in files ]
+ seen = set()
+ while queue:
+ mod = queue.pop(0)
+ seen.add(mod)
+ # print mod, "requirements:", mod.requires
+ for query in mod.requires:
+ # print mod, "requires", query
+ req = cjs.lookup(query, mod)
+ if req not in seen:
+ queue.append(req)
+
+ if options.get('deps_name', None):
+ cjs.genDepLoader()
+
+ if options.get('print_deps', None):
+ print >> sys.stderr, '\n[ %s ]' % f
+ print >> sys.stderr, 'All Dependencies:'
+ print >> sys.stderr, cjs.dumpDependencies()
+ print >> sys.stderr, ''
+ print >> sys.stderr, 'Resolution:'
+ print >> sys.stderr, '\n'.join(cjs.dependencies)
+
+ if options.get('script_tags', None):
+ print cjs.scriptTags
+
+ if options.get('state', None):
+ cjs.save(options['state'])
+
+ return cjs
+
help="Generates a JS script with the given name which doc-writes tags for this set of modules.")
parser.add_option("-s", "--script-tags", default=False, action="store_true",
help="Emits script-tags for this set of modules in dependency order. [default: %default]")
+ parser.add_option("-S", "--state", default='build',
+ help="Directory to save build state. [default: %default]")
+
(options, args) = parser.parse_args()
# print 'files:', files, 'repos:', (repos or ['.'])
try:
- js = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__)
+ cjs = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__)
except ResolutionError as ex:
print >> sys.stderr, str(ex)
return 1
- if options.script_tags:
- print js.scriptTags
-
- if options.print_deps:
- print >> sys.stderr, 'All Dependencies:'
- print >> sys.stderr, js.dumpDependencies()
- print >> sys.stderr, ''
- print >> sys.stderr, 'Resolution:'
- print >> sys.stderr, '\n'.join(js.dependencies)
-
return 0
if __name__ == '__main__':
# The Littlest Battletank
-Inspired by Tanks minigame off Wii Play.
+Inspired by the Tanks minigame in Wii Play.
+
## The Basics
- Your tank has 1hp.
- Your basic cannon shoots bullets. A bullet lives for one bounce or until it kills something. You can have up to 5 bullets at a time in the air.
- When you die, you respawn after a few seconds. (In single player, it restarts the level.)
+
## Controls
-- wasd, arrows: move your tank.
-- mouse: aim your reticule.
-- click, space, enter: fire a shot.
-- 0-9: use items.
-- All controls can be configured.
-
-## Ideas
-- Simplicity is king.
-- Only bosses have more than 1hp
-- Always on one screen
-- No stats: all items are qualitative
-- AI makes commentary on situations. "That was close. Sucker." "loool, you always die here."
-- Use TruRank-style player rating system, but the point of multiplayer will not be the ladder
-- Optional registration (Flash cookie every user)
-- Badges, awards, stats
-- Virtual currency from various in-game activities
-
-## Power-Ups
-- Missiles (fast, no bounce)
-- Refractor Rounds (slow, infi bounces)
-- Flamethrower (Cone AOE)
-- Shrapnel Rounds (splits once on impact)
-- Mortar Rounds (Projectile AOE)
-- Mines (Stationary Prox AOE)
-- Heatseekers (Projectile Prox)
-- Nukes (Absurd AOE which often kills you)
-- Portal Cannon (Exchanges your position with your cursor)
-- K-Boss Shield (Absorbs 1 damage)
-- Gin & Tonic / iPhone 3G / Hannah Montana Lamp (Extra Life)
-- Deuschund Trebuchet (Parabolic Arc lulz)
-- Light Cannon (Faster Move, -1 Shots in Air)
-- EMP Device (aka, the "Stunna") (Freezes everyone in place. Including you.)
-
-## Mechanics
-- AOE
-- Shot Travel Speed
-- Shot Bounces
-- Shot Cooldown
-- Shots in Air
-- Move Speed
-- HP (Bosses only)
-- Allies -- companion AI tanks which play for your side, but they can still damage you
-- Can use Items
-- AI type, quality
-- Mutable Environment?
+
+### Desktop Controls
+
+- Move: wasd, arrows for cardinal directions; combine for 45-degree moves
+- Shoot:
+ - Aim your reticule with the mouse
+ - Click, or press space to fire a shot
+- Items: Press `12345zxc
+- Zoom: Mouse wheel
+- Customization:
+ - Keyboard controls can be rebound
+ - Multiple UI trays
+
+### iOS Controls
+- Move: Trace a path from your tank with your finger
+- Shoot:
+ - Tap once somewhere sufficiently far from your tank to shoot one bullet on a trajectory aimed at that point
+ - Tap once and hold to see the trajectory line
+ - Move your finger to drag the line at a new point
+ - Release the tap to shoot along that trajectory
+ - Tap with your other hand sufficiently far from your tank and the current trajectory point to cancel
+- Items: On-screen buttons, possibly modal
+- Zoom: Pinch
+- Customization:
+ - Modal buttons used to switch between multiple UI "trays" with buttons
+ - Free gestures can be rebound (two/three finger swipe, double-tap, triple-tap)
+
+
+## Gameplay Ideas
+- No camera control: camera centers on tank and moves with it, though player can zoom the map
+- No number games: all items are qualitative (so, ex: only bosses have more than 1hp, but items (like shields) might absorb damage)
+- Allies: companion AI tanks which play for your side (beware friendly fire)- Mutable Environment: obstacles can be destroyed with the right weapons, enemies can be pushed
+## Items
+### Cannons
+- Missiles: Faster projectile, but no bounce
+- Refractor Rounds: Slower projectile, but infi bounces
+- Shrapnel Rounds: Projectile that splits instead of bounce
+### Heavy Munitions
+- Flamethrower: Cone AOE
+- Mortar Rounds: Projectile with AOE on detonation
+- Proximity Missiles: Projectile that explodes on prox
+- Mines: Stationary prox AOE
+### Special Weapons
+- Heatseekers: Projectile that will guide around walls to get to your target point, and then explodes even if no one is there
+- Nuke: Slow projectile with enormous AOE (reminder: friendly fire)
+- Trebuchet Rounds: Projectile which only collides when it reaches its destination (as if it was fired in a parabolic arc)
+- Portal Cannon: Makes source/dest wormholes
+### Passives
+- Light Ordinance: Faster move, fewer shots in air
+- Tactical Driver: Attempts to automatically move your tank out of harm's way when a bullet is imminent
+- Chameleon Engine: Makes enemies unable to see you for a limited time, confusing pathing
+
+### Shields
+- Turret Shield: extends 45-degrees around tour tank, centered on the barrel; impact pushes your tank back
+- Orbital Shield: small pellet-shield that orbits your tank
+- Stationary Shield: stationary shield sphere, passable to tanks but not bullets
+- Imprinted Stationary Shield: as the stationary shield, but passable only to your team and not others
+
+### Consumables
+- Cloudpot: Your tank deploys an emergency zeppelin to float quickly from your current position to your cursor. Tank can only be hit by Trebuchet Rounds during that time.
+- The Stunna Shocka (EMP Device): Freezes all tanks in place (including you)
+
+
+## Tank Attributes
+- Movement: speed, turning speed
+- Cannon: cooldown, max in air, recoil?
+- Bullets: speed, num bounces, proximity, AOE explosion
+- Toughness: hp (Bosses only), shields, resistance to certain classes of weapons
+
+
+## Flavor
+- AI makes commentary on situations. "That was close. Sucker." "loool, you always die here."
+- Papercraft art style, vector graphics
<div id="ai" style="display:none" class="bigblue">
<div class="box">
<h2>Add Tank AI</h2>
- <textarea id="custom-tank"></textarea>
+ <textarea id="customtank"></textarea>
<div class="ready pinkbutton rounded">Ready</div>
</div>
</div>
if ( !modules.hasOwnProperty(id) )
throw new Error('No such module "'+id+'"!');
- var module = modules[ids]
+ var module = modules[id]
, exports = module.exports = {};
// TODO: load the module
+//@require('jquery')
/*
* jQuery Hotkeys Plugin
* Copyright 2010, John Resig
+//@require('jquery')
(function(b){b.fn.__bind__=b.fn.bind;b.fn.__unbind__=b.fn.unbind;b.fn.__find__=b.fn.find;var a={version:"0.7.9",override:/keypress|keydown|keyup/g,triggersMap:{},specialKeys:{27:"esc",9:"tab",32:"space",13:"return",8:"backspace",145:"scroll",20:"capslock",144:"numlock",19:"pause",45:"insert",36:"home",46:"del",35:"end",33:"pageup",34:"pagedown",37:"left",38:"up",39:"right",40:"down",109:"-",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",191:"/"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":'"',",":"<",".":">","/":"?","\\":"|"},newTrigger:function(e,d,f){var c={};c[e]={};c[e][d]={cb:f,disableInInput:false};return c}};a.specialKeys=b.extend(a.specialKeys,{96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/"});b.fn.find=function(c){this.query=c;return b.fn.__find__.apply(this,arguments)};b.fn.unbind=function(h,e,g){if(b.isFunction(e)){g=e;e=null}if(e&&typeof e==="string"){var f=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();var d=h.split(" ");for(var c=0;c<d.length;c++){delete a.triggersMap[f][d[c]][e]}}return this.__unbind__(h,g)};b.fn.bind=function(j,f,k){var h=j.match(a.override);if(b.isFunction(f)||!h){return this.__bind__(j,f,k)}else{var n=null,i=b.trim(j.replace(a.override,""));if(i){n=this.__bind__(i,f,k)}if(typeof f==="string"){f={combi:f}}if(f.combi){for(var m=0;m<h.length;m++){var d=h[m];var g=f.combi.toLowerCase(),e=a.newTrigger(d,g,k),l=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();e[d][g].disableInInput=f.disableInInput;if(!a.triggersMap[l]){a.triggersMap[l]=e}else{if(!a.triggersMap[l][d]){a.triggersMap[l][d]=e[d]}}var c=a.triggersMap[l][d][g];if(!c){a.triggersMap[l][d][g]=[e[d][g]]}else{if(c.constructor!==Array){a.triggersMap[l][d][g]=[c]}else{a.triggersMap[l][d][g][c.length]=e[d][g]}}this.each(function(){var o=b(this);if(o.attr("hkId")&&o.attr("hkId")!==l){l=o.attr("hkId")+";"+l}o.attr("hkId",l)});n=this.__bind__(h.join(" "),f,a.handler)}}return n}};a.findElement=function(c){if(!b(c).attr("hkId")){if(b.browser.opera||b.browser.safari){while(!b(c).attr("hkId")&&c.parentNode){c=c.parentNode}}}return c};a.handler=function(e){var o=a.findElement(e.currentTarget),i=b(o),d=i.attr("hkId");if(d){d=d.split(";");var g=e.which,q=e.type,p=a.specialKeys[g],n=!p&&String.fromCharCode(g).toLowerCase(),h=e.shiftKey,c=e.ctrlKey,m=e.altKey||e.originalEvent.altKey,f=null;for(var r=0;r<d.length;r++){if(a.triggersMap[d[r]][q]){f=a.triggersMap[d[r]][q];break}}if(f){var j;if(!h&&!c&&!m){j=f[p]||(n&&f[n])}else{var l="";if(m){l+="alt+"}if(c){l+="ctrl+"}if(h){l+="shift+"}j=f[l+p];if(!j){if(n){j=f[l+n]||f[l+a.shiftNums[n]]||(l==="shift+"&&f[a.shiftNums[n]])}}}if(j){var s=false;for(var r=0;r<j.length;r++){if(j[r].disableInInput){var k=b(e.target);if(i.is("input")||i.is("textarea")||i.is("select")||k.is("input")||k.is("textarea")||k.is("select")){return true}}s=s||j[r].cb.apply(this,[e])}return s}}}};window.hotkeys=a;return b})(jQuery);
\ No newline at end of file
+//@require('jquery')
/**
*
* jquery.sparkline.js
+//@require('jquery')
/* jquery.sparkline 1.5.1 - http://omnipotent.net/jquery.sparkline/ */
-
(function($){$.fn.simpledraw=function(width,height,use_existing){if(use_existing&&this[0].vcanvas)return this[0].vcanvas;if(width==undefined)width=$(this).innerWidth();if(height==undefined)height=$(this).innerHeight();if($.browser.hasCanvas){return new vcanvas_canvas(width,height,this);}else if($.browser.msie){return new vcanvas_vml(width,height,this);}else{return false;}};var pending=[];$.fn.sparkline=function(uservalues,options){var options=$.extend({type:'line',lineColor:'#00f',fillColor:'#cdf',defaultPixelsPerValue:3,width:'auto',height:'auto',composite:false},options?options:{});return this.each(function(){var render=function(){var values=(uservalues=='html'||uservalues==undefined)?$(this).text().split(','):uservalues;var width=options.width=='auto'?values.length*options.defaultPixelsPerValue:options.width;if(options.height=='auto'){if(!options.composite||!this.vcanvas){var tmp=document.createElement('span');tmp.innerHTML='a';$(this).html(tmp);height=$(tmp).innerHeight();$(tmp).remove();}}else{height=options.height;}
$.fn.sparkline[options.type].call(this,values,options,width,height);}
if(($(this).html()&&$(this).is(':hidden'))||($.fn.jquery<"1.3.0"&&$(this).parents().is(':hidden'))){pending.push([this,render]);}else{render.call(this);}});};$.sparkline_display_visible=function(){for(var i=pending.length-1;i>=0;i--){var el=pending[i][0];if($(el).is(':visible')&&!$(el).parents().is(':hidden')){pending[i][1].call(el);pending.splice(i,1);}}};$.fn.sparkline.line=function(values,options,width,height){var options=$.extend({spotColor:'#f80',spotRadius:1.5,minSpotColor:'#f80',maxSpotColor:'#f80',lineWidth:1,normalRangeMin:undefined,normalRangeMax:undefined,normalRangeColor:'#ccc',chartRangeMin:undefined,chartRangeMax:undefined,chartRangeMinX:undefined,chartRangeMaxX:undefined},options?options:{});var xvalues=[],yvalues=[],yminmax=[];for(i=0;i<values.length;i++){var v=values[i];var isstr=typeof(values[i])=='string';var isarray=typeof(values[i])=='object'&&values[i]instanceof Array;var sp=isstr&&values[i].split(':');if(isstr&&sp.length==2){xvalues.push(Number(sp[0]));yvalues.push(Number(sp[1]));yminmax.push(Number(sp[1]));}else if(isarray){xvalues.push(values[i][0]);yvalues.push(values[i][1]);yminmax.push(values[i][1]);}else{xvalues.push(i);if(values[i]===null||values[i]=='null'){yvalues.push(null);}else{yvalues.push(Number(values[i]));yminmax.push(Number(values[i]));}}}
// Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/
-var type = require('Y/type')
-, YFunction = require('Y/types/function').YFunction
-, core = require('Y/core')
-, slice = core.slice
+var type = require('Y/type')
+, core = require('Y/core')
+, YFunction = require('Y/types/function').YFunction
+, isFunction = type.isFunction
+, slice = core.slice
+
+, globals = (function(){ return this; })()
+, _Object = globals.Object
+, _Function = globals.Function
+, _Array = globals.Array
+, _String = globals.String
+, _Number = globals.Number
+
+, slice = _Array.prototype.slice
+, hasOwn = _Object.prototype.hasOwnProperty
+, getProto = _Object.getPrototypeOf
, KNOWN_CLASSES = type.type.KNOWN_CLASSES
, classToString = function toString(){ return this.className+"()"; }
, _Array = globals.Array
, PT = "prototype"
, slice = _Array[PT].slice
+, type = require('Y/type')
;
// function slice(a){
}
function forEach(o, fn){
- map(o, fn, cxt);
+ map(o, fn, arguments[2] || o);
return o;
}
function attr(o, key, value, def){
if ( !o || key === undefined ) return o;
- if ( Y.isPlainObject(key) )
+ if ( type.isPlainObject(key) )
return extend(o, key);
if ( value !== undefined || def !== undefined ){
// Curry all operators
var op = require('Y/op');
core.forEach(op, YFunction);
-Y['op'] = op['curried'] = op.extend({}, core.map(yfn.curry, op));
-// Y['op'] = core.reduce(op, function(Yop, fn, k){
-// Yop[k] = yfn.curry(fn);
-// return Yop;
-// }, {});
+// Y['op'] = op['curried'] = op.extend({}, core.map(op, yfn.curry));
+Y['op'] =
+op['curried'] =
+ core.reduce(op, function(Yop, fn, k){
+ Yop[k] = fn.curry();
+ return Yop;
+ }, {});
// var yclass = require('Y/types/class');
addNames('Class subclass instantiate fabricate YBase',