Как иметь операции с символом / элементами в двоичном виде с конкретными операциями?

8

У меня следующая проблема.

Предмет может иметь много состояний:

NORMAL  = 0000000
DRY     = 0000001
HOT     = 0000010
BURNING = 0000100
WET     = 0001000
COLD    = 0010000
FROZEN  = 0100000
POISONED= 1000000

Элемент может иметь несколько состояний одновременно, но не все из них

  • Невозможно быть сухим и мокрым одновременно.
  • Если вы ХОЛОДНО промоклите предмет, он превращается в ЗАМОРОЖЕННЫЙ.
  • Если вы ГОРЯЧЕЕ ВЛАЖНОЕ изделие, оно превращается в нормальный
  • Предмет может быть ГОРЯЩИМ и ЯДНЫМ

И т.п.

Я попытался установить бинарные флаги для состояний и использовать AND для объединения различных состояний, прежде чем проверять, возможно ли это или нет, или переходить в другой статус.

Существует ли конкретный подход для эффективного решения этой проблемы без использования бесконечного переключателя, который проверяет каждое состояние с каждым новым состоянием?

Относительно легко проверить 2 разных состояния, но если существует третье состояние, это не тривиально.

vgonisanz
источник
Я не понимаю, почему вы думаете, что третье состояние делает его не таким тривиальным, как это должно быть так просто, как проверка 2. Можете ли вы опубликовать пример того, как вы это делаете в настоящее время, и пример с использованием трех состояний.
Джон
Может быть, вы можете проверить лучшее решение для решения декларации, если у нее много состояний: stackoverflow.com/q/13385744/1077364
vgonisanz

Ответы:

6

Когда мне нужно использовать флаги, я обычно делаю что-то подобное.

enum obj_state
{
    NORMAL      = 0x00000;
    DRY         = 0x00002;
    HOT         = 0x00004;
    BURNING     = 0x00008;
    WET         = 0x00010;
    COLD        = 0x00020;
    FROZEN      = 0x00040;
    POISONED    = 0x00080;
};

int objFlags;

void DryOn() { objFlags |= DRY; }
void HotOn() { objFlags |= HOT; }
// etc...

void DryOff() { if (FlagOn(DRY)) objFlags ^= DRY; }
void HotOff() { if (FlagOn(HOT)) objFlags ^= HOT; }
// etc...

bool isDryOn() { return FlagOn(DRY); }
bool isHotOn() { return FlagOn(HOT); }
// etc...


// If the given Bit is on this will return true.
bool FlagOn(obj_state s) { return (objFlags & s) == s; }

// returns -1 if failed, 1 if successful
int apply_cold(Object obj)
{
    if (obj.isWetOn())
    {
        obj.ColdOff();
        obj.WetOff();
        obj.FrozenOn();
    }
    else
        return -1;

    return 1;
}


//---------------------------------
// alt way of doing DryOn and WetOn
// since these conditions can not be
// active at the same time.
void DryOn() 
{ 
    if (isWetOn()) 
        return;
    else
        objFlags |= DRY; 
}

void WetOn() 
{ 
    if (isDryOn())
        return;
    else;
        objFlags |= WET; 
}

Это делает их использование для таких вещей, как apply_cold (), очень простым, и вы, очевидно, можете создавать условия в вашем состоянии, такие как dry и wet.

Feltope
источник
Может быть, вы можете проверить лучшее решение для решения декларации, если у нее много состояний: stackoverflow.com/q/13385744/1077364
vgonisanz
7

Два наблюдения:

  1. Ваша система условий, кажется, имеет две ортогональные оси: температура и яд. Представлять их как таковые.
  2. Размышляя об этом, вы должны отделить переходы от состояний . COLDи HOTпереходы в том, как вы их упоминаете, а не состояния.

Объединение этих наблюдений приведет к чему-то вроде этого:

// These is the representation of the two axes.
int temperature; // can be between -2 and +2, 0 is normal, 1 is hot, 2 is burning, -1 is cold, -2 is frozen
bool poisoned;

// These methods represent state transitions.
void applyHeat() {
    if ( temperature <= 2 ) {
        ++temperature;
    }
}

void applyCold() {
    if ( temperature >= -2 ) {
        --temperature;
    }
}

void applyPoison() {
    poisoned = true;
}

void removePoison() {
    poisoned = false;
}
Эрик
источник
Дело в том, что я добавлю больше состояний, а не ортогональных, это можно сделать? ГОРЯЧИЙ - это тоже состояние, нормальное = 30 ºC, горячее = 70 ºC, холодное = 5 ºC. Но если добавить тепла и жарко, то оно превращается в жжение.
vgonisanz
Как насчет того, чтобы смоделировать температуру как целое значение в градусах Цельсия вместо логического выражения «горячий» или «холодный»?
Филипп
Конечно, вы можете добавить больше температурных состояний, как вы видите в моем ответе, я фактически уже представлял горячее состояние. То, что говорит Филипп, означает видеть каждый градус Цельсия как состояние, и это хорошо, хотя обратите внимание, что это может быть не то, что вы хотите с точки зрения игрового дизайна: больше симуляции не влечет за собой более глубокого игрового процесса, как такового.
Эрик
3

Представляя ваши состояния в виде битовой маски, как вы пишете, вы можете просто перевести ваши описания ограничений в код:

if ( (state & HOT) && (state & COLD) ) {
    state &= ~HOT;
    state &= ~COLD;   // reset both HOT and COLD flags if both are set
}

if ( (state & COLD) && (state & WET) ) {
    state &= ~WET;    // cold items can't be wet
    state |= FROZEN;  // instead, they're frozen
}

if ( (state & HOT) && (state & WET) ) {
    state &= ~WET;    // hot and wet items dry up...
    state &= ~HOT;    // ...and cool down
}

// add other constraints here...

Вы можете заключить это в объект, makeStateConsistent()который вы можете вызвать перед проверкой битов состояния, чтобы убедиться, что состояние имеет смысл.

Однако одним из ограничений этого подхода является то, что он не может учитывать порядок изменений состояния. Например, если вы хотите получить другой результат для горячих предметов, которые становятся мокрыми, чем для мокрых предметов, которые становятся горячими, вы не можете сделать это следующим образом: все, что makeStateConsistent()видит метод - это горячий и мокрый объект, без информации о том, как так и должно быть.

Вместо того , что вы могли бы сделать , это государство пункт частным (по крайней мере , концептуально) и управлять ею с помощью набора методов , таких как coolItem(), heatItem(), wetItem(), dryItem()и так далее. Таким образом, сами методы изменения состояния могут позаботиться о любых дополнительных изменениях. Например, heatItem()метод может выглядеть примерно так:

if ( state & COLD ) {
    state &= ~COLD;    // cold items become normal temp when heated
    if ( state & FROZEN ) {
        state &= ~FROZEN;  // ...and melt if they were frozen
        state |= WET;
    }
} else if ( state & WET ) {
    state &= ~WET;    // wet items dry up when heated, stay normal temp
} else {
    state |= HOT;     // dry normal temp items become hot
}

Конечно, вы все еще можете иметь makeStateConsistent()метод в качестве резервной копии, на случай, если у вас есть ошибка в ваших методах изменения состояния.

Кроме того, в некоторых случаях вы можете упростить код за счет устранения ненужных состояний. Например, вам действительно нужно отдельное FROZENсостояние, или этого будет достаточно, чтобы относиться к холодным и влажным предметам как к замороженным?

Илмари Каронен
источник