@UltCombo Could I ask you try it now?
# # Exploit #1.3 # hero.moveXY(130, 10) hero.jumpTo(hero.pos.copy().add(Vector(5, 0))) hero.moveXY(center.x, center.y)
How!? How are doing this!?!?
Fixed. I increased the trap power. And radius.
Ahahah, I just enjoy finding creative solutions.
I also enjoy reviewing code quality and security.
Yup, it seems good now. You know, to be 100% failproof you could’ve added a “No jumps” goal and fail it once you detect a jump (using the same logic for detecting jumps that you have just implemented).
In any case, I cannot see any obvious flaw now. Good job!
I got to rest, but maybe I’ll experiment a bit more tomorrow.
First of all I would like to say hello to you, @Bryukh checkiO fan here
Regarding the level - I completed it the same way as your picture above shows. I haven’t used isPathClear as I knew about the part of ignoring traps from previous levels already, instead I wrote a simple distance function and calculated when I can go to the inner circle(no vectors involved). Speaking of distance function - I’ve only used distanceTo so far, but I think it could be quite a good idea to have one mission teaching younger players about it. It was really enjoyable level, I appreciate it greatly, thank you!
Thanks. You are an uber QA dev.
Hm. It’s a good point. I’ll use that advice for next advanced level.
Thanks! Now I’m here
Tssss Yeah, I solved it with similar method. But first I tried to build the full map of zones and implement A* search. It was too long and required optimisations. The next one idea - to create extended analogue of
isPathClear with rectangles. And while I was writing that I found the simplest method to solve that puzzle.
Hey @Bryukh. New Exploit!
while True: flag = hero.findFlag("green") if flag: pos = flag.pos.copy() hero.removeFlag(flag) while hero.distanceTo(pos) > 0: hero.move(pos)
Heh. It’s interesting. Looks like the flag appears and disappears in the same frame and it’s not visible for referee in this case. But I think it can be checked in the non-existed thangs. Thank you again – it’s really useful for understanding of mechanincs.
Yes, I think you are right. From the observed behavior, we can determine that the game engine processes each frame in the following order:
handle input (flags) -> run player code -> run referee's `chooseAction` function
As an alternative approach, you could restrict the flags equipment for this level.
Hey, I was able to combine a few exploits and resurrect the jumping exploit. This is slightly more advanced than the previous exploits, so bear with me. I believe this exploration is useful for understanding the game mechanics as well.
Basically, here are the two exploits I’ve found:
Apparently, it is possible to get a reference to the original methods in the player code before you overwrite them in the referee’s
onFirstFramefunction. This works for the
jumpTomethod, but it did not work correctly for the
casthas something special, like having a “recursive” call to
hero.castwhich then runs the overridden function? Not sure.
I took a look at the referee’s code. It is only checking the hero’s
zcoordinate while the guards are asleep, so it is possible to jump freely after waking them up with an alternative method (e.g. flags).
Here’s my game session.
# # Exploit 1.4 # hero.originalJumpTo = hero.jumpTo center = Vector(68, 68) hero.moveXY(125, 65) hero.moveXY(116, 40) hero.moveXY(100, 22) for i in range(2): # Cancel inertia hero.moveXY(76, 12) while True: flag = hero.findFlag("green") if flag: # Wake up guards hero.wait(0.01) # Go, Go, GO! nextJump = Vector(74, 33.2) hero.originalJumpTo(center.copy().subtract(hero.pos).multiply(.5).add(hero.pos)) for i in range(2): # Move twice to disable inertia hero.moveXY(nextJump.x, nextJump.y) while not hero.isReady("jump"): hero.moveXY(78, 35.5) hero.moveXY(70, 33) nextJump = Vector(69, 53) hero.originalJumpTo(nextJump.copy().subtract(hero.pos).multiply(1.3).add(hero.pos)) for i in range(2): # Disable inertia hero.moveXY(nextJump.x, nextJump.y) while not hero.isReady("jump"): hero.moveXY(nextJump.x + 3, nextJump.y + 1) hero.moveXY(nextJump.x - 3, nextJump.y - 1) hero.originalJumpTo(center) hero.moveXY(center.x, center.y - 2) while True: hero.shield()
We’re trying to avoid it. It was a problem with my paranoia – I checked only existed flags (The good habit for common levels)
Fixed that one.
Now your exploits are more complex than the solution
Thanks. I’ll explore that trick. But it can be broken (exploit) already, because I add “explode” for flags.
It’s interesting. @nick could you say something about it? I thought
onFirstFrame are executed before the player code.
Heheh yeah, I was mostly exploring the engine and possible flaws in the referee logic. This last “exploit” was mostly to demonstrate a logic flaw in the referee code—checking hero’s
z only while guards are sleeping, given that they can be woken up by conditions other than changing the hero’s
z. In any case, it would be sturdier to check the hero’s
z and blow up the mines independent of the guards’ state. Or, always blow up the mines when guards wake up (i.e. all fail conditions trigger both guards and bombs), though that would make the guards redundant given the traps’ range.
The Wiki says:
This function happens exactly once, as well, but this time this happens after the hero is all set up and about to begin.
So I too would assume it runs before the player code gets to run. Let’s wait for @nick or someone else’s input on this then.
Yep. I added “blown()” near “wakeUp”. And I reduced
diffZ for jumps.
I left them as decorations.
I don’t remember exactly. I thought that
onFirstFrame was supposed to happen before player code started.
Hm. maybe some problems with “jumpTo”, because that hack doesn’t work for
cast. I used the same place and method to overwrite them. UltCombo has found an interesting puzzle for us
I don’t think the problem is specific to
jumpTo. Although the exploit does not quite work for
cast, it does make a clear difference in the observed behavior: without the exploit the traps are blown up in the first frame (as expected), while with the exploit they explode several frames after the
cast invocation. Hence why I speculated this difference to be due to
Well, I guess I may be able to create a new level as an isolated test case for this issue, mostly to exercise my level creation abilities as well.