Skip to content

Commit

Permalink
Adjust body part hit difficulty to make critical hits matter for NPCs (
Browse files Browse the repository at this point in the history
…cataclysmbnteam#3139)

* Adjust body part hit difficulty to make critical hits matter for NPCs

* Update creature_test.cpp

I hope I got the math right on this...

* There was no need to touch those

* style: format array

* test: refactor display and adjust values

---------

Co-authored-by: scarf <[email protected]>
  • Loading branch information
chaosvolt and scarf005 authored Sep 16, 2023
1 parent 409f819 commit 36f687b
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 35 deletions.
12 changes: 6 additions & 6 deletions data/json/body_parts.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"essential": true,
"hit_size": 6,
"hit_size_relative": [ 0, 2.33, 5.71 ],
"hit_difficulty": 1.35,
"hit_difficulty": 1.4,
"stylish_bonus": 3,
"hot_morale_mod": 2,
"cold_morale_mod": 2,
Expand All @@ -57,7 +57,7 @@
"main_part": "head",
"hit_size": 1,
"hit_size_relative": [ 0, 0.33, 0.57 ],
"hit_difficulty": 1.15,
"hit_difficulty": 1.2,
"stylish_bonus": 2,
"squeamish_penalty": 8,
"bionic_slots": 4
Expand Down Expand Up @@ -95,7 +95,7 @@
"opposite_part": "arm_r",
"hit_size": 9,
"hit_size_relative": [ 15, 20, 22.86 ],
"hit_difficulty": 0.95,
"hit_difficulty": 0.75,
"side": "left",
"hot_morale_mod": 0.5,
"cold_morale_mod": 0.5,
Expand All @@ -117,7 +117,7 @@
"opposite_part": "arm_l",
"hit_size": 9,
"hit_size_relative": [ 15, 20, 22.86 ],
"hit_difficulty": 0.95,
"hit_difficulty": 0.75,
"side": "right",
"hot_morale_mod": 0.5,
"cold_morale_mod": 0.5,
Expand Down Expand Up @@ -183,7 +183,7 @@
"opposite_part": "leg_r",
"hit_size": 9,
"hit_size_relative": [ 25, 12, 5.71 ],
"hit_difficulty": 0.975,
"hit_difficulty": 0.9,
"side": "left",
"stylish_bonus": 1,
"hot_morale_mod": 0.5,
Expand All @@ -206,7 +206,7 @@
"opposite_part": "leg_l",
"hit_size": 9,
"hit_size_relative": [ 25, 12, 5.71 ],
"hit_difficulty": 0.975,
"hit_difficulty": 0.9,
"side": "right",
"stylish_bonus": 1,
"hot_morale_mod": 0.5,
Expand Down
102 changes: 73 additions & 29 deletions tests/creature_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cstdlib>
#include <map>
#include <sstream>
#include <utility>

#include "creature.h"
Expand All @@ -10,76 +11,119 @@
#include "test_statistics.h"
#include "bodypart.h"
#include "rng.h"
namespace
{

using Weights = std::map<body_part, float>;
struct Expected {
Weights base, max;
};


const auto expected_smaller = Expected
{
Weights{
{ bp_torso, 20 }, { bp_head, 0 }, { bp_eyes, 0 }, { bp_mouth, 0 }, { bp_arm_l, 15 }, { bp_arm_r, 15 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 25 }, { bp_leg_r, 25 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
},
Weights{
{ bp_torso, 4960 }, { bp_head, 0 }, { bp_eyes, 0 }, { bp_mouth, 0 }, { bp_arm_l, 1143 }, { bp_arm_r, 1186 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 3844 }, { bp_leg_r, 3867 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
}
};

float expected_weights_base[][12] = { { 20, 0, 0, 0, 15, 15, 0, 0, 25, 25, 0, 0 },
{ 33.33, 2.33, 0.33, 0, 20, 20, 0, 0, 12, 12, 0, 0 },
{ 36.57, 5.71, .57, 0, 22.86, 22.86, 0, 0, 5.71, 5.71, 0, 0 }

const auto expected_same = Expected
{
Weights{
{ bp_torso, 33.33 }, { bp_head, 2.33 }, { bp_eyes, 0.33 }, { bp_mouth, 0 }, { bp_arm_l, 20 }, { bp_arm_r, 20 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 12 }, { bp_leg_r, 12 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
},
Weights{
{ bp_torso, 6513 }, { bp_head, 2928 }, { bp_eyes, 150 }, { bp_mouth, 0 }, { bp_arm_l, 1224 }, { bp_arm_r, 1235 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 1458 }, { bp_leg_r, 1492 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
}
};

float expected_weights_max[][12] = { { 2000, 0, 0, 0, 1191.49, 1191.49, 0, 0, 2228.12, 2228.12, 0, 0 },
{ 3333, 1167.77, 65.84, 0, 1588.66, 1588.66, 0, 0, 1069.50, 1069.50, 0, 0 },
{ 3657, 2861.78, 113.73, 0, 1815.83, 1815.83, 0, 0, 508.904, 508.904, 0, 0 }
const auto expected_larger = Expected
{
Weights{
{ bp_torso, 36.57 }, { bp_head, 5.71 }, { bp_eyes, 0.57 }, { bp_mouth, 0 }, { bp_arm_l, 22.86 }, { bp_arm_r, 22.86 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 5.71 }, { bp_leg_r, 5.71 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
},
Weights{
{ bp_torso, 5689 }, { bp_head, 5682 }, { bp_eyes, 221 }, { bp_mouth, 0 }, { bp_arm_l, 1185 }, { bp_arm_r, 1089 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 578 }, { bp_leg_r, 556 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
}
};

static void calculate_bodypart_distribution( const enum m_size asize, const enum m_size dsize,
const int hit_roll, float ( &expected )[12] )

void calculate_bodypart_distribution( const enum m_size attacker_size,
const enum m_size defender_size,
const int hit_roll, const Weights &expected )
{
INFO( "hit roll = " << hit_roll );
std::map<body_part, int> selected_part_histogram = {
auto selected_part_histogram = Weights{
{ bp_torso, 0 }, { bp_head, 0 }, { bp_eyes, 0 }, { bp_mouth, 0 }, { bp_arm_l, 0 }, { bp_arm_r, 0 },
{ bp_hand_l, 0 }, { bp_hand_r, 0 }, { bp_leg_l, 0 }, { bp_leg_r, 0 }, { bp_foot_l, 0 }, { bp_foot_r, 0 }
};

mtype atype;
atype.size = asize;
atype.size = attacker_size;
monster attacker;
attacker.type = &atype;
mtype dtype;
dtype.size = dsize;
dtype.size = defender_size;
monster defender;
defender.type = &dtype;

const int num_tests = 15000;

for( int i = 0; i < num_tests; ++i ) {
selected_part_histogram[defender.select_body_part( &attacker, hit_roll )]++;
const auto bp = defender.select_body_part( &attacker, hit_roll );
selected_part_histogram.at( bp )++;
}

float total_weight = 0.0;
for( float w : expected ) {
total_weight += w;
}
const float total_weight = std::accumulate( expected.begin(), expected.end(), 0.0f,
[]( float acc, const auto & p ) {
return acc + p.second;
} );

for( auto weight : selected_part_histogram ) {
INFO( body_part_name( weight.first ) );
const double expected_proportion = expected[weight.first] / total_weight;
CHECK_THAT( weight.second, IsBinomialObservation( num_tests, expected_proportion ) );
std::stringstream ss;
for( const auto &[bp, weight] : selected_part_histogram ) {
ss << body_part_name( bp ) << ": " << weight << ", ";
}
INFO( '{' << ss.str() << "}\n" );
for( const auto &[bp, weight] : selected_part_histogram ) {
const double expected_proportion = expected.at( bp ) / total_weight;
CHECK_THAT( weight, IsBinomialObservation( num_tests, expected_proportion ) );
}
}
} // namespace

TEST_CASE( "Check distribution of attacks to body parts for same sized opponents." )
{
rng_set_engine_seed( 4242424242 );

calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 0, expected_weights_base[1] );
calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 1, expected_weights_base[1] );
calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 100, expected_weights_max[1] );
calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 0, expected_same.base );
calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 1, expected_same.base );
calculate_bodypart_distribution( MS_SMALL, MS_SMALL, 100, expected_same.max );
}

TEST_CASE( "Check distribution of attacks to body parts for smaller attacker." )
{
rng_set_engine_seed( 4242424242 );

calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 0, expected_weights_base[0] );
calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 1, expected_weights_base[0] );
calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 100, expected_weights_max[0] );
calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 0, expected_smaller.base );
calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 1, expected_smaller.base );
calculate_bodypart_distribution( MS_SMALL, MS_MEDIUM, 100, expected_smaller.max );
}

TEST_CASE( "Check distribution of attacks to body parts for larger attacker." )
{
rng_set_engine_seed( 4242424242 );

calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 0, expected_weights_base[2] );
calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 1, expected_weights_base[2] );
calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 100, expected_weights_max[2] );
calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 0, expected_larger.base );
calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 1, expected_larger.base );
calculate_bodypart_distribution( MS_MEDIUM, MS_SMALL, 100, expected_larger.max );
}

0 comments on commit 36f687b

Please sign in to comment.