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