Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

Rules.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  */
00009 #include "Rules.h"
00010 
00011 #include "Cube.h"
00012 #include "MarkMask.h"
00013 
00014 #include "Log.h"
00015 #include "LayoutException.h"
00016 #include "OnStack.h"
00017 #include "OnWall.h"
00018 #include "OnStrongPad.h"
00019 
00020 #include <assert.h>
00021 
00022 //-----------------------------------------------------------------
00023 /**
00024  * Create new rules for model.
00025  */
00026 Rules::Rules(Cube *model)
00027 {
00028     m_readyToDie = false;
00029     m_readyToTurn = false;
00030     m_readyToActive = false;
00031     m_dir = Dir::DIR_NO;
00032     m_pushing = false;
00033     m_outDepth = 0;
00034     m_touchDir = Dir::DIR_NO;
00035 
00036     m_model = model;
00037     m_mask = NULL;
00038     m_lastFall = false;
00039 }
00040 //-----------------------------------------------------------------
00041 /**
00042  * Unmask from field.
00043  */
00044 Rules::~Rules()
00045 {
00046     if (m_mask) {
00047         m_mask->unmask();
00048         delete m_mask;
00049     }
00050 }
00051 //-----------------------------------------------------------------
00052 /**
00053  * Connect model with field.
00054  * @throws LayoutException when location is occupied
00055  */
00056     void
00057 Rules::takeField(Field *field)
00058 {
00059     if (m_mask) {
00060         m_mask->unmask();
00061         delete m_mask;
00062         m_mask = NULL;
00063     }
00064 
00065     m_mask = new MarkMask(m_model, field);
00066     Cube::t_models resist = m_mask->getResist(Dir::DIR_NO);
00067     if (!resist.empty()) {
00068         throw LayoutException(ExInfo("position is occupied")
00069                 .addInfo("model", m_model->toString())
00070                 .addInfo("resist", resist.front()->toString()));
00071     }
00072 
00073     m_mask->mask();
00074 }
00075 
00076 //-----------------------------------------------------------------
00077 /**
00078  * Accomplish last move in m_dir direction.
00079  * Mask to a new position.
00080  * Change model position.
00081  */
00082     void
00083 Rules::occupyNewPos()
00084 {
00085     m_touchDir = Dir::DIR_NO;
00086     if (m_dir != Dir::DIR_NO) {
00087         m_pushing = false;
00088 
00089         V2 shift = Dir::dir2xy(m_dir);
00090         V2 oldLoc = m_model->getLocation();
00091         m_model->change_setLocation(oldLoc.plus(shift));
00092 
00093         m_mask->mask();
00094     }
00095 }
00096 //-----------------------------------------------------------------
00097 /**
00098  * Check dead fishes.
00099  * Fish is dead:
00100  * - when any model moves in dir != Dir::DIR_UP
00101  *   and new position is SOLELY on a fish
00102  * - when any model moves in Dir::DIR_DOWN
00103  *   and new position is SOLELY on models SOLELY on a fish
00104  * - when any model rests SOLELY on models SOLELY on a fish
00105  *   with fish.power < model.weight
00106  * 
00107  * @return true when fish is dead
00108  */
00109     bool
00110 Rules::checkDead(Cube::eAction lastAction)
00111 {
00112     //NOTE: after falling phase is sufficient to check only DeadFall
00113     bool dead = false;
00114 
00115     if (m_model->isAlive()) {
00116         switch (lastAction) {
00117             case Cube::ACTION_FALL:
00118                 dead = checkDeadFall();
00119                 break;
00120             case Cube::ACTION_MOVE:
00121                 dead = checkDeadMove();
00122                 if (!dead) {
00123                     dead = checkDeadStress();
00124                 }
00125                 break;
00126             default:
00127                 dead = false;
00128                 break;
00129         }
00130 
00131         if (dead) {
00132             m_readyToDie = true;
00133         }
00134     }
00135 
00136     return dead;
00137 }
00138 
00139 //-----------------------------------------------------------------
00140 /**
00141  * Return true when any model moves in dir != Dir::DIR_UP
00142  * and new position is SOLELY on a fish.
00143  * @return true when fish is dead
00144  */
00145     bool
00146 Rules::checkDeadMove()
00147 {
00148     Cube::t_models resist = m_mask->getResist(Dir::DIR_UP);
00149     Cube::t_models::iterator end = resist.end();
00150     for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00151         if (!(*i)->isAlive()) {
00152             Dir::eDir resist_dir = (*i)->rules()->getDir();
00153             if (resist_dir != Dir::DIR_NO && resist_dir != Dir::DIR_UP) {
00154                 if (!(*i)->rules()->isOnStack()) {
00155                     return true;
00156                 }
00157             }
00158         }
00159     }
00160 
00161     return false;
00162 }
00163 //-----------------------------------------------------------------
00164 /**
00165  * Whether object is under falling object.
00166  *
00167  * @return true when fish is dead
00168  */
00169     bool
00170 Rules::checkDeadFall()
00171 {
00172     Cube::t_models killers = whoIsFalling();
00173 
00174     Cube::t_models::iterator end = killers.end();
00175     for (Cube::t_models::iterator i = killers.begin(); i != end; ++i) {
00176         if (!(*i)->rules()->isOnWall()) {
00177             return true;
00178         }
00179     }
00180 
00181     return false;
00182 }
00183 //-----------------------------------------------------------------
00184 /**
00185  * Whether object is under hight stress.
00186  *
00187  * @return true when fish is dead
00188  */
00189     bool
00190 Rules::checkDeadStress()
00191 {
00192     Cube::t_models killers = whoIsHeavier(m_model->getPower());
00193 
00194     Cube::t_models::iterator end = killers.end();
00195     for (Cube::t_models::iterator i = killers.begin(); i != end; ++i) {
00196         if (!(*i)->rules()->isOnStrongPad((*i)->getWeight())) {
00197             return true;
00198         }
00199     }
00200 
00201     return false;
00202 }
00203 
00204 //-----------------------------------------------------------------
00205 /**
00206  * Finish events from last round.
00207  * Change model state.
00208  */
00209     void
00210 Rules::changeState()
00211 {
00212     m_dir = Dir::DIR_NO;
00213 
00214     if (!m_model->isLost() && m_model->isDisintegrated()) {
00215         m_mask->unmask();
00216         m_model->change_remove();
00217     }
00218 
00219     if (m_readyToTurn) {
00220         m_readyToTurn = false;
00221         m_model->change_turnSide();
00222     }
00223 
00224     m_readyToActive = false;
00225 
00226     if (m_readyToDie) {
00227         m_readyToDie = false;
00228         m_model->change_die();
00229     }
00230 }
00231 //-----------------------------------------------------------------
00232 /**
00233  * Let model to go out of room.
00234  * @return out depth, 0 for normal, 1 for going out,
00235  * 2... for on the way, -1 for out of screen
00236  */
00237 int
00238 Rules::actionOut()
00239 {
00240     if (!m_model->isWall() && !m_model->isLost()
00241             && !m_model->isBusy())
00242     {
00243         //NOTE: normal objects are not allowed to go out of screen
00244         if (m_model->shouldGoOut()) {
00245             Dir::eDir borderDir = m_mask->getBorderDir();
00246             if (borderDir != Dir::DIR_NO) {
00247                 m_dir = borderDir;
00248                 m_outDepth += 1;
00249             }
00250             else {
00251                 if (m_outDepth > 0) {
00252                     m_model->change_goOut();
00253                     m_outDepth = -1;
00254                 }
00255             }
00256         }
00257     }
00258 
00259     return m_outDepth;
00260 }
00261 //-----------------------------------------------------------------
00262 /**
00263  * Let model fall.
00264  */
00265     void
00266 Rules::actionFall()
00267 {
00268     m_dir = Dir::DIR_DOWN;
00269     m_lastFall = true;
00270 }
00271 //-----------------------------------------------------------------
00272 /**
00273  * Unset falling flag.
00274  * @return last value of the flag
00275  */
00276     bool
00277 Rules::clearLastFall()
00278 {
00279     bool last = m_lastFall;
00280     m_lastFall = false;
00281     return last;
00282 }
00283 //-----------------------------------------------------------------
00284 /**
00285  * Unmask from old position.
00286  */
00287     void
00288 Rules::finishRound()
00289 {
00290     freeOldPos();
00291 }
00292 //-----------------------------------------------------------------
00293 /**
00294  * Unmask from old position.
00295  */
00296     void
00297 Rules::freeOldPos()
00298 {
00299     if (m_dir != Dir::DIR_NO) {
00300         m_mask->unmask();
00301     }
00302 }
00303 
00304 //-----------------------------------------------------------------
00305 /**
00306  * Whether object is direct or undirect on something specific.
00307  * @param cond condition which will be satify when object is on.
00308  */
00309     bool
00310 Rules::isOnCond(const OnCondition &cond)
00311 {
00312     bool result = false;
00313     if (cond.isSatisfy(m_model)) {
00314         result = true;
00315     }
00316     else if (cond.isWrong(m_model)) {
00317         result = false;
00318     }
00319     else {
00320         m_mask->unmask();
00321 
00322         result = false;
00323         Cube::t_models resist = m_mask->getResist(Dir::DIR_DOWN);
00324         Cube::t_models::iterator end = resist.end();
00325         for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00326             if ((*i)->rules()->isOnCond(cond)) {
00327                 //NOTE: don't forget to mask()
00328                 result = true;
00329                 break;
00330             }
00331         }
00332 
00333         m_mask->mask();
00334     }
00335 
00336     return result;
00337 }
00338 //-----------------------------------------------------------------
00339 /**
00340  * Whether object is on another unalive object.
00341  * And this another object must be on something fixed
00342  * (except on original object).
00343  *
00344  * Such object can be moved over fish.
00345  */
00346     bool
00347 Rules::isOnStack()
00348 {
00349     return isOnCond(OnStack());
00350 }
00351 //-----------------------------------------------------------------
00352 /**
00353  * Whether object is direct or undirect on a wall.
00354  */
00355     bool
00356 Rules::isOnWall()
00357 {
00358     return isOnCond(OnWall());
00359 }
00360 //-----------------------------------------------------------------
00361 /**
00362  * Whether object is direct or undirect on Wall or on powerful fish.
00363  *
00364  * @param weight stress weight which must fish carry
00365  * @return whether Wall or a strong fish carry this object
00366  */
00367     bool
00368 Rules::isOnStrongPad(Cube::eWeight weight)
00369 {
00370     return isOnCond(OnStrongPad(weight));
00371 }
00372 
00373 //-----------------------------------------------------------------
00374 /**
00375  * Whether object is falling.
00376  */
00377 bool
00378 Rules::isFalling() const
00379 {
00380     bool result = false;
00381     if (!m_model->isAlive()) {
00382         result = (m_dir == Dir::DIR_DOWN);
00383     }
00384     return result;
00385 }
00386 //-----------------------------------------------------------------
00387 /**
00388  * Who is falling on us.
00389  * @return array of killers, they can fall undirect on us
00390  */
00391     Cube::t_models 
00392 Rules::whoIsFalling() 
00393 {
00394     Cube::t_models result;
00395     m_mask->unmask();
00396 
00397     Cube::t_models resist = m_mask->getResist(Dir::DIR_UP);
00398     Cube::t_models::iterator end = resist.end();
00399     for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00400         //NOTE: falling is not propagated over fish
00401         if (!(*i)->isWall() && !(*i)->isAlive()) {
00402             if ((*i)->rules()->isFalling()) {
00403                 result.push_back(*i);
00404             }
00405             else {
00406                 Cube::t_models distance_killers = (*i)->rules()->whoIsFalling();
00407                 result.insert(result.end(), distance_killers.begin(),
00408                         distance_killers.end());
00409             }
00410         }
00411     }
00412 
00413     m_mask->mask();
00414     return result;
00415 }
00416 //-----------------------------------------------------------------
00417 /**
00418  * Whether object is heavier than our power.
00419  * @param power our max power
00420  * @return whether object is heavier
00421  */
00422 bool
00423 Rules::isHeavier(Cube::eWeight power) const
00424 {
00425     bool result = false;
00426     if (!m_model->isWall() && !m_model->isAlive()) {
00427         if (m_model->getWeight() > power) {
00428             result = true;
00429         }
00430     }
00431 
00432     return result;
00433 }
00434 //-----------------------------------------------------------------
00435 /**
00436  * Who is heavier than our power.
00437  * @param power our max power
00438  * @return array of killers, they can lie undirect on us
00439  */
00440     Cube::t_models
00441 Rules::whoIsHeavier(Cube::eWeight power)
00442 {
00443     Cube::t_models result;
00444     m_mask->unmask();
00445 
00446     Cube::t_models resist = m_mask->getResist(Dir::DIR_UP);
00447     Cube::t_models::iterator end = resist.end();
00448     for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00449         if (!(*i)->isWall()) {
00450             if ((*i)->rules()->isHeavier(power)) {
00451                 result.push_back(*i);
00452             }
00453             else {
00454                 Cube::t_models distance_killers =
00455                     (*i)->rules()->whoIsHeavier(power);
00456                 result.insert(result.end(), distance_killers.begin(),
00457                         distance_killers.end());
00458             }
00459         }
00460     }
00461 
00462     m_mask->mask();
00463     return result;
00464 }
00465 
00466 //-----------------------------------------------------------------
00467 /**
00468  * Whether other will retreat before us.
00469  *
00470  * @param power we will use this power
00471  */
00472     bool
00473 Rules::canMoveOthers(Dir::eDir dir, Cube::eWeight power)
00474 {
00475     bool result = true;
00476     //NOTE: make place after oneself, e.g. fish in U
00477     m_mask->unmask();
00478 
00479     Cube::t_models resist = m_mask->getResist(dir);
00480     Cube::t_models::iterator end = resist.end();
00481     for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00482         if (!(*i)->rules()->canDir(dir, power)) {
00483             result = false;
00484             break;
00485         }
00486     }
00487 
00488     m_mask->mask();
00489     return result;
00490 }
00491 //-----------------------------------------------------------------
00492 /**
00493  * Whether others can move us.
00494  * Live models cannot be moved by others.
00495  * Power and weight is compared.
00496  *
00497  * @param dir move direction
00498  * @param power others power
00499  * @return whether we can move
00500  */
00501     bool
00502 Rules::canDir(Dir::eDir dir, Cube::eWeight power)
00503 {
00504     bool result = false;
00505     if (!m_model->isAlive() && power >= m_model->getWeight()) {
00506         result = canMoveOthers(dir, power);
00507     }
00508 
00509     return result;
00510 }
00511 //-----------------------------------------------------------------
00512 /**
00513  * There is one special case.
00514  * When model touch output_DIR then it goes out.
00515  * This is used only in level 'windoze'.
00516  */
00517     bool
00518 Rules::touchSpec(Dir::eDir dir)
00519 {
00520     bool result = false;
00521     Cube::t_models resist = m_mask->getResist(dir);
00522     if (resist.size() == 1) {
00523         if (resist[0]->isOutDir(dir)) {
00524             resist[0]->decOutCapacity();
00525             m_mask->unmask();
00526             m_model->change_goOut();
00527             result = true;
00528         }
00529     }
00530     return result;
00531 }
00532 //-----------------------------------------------------------------
00533 /**
00534  * Marks all resisted models as touched.
00535  */
00536 void
00537 Rules::setTouched(Dir::eDir dir)
00538 {
00539     m_touchDir = dir;
00540     if (!m_model->isWall()) {
00541         m_mask->unmask();
00542         Cube::t_models resist = m_mask->getResist(dir);
00543         Cube::t_models::iterator end = resist.end();
00544         for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00545             if (!(*i)->isAlive()) {
00546                 (*i)->rules()->setTouched(dir);
00547             }
00548         }
00549         m_mask->mask();
00550     }
00551 }
00552 //-----------------------------------------------------------------
00553 /**
00554  * Try to move.
00555  * Only m_dir will be set.
00556  * NOTE: we can move all resist or none
00557  *
00558  * @return whether we have moved
00559  */
00560     bool
00561 Rules::actionMoveDir(Dir::eDir dir)
00562 {
00563     bool result = false;
00564     if (canMoveOthers(dir, m_model->getPower())) {
00565         moveDirBrute(dir);
00566         result = true;
00567     }
00568     else {
00569         if (touchSpec(dir)) {
00570             result = true;
00571         }
00572         else {
00573             setTouched(dir);
00574         }
00575     }
00576 
00577     return result;
00578 }
00579 //-----------------------------------------------------------------
00580 /**
00581  * Irrespective move.
00582  * Set m_dir to this dir and do the same for all resist.
00583  * Only m_dir and m_pushing will be set.
00584  */
00585     void
00586 Rules::moveDirBrute(Dir::eDir dir)
00587 {
00588     //NOTE: make place after oneself, e.g. object in U
00589     m_mask->unmask();
00590 
00591     Cube::t_models resist = m_mask->getResist(dir);
00592     Cube::t_models::iterator end = resist.end();
00593     if (end != resist.begin()) {
00594         m_pushing = true;
00595     }
00596     for (Cube::t_models::iterator i = resist.begin(); i != end; ++i) {
00597         (*i)->rules()->moveDirBrute(dir);
00598     }
00599 
00600     m_dir = dir;
00601     m_mask->mask();
00602 }
00603 
00604 //-----------------------------------------------------------------
00605 /**
00606  * Return what we do the last round.
00607  * Useful for script functions.
00608  * NOTE: dead is not action
00609  */
00610 std::string
00611 Rules::getAction() const
00612 {
00613     if (m_readyToTurn) {
00614         return "turn";
00615     }
00616     else if (m_readyToActive) {
00617         return "activate";
00618     }
00619     else if (m_model->isBusy()) {
00620         return "busy";
00621     }
00622 
00623     switch (m_dir) {
00624         case Dir::DIR_LEFT: return "move_left";
00625         case Dir::DIR_RIGHT: return "move_right";
00626         case Dir::DIR_UP: return "move_up";
00627         case Dir::DIR_DOWN: return "move_down";
00628         case Dir::DIR_NO: return "rest";
00629         default: assert(!"unknown dir"); break;
00630     }
00631 
00632     return "rest";
00633 }
00634 //-----------------------------------------------------------------
00635 /**
00636  * Return how we have feel the last round.
00637  * Useful for script functions.
00638  *
00639  * States:
00640  * "goout" ... go out of room
00641  * "dead" ... is dead
00642  * "talking" ... is talking
00643  * "pushing" ... is pushing
00644  * "normal" ... is alive and resting
00645  */
00646 std::string
00647 Rules::getState() const
00648 {
00649     if (m_outDepth == 1) {
00650         return "goout";
00651     }
00652     else if (!m_model->isAlive()) {
00653         return "dead";
00654     }
00655     else if (m_model->isTalking()) {
00656         return "talking";
00657     }
00658     else if (m_pushing) {
00659         return "pushing";
00660     }
00661     else {
00662         return "normal";
00663     }
00664 }
00665 //-----------------------------------------------------------------
00666 bool
00667 Rules::isAtBorder() const
00668 {
00669     return m_mask->getBorderDir() != Dir::DIR_NO;
00670 }
00671 //-----------------------------------------------------------------
00672 bool
00673 Rules::isFreePlace(const V2 &loc) const
00674 {
00675     return m_mask->getPlacedResist(loc).empty();
00676 }
00677 //-----------------------------------------------------------------
00678 const Cube::t_models
00679 Rules::getResist(Dir::eDir dir) const
00680 {
00681     return m_mask->getResist(dir);
00682 }
00683 

Generated on Wed Jun 1 09:54:31 2005 for Fish Fillets - Next Generation by  doxygen 1.4.2