How can I iterate a std::bitset in a circular fashion and return the index of the next 'true' bit?

StackOverflow https://stackoverflow.com/questions/22588059

  •  19-06-2023
  •  | 
  •  

سؤال

I use a std::bitset to track what weapons a player has in a game engine. The player should be able to cycle through the available weapons in a circular fashion. i.e. If the player has the last weapon selected then pressing the next weapon button would cycle back to the first. The player always has at least one weapon to choose from but could have any number of the other available weapons and not necessarily in sequential order.

However, my attempts to write a generic next_weapon and previous_weapon function have failed. I've reduced the problem to the following example with tests: Code at ideone

#include <cassert>
#include <cstddef>
#include <bitset>

using weapon_id = unsigned long long;

// Available weapons - reduced for brevity
// Ideally, adding a new weapon here wouldn't require editing anything below.
static const weapon_id WEAPON_HARSH_LANGUAGE = 0;
static const weapon_id WEAPON_SHARP_STICK =    1;
static const weapon_id WEAPON_KNIFE =          2;
static const weapon_id WEAPON_NUKE =           3;
static const std::size_t WEAPON_COUNT =        4; // Could potentially be > 64

using weapon_set = std::bitset< WEAPON_COUNT >;

// Select the next available weapon.
// If only one weapon is available, return that weapon.
// If the last weapon is currently selected then cycle back around to the first.
weapon_id next_weapon(const weapon_set& weapons, const weapon_id current_weapon)
{
    assert(weapons.any());

    if (weapons.count() == 1)
    {
        return current_weapon;
    }

    auto new_weapon = current_weapon;
    // Insert magic here.
    return new_weapon;
}

// Select the previous available weapon.
// If only one weapon is available, return that weapon.
// If the first weapon is currently selected then cycle back around to the last.
weapon_id prev_weapon(const weapon_set& weapons, const weapon_id current_weapon)
{
    assert(weapons.any());

    if (weapons.count() == 1)
    {
        return current_weapon;
    }

    auto new_weapon = current_weapon;
    // Insert magic here
    return new_weapon;
}

// Test cases I can think of. Have I missed any?
int main()
{
    // Test 1 - One weapon
    {
        weapon_set weapons;
        weapons.set(WEAPON_HARSH_LANGUAGE);
        assert(next_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_HARSH_LANGUAGE);
        assert(prev_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_HARSH_LANGUAGE);
    }

    // Test 2 - Two 'sequential' weapons
    {
        weapon_set weapons;
        weapons.set(WEAPON_SHARP_STICK);
        weapons.set(WEAPON_KNIFE);
        assert(next_weapon(weapons, WEAPON_SHARP_STICK) == WEAPON_KNIFE);
        assert(prev_weapon(weapons, WEAPON_SHARP_STICK) == WEAPON_KNIFE);
        assert(next_weapon(weapons, WEAPON_KNIFE) == WEAPON_SHARP_STICK);
        assert(prev_weapon(weapons, WEAPON_KNIFE) == WEAPON_SHARP_STICK);
    }

    // Test 3 - Two 'non sequential' weapons
    {
        weapon_set weapons;
        weapons.set(WEAPON_HARSH_LANGUAGE);
        weapons.set(WEAPON_KNIFE);
        assert(next_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_KNIFE);
        assert(prev_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_KNIFE);
        assert(next_weapon(weapons, WEAPON_KNIFE) == WEAPON_HARSH_LANGUAGE);
        assert(prev_weapon(weapons, WEAPON_KNIFE) == WEAPON_HARSH_LANGUAGE);
    }

    // Test 4 - All weapons
    {
        weapon_set weapons;
        weapons.set(WEAPON_HARSH_LANGUAGE);
        weapons.set(WEAPON_SHARP_STICK);
        weapons.set(WEAPON_KNIFE);
        weapons.set(WEAPON_NUKE);
        assert(next_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_SHARP_STICK);
        assert(prev_weapon(weapons, WEAPON_HARSH_LANGUAGE) == WEAPON_NUKE);
        assert(next_weapon(weapons, WEAPON_NUKE) == WEAPON_HARSH_LANGUAGE);
        assert(prev_weapon(weapons, WEAPON_NUKE) == WEAPON_KNIFE);
    }

    return 0;
}

What should next_weapon and prev_weapon do to achieve the desired results and pass the tests?

(If you can think of a more appropriate title for this question please feel free to make an edit or leave a comment.)

هل كانت مفيدة؟

المحلول

First magic:

do {
    new_weapon = (new_weapon + 1) % WEAPON_COUNT;
} while (new_weapon != current_weapon && !weapons[new_weapon]);

Second magic:

do {
    new_weapon = (new_weapon == 0 ? WEAPON_COUNT : new_weapon) - 1;
} while (new_weapon != current_weapon && !weapons[new_weapon]);

نصائح أخرى

You can use a circular_buffer class from boost: http://www.boost.org/doc/libs/1_55_0/doc/html/circular_buffer.html

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top