Skip to content

Commit

Permalink
Fix native randomization opcodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
MiranDMC committed Nov 29, 2024
1 parent aba9aa8 commit 58eb42f
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
- new opcode **2304 ([get_script_filename](https://library.sannybuilder.com/#/sa/file/2304))**
- new [Math](https://github.com/cleolibrary/CLEO5/tree/master/cleo_plugins/Math) plugin
- math related opcodes moved from CLEO core into separated plugin
- fixed poor randomization of **0208 ([generate_random_float_in_range](https://library.sannybuilder.com/#/sa/default/0208))** and **0209 ([generate_random_int_in_range](https://library.sannybuilder.com/#/sa/default/0209))**
- fixed invalid result range of **0098 ([generate_random_float](https://library.sannybuilder.com/#/sa/default/0098))**
- new opcode **2700 ([is_bit_set](https://library.sannybuilder.com/#/sa/math/2700))**
- new opcode **2701 ([set_bit](https://library.sannybuilder.com/#/sa/math/2701))**
- new opcode **2702 ([clear_bit](https://library.sannybuilder.com/#/sa/math/2702))**
Expand Down
54 changes: 54 additions & 0 deletions cleo_plugins/Math/Math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class Math
}

// register opcodes

// reimplemented original (bugged) game's random opcodes
CLEO_RegisterOpcode(0x0098, opcode_0098); // generate_random_float
CLEO_RegisterOpcode(0x0208, opcode_0208); // generate_random_float_in_range
CLEO_RegisterOpcode(0x0209, opcode_0209); // generate_random_int_in_range

CLEO_RegisterOpcode(0x0A8E, opcode_0A8E); // x = a + b (int)
CLEO_RegisterOpcode(0x0A8F, opcode_0A8F); // x = a - b (int)
CLEO_RegisterOpcode(0x0A90, opcode_0A90); // x = a * b (int)
Expand Down Expand Up @@ -50,6 +56,54 @@ class Math
CLEO_RegisterOpcode(0x2704, opcode_2704); // is_truthy
}

//0098=1,generate_random_float
static OpcodeResult WINAPI opcode_0098(CRunningScript* thread)
{
uniform_real_distribution<float> dist(0.0f, std::nextafterf(1.0f, FLT_MAX)); // includes max value
auto value = dist(Instance.randomGenerator);

OPCODE_WRITE_PARAM_FLOAT(value);
return OR_CONTINUE;
}

//0208=3,generate_random_float_in_range min %1d% max %2d% store_to %3d%
static OpcodeResult WINAPI opcode_0208(CRunningScript* thread)
{
auto valMin = OPCODE_READ_PARAM_FLOAT();
auto valMax = OPCODE_READ_PARAM_FLOAT();

if (valMax < valMin)
{
SHOW_ERROR("Argument 'min' (%f) greater than 'max' (%f) in script %s\nScript suspended.", valMin, valMax, ScriptInfoStr(thread).c_str());
return thread->Suspend();
}

uniform_real_distribution<float> dist(valMin, std::nextafterf(valMax, FLT_MAX)); // includes max value
auto value = dist(Instance.randomGenerator);

OPCODE_WRITE_PARAM_FLOAT(value);
return OR_CONTINUE;
}

//0209=3,generate_random_int_in_range min %1d% max %2d% store_to %3d%
static OpcodeResult WINAPI opcode_0209(CRunningScript* thread)
{
auto valMin = OPCODE_READ_PARAM_INT();
auto valMax = OPCODE_READ_PARAM_INT();

// may happen when user thinks about input params as unsigned hex
if (valMax < valMin)
{
swap(valMin, valMax);
}

uniform_int_distribution<int> dist(valMin, valMax);
auto value = dist(Instance.randomGenerator);

OPCODE_WRITE_PARAM_INT(value);
return OR_CONTINUE;
}

//0A8E=3,%3d% = %1d% + %2d% ; int
static OpcodeResult WINAPI opcode_0A8E(CRunningScript* thread)
{
Expand Down
42 changes: 42 additions & 0 deletions tests/cleo_tests/Math/0098.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{$CLEO .s}
{$INCLUDE_ONCE ../cleo_tester.inc}

script_name '0098'
test("0098 (generate_random_float)", tests)
terminate_this_custom_script


function tests
it("should generate random floats", test1)
return

function test1
float min = 999.0
float max = -999.0
float avg = 0.0

int i
for i = 1 to 1000
float value = generate_random_float

if
value < min
then
min = value
end

if
value > max
then
max = value
end

avg += value
end
avg /= 1000.0

assert_rangef(min, 0.00, 0.05)
assert_rangef(max, 0.95, 1.00)
assert_rangef(avg, 0.40, 0.60) // expected value of uniform distribution
end
end
41 changes: 41 additions & 0 deletions tests/cleo_tests/Math/0208.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{$CLEO .s}
{$INCLUDE_ONCE ../cleo_tester.inc}

script_name '0208'
test("0208 (generate_random_float_in_range)", tests)
terminate_this_custom_script

function tests
it("should generate random floats in range", test1)
return

function test1
float min = 999.0
float max = -999.0
float avg = 0.0

int i
for i = 1 to 2000
float value = generate_random_float_in_range {min} -10.0 {max} 40.0

if
value < min
then
min = value
end

if
value > max
then
max = value
end

avg += value
end
avg /= 2000.0

assert_rangef(min, -10.0, -9.0)
assert_rangef(max, 39.0, 40.0)
assert_rangef(avg, 14.0, 16.0) // expected value of uniform distribution
end
end
41 changes: 41 additions & 0 deletions tests/cleo_tests/Math/0209.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{$CLEO .s}
{$INCLUDE_ONCE ../cleo_tester.inc}

script_name '0209'
test("0209 (generate_random_int_in_range)", tests)
terminate_this_custom_script

function tests
it("should generate random ints in range", test1)
return

function test1
int min = 999
int max = -999
int avg = 0

int i
for i = 1 to 2000
int value = generate_random_int_in_range {min} -10 {max} 40

if
value < min
then
min = value
end

if
value > max
then
max = value
end

avg += value
end
avg /= 2000

assert_eq(min, -10)
assert_eq(max, 40)
assert_range(avg, 14, 16) // expected value of uniform distribution
end
end

0 comments on commit 58eb42f

Please sign in to comment.