Two Guys Arguing

mario math – part 2

Posted in java, mario, math by youngnh on 08.14.09

To calculate Mario’s position with KEY_RIGHT pressed down continuously for n frames:

let a = 0.6

at frame 1:
\begin{align} & v_1 = a \\ & p_1 = a \end{align}

at frame 2:
\begin{align} & v_2 = a(.89) + a \\ & p_2 = a + [a(.89) + a] \end{align}

at frame 3:
\begin{align} & v_3 = (a(.89) + a).89 + a \\ & p_3 = a + [a(.89) + a] + [(a(.89) + a).89 + a] \end{align}

at frame 4:
\begin{align} & v_4 = ((a(.89) + a).89 + a).89 + a \\ & p_4 = a + [a(.89) + a] + [(a(.89) + a).89 + a] + [((a(.89) + a).89 + a).89 + a] \end{align}

If you multiply everything through, a pattern starts to emerge:
\begin{align} p_4 & = a + a(.89) + a + a(.89)^2 + a(.89) + a + a(.89)^3 + a(.89)^2 + a(.89) + a \\ & = 4a + 3a(.89) + 2a(.89)^2 + a(.89)^3 \end{align}

So we can say that in general, Mario’s position after n frames will be:
p_n = \sum_{i=0}^n(n-i)a(.89)^i

But we’d like a formula we can evaluate in O(1), so massaging the equation a bit we get:
\begin{align} p_n & = a * \sum_{i=0}^n(n-i)(.89)^i \\ & = a * \sum_{i=0}^n n(.89)^i - i(.89)^i \\  & = a \left ( \sum_{i=0}^n n(.89)^i - \sum_{i=0}^n i(.89)^i \right ) \\ & = a \left ( n * \sum_{i=0}^n n(.89)^i - \sum_{i=0}^n i(.89)^i \right ) \end{align}

Both of sums in the above equation, we have identities for:
\sum_{i=0}^n x^i = \frac{1-x^{n+1}}{1-x}       and       \sum_{i=0}^n i x^i= \frac{x}{(1-x^2)}(x^n(n(x-1)-1)+1)

Of which the second one is kind of convoluted, but allows us to come up with:
\begin{align} p_n & = a \left ( n * \frac{1-(.89)^{n+1}}{1-(.89)} - \frac{.89}{(1-(.89)^2)}((.89)^n(n(.89-1)-1)+1) \right ) \\ & = a \left ( n * \frac{1-(.89)^{n+1}}{.11} - \frac{(.89)^{n+1}(-.11n-1)+.89}{.2079} \right ) \end{align}

Which can then write in Java as:

public float positionAfter(float accel, int nFrames) {
    float term1 = n * (1 - Math.pow(.89, nFrames + 1)) / .11;
    float term2 = (Math.pow(.89, nFrames + 1) * (-.11 - 1) + .89) / .2079;
    return a * (term1 - term2);
}

The (.89) term is friction (I think?) and on the ground the code uses that value. In the air, the dynamics and equations are the same, but use a different coefficient of friction: (.85). The initial acceleration of a jump is vastly different, though, so I’ll tackle that in the next Mario Math installment.

Tagged with: , , ,

mario math

Posted in java, mario, math by youngnh on 08.13.09

To calculate Mario’s velocity, with KEY_RIGHT pressed down continuously for n frames:

let a = 0.6 (1.2 if KEY_SPEED is held down as well)

at frame one, then, your velocity v_1 = a

frame 2: v_2 = a(.89) + a
frame 3: v_3 = (a(.89) + a).89 + a
frame 4: v_4 = ((a(.89) + a).89 + a).89 + a

which, if you multiply through, looks like this:

v_4 = a + a(.89) + a(.89)^2 + a(.89)^3

Which makes it a bit easier to generalize Mario’s velocity at any frame n:

v_n = \sum_{i=0}^{n-1}a(.89)^i

v_n = a * \sum_{i=0}^{n-1}(.89)^i

we know from paying attention in high school wikipedia that:

\sum_{i=0}^n x^i = \frac{1-x^{n+1}}{1-x}

however, our sequence only goes to n-1 frames. It’s missing the last term:

\left ( \sum_{i=0}^{n-1}x^i \right ) + x^n = \sum_{i=0}^n x^i = \frac{1-x^{n+1}}{1-x}

subtracting xn from both sides

\frac{1 - x^{n+1}}{1-x} - x^n = \frac{1-x^{n+1}}{1-x} - \frac{(1-x)}{(1-x)}x^n

= \frac{1-x^{n+1}-x^n+x^{n+1}}{1-x} = \frac{1-x^n}{1-x}

which we could have maybe figured out faster if we had remembered this identity:

\sum_{i=m}^n x^i = \frac{x^{n+1} - x^m}{x-1}

replacing the lower m with 0 and upper n with n-1 we get:

\sum_{i=0}^{n-1}x^i = \frac{x^{(n-1)+1}-x^0}{x-1}

=\frac{x^n-1}{x-1}

which multiplied by \left ( \frac{-1}{-1} \right ) is what we found earlier.

and so mario’s velocity after n frames of acceleration is:

v_n = a* \frac{1 - (.89)^n}{.11}

which can be calculated in constant time by this Java function:

public float velocityAfter(float accel, int nFrames) {
    return accel * ((1 - Math.pow(0.89, nFrames)) / 0.11);
}

Whew.

Thanks to Heath Borders and my co-blogger Ben for working this out on a whiteboard with me.

Tagged with: , , ,

tour de mario – jumping

Posted in ai, challenge, java, mario by youngnh on 08.10.09

My co-blogger Ben and I are working on an entry for the Mario AI Competition 2009. The idea is to develop a Java program that simulates button presses to guide Mario through randomly generated levels of increasing difficulty. The competition is aimed at exploring Artificial Intelligence methods in gaming, but before Ben and I can even get to that, I think a short tour of the code we will be working with is in order.

Jumping is key in Mario. Maybe more precisely, landing is key. Controlling where you land will stomp an enemy or clear a gap. Jumping is easy, landing is hard.

Since this is so integral, let’s take a look at the code that makes Mario tick.

The class ch.idsia.mario.engine.sprites.Mario controls Mario’s behavior.

The tick() method of Mario’s superclass, Sprite is called once for every frame of the game, 24/sec. tick() is pretty simple, it just calls move(), which Mario happens to implement.

The move() method has a lot of code in it — 224 lines — but at it’s core, it just sets the xa and ya variables — x-acceleration and y-acceleration — and then uses them to increment Mario’s current x and y position variables.

move() starts on line 156, but doesn’t start doing anything we’re interested in until line 211:

        float sideWaysSpeed = keys[KEY_SPEED] ? 1.2f : 0.6f;

Mario moves horizontally at a rate 0.6 per frame at normal speed and 1.2 when the SPEED key is pressed.

The jumping code starts on line 234. I’ve left out the conditions we don’t care about:

if(keys[KEY_JUMP] || ...) {
    ...
    if(onGround && mayJump) {
        xJumpSpeed = 0;
        yJumpSpeed = -1.9f;
        jumpTime = 7;
        ya = jumpTime * yJumpSpeed;
        onGround = false;
        sliding = false;
    }
    ...
    else if(jumpTime > 0) {
        xa += xJumpSpeed;
        ya = jumpTime * yJumpSpeed;
        jumpTime--;
    }
}

This is the heart of the jump. jumpTime is a frame counter variable, and a jump lasts 7 frames. I’m not positive, but I think that the y-origin is in the upper right of the screen, hence the negative yJumpSpeed. After 7 frames, jumpTime is reset to 0.

There are extra bits of code that limit Mario’s maximum acceleration to 8, cut Mario’s xa and ya to 85% of their value and every frame that Mario isn’t on the ground his y-acceleration is is decreased by 3.

        ya *= 0.85f;        
        if (!onGround) {
            ya += 3;
        }

But it’s enough to figure out where you’re going to land.

Update
I made a couple of mistakes, which are crossed out above, and besides the mention of sideWaysSpeed I didn’t mention anything about horizontal speeds. As you can see above, when you are in the air xJumpSpeed starts out as 0 and you don’t gain any acceleration from it for the duration of your jump. The code that sets your horizontal acceleration regardless of whether you are on the ground or not is at line 288:

        if (keys[KEY_RIGHT] && !ducking) {
            if (facing == -1) sliding = false;
            xa += sideWaysSpeed;
            if (jumpTime >= 0) facing = 1;
        }

which merely sets your acceleration depending on what direction you are pressing.

Tagged with: , , ,