Wie kann man Operationen mit Zeichen / Elementen in Binärform mit konkreten Operationen durchführen?

8

Ich habe das nächste Problem.

Ein Gegenstand kann viele Zustände haben:

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

Ein Gegenstand kann einige Zustände gleichzeitig haben, aber nicht alle

  • Es ist unmöglich, gleichzeitig trocken und nass zu sein.
  • Wenn Sie einen NASS-Gegenstand KALTEN, wird er zu GEFROREN.
  • Wenn Sie ein WET-Element HEISSEN, wird es zu NORMAL
  • Ein Gegenstand kann BRENNEN und GIFT sein

Usw.

Ich habe versucht, binäre Flags auf Zustände zu setzen und AND zu verwenden, um verschiedene Zustände zu kombinieren, bevor ich prüfe, ob dies möglich ist oder nicht, oder um in einen anderen Status zu wechseln.

Gibt es einen konkreten Ansatz, um dieses Problem effizient zu lösen, ohne einen endlosen Schalter zu haben, der jeden Zustand mit jedem neuen Zustand überprüft?

Es ist relativ einfach, zwei verschiedene Zustände zu überprüfen, aber wenn es einen dritten Zustand gibt, ist dies nicht trivial.

vgonisanz
quelle
Ich verstehe nicht, warum Sie denken, dass ein dritter Zustand es nicht so trivial macht, wie es so einfach sein sollte wie das Überprüfen von 2. Können Sie ein Beispiel dafür veröffentlichen, wie Sie es derzeit tun, und ein Beispiel mit drei Zuständen.
John
Vielleicht können Sie die beste Lösung überprüfen, um die Deklaration zu lösen, wenn sie viele Zustände hat: stackoverflow.com/q/13385744/1077364
vgonisanz

Antworten:

6

Wenn ich Flags verwenden muss, mache ich normalerweise etwas in diese Richtung.

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

Dies macht die Verwendung für Dinge wie apply_cold () sehr einfach und Sie können offensichtlich in Ihrem Zustand Bedingungen wie trocken und nass bauen.

Filz
quelle
Vielleicht können Sie die beste Lösung überprüfen, um die Deklaration zu lösen, wenn sie viele Zustände hat: stackoverflow.com/q/13385744/1077364
vgonisanz
7

Zwei Beobachtungen:

  1. Ihr Zustandssystem scheint zwei orthogonale Achsen zu haben: Temperatur und Gift. Stellen Sie sie als solche dar.
  2. Wenn Sie darüber nachdenken, sollten Sie Übergänge von Zuständen trennen . COLDund HOTsind Übergänge in der Art, wie Sie sie erwähnen, keine Zustände.

Die Kombination dieser Beobachtungen würde zu ungefähr folgendem Ergebnis führen:

// 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
quelle
Der Punkt ist, ich werde mehr Zustände hinzufügen, nicht orthogonale, ist möglich zu tun? HEISS ist auch ein Zustand, normal = 30 ºC, heiß = 70 ºC kalt = 5 ºC. Wenn Sie jedoch Wärme hinzufügen und heiß sind, kann dies zu Verbrennungen führen.
Vgonisanz
Wie wäre es, wenn Sie die Temperatur als ganzzahligen Wert in Grad Celsius anstelle eines booleschen Sprichworts "heiß" oder "kalt" modellieren?
Philipp
Natürlich können Sie weitere Temperaturzustände hinzufügen, wie Sie in meiner Antwort sehen, habe ich den heißen Zustand tatsächlich bereits dargestellt. Was Philipp sagt, bedeutet, jeden Grad Celsius als einen Zustand zu sehen, was in Ordnung ist, obwohl dies aus Sicht des Spieldesigns möglicherweise nicht das ist, was Sie wollen: Mehr Simulation bedeutet per se kein tieferes Spiel.
Eric
3

Indem Sie Ihre Zustände als Bitmaske darstellen, wie Sie sie schreiben, können Sie einfach Ihre Beschreibungen der Einschränkungen in Code übersetzen:

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

Sie könnten das in eine einwickeln makeStateConsistent() das Sie aufrufen können, bevor Sie die Statusbits testen, um sicherzustellen, dass der Status sinnvoll ist.

Eine Einschränkung dieses Ansatzes besteht jedoch darin, dass die Reihenfolge der Statusänderungen nicht berücksichtigt werden kann. Wenn Sie beispielsweise für heiße Gegenstände, die nass werden, ein anderes Ergebnis erzielen möchten als für nasse Gegenstände, die heiß werden, können Sie dies nicht so tun: Die makeStateConsistent()Methode sieht nur ein heißes und nasses Objekt ohne Informationen darüber, wie es muss so sein.

Statt dessen, was Sie tun können , ist das Einzelteil Zustand privaten (zumindest vom Konzept her ) und manipulieren , um sie durch eine Reihe von Methoden wie coolItem(), heatItem(), wetItem(), dryItem()und so weiter. Auf diese Weise können die Zustandsänderungsmethoden selbst zusätzliche Änderungen vornehmen. Die heatItem()Methode könnte beispielsweise folgendermaßen aussehen:

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
}

Natürlich möchten Sie vielleicht auch noch eine haben makeStateConsistent() Methode als Backup haben, falls Sie einen Fehler in Ihren Statusänderungsmethoden haben.

In einigen Fällen können Sie Ihren Code möglicherweise auch vereinfachen, indem Sie unnötige Zustände beseitigen. Benötigen Sie zum Beispiel wirklich einen separaten FROZENZustand, oder würde es ausreichen, nur kalte und nasse Gegenstände als gefroren zu behandeln?

Ilmari Karonen
quelle