Skip to content

Commit

Permalink
Add experimental dynamic humanization
Browse files Browse the repository at this point in the history
  • Loading branch information
fs-c committed Mar 4, 2023
1 parent 8604e17 commit b2cdfd1
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 70 deletions.
2 changes: 1 addition & 1 deletion app/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void window::start(const std::function<void()> &body) {
WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL,
NULL, NULL, NULL, _T("maniac"), NULL};
::RegisterClassEx(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("maniac"), WS_OVERLAPPEDWINDOW, 100, 100, 450,
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("maniac"), WS_OVERLAPPEDWINDOW, 100, 100, 500,
300, NULL, NULL, wc.hInstance, NULL);

// Initialize Direct3D
Expand Down
42 changes: 40 additions & 2 deletions lib/humanization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void maniac::randomize(std::vector<osu::HitObject> &hit_objects, std::pair<int,
range.second);
}

void maniac::humanize(std::vector<osu::HitObject> &hit_objects, int modifier) {
void maniac::humanize_static(std::vector<osu::HitObject> &hit_objects, int modifier) {
if (!modifier) {
return;
}
Expand Down Expand Up @@ -57,5 +57,43 @@ void maniac::humanize(std::vector<osu::HitObject> &hit_objects, int modifier) {
}
}

debug("humanized %d hit objects (%d slices of %dms) with modifier %d", hit_objects.size(), slices.size(), slice_size, modifier);
debug("statically humanized %d hit objects (%d slices of %dms) with modifier %d", hit_objects.size(), slices.size(), slice_size, modifier);
}

void maniac::humanize_dynamic(std::vector<osu::HitObject> &hit_objects, int modifier) {
const auto actual_modifier = static_cast<double>(modifier) / 100.0;

constexpr auto max_delta = 1000;

for (int i = 0; i < hit_objects.size(); i++) {
auto &cur = hit_objects.at(i);

int start_density = 0;

for (int j = i - 1; j >= 0; j--) {
const auto pre = hit_objects.at(j);

if ((pre.start_time + max_delta > cur.start_time) || (pre.is_slider && pre.end_time + max_delta > cur.start_time)) {
start_density++;
}
}

cur.start_time += static_cast<int>(start_density * actual_modifier);

if (cur.is_slider) {
int end_density = 0;

for (int j = i - 1; j >= 0; j--) {
const auto pre = hit_objects.at(j);

if ((pre.start_time + max_delta > cur.end_time) || (pre.is_slider && pre.end_time + max_delta > cur.end_time)) {
end_density++;
}
}

cur.end_time += static_cast<int>(end_density * actual_modifier);
}
}

debug("dynamically humanized %d hit objects with modifier %d", hit_objects.size(), modifier);
}
66 changes: 66 additions & 0 deletions lib/include/maniac/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once

#include <maniac/common.h>

#include <utility>
#include <fstream>

namespace maniac {
struct config {
static constexpr auto STATIC_HUMANIZATION = 0;
static constexpr auto DYNAMIC_HUMANIZATION = 1;

int tap_time = 20;
bool mirror_mod = false;
int compensation_offset = -15;
int humanization_modifier = 0;
std::pair<int, int> randomization_range = { 0, 0 };
int humanization_type = DYNAMIC_HUMANIZATION;

// TODO: This isn't configurable yet, use a non-shit config format
std::string keys = "asdfjkl;";

// TODO: Would be good to have the read/write stuff be in a constructor/destructor

void read_from_file() {
std::fstream file{"maniac-config", std::fstream::binary | std::fstream::in};

if (!file.is_open()) {
debug("couldn't open config file for reading");

return;
}

file.read(reinterpret_cast<char *>(&tap_time), sizeof tap_time);
file.read(reinterpret_cast<char *>(&mirror_mod), sizeof mirror_mod);
file.read(reinterpret_cast<char *>(&compensation_offset), sizeof compensation_offset);
file.read(reinterpret_cast<char *>(&humanization_modifier), sizeof humanization_modifier);
file.read(reinterpret_cast<char *>(&randomization_range.first), sizeof randomization_range.first);
file.read(reinterpret_cast<char *>(&randomization_range.second), sizeof randomization_range.second);
file.read(reinterpret_cast<char *>(&humanization_type), sizeof humanization_type);

debug("loaded config from file");
}

void write_to_file() {
std::fstream file{"maniac-config", std::fstream::binary | std::fstream::trunc | std::fstream::out};

if (!file.is_open()) {
debug("couldn't open config file for writing");

return;
}

file.write(reinterpret_cast<char *>(&tap_time), sizeof tap_time);
file.write(reinterpret_cast<char *>(&mirror_mod), sizeof mirror_mod);
file.write(reinterpret_cast<char *>(&compensation_offset), sizeof compensation_offset);
file.write(reinterpret_cast<char *>(&humanization_modifier), sizeof humanization_modifier);
file.write(reinterpret_cast<char *>(&randomization_range.first), sizeof randomization_range.first);
file.write(reinterpret_cast<char *>(&randomization_range.second), sizeof randomization_range.second);
file.write(reinterpret_cast<char *>(&humanization_type), sizeof humanization_type);

debug("wrote config to file");
}
};
}

62 changes: 6 additions & 56 deletions lib/include/maniac/maniac.h
Original file line number Diff line number Diff line change
@@ -1,63 +1,12 @@
#pragma once

#include <vector>
#include <utility>
#include <maniac/osu.h>
#include <maniac/config.h>
#include <maniac/common.h>
#include <fstream>

namespace maniac {
struct config {
int tap_time = 20;
bool mirror_mod = false;
int compensation_offset = -15;
int humanization_modifier = 0;
std::pair<int, int> randomization_range = { 0, 0 };

// TODO: This isn't configurable yet, use a non-shit config format
std::string keys = "asdfjkl;";

// TODO: Would be good to have the read/write stuff be in a constructor/destructor

void read_from_file() {
std::fstream file{"maniac-config", std::fstream::binary | std::fstream::in};

if (!file.is_open()) {
debug("couldn't open config file for reading");

return;
}

file.read(reinterpret_cast<char *>(&tap_time), sizeof tap_time);
file.read(reinterpret_cast<char *>(&mirror_mod), sizeof mirror_mod);
file.read(reinterpret_cast<char *>(&compensation_offset), sizeof compensation_offset);
file.read(reinterpret_cast<char *>(&humanization_modifier), sizeof humanization_modifier);
file.read(reinterpret_cast<char *>(&randomization_range.first), sizeof randomization_range.first);
file.read(reinterpret_cast<char *>(&randomization_range.second), sizeof randomization_range.second);

debug("loaded config from file (%s)", keys.c_str());
}

void write_to_file() {
std::fstream file{"maniac-config", std::fstream::binary | std::fstream::trunc | std::fstream::out};

if (!file.is_open()) {
debug("couldn't open config file for writing");

return;
}

file.write(reinterpret_cast<char *>(&tap_time), sizeof tap_time);
file.write(reinterpret_cast<char *>(&mirror_mod), sizeof mirror_mod);
file.write(reinterpret_cast<char *>(&compensation_offset), sizeof compensation_offset);
file.write(reinterpret_cast<char *>(&humanization_modifier), sizeof humanization_modifier);
file.write(reinterpret_cast<char *>(&randomization_range.first), sizeof randomization_range.first);
file.write(reinterpret_cast<char *>(&randomization_range.second), sizeof randomization_range.second);

debug("wrote config to file");
}
};
#include <vector>

namespace maniac {
struct Action {
char key;
bool down;
Expand Down Expand Up @@ -89,12 +38,13 @@ namespace maniac {

inline osu::Osu *osu;

void play(const std::vector<Action> &&actions);
void play(const std::vector<Action> &actions);

void block_until_playing();

void humanize(std::vector<osu::HitObject> &hit_objects, int modifier);
void randomize(std::vector<osu::HitObject> &hit_objects, std::pair<int, int> range);
void humanize_static(std::vector<osu::HitObject> &hit_objects, int modifier);
void humanize_dynamic(std::vector<osu::HitObject> &hit_objects, int modifier);

std::vector<Action> to_actions(std::vector<osu::HitObject> &hit_objects, int32_t min_time);
}
11 changes: 3 additions & 8 deletions lib/include/maniac/osu.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace osu {
// TODO: Generic pointers are bad in the long run.
uintptr_t time_address = 0;
uintptr_t player_pointer = 0;
uintptr_t status_pointer = 0;

public:
Osu();
Expand Down Expand Up @@ -47,14 +48,8 @@ namespace osu {
}

inline bool Osu::is_playing() {
uintptr_t address = 0;
const auto status = read_memory<int>(status_pointer);

size_t read = read_memory<uintptr_t>(player_pointer, &address, 1);

if (!read) {
debug("%s %#x", "failed getting player address at", player_pointer);
}

return address != 0;
return status == internal::OSU_STATUS_PLAYING;
}
}
4 changes: 4 additions & 0 deletions lib/include/maniac/osu/internal.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

constexpr auto OSU_STATUS_IN_MENU = 0;
constexpr auto OSU_STATUS_PLAYING = 2;
constexpr auto OSU_STATUS_IN_SONG_SELECT = 5;

inline Process *process;

class hit_object {
Expand Down
6 changes: 3 additions & 3 deletions lib/maniac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace maniac {
}
}

void play(const std::vector<Action> &&actions) {
void play(const std::vector<Action> &actions) {
reset_keys();

size_t cur_i = 0;
Expand Down Expand Up @@ -65,9 +65,9 @@ namespace maniac {
hit_object.end_time = hit_object.start_time + config.tap_time;

actions.emplace_back(keys[hit_object.column], true,
hit_object.start_time + config.compensation_offset);
hit_object.start_time + config.compensation_offset);
actions.emplace_back(keys[hit_object.column], false,
hit_object.end_time + config.compensation_offset);
hit_object.end_time + config.compensation_offset);
}

debug("converted %d hit objects to %d actions", hit_objects.size(), actions.size());
Expand Down

0 comments on commit b2cdfd1

Please sign in to comment.