¿Cómo tener operaciones con caracteres / ítems en binario con operaciones concretas?

8

Tengo el siguiente problema

Un artículo puede tener muchos estados:

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

Un elemento puede tener algunos estados al mismo tiempo pero no todos

  • Es imposible estar seco y mojado al mismo tiempo.
  • Si FRÍO un artículo MOJADO, se convierte en CONGELADO.
  • Si CALIENTAS un artículo MOJADO, se convierte en NORMAL
  • Un artículo puede estar ENCENDIDO y ENVENENADO

Etc.

He intentado establecer indicadores binarios en estados y usar AND para combinar diferentes estados, comprobando antes si es posible o no hacerlo, o cambiar a otro estado.

¿Existe un enfoque concreto para resolver este problema de manera eficiente sin tener un interruptor interminable que verifique cada estado con cada nuevo estado?

Es relativamente fácil verificar 2 estados diferentes, pero si existe un tercer estado, no es trivial.

vgonisanz
fuente
No entiendo por qué piensas que un tercer estado hace que no sea tan trivial como debería ser tan simple como marcar 2. ¿Puedes publicar un ejemplo de cómo lo estás haciendo actualmente y un ejemplo usando 3 estados?
John
Tal vez pueda verificar la mejor solución para resolver la declaración si tiene muchos estados: stackoverflow.com/q/13385744/1077364
vgonisanz

Respuestas:

6

Cuando necesito usar banderas, generalmente hago algo en este sentido.

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; 
}

Esto hace que usarlos para cosas como apply_cold () sea muy fácil y obviamente puede construir en condiciones de estado como seco y húmedo.

Feltope
fuente
Tal vez pueda verificar la mejor solución para resolver la declaración si tiene muchos estados: stackoverflow.com/q/13385744/1077364
vgonisanz
7

Dos observaciones:

  1. Su sistema de condición parece tener dos ejes ortogonales: temperatura y veneno. Representarlos como tal.
  2. Al pensar en esto, debe separar las transiciones de los estados . COLDy HOTson transiciones en la forma en que las mencionas, no estados.

La combinación de esas observaciones daría como resultado algo como esto:

// 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;
}
Eric
fuente
El punto es que agregaré más estados, no ortogonales, ¿es posible hacerlo? CALIENTE también es un estado, normal = 30 ºC, caliente = 70 ºC frío = 5 ºC. Pero si agrega calor y está caliente, se supone que se convierte en ardor.
vgonisanz
¿Qué tal si modela la temperatura como un valor entero en grados Celsius en lugar de un booleano que dice "caliente" o "frío"?
Philipp
Por supuesto, puede agregar más estados de temperatura, como puede ver en mi respuesta, en realidad ya he representado el estado caliente. Lo que dice Philipp significa ver cada grado Celsius como un estado, lo cual está bien, aunque tenga en cuenta que esto puede no ser lo que desea desde la perspectiva del diseño del juego: una mayor simulación no implica un juego más profundo, per se.
Eric
3

Al representar sus estados como máscara de bits como usted escribe, puede traducir sus descripciones de las restricciones en código:

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...

Puede envolverlo en un makeStateConsistent()que puede llamar antes de probar los bits de estado para asegurarse de que el estado tenga sentido.

Sin embargo, una limitación de este enfoque es que no puede dar cuenta del orden de los cambios de estado. Por ejemplo, si desea obtener un resultado diferente para los artículos calientes que se humedecen que para los artículos húmedos que se calientan, no puede hacerlo así: todo lo makeStateConsistent()que ve el método es un objeto caliente y húmedo, sin información sobre cómo Tiene que ser así.

En su lugar, lo que podría hacer es que el estado del elemento privada (al menos conceptualmente) y manipularlo a través de un conjunto de métodos como coolItem(), heatItem(), wetItem(), dryItem()y así sucesivamente. De esa manera, los métodos de cambio de estado pueden encargarse de cualquier cambio adicional. Por ejemplo, el heatItem()método podría verse así:

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
}

Por supuesto, es posible que también desee tener un makeStateConsistent()método como copia de seguridad, en caso de que tenga un error en sus métodos de cambio de estado.

Además, en algunos casos, puede simplificar su código eliminando estados innecesarios. Por ejemplo, ¿realmente necesita un FROZENestado separado , o sería suficiente para tratar cualquier artículo frío y húmedo como congelado?

Ilmari Karonen
fuente