Broken Circle: need feedback (and cheats)

@UltCombo Could I ask you try it now?

@Bryukh Yeah, with your newest patch the bombs trigger correctly when I jump. Howeverā€¦ game session.

#
# Exploit #1.3
#
hero.moveXY(130, 10)
hero.jumpTo(hero.pos.copy().add(Vector(5, 0)))
hero.moveXY(center.x, center.y)
2 Likes

How!? How are doing this!?!?

1 Like

Fixed. I increased the trap power. And radius.

Ahahah, I just enjoy finding creative solutions. :stuck_out_tongue_winking_eye:
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. :slight_smile:

1 Like

First of all I would like to say hello to you, @Bryukh checkiO fan here :slight_smile:

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! :slight_smile:

2 Likes

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 :slight_smile:

Tssss :wink: 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.

2 Likes

Hey @Bryukh. New Exploit! :smile:

Game session.

while True:
    flag = hero.findFlag("green")
    if flag:
        pos = flag.pos.copy()
        hero.removeFlag(flag)
        while hero.distanceTo(pos) > 0:
            hero.move(pos)
2 Likes

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.

2 Likes

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.

1 Like

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. :slight_smile:

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 onFirstFrame function. This works for the jumpTo method, but it did not work correctly for the cast functionā€”maybe cast has something special, like having a ā€œrecursiveā€ call to hero.cast which then runs the overridden function? Not sure.

  • I took a look at the refereeā€™s code. It is only checking the heroā€™s z coordinate 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()
1 Like

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.

1 Like

Now your exploits are more complex than the solution :wink:

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:

onFirstFrame

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.

2 Likes

Yep. I added ā€œblown()ā€ near ā€œwakeUpā€. And I reduced diffZ for jumps.

1 Like

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 :slight_smile:

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 castā€™s implementation.

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.

1 Like