mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Added non-mutating methods for player code
This commit is contained in:
parent
2539814687
commit
73e3afa5f8
2 changed files with 129 additions and 44 deletions
|
@ -5,10 +5,14 @@ class Vector
|
|||
for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
|
||||
do (name) ->
|
||||
Vector[name] = (a, b, useZ) ->
|
||||
a.copy()[name](b, useZ)
|
||||
a.copy()["#{name}Self"](b, useZ)
|
||||
for name in ['magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared']
|
||||
do (name) ->
|
||||
Vector[name] = (a, b, useZ) ->
|
||||
a[name](b, useZ)
|
||||
|
||||
isVector: true
|
||||
apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'rotate']
|
||||
apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'rotate', 'add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
|
||||
|
||||
constructor: (x=0, y=0, z=0) ->
|
||||
return new Vector x, y, z unless @ instanceof Vector
|
||||
|
@ -17,6 +21,74 @@ class Vector
|
|||
copy: ->
|
||||
new Vector(@x, @y, @z)
|
||||
|
||||
|
||||
# Mutating methods:
|
||||
|
||||
normalizeSelf: (useZ) ->
|
||||
m = @magnitude useZ
|
||||
@divideSelf m, useZ if m > 0
|
||||
@
|
||||
|
||||
normalize: (useZ) ->
|
||||
# Hack to detect when we are in player code so we can avoid mutation
|
||||
(if @__aetherAPIValue? then @copy() else @).normalizeSelf(useZ)
|
||||
|
||||
limitSelf: (max) ->
|
||||
if @magnitude() > max
|
||||
@normalizeSelf()
|
||||
@multiplySelf(max)
|
||||
else
|
||||
@
|
||||
|
||||
limit: (useZ) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).limitSelf(useZ)
|
||||
|
||||
subtractSelf: (other, useZ) ->
|
||||
@x -= other.x
|
||||
@y -= other.y
|
||||
@z -= other.z if useZ
|
||||
@
|
||||
|
||||
subtract: (other, useZ) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).subtractSelf(other, useZ)
|
||||
|
||||
addSelf: (other, useZ) ->
|
||||
@x += other.x
|
||||
@y += other.y
|
||||
@z += other.z if useZ
|
||||
@
|
||||
|
||||
add: (other, useZ) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).addSelf(other, useZ)
|
||||
|
||||
divideSelf: (n, useZ) ->
|
||||
[@x, @y] = [@x / n, @y / n]
|
||||
@z = @z / n if useZ
|
||||
@
|
||||
|
||||
divide: (n, useZ) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).divideSelf(n, useZ)
|
||||
|
||||
multiplySelf: (n, useZ) ->
|
||||
[@x, @y] = [@x * n, @y * n]
|
||||
@z = @z * n if useZ
|
||||
@
|
||||
|
||||
multiply: (n, useZ) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).multiplySelf(n, useZ)
|
||||
|
||||
# Rotate it around the origin
|
||||
# If we ever want to make this also use z: https://en.wikipedia.org/wiki/Axes_conventions
|
||||
rotateSelf: (theta) ->
|
||||
return @ unless theta
|
||||
[@x, @y] = [Math.cos(theta) * @x - Math.sin(theta) * @y, Math.sin(theta) * @x + Math.cos(theta) * @y]
|
||||
@
|
||||
|
||||
rotate: (theta) ->
|
||||
(if @__aetherAPIValue? then @copy() else @).rotateSelf(theta)
|
||||
|
||||
# Non-mutating methods:
|
||||
|
||||
magnitude: (useZ) ->
|
||||
sum = @x * @x + @y * @y
|
||||
sum += @z * @z if useZ
|
||||
|
@ -27,18 +99,6 @@ class Vector
|
|||
sum += @z * @z if useZ
|
||||
sum
|
||||
|
||||
normalize: (useZ) ->
|
||||
m = @magnitude useZ
|
||||
@divide m, useZ if m > 0
|
||||
@
|
||||
|
||||
limit: (max) ->
|
||||
if @magnitude() > max
|
||||
@normalize()
|
||||
return @multiply(max)
|
||||
else
|
||||
@
|
||||
|
||||
heading: ->
|
||||
-1 * Math.atan2(-1 * @y, @x)
|
||||
|
||||
|
@ -60,28 +120,6 @@ class Vector
|
|||
sum += dz * dz
|
||||
sum
|
||||
|
||||
subtract: (other, useZ) ->
|
||||
@x -= other.x
|
||||
@y -= other.y
|
||||
@z -= other.z if useZ
|
||||
@
|
||||
|
||||
add: (other, useZ) ->
|
||||
@x += other.x
|
||||
@y += other.y
|
||||
@z += other.z if useZ
|
||||
@
|
||||
|
||||
divide: (n, useZ) ->
|
||||
[@x, @y] = [@x / n, @y / n]
|
||||
@z = @z / n if useZ
|
||||
@
|
||||
|
||||
multiply: (n, useZ) ->
|
||||
[@x, @y] = [@x * n, @y * n]
|
||||
@z = @z * n if useZ
|
||||
@
|
||||
|
||||
dot: (other, useZ) ->
|
||||
sum = @x * other.x + @y * other.y
|
||||
sum += @z + other.z if useZ
|
||||
|
@ -89,7 +127,7 @@ class Vector
|
|||
|
||||
# Not the strict projection, the other isn't converted to a unit vector first.
|
||||
projectOnto: (other, useZ) ->
|
||||
other.copy().multiply(@dot(other, useZ), useZ)
|
||||
other.copy().multiplySelf(@dot(other, useZ), useZ)
|
||||
|
||||
isZero: (useZ) ->
|
||||
result = @x is 0 and @y is 0
|
||||
|
@ -101,13 +139,6 @@ class Vector
|
|||
result = result and @z is other.z if useZ
|
||||
result
|
||||
|
||||
# Rotate it around the origin
|
||||
# If we ever want to make this also use z: https://en.wikipedia.org/wiki/Axes_conventions
|
||||
rotate: (theta) ->
|
||||
return @ unless theta
|
||||
[@x, @y] = [Math.cos(theta) * @x - Math.sin(theta) * @y, Math.sin(theta) * @x + Math.cos(theta) * @y]
|
||||
@
|
||||
|
||||
invalid: () ->
|
||||
return (@x is Infinity) || isNaN(@x) || @y is Infinity || isNaN(@y) || @z is Infinity || isNaN(@z)
|
||||
|
||||
|
|
|
@ -27,3 +27,57 @@ describe 'Vector', ->
|
|||
v2 = v.copy()
|
||||
v2.rotate -0.0000001 * Math.PI
|
||||
expect(v.distance v2).toBeCloseTo 0
|
||||
|
||||
it 'has class methods equivalent to the instance methods', ->
|
||||
expectEquivalentMethods = (method, arg) ->
|
||||
v = new Vector 7, 7
|
||||
classResult = Vector[method](v, arg)
|
||||
instanceResult = v[method](arg)
|
||||
expect(classResult).toEqual instanceResult
|
||||
|
||||
expectEquivalentMethods 'add', new Vector 1, 1
|
||||
expectEquivalentMethods 'subtract', new Vector 3, 3
|
||||
expectEquivalentMethods 'multiply', 4
|
||||
expectEquivalentMethods 'divide', 2
|
||||
expectEquivalentMethods 'limit', 3
|
||||
expectEquivalentMethods 'normalize'
|
||||
expectEquivalentMethods 'rotate', 0.3
|
||||
expectEquivalentMethods 'magnitude'
|
||||
expectEquivalentMethods 'heading'
|
||||
expectEquivalentMethods 'distance', new Vector 2, 2
|
||||
expectEquivalentMethods 'distanceSquared', new Vector 4, 4
|
||||
expectEquivalentMethods 'dot', new Vector 3, 3
|
||||
expectEquivalentMethods 'equals', new Vector 7, 7
|
||||
expectEquivalentMethods 'copy'
|
||||
|
||||
it "doesn't mutate when in player code", ->
|
||||
expectNoMutation = (fn) ->
|
||||
v = new Vector 5, 5
|
||||
# player code detection hack depends on this property being != null
|
||||
v.__aetherAPIValue = {}
|
||||
v2 = fn v
|
||||
expect(v.x).toEqual 5
|
||||
expect(v).not.toBe v2
|
||||
|
||||
expectNoMutation (v) -> v.normalize()
|
||||
expectNoMutation (v) -> v.limit 2
|
||||
expectNoMutation (v) -> v.subtract new Vector 2, 2
|
||||
expectNoMutation (v) -> v.add new Vector 2, 2
|
||||
expectNoMutation (v) -> v.divide 2
|
||||
expectNoMutation (v) -> v.multiply 2
|
||||
expectNoMutation (v) -> v.rotate 0.5
|
||||
|
||||
it 'mutates when not in player code', ->
|
||||
expectMutation = (fn) ->
|
||||
v = new Vector 5, 5
|
||||
v2 = fn v
|
||||
expect(v.x).not.toEqual 5
|
||||
expect(v).toBe v2
|
||||
|
||||
expectMutation (v) -> v.normalize()
|
||||
expectMutation (v) -> v.limit 2
|
||||
expectMutation (v) -> v.subtract new Vector 2, 2
|
||||
expectMutation (v) -> v.add new Vector 2, 2
|
||||
expectMutation (v) -> v.divide 2
|
||||
expectMutation (v) -> v.multiply 2
|
||||
expectMutation (v) -> v.rotate 0.5
|
||||
|
|
Loading…
Reference in a new issue