Comment avoir des opérations avec caractère / items en binaire avec des opérations concrètes?

8

J'ai le prochain problème.

Un article peut avoir plusieurs états:

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

Un article peut avoir plusieurs états en même temps mais pas tous

  • Il est impossible d'être sec et humide en même temps.
  • Si vous COLD un élément MOUILLÉ, il devient FROZEN.
  • Si vous CHAUDEZ un élément MOUILLÉ, il devient NORMAL
  • Un article peut être BRÛLANT et POISON

Etc.

J'ai essayé de définir des drapeaux binaires sur des états et d'utiliser AND pour combiner différents états, en vérifiant avant s'il est possible ou non de le faire, ou en changeant de statut.

Existe-t-il une approche concrète pour résoudre ce problème efficacement sans avoir un commutateur interminable qui vérifie chaque état avec chaque nouvel état?

Il est relativement facile de vérifier 2 états différents, mais s'il existe un troisième état, ce n'est pas anodin à faire.

vgonisanz
la source
Je ne comprends pas pourquoi vous pensez qu'un 3ème état ne le rend pas aussi trivial qu'il devrait être aussi simple que d'en vérifier 2. Pouvez-vous poster un exemple de la façon dont vous le faites actuellement et un exemple utilisant 3 états.
John
Vous pouvez peut-être vérifier la meilleure solution pour résoudre la déclaration si elle a beaucoup d'états: stackoverflow.com/q/13385744/1077364
vgonisanz

Réponses:

6

Lorsque j'ai besoin d'utiliser des indicateurs, je fais généralement quelque chose dans ce sens.

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

Cela les rend très faciles à utiliser pour des trucs comme apply_cold () et vous pouvez évidemment construire dans vos conditions d'état comme sec et humide.

Feltope
la source
Vous pouvez peut-être vérifier la meilleure solution pour résoudre la déclaration si elle a beaucoup d'états: stackoverflow.com/q/13385744/1077364
vgonisanz
7

Deux observations:

  1. Votre système de condition semble avoir deux axes orthogonaux: la température et le poison. Représentez-les comme tels.
  2. En y réfléchissant, vous devez séparer les transitions des états . COLDet HOTsont des transitions dans la façon dont vous les mentionnez, pas des états.

La combinaison de ces observations aboutirait à quelque chose comme ceci:

// 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
la source
Le fait est que je vais ajouter plus d'états non orthogonaux, est-il possible de le faire? HOT est aussi un état, normal = 30 ºC, chaud = 70 ºC froid = 5 ºC. Mais si vous ajoutez de la chaleur et qu'il fait chaud, il se transforme en brûlure.
vgonisanz
Que diriez-vous de modéliser la température comme une valeur entière en degrés Celsius au lieu d'un booléen disant "chaud" ou "froid"?
Philipp
Bien sûr, vous pouvez ajouter plus d'états de température, comme vous le voyez dans ma réponse, j'ai déjà représenté l'état chaud. Ce que Philipp dit signifie voir chaque degré Celsius comme un état, ce qui est bien, mais notez que ce n'est peut-être pas ce que vous voulez du point de vue de la conception de jeux: plus de simulation n'implique pas un jeu plus profond, en soi.
Eric
3

Représentant vos états sous forme de masque binaire comme vous écrivez, vous pouvez simplement traduire vos descriptions des contraintes en code:

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

Vous pouvez envelopper cela dans un makeStateConsistent()que vous pouvez appeler avant de tester les bits d'état pour vous assurer que l'état a du sens.

Cependant, une limitation de cette approche est qu'elle ne peut pas rendre compte de l'ordre des changements d'état. Par exemple, si vous voulez avoir un résultat différent pour les éléments chauds qui deviennent humides que pour les éléments humides qui deviennent chauds, vous ne pouvez pas le faire comme ceci: tout ce que la makeStateConsistent()méthode voit est un objet chaud et humide, sans aucune information sur la façon dont ça doit être comme ça.

Au lieu de cela, ce que vous pouvez faire est de faire l'état de l' élément privé (au moins conceptuellement) et de le manipuler à travers un ensemble de méthodes comme coolItem(), heatItem(), wetItem(), dryItem()et ainsi de suite. De cette façon, les méthodes de changement d'état elles-mêmes peuvent prendre en charge tout changement supplémentaire. Par exemple, la heatItem()méthode pourrait ressembler à ceci:

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
}

Bien sûr, vous souhaiterez peut-être également avoir une makeStateConsistent()méthode en tant que sauvegarde, juste au cas où vous auriez un bogue dans vos méthodes de changement d'état.

De plus, dans certains cas, vous pouvez simplifier votre code en éliminant les états inutiles. Par exemple, avez-vous vraiment besoin d'un FROZENétat séparé , ou suffirait-il simplement de traiter les articles froids et humides comme congelés?

Ilmari Karonen
la source