mirror of
https://github.com/mitch030504/AstroQuest-Frame.git
synced 2026-10-20 12:00:43 +02:00
ASTRO BOT's catapult shoots for a finger that was down for more than a quarter of a second and has been dragged over more than four tenths of the pad when it lifts (its SlingShot settings, and the code that reads them). A finger that comes down in the middle of the pad has half the pad to go: with the stick's full push worth 0.45 of it, anything short of a full push was not enough. The finger is now at the edge with the stick four fifths out, and a touch lasts 0.3 seconds at least, so a quick pull and release shoots as well. Issue #2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
191 lines
8.2 KiB
C++
191 lines
8.2 KiB
C++
// Checks the finger a stick moves over a touchpad (controllers that have no touchpad): that a
|
|
// stick let go lifts the finger where it was, not back at the centre it flies to, that a stick
|
|
// moved through its centre drags without lifting, and that a stick which drifts touches nothing.
|
|
//
|
|
// clang-cl /std:c++latest /EHsc -fuse-ld=lld /I shadps4-arm64-main/src
|
|
// tools/tests/stick_finger_test.cpp /Fe:build/tests/stick_finger_test.exe
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "input/stick_finger.h"
|
|
|
|
using Input::StickFinger;
|
|
|
|
static int failures = 0;
|
|
|
|
static void Check(bool ok, const std::string& what) {
|
|
std::printf("%s %s\n", ok ? "ok " : "FAIL", what.c_str());
|
|
failures += ok ? 0 : 1;
|
|
}
|
|
|
|
struct Run {
|
|
int touches{};
|
|
bool down{};
|
|
double touched_at{-1.0};
|
|
// Where the finger came down and where it lifted, the last time it did either.
|
|
float first_x{}, first_y{}, last_x{}, last_y{};
|
|
// The nearest and the furthest it got, and when it lifted.
|
|
float least_y{1.0f}, most_y{0.0f};
|
|
double lifted_at{-1.0};
|
|
};
|
|
|
|
/// Has a stick go where `at(seconds)` says, read `rate` times a second for `length` seconds.
|
|
static Run Play(double rate, double length,
|
|
const std::function<void(double, float&, float&)>& at) {
|
|
StickFinger finger;
|
|
Run run;
|
|
for (double time = 0.0; time < length; time += 1.0 / rate) {
|
|
float x = 0.0f, y = 0.0f;
|
|
at(time, x, y);
|
|
const StickFinger::Touch touch = finger.Update(time, x, y);
|
|
if (touch.down && !run.down) {
|
|
++run.touches;
|
|
run.touched_at = time;
|
|
run.first_x = touch.x;
|
|
run.first_y = touch.y;
|
|
}
|
|
if (touch.down) {
|
|
run.last_x = touch.x;
|
|
run.last_y = touch.y;
|
|
run.least_y = std::fmin(run.least_y, touch.y);
|
|
run.most_y = std::fmax(run.most_y, touch.y);
|
|
} else if (run.down) {
|
|
run.lifted_at = time;
|
|
}
|
|
run.down = touch.down;
|
|
}
|
|
return run;
|
|
}
|
|
|
|
/// From `from` to `to` between two moments, at rest before and after.
|
|
static float Ramp(double time, double begin, double end, float from, float to) {
|
|
if (time <= begin) {
|
|
return from;
|
|
}
|
|
if (time >= end) {
|
|
return to;
|
|
}
|
|
return from + (to - from) * static_cast<float>((time - begin) / (end - begin));
|
|
}
|
|
|
|
/// A stick let go at `when` from `from`: back at its centre within a few hundredths of a second.
|
|
static float LetGo(double time, double when, float from) {
|
|
return time < when ? from : from * std::exp(-static_cast<float>((time - when) / 0.008));
|
|
}
|
|
|
|
int main() {
|
|
for (const double rate : {72.0, 90.0, 120.0, 250.0}) {
|
|
const std::string hz = " (read " + std::to_string(static_cast<int>(rate)) + " times a second)";
|
|
|
|
// Pulled towards the player, held, let go: the catapult.
|
|
Run run = Play(rate, 1.2, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.5 ? Ramp(time, 0.1, 0.25, 0.0f, 1.0f) : LetGo(time, 0.5, 1.0f);
|
|
});
|
|
Check(run.touches == 1 && !run.down, "pulled and let go: one touch, lifted" + hz);
|
|
Check(std::abs(run.first_y - 0.5f) < 0.01f, "it comes down in the middle of the pad" + hz);
|
|
Check(run.last_y > 0.99f, "it lifts at the pad's near edge, not back at the centre" + hz);
|
|
Check(run.lifted_at > 0.5 && run.lifted_at < 0.62, "it lifts within a moment" + hz);
|
|
|
|
// Pulled and let go at once, without holding.
|
|
run = Play(rate, 1.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.2 ? Ramp(time, 0.1, 0.2, 0.0f, 1.0f) : LetGo(time, 0.2, 1.0f);
|
|
});
|
|
Check(run.touches == 1 && run.last_y > 0.9f, "pulled and let go at once" + hz);
|
|
Check(run.lifted_at - run.touched_at > 0.26, "and still down for a quarter of a second" + hz);
|
|
|
|
// Pulled four fifths of the way only: dragged far enough for the catapult all the same.
|
|
run = Play(rate, 1.2, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.5 ? Ramp(time, 0.1, 0.25, 0.0f, 0.8f) : LetGo(time, 0.5, 0.8f);
|
|
});
|
|
Check(run.touches == 1 && run.last_y - run.first_y > 0.45f,
|
|
"four fifths of the way is the whole way" + hz);
|
|
|
|
// Flicked away from the player.
|
|
run = Play(rate, 1.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.16 ? Ramp(time, 0.1, 0.16, 0.0f, -1.0f) : LetGo(time, 0.16, -1.0f);
|
|
});
|
|
// (Read seventy times a second, a flick is seen at four or five places: the furthest
|
|
// of them is where the finger lifts.)
|
|
Check(run.touches == 1 && run.last_y < 0.01f, "flicked forward: lifts at the far edge" + hz);
|
|
|
|
// Pushed forward, then pulled back through the centre in one go, then let go.
|
|
run = Play(rate, 1.5, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.4 ? Ramp(time, 0.1, 0.2, 0.0f, -1.0f)
|
|
: time < 0.8 ? Ramp(time, 0.4, 0.52, -1.0f, 1.0f)
|
|
: LetGo(time, 0.8, 1.0f);
|
|
});
|
|
Check(run.touches == 1, "through the centre without lifting" + hz);
|
|
Check(run.least_y < 0.01f && run.last_y > 0.99f, "dragged from the far edge to the near" + hz);
|
|
|
|
// The same, slowly: a third of a second from one side to the other.
|
|
run = Play(rate, 2.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.4 ? Ramp(time, 0.1, 0.2, 0.0f, -1.0f)
|
|
: time < 1.2 ? Ramp(time, 0.4, 0.75, -1.0f, 1.0f)
|
|
: LetGo(time, 1.2, 1.0f);
|
|
});
|
|
Check(run.touches == 1 && run.last_y > 0.99f, "slowly through the centre without lifting" + hz);
|
|
|
|
// Brought back by hand, slowly: the finger comes back with it and lifts near the middle.
|
|
run = Play(rate, 2.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = time < 0.5 ? Ramp(time, 0.1, 0.25, 0.0f, 1.0f) : Ramp(time, 0.5, 1.2, 1.0f, 0.0f);
|
|
});
|
|
Check(run.touches == 1 && run.last_y < 0.72f && run.most_y > 0.99f,
|
|
"brought back by hand: the finger comes back with it" + hz);
|
|
|
|
// Sideways, and round the rim: the finger follows.
|
|
run = Play(rate, 1.5, [](double time, float& x, float& y) {
|
|
const float out = time < 1.0 ? Ramp(time, 0.1, 0.2, 0.0f, 1.0f) : LetGo(time, 1.0, 1.0f);
|
|
const float angle = Ramp(time, 0.3, 0.9, 0.0f, 1.5708f);
|
|
x = out * std::cos(angle);
|
|
y = out * std::sin(angle);
|
|
});
|
|
Check(run.touches == 1 && std::abs(run.last_x - 0.5f) < 0.03f && run.last_y > 0.99f,
|
|
"round the rim from the right to the near edge" + hz);
|
|
|
|
// A stick that drifts: it never rests, and never touches.
|
|
run = Play(rate, 1.0, [](double, float& x, float& y) {
|
|
x = 0.32f;
|
|
y = 0.05f;
|
|
});
|
|
Check(run.touches == 0, "a stick that drifts touches nothing" + hz);
|
|
|
|
// Two pulls: two touches.
|
|
run = Play(rate, 2.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
const double in = std::fmod(time, 1.0);
|
|
y = in < 0.5 ? Ramp(in, 0.1, 0.25, 0.0f, 1.0f) : LetGo(in, 0.5, 1.0f);
|
|
});
|
|
Check(run.touches == 2, "pulled twice: two touches" + hz);
|
|
|
|
// Resting just short of where a touch begins, with a tremble: nothing.
|
|
run = Play(rate, 1.0, [](double time, float& x, float& y) {
|
|
x = 0.0f;
|
|
y = 0.2f + 0.03f * std::sin(static_cast<float>(time) * 80.0f);
|
|
});
|
|
Check(run.touches == 0, "a stick short of touching does not touch" + hz);
|
|
}
|
|
|
|
// A finger taken off by something else (a real one took over): the stick has to rest first.
|
|
StickFinger finger;
|
|
finger.Update(0.00, 0.0f, 0.0f);
|
|
Check(finger.Update(0.01, 0.0f, 0.9f).down, "touching");
|
|
finger.Reset();
|
|
Check(!finger.Update(0.02, 0.0f, 0.9f).down && !finger.Update(0.03, 0.0f, 0.9f).down,
|
|
"taken off: not touching again while the stick stays out");
|
|
finger.Update(0.04, 0.0f, 0.0f);
|
|
Check(finger.Update(0.05, 0.0f, 0.9f).down, "touching again after the stick has rested");
|
|
|
|
std::printf("failed: %d\n", failures);
|
|
return failures == 0 ? 0 : 1;
|
|
}
|