Search code examples
javamathschedule

How to make a (roulette) schedule slow down


I am trying to make a roulette (like a Wheel of Fortune kind of) that slows down smoothly (fastest it can go is once every 50 ms).

Right now I have it in a configuration like this:

  runs:
    - max-runs: 40
      default-tick-duration: 10
      runs:
        - first: 1
          last: 20
          tick-duration: 10
        - first: 20
          last: 30
          tick-duration: 15
        - first: 30
          last: 35
          tick-duration: 20
        - first: 35
          last: 37
          tick-duration: 25
        - first: 37
          last: 38
          tick-duration: 30
        - first: 38
          last: 39
          tick-duration: 35
        - first: 39
          last: 40
          tick-duration: 40

This is not smooth at all, but could be made smooth by trying a bunch of configurations and I am wondering if this would be possible using a modifier or something.

I am pretty sure I have seen something to do this in my math classes at some point, but I cannot get to it. The idea is just to have a configurable amount of max runs, for example 40, and the closer it gets to this, the slower it becomes (smoothly) for a roulette.

The language I am writing this in is Java and this is my current Task / Runnable (which runs every 50 ms):

package com.dbsoftwares.dangerwheel.wheel.hologram;

import com.dbsoftwares.dangerwheel.utils.objects.CircleColor;
import com.dbsoftwares.dangerwheel.utils.objects.WheelRun;
import com.dbsoftwares.dangerwheel.utils.objects.WheelRunData;
import com.dbsoftwares.dangerwheel.wheel.WheelCircle;
import com.dbsoftwares.dangerwheel.wheel.WheelManager;
import com.gmail.filoghost.holographicdisplays.api.Hologram;
import lombok.RequiredArgsConstructor;
import org.bukkit.scheduler.BukkitRunnable;

import java.util.List;

@RequiredArgsConstructor
public class HologramWheelTask extends BukkitRunnable {

    private final WheelManager manager;
    private final Hologram hologram;
    private final WheelCircle circle;
    private final WheelRunData runData;
    private final int startIdx;

    private int ticks = 0;
    private int runs = 0;

    @Override
    public void run() {
        if (ticks < calculateRequiredTimeForRun()) {
            ticks++;
            return;
        }

        ticks = 0;
        runs++;

        // execute roulette tick

        if (runs >= runData.getMaxRuns()) {
            // roulette finished
            cancel();
        }
    }

    private int calculateRequiredTimeForRun() {
        final WheelRun data = runData.getRuns()
                .stream()
                .filter(run -> runs >= run.getFirst() && runs < run.getLast())
                .findFirst()
                .orElse(null);

        if (data == null) {
            return runData.getDefaultDuration();
        }
        return data.getTickDuration();
    }

    private String buildLine(final List<CircleColor> colors) {
        final StringBuilder line = new StringBuilder();

        colors.forEach(color -> line.append(color.getAsText()));

        return line.toString();
    }
}

Solution

  • I think your approach is wrong in setting a target number of revolutions. Although it might be possible to work this out, it would be easier to start with a simple model of the wheel.

    I am not an expert, but I believe that there will be two component to the friction that is slowing the wheel down: a fixed amount of static friction independent of the speed of the wheel and a variable amount dependent on the speed of the wheel. Normally the variable amount will be proportional to the square of the speed.

    So you will have a formula:

    friction = a + b*(v*v)
    

    where a and b are some values that you can tune (probably a needs to be a lot larger than b) and v is the speed the wheel is rotating in some arbitrary units.

    If your frame-rate is sufficiently fast you could just try subtracting the friction from the speed each frame until the speed gets close enough to zero you can consider it stopped. Play around with a and b until it looks good.

    Otherwise you will have to do some integration or interpolation, which won't be pleasant, so try the first method first.

    If you are really interested in an accurate model of the roulette wheel (in abstract terms, not as code) I suggest to ask on physics stack exchange.