Feedback: Bombing run


#1

Very nice level! I’ve finished it without cheating and it was very pleasant.
Still, may be it would be nice to add to the description that the answer must be the number of degrees.


#2

Also, even if the line containing Vector.heading() is commented, the level code still detects it and considers it an error.


#3

I’m not sure what you mean, I can use Vector.heading() without problem.


#4


#5

Copying my comments from slack, for reference:

  • it’s not specified if the angle should be in degrees or radians
  • put some blocking terrain around the level, because you can walk off the screen
  • ban boss stars, hammers, gold wand, summon burl, summon skeleton, etc.
  • unnecessary ; in python sample code + # Find the vector of attack is written twice
  • nice twist: “using the vector class’s heading() is not allowed” :slight_smile:

#6

Can you show me how you’re able to use .heading()?


#7

Sure, here it is:

    if enemy:
        # Find the vector of attack
        diff = enemy.pos.subtract(self.pos)
        
        # Say the angle!
        self.say(Vector.heading(diff) * 180 / Math.PI)

And yeah, I guess I was supposed to do some more math in this level, right? This feels a bit like cheating, but good programmers make use of the tools they have at their disposal. :smile:

Perhaps you could replace the Vector.heading (static and instance methods) with a no-op or a function that throws when called in this level?


#8

I can’t do anything because it’s in glacier and I’m in dessert right now. But maybe you should temporarily give adventurers the items they need and an explanation. And yes, I’m an adventurer. How do i know its in glacier? the ice


#9

Got around to trying the level:
It was possible to slay the ogres and pass by using conventional fighting tactics (sword, bash, summon soldiers). Disabling item gold might help.

I got stuck while trying to find a legitimate solution. Then I checked this thread and realised that it was supposed to be in degrees. Definitely specify that unit (some people in some contexts use radians as default). I was also wondering if I had the wrong angle of attack. (Do I need to ‘calculate their angle of attack’ from my perspective, or is it their perspective or what?)

I also noticed:
vector.heading() results in a failure, but vector.heading( ) does not and can be used to pass the level. I’m guessing the code for that goal checks for an exact match of ‘heading()’ because commented out also triggers it.

The swooping griffin riders seems new and looks nice though.

Edit – My suggestions:
As of posting, nothing happens when you say the wrong angle. This makes it harder for the user to figure out what might be wrong.

  • The griffins should bomb for any angle. Saying the incorrect angle wastes time and won’t hit all the ogres, so they’ll be able to reach the player.
  • The ogres behind the brawler should be replaced with tougher ones.
  • Disable item gold to prevent summoning troops.
  • Add a picture to the guide that shows where the angle starts and the unit. For example:

#10

Just keep playing. If you can not reach the glacier then don’t test this level. You must be useful as an adventurer.
IMHO


#11

this is my code
# Incoming oscars! (That's military speak for ogres). # You will need to calculate their angle of attack. # Use that angle to command your Griffin Bombers! loop: enemy = self.findNearest(self.findEnemies()); flag = self.findFlag() if flag: self.pickUpFlag(flag) if self.canCast("raise-dead"): self.cast("raise-dead") elif enemy: distance = self.distanceTo(enemy) if self.canCast("drain-life", enemy) and distance <= 15: self.cast("drain-life", enemy) if self.canCast("summon-undead"): self.cast("summon-undead") if self.canCast("poison-cloud", enemy) and distance < 30: self.cast("poison-cloud", enemy) if self.canCast("fear", enemy) and distance < 25: self.cast("fear", enemy) if distance < self.throwRange - 5: self.attack(enemy) else: self.moveXY(50, 5) while True: enemy = self.findNearest(self.findEnemies()); if enemy: # Find the vector of attack vector = Math.atan2(enemy.pos.y, enemy.pos.x) # Find the vector of attack # Say the angle! self.say(vector)
i was using omarn with unholy tome V
and had 530 hp

is there any way that you can stop people from using weapons tomes, etc.


#12

Hey everyone,

I appreciate the feedback as always. It took a bit longer to get some time to work on the level, but, I’ve improved some things:

  1. The griffins will now attack any angle angle you say, to help with debugging. This will help guide players that aren’t using radians or using the wrong Math operation.
  2. Made it more clear that we want the answer in degrees. I wasn’t sure if I wanted the answer in degrees or radians, but degrees are more human-readable so I included some hints on how to do that.
  3. Added some collision boxes around the edges of the level (but not the top, as thats where ogres spawn.)
  4. Made all ogres into Brawlers, for those that want to brute force it with might (tsk, tsk.)
  5. Removed hammers and gold wand and similar weapons, also removed boss stars. We’re trying to teach you new concepts! Can’t have you just attacking your way through lessons.
  6. Included a diagram in the guide. I would’ve used yours, zuf, but negative y results in a negative angle, not a 180+ angle.
  7. Improved the check for heading(), but, it’s still very hacky. I hope to get an engine hook to disable heading natively (but that’s not in the cards atm.)

As always, thanks adventurers! You all contribute a lot to making sure the levels are great.


#13

Very nice work! :smile:

I see, it is possible to bypass the check using some uncommon code style—e.g. putting a space before the function call’s opening parenthesis: vector.heading (). Now you could think about not checking the parentheses at all and just look for a “heading” substring, but that is not enough either as one can do vector['head' + 'ing']() or vector['\150eading']() (both equivalent to vector.heading() in JavaScript). And this is still not accounting for false positives (heading() inside commented out code).

Isn’t it possible/viable to just replace the heading function in this level? I believe this should do the trick:

  onFirstFrame: ->
    Vector::heading = =>
      error = "Using the Vector class's heading() is not allowed on this level."
      hint = "Figure out the math behind heading() instead of using the prepackaged method!"
      aether = @hero.getAetherForMethod("plan")
      problem = aether.createUserCodeProblem type: 'runtime', error: {name: "used_vector_heading", message:error, hint:hint, toString: -> "heading()"}
      @hero.addAetherProblemForMethod problem, "plan"
      @world.setGoalState "no-heading", "failure"

This will make invoking the Vector class’s heading method just set the fail condition. As far as I can see, the Vector.heading(vectorInstance) static method internally calls vectorInstance.heading(), so my code above should take care of both method variants.

I believe a new worker is spawned every time you load a level, so replacing the global Vector method as I suggested shouldn’t be a problem. Correct me if I’m mistaken.


#14

Thanks UltCombo,

My first naive attempt was Vector.heading = -> return null, but that didn’t work.

I didn’t think, (or know) about :: being a way of accessing a object’s prototypes. Thanks for your help!

The level has been updated and calling Vector.heading in any manner now tosses an error.

Edit: Doesn’t work, whoops, gonna keep experimenting, but Vector.heading() is relied on all of the units as they move around (to get which direction they should face,) lemme debug further.


#15

Aw whoops, I forgot to test a working level solution before proposing that fix, my bad. :sweat_smile:

I’m not sure if we can discern whether it is user code or internal game code that is calling Vector.heading, can we? I will probably have some time to investigate it tonight.


#16

I’m wondering, isn’t there a way to hook into Aether’s compile step from the referee code? I was thinking about inserting a Vector class scoped inside the compiled code’s plan function (the function that wraps the entire user code), this way we could patch the user code’s Vector class’ heading method without messing with the global Vector methods.

Another way would be to use something similar to aether.replaceBuiltin, but I’m not entirely sure how that works—would it be possible/make sense to perform this operation from referee code, or do we need to hook into a pre-compile step for this to work?
/cc @nick

By the way, I see @Serg has updated the check for the heading function and now it uses a regex that catches the vast majority of exploit attempts, so it should good enough to ship. :smile:


#17

I like allowing your clever hacks to access the “disabled” property, and it’s much simpler to just have a decent regex to catch the obvious things, so even if we could make it work in Aether, I don’t think it would help much!


#19

can some one help me?
i can’t figure out how to say the angle and to divide the angle?
any help would be delightful.
:grin: