Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wesnoth crash during AI turn #4068

Closed
vgaming opened this issue May 9, 2019 · 5 comments
Labels
Milestone

Comments

@vgaming
Copy link
Member

@vgaming vgaming commented May 9, 2019

This was in the console:

wesnoth: /build/wesnoth/src/wesnoth/src/attack_prediction.cpp:1668: static void {anonymous}::monte_carlo_combat_matrix::scale_probabilities(const std::vector<double>&, std::vector<double>&, double, unsigned int): Assertion `std::abs(std::accumulate(target.begin(), target.end(), 0.0) - 1.0) < 0.001' failed.

wesnoth version 1.14.7

CC @jyrkive

It happen in CreepWars, leaders had about 200-300 HP at that moment and about 30-10 damage-strikes.

@vgaming vgaming added Bug AI labels May 9, 2019
@jyrkive

This comment has been minimized.

Copy link
Member

@jyrkive jyrkive commented May 10, 2019

I'm afraid that's not enough data for me to really figure out anything. I'd need a save file as near as possible before the crash.

@vgaming

This comment has been minimized.

Copy link
Member Author

@vgaming vgaming commented May 10, 2019

I wasn't sure it'll be needed so didn't attach from the beginning. Fixing the mistake, here's the savegame:

Creep Wars snow 1.5.1-Auto-Save36.zip

Crash happens if you load the savegame and end the turn.

@jyrkive

This comment has been minimized.

Copy link
Member

@jyrkive jyrkive commented May 10, 2019

Thanks. I plan to investigate the issue tomorrow.

@vgaming

This comment has been minimized.

Copy link
Member Author

@vgaming vgaming commented May 11, 2019

For possible future reference: to repro you must also have Ageless installed, and Monte Carlo AI algorithm enabled in Preferences.

jyrkive added a commit that referenced this issue May 11, 2019
The crash occurred if
* the AI simulated three fights for a unit in a row
* the unit was initially slowed
* the unit had a chance of killing the attacking unit in the first
fight, removing the slow status
* the second fight was NOT simulated with Monte Carlo mode...
* ...and the third fight was.

It was my mistake from commit d83e017. I hadn't understood the meaning
of those two function parameters correctly. They don't need to be set to
true if the target unit is already slowed... and, in fact, they must not
be set to true if the attacking unit doesn't slow.

What happened here is that the combat matrix thought that the attacker's
attack slowed the defending unit, placing the result of some of
attacker's hits in the wrong plane. Meanwhile the calculation about the
probability that the defending unit is slowed produced the correct
result. In the next battle, when Monte Carlo mode scaled the "not
slowed" HP distribution with the probability of not being slowed, it
detected the severe discrepancy (in the example case, probabilities only
added up to 12,5 % instead of 100 %) and crashed the game.

This commit fixes the issue by passing correct parameter values.
Fixes #4068.
@jyrkive jyrkive closed this in 77da93c May 11, 2019
@vgaming

This comment has been minimized.

Copy link
Member Author

@vgaming vgaming commented May 12, 2019

Thanks!

@sevu sevu added this to the 1.14.9 milestone Jun 9, 2019
stevecotton added a commit to stevecotton/wesnoth that referenced this issue Jul 2, 2019
There's still a possibility of triggering the assert in monte_carlo_combat_matrix::scale_probabilities(), even after the fix for wesnoth#4068.

Loading the attached savegame and ending turn will sometimes assert during the AI turn, but not always. It looks as if the crash happens when simulating a combat where the opponent is side 2's bat (which can slow), and bat might level-up. The attacker (on the time that I caught it in the debugger) is one of the Warrior Wargs.

This debug trace is with a build of 8844397, but with the spider-corpse commits (7a103d1, afd5a2d) reverted for unrelated reasons, and Ageless Era 4.24.0.

```
(gdb) p u_
$17 = (const battle_context_unit_stats &) @0x555557c193d0: {weapon = std::shared_ptr<const attack_type> (use count 4, weak count 1) = {get() = 0x7fffa299c620}, attack_num = 0,
  is_attacker = true, is_poisoned = false, is_slowed = false, slows = false, drains = false, petrifies = false, plagues = false, poisons = false, backstab_pos = true, swarm = false,
  firststrike = false, disable = false, experience = 4, max_experience = 75, level = 2, rounds = 1, hp = 126, max_hp = 149, chance_to_hit = 40, damage = 8, slow_damage = 4,
  drain_percent = 0, drain_constant = 0, num_blows = 4, swarm_min = 4, swarm_max = 4, plague_type = ""}
(gdb) p *(u_.weapon)
$18 = {<std::enable_shared_from_this<attack_type>> = {_M_weak_this = std::weak_ptr<attack_type> (use count 4, weak count 1) = {get() = 0x7fffa299c620}}, self_loc_ = {x = -1000, y = -1000},
  other_loc_ = {x = -1000, y = -1000}, self_ = {px = 0x0}, other_ = {px = 0x0}, is_attacker_ = false, other_attack_ = std::shared_ptr<const attack_type> (empty) = {get() = 0x0},
  is_for_listing_ = false, description_ = {val_ = std::shared_ptr<const t_string_base> (use count 1, weak count 0) = {get() = 0x55555c6460d0}}, id_ = "claws", type_ = "blade",
  icon_ = "attacks/sword-human.png", range_ = "melee", min_range_ = 1, max_range_ = 1, damage_ = 19, num_attacks_ = 4, attack_weight_ = 1, defense_weight_ = 1, accuracy_ = 0,
  movement_used_ = 100000, parry_ = 0, specials_ = {static invalid = {static invalid = <same as static member of an already seen type>,
      static diff_track_attribute = 0x55555684cd3b "__diff_track", values_ = std::map with 0 elements, children_ = std::map with 0 elements,
      ordered_children = std::vector of length 0, capacity 0}, static diff_track_attribute = 0x55555684cd3b "__diff_track", values_ = std::map with 0 elements,
    children_ = std::map with 0 elements, ordered_children = std::vector of length 0, capacity 0}}
(gdb) p opponent.u_
$19 = (const battle_context_unit_stats &) @0x55555abd3f80: {weapon = std::shared_ptr<const attack_type> (use count 9, weak count 1) = {get() = 0x7fffa2de6e90}, attack_num = 0,
  is_attacker = false, is_poisoned = false, is_slowed = false, slows = true, drains = false, petrifies = false, plagues = false, poisons = false, backstab_pos = false, swarm = false,
  firststrike = false, disable = false, experience = 266, max_experience = 269, level = 2, rounds = 1, hp = 39, max_hp = 295, chance_to_hit = 60, damage = 4, slow_damage = 2,
  drain_percent = 0, drain_constant = 0, num_blows = 4, swarm_min = 4, swarm_max = 4, plague_type = ""}
(gdb) p *(opponent.u_.weapon)
$20 = {<std::enable_shared_from_this<attack_type>> = {_M_weak_this = std::weak_ptr<attack_type> (use count 9, weak count 1) = {get() = 0x7fffa2de6e90}}, self_loc_ = {x = -1000, y = -1000},
  other_loc_ = {x = -1000, y = -1000}, self_ = {px = 0x0}, other_ = {px = 0x0}, is_attacker_ = false, other_attack_ = std::shared_ptr<const attack_type> (empty) = {get() = 0x0},
  is_for_listing_ = false, description_ = {val_ = std::shared_ptr<const t_string_base> (use count 1, weak count 0) = {get() = 0x7fffa5ca8310}}, id_ = "sxc_blessed_sword_102",
  type_ = "arcane", icon_ = "attacks/sword-holy.png", range_ = "melee", min_range_ = 1, max_range_ = 1, damage_ = 7, num_attacks_ = 4, attack_weight_ = 1, defense_weight_ = 1,
  accuracy_ = 0, movement_used_ = 100000, parry_ = 0, specials_ = {static invalid = {static invalid = <same as static member of an already seen type>,
      static diff_track_attribute = 0x55555684cd3b "__diff_track", values_ = std::map with 0 elements, children_ = std::map with 0 elements,
      ordered_children = std::vector of length 0, capacity 0}, static diff_track_attribute = 0x55555684cd3b "__diff_track", values_ = std::map with 0 elements,
    children_ = std::map with 2 elements = {["damage"] = std::vector of length 1, capacity 1 = {std::unique_ptr<config> = {Python Exception <class 'RecursionError'> maximum recursion depth exceeded while getting the str of an object:
get() = 0x7fffa151ebf0}},
      ["slow"] = std::vector of length 1, capacity 1 = {std::unique_ptr<config> = {Python Exception <class 'RecursionError'> maximum recursion depth exceeded while getting the str of an object:
get() = 0x7fffa18708f0}}}, ordered_children = std::vector of length 2, capacity 2 = {{pos = Python Exception <class 'RecursionError'> maximum recursion depth exceeded while getting the str of an object:

      {first = "damage", second = std::vector of length 1, capacity 1 = {std::unique_ptr<config> = {get() = 0x7fffa151ebf0}}}, index = 0}, {pos = Python Exception <class 'RecursionError'> maximum recursion depth exceeded while getting the str of an object:

      {first = "slow", second = std::vector of length 1, capacity 1 = {std::unique_ptr<config> = {get() = 0x7fffa18708f0}}}, index = 0}}}}
(gdb) bt
    #0  0x00007ffff48a07bb in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
    wesnoth#1  0x00007ffff488b535 in __GI_abort () at abort.c:79
    wesnoth#2  0x00007ffff488b40f in __assert_fail_base
        (fmt=0x7ffff49edee0 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x5555567c9bb8 "std::abs(std::accumulate(target.begin(), target.end(), 0.0) - 1.0) < 0.001", file=0x5555567c9b38 "src/attack_prediction.cpp", line=1668, function=<optimized out>) at assert.c:92
    wesnoth#3  0x00007ffff4899102 in __GI___assert_fail
        (assertion=0x5555567c9bb8 "std::abs(std::accumulate(target.begin(), target.end(), 0.0) - 1.0) < 0.001", file=0x5555567c9b38 "src/attack_prediction.cpp", line=1668, function=0x5555567c9ea0 <(anonymous namespace)::monte_carlo_combat_matrix::scale_probabilities(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> >&, double, unsigned int)::__PRETTY_FUNCTION__> "static void {anonymous}::monte_carlo_combat_matrix::scale_probabilities(const std::vector<double>&, std::vector<double>&, double, unsigned int)") at assert.c:101
    wesnoth#4  0x0000555555d22406 in (anonymous namespace)::monte_carlo_combat_matrix::scale_probabilities(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> >&, double, unsigned int) (source=std::vector of length 296, capacity 296 = {...}, target=std::vector of length 296, capacity 512 = {...}, divisor=1, singular_hp=39)
        at src/attack_prediction.cpp:1668
    wesnoth#5  0x0000555555d2177c in (anonymous namespace)::monte_carlo_combat_matrix::monte_carlo_combat_matrix(unsigned int, unsigned int, unsigned int, unsigned int, (anonymous namespace)::summary_t const&, (anonymous namespace)::summary_t const&, bool, bool, unsigned int, unsigned int, unsigned int, unsigned int, int, int, int, int, unsigned int, double, double, std::vector<(anonymous namespace)::combat_slice, std::allocator<(anonymous namespace)::combat_slice> >, std::vector<(anonymous namespace)::combat_slice, std::allocator<(anonymous namespace)::combat_slice> >, double, double)
        (this=0x55555b6ca950, a_max_hp=149, b_max_hp=295, a_hp=126, b_hp=39, a_summary=..., b_summary=..., a_slows=false, b_slows=true, a_damage=8, b_damage=4, a_slow_damage=4, b_slow_damage=2, a_drain_percent=0, b_drain_percent=0, a_drain_constant=0, b_drain_constant=0, rounds=1, a_hit_chance=0.40000000000000002, b_hit_chance=0.59999999999999998, a_split=std::vector of length 1, capacity 1 = {...}, b_split=std::vector of length 1, capacity 1 = {...}, a_initially_slowed_chance=0, b_initially_slowed_chance=0)
        at src/attack_prediction.cpp:1497
    wesnoth#6  0x0000555555d245b0 in (anonymous namespace)::complex_fight((anonymous namespace)::attack_prediction_mode, battle_context_unit_stats const&, battle_context_unit_stats const&, unsigned int, unsigned int, (anonymous namespace)::summary_t&, (anonymous namespace)::summary_t&, double&, double&, bool, std::vector<(anonymous namespace)::combat_slice, std::allocator<(anonymous namespace)::combat_slice> >, std::vector<(anonymous namespace)::combat_slice, std::allocator<(anonymous namespace)::combat_slice> >, double, double)
        (mode=(anonymous namespace)::attack_prediction_mode::monte_carlo_simulation, stats=..., opp_stats=..., strikes=4, opp_strikes=4, summary=..., opp_summary=..., self_not_hit=@0x7fffffff9ef8: 1, opp_not_hit=@0x7fffffff9ef0: 1, levelup_considered=true, split=std::vector of length 1, capacity 1 = {...}, opp_split=std::vector of length 1, capacity 1 = {...}, initially_slowed_chance=0, opp_initially_slowed_chance=0) at src/attack_prediction.cpp:2175
    wesnoth#7  0x0000555555d25101 in combatant::fight(combatant&, bool) (this=0x55555c1c2900, opponent=..., levelup_considered=true)
        at src/attack_prediction.cpp:2350
    wesnoth#8  0x0000555555c0ec7e in battle_context::simulate(combatant const*) (this=0x555557b412b0, prev_def=0x55555c1c2970)
        at src/actions/attack.cpp:407
    wesnoth#9  0x0000555555c0f147 in battle_context::get_attacker_combatant(combatant const*) (this=0x555557b412b0, prev_def=0x55555c1c2970)
        at src/actions/attack.cpp:461
    wesnoth#10 0x00005555560f1c2b in ai::attack_analysis::analyze(gamemap const&, unit_map&, ai::readonly_context const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, double)
        (this=0x7fffffffb1f0, map=..., units=..., ai_obj=..., dstsrc=std::multimap with 338 elements = {...}, srcdst=std::multimap with 338 elements = {...}, enemy_dstsrc=std::multimap with 128 elements = {...}, aggression=1) at src/ai/default/attack.cpp:141
    wesnoth#11 0x00005555560ec8b5 in ai::ai_default_rca::aspect_attacks_base::do_attack_analysis(map_location const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::multimap<map_location, map_location, std::less<map_location>, std::allocator<std::pair<map_location const, map_location> > > const&, std::array<map_location, 6ul> const&, bool*, std::vector<map_location, std::allocator<map_location> >&, std::vector<ai::attack_analysis, std::allocator<ai::attack_analysis> >&, ai::attack_analysis&, team const&) const
        (this=0x5555647a6190, loc=..., srcdst=std::multimap with 338 elements = {...}, dstsrc=std::multimap with 338 elements = {...}, fullmove_srcdst=std::multimap with 3436 elements = {...}, f
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.