#ifndef BMD_ENGINE_H #define BMD_ENGINE_H #include #include "stage.h" #include using namespace std; class VictoryConditions { private: ScoringProperties victory_condition; ActiveProperties active_condition; bool winner_exists; void init(const ScoringProperties& in_v_condition, const ActiveProperties& in_a_condition) { victory_condition = in_v_condition; active_condition = in_a_condition; winner_exists = false;} public: //check if player won or is eliminated bool check_for_WinElim(const ScoringProperties& player_score) { if(active_condition.deaths && player_score.deaths >= victory_condition.deaths) return false; if(active_condition.kills && player_score.kills >= victory_condition.kills) winner_exists = true; if(active_condition.score && player_score.score >= victory_condition.score) winner_exists = true; return true; } }; class Engine { private: bool first_use; //used for reseting Stage stage; VictoryConditions v_conditions; int max(int a, int b) { if(a > b) return a; return b; } int create_bomb_id() { if(bombs.size() > 0) return max(bombs.back().get_id()+1,bombs.size()); return 0; } /* adds a new bomb to the "bombs" vector with the given ID and location */ void create_bomb( const Coordinates & bomb_coords, const unsigned int & bomber_id, const BombProperties & in_prop, const int fresh_id) { Bomb new_bomb; new_bomb.set(fresh_id, bomber_id, in_prop); //ADD DURATION new_bomb.coords = bomb_coords; bombs.push_back(new_bomb); } int create_bomber_id() { if(bombers.size() > 0) return max(bombers.back().get_id()+1,bombers.size()); return 0; } /* add a new bomber */ void create_bomber(const Coordinates & bomber_coords, const int fresh_id) { Bomber new_bomber(fresh_id); new_bomber.coords = bomber_coords; bombers.push_back(new_bomber); } void create_powerup(PowerUp & in_powerup) { int fresh_id; if(powerups.size() > 0) fresh_id = max(powerups.back().id+1,powerups.size()); else fresh_id = 0; in_powerup.id = fresh_id; powerups.push_back(in_powerup); } //void bomb_explosion(Bomb& bomb, unsigned int starter_id) void bomb_explosion(Bomb& bomb, vector::iterator start_bomber) { ExplosionResult temp_result; int owner_id; vector::iterator x_iter; vector::iterator x_bomber; vector::iterator x_bomb; vector::iterator c_powerup; //use bomb.gone - pack all this into a function in engine - this way no iterator messiness // // // bomb.gone = true; owner_id = bomb.get_owner_id(); // @see Bomber::notify_explosion bombers[owner_id].notify_explosion(bomb.get_id()); x_bomber = bombers.begin(); while(x_bomber != bombers.end()) { if(x_bomber->get_id() == owner_id) { x_bomber->nbombs--; break; //ids are unique } x_bomber++; } temp_result = stage.detonate_bomb(bomb.coords, bomb.prop.power); for(x_iter = temp_result.ef_bombers.begin(); x_iter != temp_result.ef_bombers.end(); x_iter++) { //this finds the next bomber in the list x_bomber = bombers.begin(); while(x_bomber != bombers.end() && x_bomber->get_id() != *x_iter) {x_bomber++;} //add kill if on different teams - for now always //bombers[*(x_iter)].kill(); //bomber_id always corresponds to vector index //bombers[starter_id].score.kills++; if(x_bomber != bombers.end()) { x_bomber->kill(); if(x_bomber->mstate.is_displaced()) // CHANGE stage.bomber_InformTileClear(x_bomber->mstate.reserved(x_bomber->coords)); //add tile wipe code here start_bomber->score.kills++; // tell the bomber it died x_bomber->notify_death(); // tell the teammate that its teammate died int team_id = x_bomber->get_team_id(); // find a teammate and notify death for (unsigned int i = 0; i < bombers.size(); ++i) { if (bombers[i].get_team_id() == team_id && bombers[i].get_id() != x_bomber->get_id() && bombers[i].is_alive()) { bombers[i].notify_teammate_death(); } } } } for(c_powerup = temp_result.created_powerups.begin(); c_powerup < temp_result.created_powerups.end(); c_powerup++) { create_powerup(*c_powerup); stage.place_powerup(*c_powerup); } for(x_iter = temp_result.ef_bombs.begin(); x_iter != temp_result.ef_bombs.end(); x_iter++) { //this finds the next bomb in the list, second condition should ALWAYS be true x_bomb = bombs.begin(); while(x_bomb != bombs.end() && x_bomb->get_id() != *x_iter) {x_bomb++;} // CHANGE //stage.detonate_bomb_bomb(bombs[*(x_iter)].coords, bomb[*(x_iter)].prop.power); if(x_bomb != bombs.end() && x_bomb->get_id() != bomb.get_id()) // CHANGE - safety to preven infinte recursion { bomb_explosion(*x_bomb, start_bomber); } } Explosion temp_explosion(temp_result.explosion_data); explosions.push_back(temp_explosion); } public: vector bombers; vector bombs; vector powerups; vector explosions; struct team_dat { int id; int team_id; }; int nPlayers; vector agent_file_names; vector team_data; // //implement a 'real' init once map-flies are decided etc. // //performs one time step of the game void execute_time_step() { Coordinates temp_coords; int temp_id; vector::iterator bomber; vector::iterator bomb; vector::iterator powerup; vector::iterator explosion; //bool el_removed; //was an element removed from the vector this iteration VMInput vm_input; VMBomber temp_vm_bomber; VMBomb temp_vm_bomb; VMPowerUp temp_vm_pup; MoveResult temp_move_result; /*************************** ignore this Here lies the virtual machine. ****************************/ for (unsigned int i = 0; i < bombers.size(); ++i) { if (bombers[i].is_alive() && !bombers[i].mstate.is_displaced()) { // request an order and place // the result in the bomber orders bombers[i].request_orders(construct_vm_input(bombers[i].get_id())); } } bomber = bombers.begin(); for(; bomber != bombers.end(); bomber++) { if(bomber->is_alive()) { if(bomber->orders.move_ordered()) { if(!(bomber->mstate.change_direction(bomber->orders.move_direction))) {} } if (bomber->orders.detonate) // CHANGE { if(bomber->prop.detonation) { temp_id = bomber->get_id(); bomb = bombs.begin(); while(bomb != bombs.end()) { if(bomb->get_owner_id() == temp_id) bomb->orders.detonate = true; bomb++; // CHANGE } } } if (bomber->orders.bomb_place) { if(bomber->bombs_available()) { temp_id = create_bomb_id(); // CHANGE if(stage.place_bomb(*bomber,temp_id)) // CHANGE //if(stage.place_bomb(bomber->coords, (unsigned int)bombs.size())) //it is known that the ID will be bombs.size() { create_bomb(bomber->coords,bomber->id,bomber->bomb_prop,temp_id); //if the bomb is successfully placed, actually create it bomber->nbombs++; } else {} //nothing for now, maybe send a message back? } else {} //nothing for now, maybe send a message back? } if(bomber->mstate.is_displaced()) { if(bomber->mstate.tick()) //did this 'tick' (time frame) cause a tile change? { temp_coords = bomber->mstate.dest(bomber->coords); bomber->mstate.perform_tile_change(); //updates the mstate, NOT coordinates stage.bomber_InformTileChange(bomber->get_id(),bomber->coords,temp_coords); //changes reservation data bomber->coords = temp_coords; //update coordinates } if(! bomber->mstate.is_displaced()) //if the last tick caused the bomber to be square on a tile { stage.bomber_InformTileClear(bomber->mstate.source(bomber->coords)); } } else //bomber is sitting on a tile right now { if(bomber->mstate.moving()) //this unneccessarily checks displacement { temp_coords = bomber->mstate.dest(bomber->coords); //find the destination of movement temp_move_result = stage.bomber_CheckMove(temp_coords); // CHANGED if (temp_move_result.success()) //can a bomber move to the location? { stage.bomber_UnsafeCommenceMove(temp_coords); //perform neccessary reservations //the following code should be identical to the one above if(bomber->mstate.tick()) //did this 'tick' (time frame) cause a tile change? { temp_coords = bomber->mstate.dest(bomber->coords); bomber->mstate.perform_tile_change(); //updates the mstate, NOT coordinates stage.bomber_InformTileChange(bomber->get_id(),bomber->coords,temp_coords); //changes reservation data bomber->coords = temp_coords; //update coordinates } if(! bomber->mstate.is_displaced()) //if the last tick caused the bomber to be square on a tile { stage.bomber_InformTileClear(bomber->mstate.source(bomber->coords)); } } else if (temp_move_result.get_type() == mrtBOMB) { if(bomber->prop.kicking) //can the bomber "kick" bombs? { if(bombs.size() > 0) { bomb = bombs.begin(); while(bomb != bombs.end()) { if(bomb->get_id() == temp_move_result.get_collidee_id()) break; else bomb++; } //bomb now points to the bomb to be kicked if(bomb->mstate.is_displaced()) stage.bomb_InformTileClear(bomb->mstate.reserved(bomb->coords)); //if bomb isn't already kicked in that direction if(bomb->mstate.get_direction() != bomber->mstate.get_direction()) bomb->mstate.reset(bomber->mstate.get_direction()); //the bomb is now centered on the tile and kicked } // there is no else - it would be a bug } } } } //check if current tile has a powerup, give it to bomber temp_id = stage.acquire_powerup(bomber->coords); if(temp_id >= 0) //an actual PowerUp acquired { powerup = powerups.begin(); while( powerup != powerups.end()) { if(powerup->id == temp_id) { bomber->gain_powerup(powerup->power_type_index); powerup = powerups.erase(powerup); continue; } powerup++; } } } //end if( is alive) } bomb = bombs.begin(); while(bomb != bombs.end()) { if(!bomb->gone) { //countdown, movement - decide how detonation is handled then code if (bomb->orders.detonate || bomb->tick()) //did this bomb tick cause the bomb to detonate, was there an order? { bomber = bombers.begin(); while(bomber != bombers.end() && bomber->get_id() != bomb->get_owner_id()) bomber++; if(bomber != bombers.end()) { bomb_explosion(*bomb, bomber); //bomb = bombs.erase(bomb); //don't erase bombs until later //continue; //to avoid bomb++ // CHANGE } //this point should never be reached - place break for debugging } else //if bomb still exists, do its mstate tick (mstate is movement state) { if(bomb->mstate.is_displaced()) { if(bomb->mstate.tick()) //did this 'tick' (time frame) cause a tile change? { temp_coords = bomb->mstate.dest(bomb->coords); bomb->mstate.perform_tile_change(); //updates the mstate, NOT coordinates stage.bomb_InformTileChange(bomb->get_id(),bomb->coords,temp_coords); //changes reservation data bomb->coords = temp_coords; //update coordinates } if(! bomb->mstate.is_displaced()) //if the last tick caused the bomb to be square on a tile { stage.bomb_InformTileClear(bomb->mstate.source(bomb->coords)); } } else //bomb is sitting on a tile right now { if(bomb->mstate.moving()) //this unneccessarily checks displacement { temp_coords = bomb->mstate.dest(bomb->coords); //find the destination of movement if (stage.bomb_CheckMove(temp_coords).success()) //can a bomb move to the location? { stage.bomb_UnsafeCommenceMove(temp_coords); //perform neccessary reservations //the following code should be identical to the one above if(bomb->mstate.tick()) //did this 'tick' (time frame) cause a tile change? { temp_coords = bomb->mstate.dest(bomb->coords); bomb->mstate.perform_tile_change(); //updates the mstate, NOT coordinates stage.bomb_InformTileChange(bomb->get_id(),bomb->coords,temp_coords); //changes reservation data bomb->coords = temp_coords; //update coordinates } if(! bomb->mstate.is_displaced()) //if the last tick caused the bomb to be square on a tile { stage.bomb_InformTileClear(bomb->mstate.source(bomb->coords)); } } else { //the bomb collided with something, stop its movement bomb->mstate.change_direction(dVOID); } } } } } //end if(!bomb->gone) bomb++; } //the following while loop removes any bombs not initially removed bomb = bombs.begin(); while(bomb != bombs.end()) // CHANGE cosmetic { if(bomb->gone) { stage.remove_bomb_residue(bomb->coords); if(bomb->mstate.is_displaced()) // CHANGE stage.bomb_InformTileClear(bomb->mstate.reserved(bomb->coords)); bomb = bombs.erase(bomb); } else bomb++; } powerup = powerups.begin(); //for(powerup; powerup != powerups.end(); powerup++) { //expiration time? } explosion = explosions.begin(); while(explosion != explosions.end()) { if(explosion->tick()) { for(unsigned int i = 1; i <= explosion->explosion_data.dW; i++) { temp_coords.x = explosion->explosion_data.center.x - i; temp_coords.y = explosion->explosion_data.center.y; stage.clear_explosion(temp_coords); } temp_coords.x = explosion->explosion_data.center.x - (explosion->explosion_data.dW + 1); temp_coords.y = explosion->explosion_data.center.y; stage.clear_explosion_brick(temp_coords); for(unsigned int i = 1; i <= explosion->explosion_data.dE; i++) { temp_coords.x = explosion->explosion_data.center.x + i; temp_coords.y = explosion->explosion_data.center.y; stage.clear_explosion(temp_coords); } temp_coords.x = explosion->explosion_data.center.x + (explosion->explosion_data.dE + 1); temp_coords.y = explosion->explosion_data.center.y; stage.clear_explosion_brick(temp_coords); for(unsigned int i = 1; i <= explosion->explosion_data.dS; i++) { temp_coords.x = explosion->explosion_data.center.x; temp_coords.y = explosion->explosion_data.center.y + i; stage.clear_explosion(temp_coords); } temp_coords.x = explosion->explosion_data.center.x; temp_coords.y = explosion->explosion_data.center.y + (explosion->explosion_data.dS + 1); stage.clear_explosion_brick(temp_coords); for(unsigned int i = 1; i <= explosion->explosion_data.dN; i++) { temp_coords.x = explosion->explosion_data.center.x; temp_coords.y = explosion->explosion_data.center.y - i; stage.clear_explosion(temp_coords); } temp_coords.x = explosion->explosion_data.center.x; temp_coords.y = explosion->explosion_data.center.y - (explosion->explosion_data.dN + 1); stage.clear_explosion_brick(temp_coords); explosion = explosions.erase(explosion); continue; //to avoid increment } explosion++; } //stage.ascii_dump(); //testing code } // constructs a value to send to the vm Value construct_vm_input(int bomber_id) const; int get_tile_tindex(unsigned int& i, unsigned int& j) { Coordinates temp(i,j); return stage.tile_tinfo(temp); } bool check_tile_index(unsigned int& i, unsigned int& j, int tindex) { Coordinates temp(i,j); return stage.check_tinfo(temp,tindex); } unsigned int getHeight(){return stage.getH();} unsigned int getWidth(){return stage.getW();} //////////////////////// //////////////////////// //////////////////////// /// Testing Code follows //////////////////////// /* file: nPlayers ... team numbers need to be positive */ void init_player_data() { fstream fin(fname_PLAYER_DATA); string temp_agent_file; int temp_team_id; team_dat temp_team_dat; if(!fin.eof()) fin >> nPlayers; for(int i = 0; i < nPlayers; i++) { if(!fin.eof()) { fin >> temp_agent_file; fin >> temp_team_id; temp_team_dat.id = i; temp_team_dat.team_id = temp_team_id; agent_file_names.push_back(temp_agent_file); team_data.push_back(temp_team_dat); Bomber temp_bomber(i); temp_bomber.team_id = temp_team_id; // temp_bomber.coords = *i_pcoords; bombers.push_back(temp_bomber); try { // VM loading bombers[i].actor->load_from_file(temp_agent_file); } catch (runtime_error e) { // nothing catastrophic for now... cerr << "unsuccessful loading of " << temp_agent_file << endl << e.what() << endl; } } else break; } fin.close(); // DONE: inserted vm initialization // now assign actor links based on team number for (int i = 0; i < nPlayers; ++i) { for (int j = 0; j < nPlayers; ++j) { if (i == j) continue; // if bombers are on the same team, link them if (bombers[i].team_id == bombers[j].team_id) { bombers[i].actor->set_linked_actor(bombers[j].actor); bombers[j].actor->set_linked_actor(bombers[i].actor); } } } for (int i = 0; i < nPlayers; ++i) bombers[i].call_main(); } void init() { stage.test_init(); } void test_init() { init_player_data(); //get number of players vector player_coords; vector::iterator i_pcoords; player_coords = stage.test_init(); //send number of players i_pcoords = player_coords.begin(); for(int i = 0; i_pcoords != player_coords.end(); i++,i_pcoords++) { bombers[i].coords = *i_pcoords; } for (int i = 0; i < bombers.size(); ++i) bombers[i].call_init(); } }; #endif