Come avere operazioni con carattere / oggetti in binario con operazioni concrete?


8

Ho il prossimo problema.

Un articolo può avere molti stati:

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

Un articolo può avere alcuni stati contemporaneamente ma non tutti

  • È impossibile essere asciutti e bagnati allo stesso tempo.
  • Se FREDDO un oggetto umido, si trasforma in CONGELATO.
  • Se si CALDA un oggetto WET, questo diventa NORMALE
  • Un oggetto può essere BURNING e POISON

Eccetera.

Ho provato a impostare flag binari su stati e usare AND per combinare stati diversi, verificando prima se è possibile o meno farlo, o passare a un altro stato.

Esiste un approccio concreto per risolvere questo problema in modo efficiente senza un interruttore interminabile che controlla ogni stato con ogni nuovo stato?

È relativamente facile controllare 2 stati diversi, ma se esiste un terzo stato non è banale.


Non capisco perché pensi che un terzo stato non lo renda così banale in quanto dovrebbe essere semplice come il controllo 2. Puoi pubblicare un esempio di come lo stai attualmente facendo e un esempio usando 3 stati.
Giovanni

Forse si può controllare la soluzione migliore per risolvere la dichiarazione se ha un sacco di stati: stackoverflow.com/q/13385744/1077364
vgonisanz

Risposte:


6

Quando ho bisogno di usare le bandiere, di solito faccio qualcosa in questo senso.

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

Questo rende il loro utilizzo per cose come apply_cold () molto semplice e puoi ovviamente costruire nelle tue condizioni come asciutto e bagnato.


Forse puoi verificare la soluzione migliore per risolvere la dichiarazione se ha molti stati:
stackoverflow.com/q/13385744/1077364

7

Due osservazioni:

  1. Il tuo sistema di condizioni sembra avere due assi ortogonali: temperatura e veleno. Rappresentali come tali.
  2. Quando ci pensi, dovresti separare le transizioni dagli stati . COLDe HOTsono transizioni nel modo in cui le menzioni, non negli stati.

La combinazione di queste osservazioni comporterebbe qualcosa del genere:

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

Il punto è che aggiungerò più stati non ortogonali, è possibile farlo? Anche CALDO è uno stato, normale = 30 ºC, caldo = 70 ºC freddo = 5 ºC. Ma se aggiungi calore ed è caldo, si suppone che si trasformi in combustione.
vgonisanz,

Che ne dici di modellare la temperatura come valore intero in gradi Celsius invece di un valore booleano che dice "caldo" o "freddo"?
Philipp,

Naturalmente puoi aggiungere più stati di temperatura, come vedi nella mia risposta, in realtà ho già rappresentato lo stato caldo. Quello che Philipp dice significa vedere ogni grado Celsius come uno stato, il che va bene, anche se nota che questo potrebbe non essere quello che vuoi dal punto di vista del design del gioco: più simulazione non implica un gioco più profondo di per sé.
Eric,

3

Rappresentando i tuoi stati come maschera di bit come scrivi, puoi semplicemente tradurre in codice le descrizioni dei vincoli:

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

Potresti includerlo in un nome makeStateConsistent()che puoi chiamare prima di testare i bit di stato per assicurarti che lo stato abbia un senso.

Tuttavia, una limitazione di questo approccio è che non può tenere conto dell'ordine dei cambiamenti di stato. Ad esempio, se si desidera ottenere un risultato diverso per gli articoli caldi che si bagnano rispetto agli articoli bagnati che diventano caldi, non è possibile farlo in questo modo: tutto il makeStateConsistent()metodo vede è un oggetto caldo e bagnato, senza informazioni su come deve essere così.

Invece, quello che si potrebbe fare è rendere lo stato oggetto privato (almeno concettualmente) e manipolare attraverso una serie di metodi come coolItem(), heatItem(), wetItem(), dryItem()e così via. In questo modo, i metodi stessi di cambiamento di stato possono occuparsi di eventuali cambiamenti aggiuntivi. Ad esempio, il heatItem()metodo potrebbe assomigliare a questo:

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
}

Naturalmente, potresti comunque voler avere anche un makeStateConsistent()metodo come backup, nel caso in cui tu abbia un bug nei tuoi metodi di cambio di stato.

Inoltre, in alcuni casi potresti essere in grado di semplificare il codice eliminando gli stati non necessari. Ad esempio, hai davvero bisogno di uno FROZENstato separato , o sarebbe sufficiente trattare solo oggetti freddi e bagnati come congelati?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.