Resolves pathing bug.
authordsc <david.schoonover@gmail.com>
Fri, 24 Dec 2010 05:04:42 +0000 (21:04 -0800)
committerdsc <david.schoonover@gmail.com>
Fri, 24 Dec 2010 05:04:42 +0000 (21:04 -0800)
22 files changed:
src/Y/core.cjs
src/Y/delegate.cjs
src/Y/internal.cjs [new file with mode: 0644]
src/Y/types/array.cjs
src/Y/types/chain.cjs [new file with mode: 0644]
src/Y/types/collection.cjs
src/Y/types/function.cjs
src/Y/types/object.cjs
src/Y/types/string.cjs
src/Y/utils.cjs
src/ezl/loop/cooldown.cjs
src/ezl/loop/eventloop.cjs
src/ezl/util/tree/quadtree.cjs
src/tanks/map/level.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/trajectory.cjs
src/tanks/map/traversal.cjs
src/tanks/thing/player.cjs
src/tanks/thing/tank.cjs
src/tanks/ui/main.cjs
src/tanks/ui/pathmapui.cjs
www/deps.html

index b13737c..d400314 100644 (file)
@@ -18,6 +18,8 @@ var undefined
 ,   type      = require('Y/type')
 ;
 
+
+
 function reduce(o, fn, acc){
     if ( !o )
         return acc;
@@ -93,51 +95,120 @@ function attr(o, key, value, def){
         return o[key];
 }
 
+/**
+ * Copies all properties from all arguments after the first onto the first.
+ * Accepts any number of arguments greater than one.
+ * 
+ * @param {Object} target Target object for the copied properties.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A
+ * 
+ * nb. "Properties" are only value descriptors. To copy accessor descriptors, use
+ * `core.accessors(A,B)`. To copy everything, use `core.descriptors(A,B)`.
+ * 
+ */
 function extend( A, B ){
     var args = slice.call(arguments,1);
     if ( type.isArray(A) )
         return A.concat.apply(A, args);
     else
-        return args.reduce(extendall, A);
+        return args.reduce(_extend, A);
 }
-function extendall(A, donor){ return reduce(donor, attrvk, A); }
-function attrvk(o, v, k){     return attr(o, k, v, o[k]); }
+function _extend(A, donor){ return reduce(donor, attrvk, A); }
+function attrvk(o, v, k){   return attr(o, k, v, o[k]); }
 
 /**
  * Fetches the descriptor for each property that is an accessor (getter/setter).
- * @param {Object} o Object for inspection.
- * @return {Object} A map of property to descriptor.
- * nb. An Array is safer, but:
- *  1) Object.defineProperties takes an Object
- *  2) That'd be much worse to work with
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to accessor descriptor.
  */
-function accessors(o){
-    if ( !(o && typeof o === "object") )
-        return {};
-    return reduce(o, function(acc, v, k, obj){
-        
-        // Ignore inherited properties
-        if ( !hasOwn.call(obj,k) )
-            return acc;
-        
-        // Only yield accessors
-        var desc = getDesc(obj,k);
-        if ( !hasOwn.call(desc,'value') )
-            acc[k] = desc;
-        
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ * 
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A.
+ */
+function accessors(A, B){
+    if ( !(A && typeof A === "object") )
+        return A;
+    
+    if (arguments.length < 2)
+        return reduce(A, _getAccessors, {});
+    
+    var desc = slice.call(arguments,1).reduce(_copyAccessors, {});
+    Object.defineProperties(A, desc);
+    return A;
+}
+function _getAccessors(acc, v, k, obj){
+    
+    // Ignore inherited properties
+    if ( !hasOwn.call(obj,k) )
         return acc;
-    }, {});
+    
+    // Only yield accessors
+    var desc = getDesc(obj,k);
+    if ( !hasOwn.call(desc,'value') )
+        acc[k] = desc;
+    
+    return acc;
+}
+function _copyAccessors(desc, donor){
+    return reduce(donor, _getAccessors, desc);
 }
 
+/**
+ * Fetches the descriptor for each own property of the object.
+ * 
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to descriptor.
+ */
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ * 
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object.
+ * @return {Object} A.
+ */
+function descriptors(A, B){
+    if ( !(A && typeof A === "object") )
+        return A;
+    
+    if (arguments.length < 2)
+        return reduce(A, _getDescriptors, {});
+    
+    var desc = slice.call(arguments,1).reduce(_copyDescriptors, {});
+    Object.defineProperties(A, desc);
+    return A;
+}
+function _getDescriptors(acc, v, k, obj){
+    
+    // Ignore inherited properties
+    if ( !hasOwn.call(obj,k) )
+        return acc;
+    
+    acc[k] = getDesc(obj,k);
+    return acc;
+}
+function _copyDescriptors(desc, donor){
+    return reduce(donor, _getDescriptors, desc);
+}
+
+
+
+exports['slice']       = slice;
+
+exports['reduce']      = reduce;
+exports['map']         = map;
+exports['forEach']     = forEach;
+exports['filter']      = filter;
 
-exports['reduce']    = reduce;
-exports['map']       = map;
-exports['forEach']   = forEach;
-exports['filter']    = filter;
+exports['set']         = set;
+exports['attr']        = attr;
+exports['extend']      = extend;
 
-exports['set']       = set;
-exports['attr']      = attr;
-exports['extend']    = extend;
+exports['accessors']   = accessors;
+exports['descriptors'] = descriptors;
 
-exports['accessors'] = accessors;
-exports['slice']     = slice;
\ No newline at end of file
index 469a1e0..7a68061 100644 (file)
@@ -108,24 +108,34 @@ function extend( A, B ){
 function extendall(A, donor){ return reduce(donor, attrvk, A); }
 function attrvk(o, v, k){     return attr(o, k, v, o[k]); }
 
-function accessors(o){
-    if ( !(o && typeof o === "object") )
-        return {};
+function accessors(A, B){
+    if ( !(A && typeof A === "object") )
+        return A;
     
-    if ( notWrapped(o.accessors) )
-        return o.accessors.apply(o, slice.call(arguments,1));
+    if ( notWrapped(A.accessors) )
+        return A.accessors.apply(A, slice.call(arguments,1));
     
-    return core.accessors(o);
+    return core.accessors.apply(this, arguments);
 }
 
-exports['reduce']  = reduce;
-exports['map']     = map;
-exports['forEach'] = forEach;
-exports['filter']  = filter;
+function descriptors(A,B){
+    if ( !(A && typeof A === "object") )
+        return A;
+    
+    if ( notWrapped(A.descriptors) )
+        return A.descriptors.apply(A, slice.call(arguments,1));
+    
+    return core.descriptors.apply(this, arguments);
+}
 
-exports['set']     = set;
-exports['attr']    = attr;
-exports['extend']  = extend;
+exports['reduce']      = reduce;
+exports['map']         = map;
+exports['forEach']     = forEach;
+exports['filter']      = filter;
 
-exports['accessors'] = accessors;
+exports['set']         = set;
+exports['attr']        = attr;
+exports['extend']      = extend;
 
+exports['accessors']   = accessors;
+exports['descriptors'] = descriptors;
diff --git a/src/Y/internal.cjs b/src/Y/internal.cjs
new file mode 100644 (file)
index 0000000..1486e1a
--- /dev/null
@@ -0,0 +1,11 @@
+var
+Blank =
+exports['Blank'] =
+function Blank(v){ this.value = v; };
+
+exports['_'] = _ =
+exports['BLANK'] = BLANK = new Blank();
+
+exports['break_'] =
+function break_(v){ return new Blank(v); };
+
index f4a8733..8b6fc0a 100644 (file)
@@ -81,6 +81,13 @@ YCollection.subclass('YArray', function(YArray){
         return ( L ? A[L-1] : undefined );
     };
     
+    this['clear'] =
+    function clear(){
+        var A = this._o;
+        while (A.length) A.pop();
+        return this;
+    };
+    
     function unwrapY(o){
         return (o instanceof YArray ? o.end() : o);
     }
diff --git a/src/Y/types/chain.cjs b/src/Y/types/chain.cjs
new file mode 100644 (file)
index 0000000..98fc812
--- /dev/null
@@ -0,0 +1,8 @@
+var YCollection = require('Y/types/collection').YCollection
+,   YFunction   = require('Y/types/function').YFunction
+,   core        = require('Y/core')
+,   del         = require('Y/delegate')
+,   op          = require('Y/op')
+,   type        = require('Y/type')
+;
+
index beb799e..8611eb7 100644 (file)
@@ -1,14 +1,11 @@
-/**
- * A generic collection (which also provides the majority of core functionality for YObject).
- * Specializations are provided by subclasses. All subclasses must implement reduce().
- */
-
 var YBase  = require('Y/class').YBase
 // ,   type   = require('Y/type')
 ,   core   = require('Y/core')
 ,   del    = require('Y/delegate')
 ,   op     = require('Y/op')
 ,   slice  = core.slice
+
+,   internal = require('Y/internal')
 ;
 
 
@@ -17,58 +14,94 @@ function extendY(){
     return this;
 }
 
+/**
+ * A generic collection (which also provides the majority of core functionality for YObject).
+ * Specializations are provided by subclasses. 
+ * 
+ * All subclasses must implement `reduce()`.
+ * 
+ * There are several optional core methods for customization and optimization. By default:
+ *  - `attr()` calls `Y.delegate.attr()` on the wrapped object, `this._o`. If the result is not the
+ *      wrapped object, it is returned, otherwise `this` is returned.
+ *  - `clone()` invokes the constructor with nothing, and then calls `extend()` on the new object passing itself.
+ *  - `end()` returns this._o and does nothing else.
+ *  - `remove()` must iterate the whole collection to ensure all values are removed.
+ *  - `find()` is a variant of reduce that uses the break-wrapper `Y.internal.break_()`, as a sentinel to stop iteration.
+ */
+var YCollection =
 exports['YCollection'] =
 YBase.subclass('YCollection', {
-    'init' : function(o){
+    'init' : function initYCollection(o){
         this._o = o || {};
     },
     
-    'attr'   : function attr(k, v, def){
+    'end' : function end(){ return this._o; },
+    
+    'clone' : function clone(){
+        return new this.constructor().extend(this);
+    },
+    
+    'attr' : function attr(k, v, def){
         var r = del.attr(this._o, k, v, def);
         if (r === this._o)
             return this;
         else
             return r;
     },
-    'extend' : extendY,
-    'merge'  : extendY,
-    'concat' : extendY,
     
-    'end' : function end(){ return this._o; },
+    /**
+     * Removes all instances of the given values from the collection.
+     * 
+     * @param {Any...} value Value(s) to be removed from the collection.
+     * @return {this}
+     */
+    'remove' : function remove(v){
+        var o = this._o
+        ,   values = slice.call(arguments,0);
+        this.forEach(function(v, k){
+            if ( values.indexOf(v) !== -1 )
+                delete o[k];
+        });
+        return this;
+    },    
+    
     
     'reduce' : function reduce( fn, acc, context ){
-        var o = this._o || this,
-            acc = acc || new o.constructor();
-        for ( var name in o )
-            acc = fn.call( context || this, acc, o[name], name, o );
-        return acc;
+        throw new UnimplementedError("Reduce is unimplemented!");
     },
     
     'map' : function map( fn ){
-        var o = this._o
-        ,   cxt = arguments[1] || this
-        ,   acc = new o.constructor()
-        ;
-        for ( var name in o )
-            acc[name] = fn.call(cxt, o[name], name, o);
-        return acc;
+        var cxt = arguments[1] || this;
+        return this.reduce(function(acc, v, k, o){
+            var _v = fn.call(cxt, v, k, o);
+            return acc.attr(k, _v);
+        }, new this.__class__() );
     },
     
     'forEach' : function forEach( fn, context ){
-        var o = this._o, cxt = arguments[1] || this;
-        for ( var name in o )
-            fn.call(cxt, o[name], name, o);
+        this.reduce(function(cxt, v, k, o){
+            fn.call(cxt, v, k, o);
+        }, arguments[1] || this);
         return this;
     },
     
     'filter' : function filter( fn, context ){
-        var o = this._o, acc = new o.constructor();
-        for ( var name in o )
-            if ( fn.call( context || this, o[name], name, o ) )
-                acc[name] = o[name];
-        return acc;
+        var cxt = arguments[1] || this;
+        return this.reduce(function(acc, v, k, o){
+            if ( fn.call(cxt, v, k, o) )
+                acc.attr(k, v);
+            return acc;
+        }, new this.__class__() );
     },
     
+    // 'find' : function find( fn ){
+    //     var cxt = arguments[1] || this;
+    //     
+    // },
+    
+    /**
+     * FIXME
+     */
     'indexOf' : function indexOf( value ){
         var o = this._o;
         for ( var name in o )
@@ -81,41 +114,6 @@ YBase.subclass('YCollection', {
         return ( this.indexOf(value) !== -1 );
     },
     
-    'clone' : function clone(){
-        return new this.constructor().extend(this);
-    },
-    
-    // Dependence on YArray closes the loop
-    // 'remove' : function remove(v){
-    //     var o = this._o
-    //     ,   toRemove = new Y(arguments);
-    //     
-    //     for (var k in o) {
-    //         var v = o[k];
-    //         if ( toRemove.has(v) ) {
-    //             delete o[k];
-    //             toRemove.remove(v);
-    //         }
-    //     }
-    //     return this;
-    // },
-    
-    'remove' : function remove(v){
-        var o = this._o
-        ,   toRemove = slice.call(arguments);
-        
-        for (var k in o) {
-            var v = o[k], idx = toRemove.indexOf(v);
-            if ( idx !== -1 ) {
-                delete o[k];
-                toRemove.splice(idx,1);
-            }
-            if (!toRemove.length)
-                break;
-        }
-        return this;
-    },
-    
     'every' : function every( fn ){
         var self = this, fn = fn || op.bool;
         return this.reduce(function(acc, v, k, o){
@@ -131,7 +129,7 @@ YBase.subclass('YCollection', {
     },
     
     'zip' : function zip(){
-        var sequences = slice.call(arguments);
+        var sequences = slice.call(arguments,0);
         sequences.unshift(this);
         return this.map(function(_, k){
             return sequences.map(op.curried.kget(k));
@@ -141,10 +139,10 @@ YBase.subclass('YCollection', {
     'pluck' : function pluck(key){
         return this.map(function(v){
             return del.attr(v, key);
-            // return v && (type.isFunction(v.attr) ? v.attr(key) : v[key]);
         });
     },
     
+    // FIXME: del.map
     'invoke' : function invoke(name){
         var args = slice.call(arguments,1);
         return del.map(this, function(o){
@@ -152,6 +150,7 @@ YBase.subclass('YCollection', {
         });
     },
     
+    // FIXME: this._o[name].apply
     'apply' : function apply(name, args){
         return this[name].apply(this, args);
     }
index e11e31b..63ff66c 100644 (file)
@@ -10,7 +10,6 @@ var undefined
 ,   slice         = core.slice
 
 ,   YF  = YFunction
-,   _   = YF._ = {}
 ,   YFP = YF.prototype
 ;
 
@@ -284,4 +283,3 @@ function getName( fn ){
 
 // Export these last to avoid methodizing them
 exports['YFunction']  = YF(YF);
-exports['_'] = _;
index 63e89f7..b06d0e3 100644 (file)
@@ -117,14 +117,69 @@ YObject =
 exports['YObject'] =
 YCollection.subclass('YObject', {
     
-    init : function initYObject(o){
+    'init' : function initYObject(o){
         this._o = o || {};
     },
     
-    clone : function clone(){
+    'clone' : function clone(){
         return new YObject( deepcopy(this._o) );
+    },
+    
+    'remove' : function remove(v){
+        var o = this._o
+        ,   values = slice.call(arguments,0);
+        
+        for (var k in o) {
+            var v = o[k];
+            if ( values.indexOf(v) !== -1 )
+                delete o[k];
+        }
+        return this;
+    },
+    
+    
+    'reduce' : function reduce( fn, acc, context ){
+        var o = this._o || this,
+            acc = acc || new o.constructor();
+        for ( var name in o )
+            acc = fn.call( context || this, acc, o[name], name, o );
+        return acc;
+    },
+    
+    'map' : function map( fn ){
+        var o = this._o
+        ,   cxt = arguments[1] || this
+        ,   acc = new o.constructor()
+        ;
+        for ( var name in o )
+            acc[name] = fn.call(cxt, o[name], name, o);
+        return acc;
+    },
+    
+    'forEach' : function forEach( fn, context ){
+        var o = this._o, cxt = arguments[1] || this;
+        for ( var name in o )
+            fn.call(cxt, o[name], name, o);
+        return this;
+    },
+    
+    'filter' : function filter( fn, context ){
+        var o = this._o, acc = new o.constructor();
+        for ( var name in o )
+            if ( fn.call( context || this, o[name], name, o ) )
+                acc[name] = o[name];
+        return acc;
+    },
+    
+    'indexOf' : function indexOf( value ){
+        var o = this._o;
+        for ( var name in o )
+            if ( o[name] === value )
+                return name;
+        return -1;
     }
     
+    
 });
 
 
index 8acefdb..4339f9a 100644 (file)
@@ -36,7 +36,7 @@ YCollection.subclass('YString', function(YString){
         },
         
         'clone' : function clone(){
-            return YString(this._o);
+            return YString(this._o); // strings are immutable
         },
         
         'size' : size,
index b55e8b2..a27df65 100644 (file)
@@ -5,9 +5,14 @@ var YFunction  = require('Y/types/function').YFunction
 ,   slice      = core.slice
 ,   isFunction = type.isFunction
 ,   isArray    = type.isArray
-;
 
 
+,   UE =
+exports['UnimplementedError'] =
+function UnimplementedError(){ Error.call(this, arguments); }
+;
+UE.prototype = new Error('Unimplemented!');
+
 function bindName(k){
     var v = this[k];
     if ( isFunction(v) )
index fbf4d45..f0ebf8a 100644 (file)
@@ -19,7 +19,7 @@ Y.subclass('Cooldown', {
         return false;
     },
     
-    'tick' : function(elapsed){
+    'tick' : function(elapsed, now){
         if (this.ready) return true;
         
         this.elapsed += elapsed || 0;
index 6d2895f..5bc35dd 100644 (file)
@@ -17,7 +17,8 @@ Emitter.subclass('EventLoop', {
     framerate : 0, // Target framerate
     frametime : 0, // 1000 / framerate
     
-    now       : 0, // Last tick time (ms)
+    clock     : 0, // Subjective clock
+    now       : 0, // Last tick time (real ms)
     fps       : 0, // Effective framerate
     ticks     : 0, // Number of ticks since start
     
@@ -55,6 +56,7 @@ Emitter.subclass('EventLoop', {
         this.stop();
         this.elapsedAtStop = 0;
         this.ticks = 0;
+        this.clock = 0;
         this.times = [];
         this.realtimes = [];
     },
@@ -97,12 +99,13 @@ Emitter.subclass('EventLoop', {
         this.now     = new Date().getTime();
         this.elapsed = this.now - lastTick;
         this.perceived = this.elapsed/this.timeDilation;
+        this.clock += this.perceived;
         
         clearTimeout(this.timer);
         this.timer = null;
         
         this.fire('tick', this, {
-            'now'     : this.now,
+            'now'     : this.clock,
             'elapsed' : this.perceived,
             'ticks'   : ++this.ticks
         });
index 14e3713..6f1b489 100644 (file)
@@ -1,9 +1,10 @@
+
+
 var Y = require('Y').Y
 ,   CAPACITY  = 8
 ,   REGION_ID = 0
 ,
 
-
 Region =
 exports['Region'] =
 Y.subclass('Region', {
@@ -13,11 +14,16 @@ Y.subclass('Region', {
         this.value = value;
     },
     
-    // Expects caller will have ordered x1 < x2, y1 < y2
-    overlaps : function overlaps(x1,y1, x2,y2){
-        return !( x1 >  this.x2 || y1 >  this.y2
-               || x2 <= this.x1 || y2 <= this.y1 );
-    },
+    /**
+     * Determines if the given Rect overlaps with this tree,
+     * including the top/left edges, but excluding the bottom/right.
+     */
+    overlaps : orderThen('_overlaps'),
+    
+    /**
+     * As overlaps, but presumes input is sorted x1 &lt; x2 and y1 &lt; y2.
+     */
+    _overlaps : overlaps,
     
     toString : function toString(){
         return this.className+'(id='+this.id+', rect='+rectToString(this)+', value='+this.value+')';
@@ -47,12 +53,17 @@ Y.subclass('QuadTree', {
         this.clear();
     },
     
-    // Expects caller will have ordered x1 < x2, y1 < y2
-    overlaps : function overlaps(x1,y1, x2,y2){
-        return !( x1 >  this.x2 || y1 >  this.y2
-               || x2 <= this.x1 || y2 <= this.y1 );
-    },
     
+    /**
+     * Determines if the given Rect overlaps with this tree,
+     * including the top/left edges, but excluding the bottom/right.
+     */
+    overlaps : orderThen('_overlaps'),
+    
+    /**
+     * As overlaps, but presumes input is sorted x1 &lt; x2 and y1 &lt; y2.
+     */
+    _overlaps : overlaps,
     
     // TODO: Add optional filter
     get : function get(x1,y1, x2,y2){
@@ -76,7 +87,7 @@ Y.subclass('QuadTree', {
     //     ,   x1 = Math.min(_x1,_x2), x2 = Math.max(_x1,_x2)
     //     ,   y1 = Math.min(_y1,_y2), y2 = Math.max(_y1,_y2) ;
     //     
-    //     if ( !self.overlaps(x1,y1, x2,y2) )
+    //     if ( !self._overlaps(x1,y1, x2,y2) )
     //         return acc;
     //     
     //     // Implies this is a leaf: check its values
@@ -137,36 +148,56 @@ Y.subclass('QuadTree', {
         });
     },
     
-    leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
-        var self = this
-        ,   cxt = context || self
-        ,   _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
-        ,   _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2) ;
-        
-        if ( !self.overlaps(_x1,_y1, _x2,_y2) )
+    /**
+     * @protected
+     * Invokes iterator on each matching subtree (passing the accumulator, the tree's regions, the tree, and the Rect query)
+     * to acquire the new accumulator:
+     * 
+     *      acc = fn.call(context||tree, acc, tree.regions, tree, query);
+     * 
+     * Where the Rect query is passed as an object with properties (x1,y1, x2,y2).
+     * 
+     * For more friendly iterators, useful to public callers, @see this.collect(), @see this.reduce().
+     */
+    leaves : orderThen('_leaves'),
+     
+    // leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
+    //     var _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
+    //     ,   _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2);
+    //     return this._leaves(_x1,_y1, _x2,_y2, fn, acc, context);
+    // },
+    
+    _leaves : function _leaves(x1,y1, x2,y2, fn, acc, context){
+        if ( !this._overlaps(x1,y1, x2,y2) )
             return acc;
         
         // Implies this is a leaf: check its values
-        if ( self.regions ) acc = fn.call(cxt, acc, self.regions, self, self);
+        if ( this.regions ) acc = fn.call(context || this, acc, this.regions, this);
         
         // Recurse into any children -- Note we do not place this in an else-block,
         // as mutation during the above call might cause the tree to split.
-        if (self.nw) acc = self.nw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
-        if (self.ne) acc = self.ne.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
-        if (self.sw) acc = self.sw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
-        if (self.se) acc = self.se.leaves(_x1,_y1, _x2,_y2, fn, acc, context);