Como ter operações com caracteres / itens em binário com operações concretas?

8

Eu tenho o próximo problema.

Um item pode ter muitos estados:

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

Um item pode ter alguns estados ao mesmo tempo, mas nem todos

  • É impossível estar seco e molhado ao mesmo tempo.
  • Se você FRIO um item MOLHADO, ele se transforma em CONGELADO.
  • Se você aquece um item MOLHADO, ele se transforma em NORMAL
  • Um item pode ser QUEIMADOR e VENENO

Etc.

Tentei definir sinalizadores binários para estados e use AND para combinar estados diferentes, verificando antes se é possível ou não fazê-lo ou mude para outro status.

Existe uma abordagem concreta para resolver esse problema de maneira eficiente, sem ter uma opção interminável que verifique cada estado com cada novo estado?

É relativamente fácil verificar dois estados diferentes, mas se existe um terceiro estado, não é trivial.

vgonisanz
fonte
Não entendo por que você acha que um terceiro estado não o torna tão trivial quanto deveria ser tão simples quanto a verificação 2. Você pode postar um exemplo de como você está fazendo isso atualmente e um exemplo usando três estados.
John
Talvez você possa verificar a melhor solução para resolver a declaração se ele tem um monte de estados: stackoverflow.com/q/13385744/1077364
vgonisanz

Respostas:

6

Quando preciso usar sinalizadores, geralmente faço algo nesse 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; 
}

Isso torna muito fácil usá-los para coisas como apply_cold () e você pode obviamente criar em seu estado condições como seco e úmido.

Feltope
fonte
Talvez você possa verificar a melhor solução para resolver a declaração se ele tem um monte de estados: stackoverflow.com/q/13385744/1077364
vgonisanz
7

Duas observações:

  1. Seu sistema de condição parece ter dois eixos ortogonais: temperatura e veneno. Representá-los como tal.
  2. Ao pensar sobre isso, você deve separar as transições dos estados . COLDe HOTsão transições da maneira que você as menciona, não estados.

A combinação dessas observações resultaria em algo assim:

// 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
fonte
O ponto é, vou adicionar mais estados não ortogonais, é possível fazer? HOT também é um estado normal = 30 ºC, quente = 70 ºC frio = 5 ºC. Mas se adicionar calor e estiver quente, é provável que se transforme em queima.
vgonisanz
Que tal modelar a temperatura como um valor inteiro em graus Celsius, em vez de um booleano dizendo "quente" ou "frio"?
Philipp
É claro que você pode adicionar mais estados de temperatura, como pode ver na minha resposta, na verdade já representei o estado quente. O que Philipp diz significa ver todos os graus Celsius como um estado, o que é bom, embora observe que isso pode não ser o que você quer da perspectiva do design de jogos: mais simulações não implicam um jogo mais profundo, por si só.
Eric
3

Representando seus estados como máscara de bits como você escreve, você pode apenas traduzir suas descrições das restrições em 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...

Você pode agrupar isso em um makeStateConsistent()que você pode chamar antes de testar os bits de estado para garantir que o estado faça sentido.

No entanto, uma limitação dessa abordagem é que ela não pode explicar a ordem das mudanças de estado. Por exemplo, se você deseja obter um resultado diferente para itens quentes que ficam úmidos do que para itens quentes que ficam quentes, não é possível fazer o seguinte: tudo o makeStateConsistent()que o método vê é um objeto quente e úmido, sem informações sobre como tem que ser assim.

Em vez disso, o que você poderia fazer é tornar o estado do item privado (pelo menos conceitualmente) e manipulá-lo através de um conjunto de métodos como coolItem(), heatItem(), wetItem(), dryItem()e assim por diante. Dessa forma, os próprios métodos de mudança de estado podem cuidar de quaisquer alterações adicionais. Por exemplo, o heatItem()método pode ser algo como isto:

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
}

Obviamente, você ainda pode querer ter um makeStateConsistent()método como backup, caso tenha algum erro nos métodos de alteração de estado.

Além disso, em alguns casos, você pode simplificar seu código eliminando estados desnecessários. Por exemplo, você realmente precisa de um FROZENestado separado ou seria suficiente apenas tratar itens frios e molhados como congelados?

Ilmari Karonen
fonte