Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hituziando/make_monster_params


https://github.com/hituziando/make_monster_params

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# make\_monster\_params

## Introduction

Monster parameters are one of the most important elements in an RPG. If the monsters are too strong, the game can become excessively difficult; if they are too weak, the game can become tedious. Adjusting monster parameters is key to maintaining a balance between fun and achievement.

Adjusting monster parameters is a process of repeated modification and test play to find the right values. For example, we test by increasing attack power, decreasing HP, etc. It is very difficult to do all of this by hand, so some technique is needed.

Therefore, in this paper, I will try to imitate an RPG battle system and use a **[Genetic Algorithm (GA)](https://en.wikipedia.org/wiki/Genetic_algorithm)** to find the parameters of a monster.

The implementation will be done in Rust, which is fast. I also implemented the algorithm in Python and compared the execution time.

## Monster Parameters and Battle System
### Definition of Monster Parameters

The definitions of monster parameters covered in this paper are as follows.

```
(max_hp, atk, def, luc, speed)
```

- max_hp: maximum HP
- atk: Attack Power
- def: defensive power
- luc: Luck. Probability of a critical hit.
- speed: Speed. The higher the speed, the earlier the player can act.

### Battle System Specifications

The battle system to be implemented in this paper is as follows.

- One-on-one battle between player and monster.
- Turn-based, with the player with the higher speed acting first.
- Only one action is possible: attack.
- Attacks always hit.
- The attacked party calculates the damage and subtracts it from the remaining HP.
- Conditions for victory or defeat:
- Defeat occurs when the player's remaining HP reaches 0 first.
- Victory is awarded when the monster's remaining HP is reduced to zero first.
- There are no attributes, weaknesses, or other factors.

### Damage Calculation Formula

Damage is calculated by the following formula.

```rust
let mut damage = attacker.atk - defender.def * 0.5;

// if it is a critical hit, double the damage
if is_critical {
damage = damage * 2.0;
}

damage = max(damage.floor(), 0);
```

## Designing a fitness function

The fitness function is a function used to evaluate the fitness of an individual.
In the problem addressed in this paper, the fitness is calculated by the following equation.

```rust
let hp_ratio = character.hp as f32 / character.max_hp as f32;

let score = if result == BattleResult::GameOver {
WORST_SCORE
} else if result == BattleResult::Draw {
WORST_SCORE - 1.0
} else if turn < TURN_MIN {
WORST_SCORE - 1.0
} else {
hp_ratio
};
```

The smaller the fitness, the better the fit, reflecting the following idea:

- The less HP the player has left, the better the fitness. In other words, the best fitness is when HP=1.

- When HP=0, the game is lost (GameOver), so the worst value ( `WORST_SCORE` ) is set. `WORST_SCORE` is a large enough value to set the problem.

- If both sides have strong defenses and no damage can be dealt, the game will enter an endless loop with no winner. If the upper limit of turns is reached and there is still no winner, the game is considered a draw (Draw) and `WORST_SCORE - 1` is set.

- The lower limit ( `TURN_MIN` ) is set for the number of turns so that the players can exchange attacks several times. If the number of turns is less than the lower limit, `WORST_SCORE - 1` is set.

## Implementation

- [Using Rust](https://github.com/HituziANDO/make_monster_params/tree/main/make_monster_params_rust)
- [Using Python](https://github.com/HituziANDO/make_monster_params/tree/main/make_monster_params_python)

## Result
### Configuration

Each setting value used in the experiment is as follows.

|Setting|Variable name|Value|
|:-:|:-:|:-:|
|Number of individuals|gene_num|50|
|Selection rate|selection_rate|30%|
|Mutation rate|mutation_rate|0.5|
|Maximum number of generations|generation_max|500,000|
|Minimum number of turns|turn_min|3|
|Maximum number of turns|turn_max|10|
|Lower limit of monster parameters|min_status| { max_hp=1 atk=1 def=0 luc=0 speed=1 }|
|Upper limit of monster parameters|max_status| { max_hp=1000 atk=1000 def=1000 luc=10 speed=255 }|
|Player character status|character_status|{ max_hp=200 atk=15 def=10 luc=0 speed=50 }|

The execution environment is as follows.

||Value|
|:-:|:-:|
|OS|macOS Sonoma 14.0|
|Model Name|MacBook Air|
|Chip|Apple M2|
|Memory|24 GB|

### Generated Monster Parameters (Output Solution)

The following are some examples of solutions generated by the GA.

```
fitness=0.01 { max_hp=36 atk=27 def=22 luc=6 speed=232 }
```

```
fitness=0.01 { max_hp=6 atk=71 def=26 luc=10 speed=52 }
```

### Comparison of Rust and Python execution times

The following is the result of 10 trials with the same settings and under the same environment.

||Rust|Python|
|:-:|:-:|:-:|
|Average execution time [seconds]|36.491|359.847|
|Coefficient of Variation (CV)|0.31|0.28|
|Minimum execution time [seconds]|19.969|244.065|
|Maximum execution time [seconds]|57.007|602.864|

The execution time of Python was about 10 times faster than Rust, indicating that Rust is significantly faster. The coefficients of variation for Rust and Python are close to each other, which means that the relative stability of the execution times of the two languages is about the same.