#include #include #include // Programmer: Brett Weiland // Date: 4/30/23 // File: city.cpp // Assignment: SP-B-finalProject // Purpose: functions for class city. see header for more details #include "city.h" unsigned int city::rounds_taken = 0; city::city(const unsigned int robber_win_value, const unsigned int rounds) : ROBBER_WIN_VALUE(robber_win_value), MAX_ROUNDS(rounds) { for(size_t cop_i = 0; cop_i < COPS_COUNT; cop_i++) { cops[cop_i].includeInMap(this, randomLocation()); addMapMarker(cops[cop_i]); } for(size_t robber_i = 0; robber_i < ROBBERS_COUNT; robber_i++) { robbers[robber_i].includeInMap(this, randomLocation()); addMapMarker(robbers[robber_i]); robbers[robber_i].setGreedy(robber_i < ROBBERS_GREEDY); } for(size_t jewl_i = 0; jewl_i < JEWL_COUNT; jewl_i++) { jewls[jewl_i].includeInMap(this, randomLocation()); addMapMarker(jewls[jewl_i]); jewls_available[jewl_i] = &jewls[jewl_i]; } renderMap(); printMap(); } void city::printMap() const { if(rounds_taken) { //prints round in middle at top of map const std::string round_header = "ROUND " + std::to_string(rounds_taken); const size_t header_size = round_header.length(); if(header_size > (MAP_WIDTH * 2)) { std::cout << round_header << std::endl; } else { for(size_t spacing = 0; spacing < (MAP_WIDTH - ((header_size + 1) / 2.0)); spacing++) std::cout << " "; std::cout << round_header << std::endl; } } else { //initial print before rounds are startred std::cout << "Initial positions" << std::endl; } for(size_t y = 0; y < MAP_HEIGHT; y++) { for(size_t x = 0; x < MAP_HEIGHT; x++) { std::cout << rendered_map[y][x] << " " << std::flush; } std::cout << "\r\n"; } std::cout << std::endl; } coordinate city::randomLocation() const { coordinate rand_cord; do { rand_cord.set_xy(rand() % MAP_WIDTH, rand() % MAP_HEIGHT); } while (map_ptr[rand_cord.get_y()][rand_cord.get_x()] != NULL); return rand_cord; } //see coordinate.h for a descriptions of how directions work. direction::TYPE city::randomDirection(const coordinate &scanner) const { direction::TYPE rand_dir; direction::TYPE border_map = ( ((scanner.get_y() <= 0) << direction::N) | ((scanner.get_x() >= (MAP_WIDTH - 1)) << direction::E) | ((scanner.get_y() >= (MAP_HEIGHT - 1)) << direction::S) | ((scanner.get_x() <= 0) << direction::W)); do { rand_dir = ((1 | ((rand() % 2) << 1)) << (rand() % 4)); rand_dir |= ((rand_dir & (1 << 4)) >> 4); rand_dir &= (~(border_map) & direction::MASK) ; } while(!rand_dir); return rand_dir; } direction::TYPE city::directionToAnyJewl(const coordinate &scanner) const { direction::TYPE d = scanner.getDirection(jewls_available[rand() % jewls_available_qty]->getLocation()); if(d) return d; return randomDirection(scanner); } gameObject *city::findObjsByLocation(const coordinate loc) { return map_ptr[loc.get_y()][loc.get_x()]; } direction::TYPE city::nearestCopDirection(const coordinate &scanner) const { int min_steps = INT_MAX; int min_cop_id = INT_MAX; int distance; const cop *closest_cop; for(size_t cop_i = 0; cop_i < COPS_COUNT; cop_i++) { distance = scanner.getSteps(cops[cop_i].getLocation()); if((distance < min_steps) || ((distance == min_steps) && (cops[cop_i].get_id() < min_cop_id))) { min_steps = distance; min_cop_id = cops[cop_i].get_id(); closest_cop = &cops[cop_i]; } } return scanner.getDirection(closest_cop->getLocation()); } void city::renderMap() { const char MAP_EMPTY = '*'; for(size_t y = 0; y < MAP_HEIGHT; y++) { for(size_t x = 0; x < MAP_HEIGHT; x++) { if(map_ptr[y][x] == NULL) { rendered_map[y][x] = MAP_EMPTY; continue; } rendered_map[y][x] = map_ptr[y][x]->getIcon(); } } } void city::addMapMarker(gameObject &item) { gameObject **map_location = &map_ptr[item.getLocation().get_y()][item.getLocation().get_x()]; gameObject *lower_item = *map_location; gameObject *upper_item = NULL; while((lower_item != NULL) && (item.get_drawPriority() > lower_item->get_drawPriority())) { lower_item = lower_item->get_bottom(); if(upper_item == NULL) { upper_item = *map_location; } else { upper_item = upper_item->get_bottom(); } } if(upper_item != NULL) { upper_item->set_bottom(&item); } else { *map_location = &item; } if(lower_item != NULL) { lower_item->set_top(&item); } item.set_top(upper_item); item.set_bottom(lower_item); } void city::delMapMarker(gameObject &item) { gameObject **map_location = &map_ptr[item.getLocation().get_y()][item.getLocation().get_x()]; if(item.get_top() != NULL) item.get_top()->set_bottom(item.get_bottom()); if(item.get_bottom() != NULL) item.get_bottom()->set_top(item.get_top()); if(&item == *map_location) *map_location = item.get_bottom(); } void city::moveNPC(npc &character) { if((!character.is_attached()) || (!character.is_active())) return; delMapMarker(character); character.move(); addMapMarker(character); } city::CITY_STATUS city::runTurn() { rounds_taken += 1; for(size_t robber_i = 0; robber_i < ROBBERS_COUNT; robber_i++) { moveNPC(robbers[robber_i]); if(robbers[0].get_totalRobberValue() >= ROBBER_WIN_VALUE) return CITY_STATUS::ROBBERS_MAX_VAL; if(allRobbersArrested()) return CITY_STATUS::COPS_WIN; } for(size_t cop_i = 0; cop_i < COPS_COUNT; cop_i++) { moveNPC(cops[cop_i]); if(allRobbersArrested()) return CITY_STATUS::COPS_WIN; } renderMap(); if(rounds_taken >= MAX_ROUNDS) return CITY_STATUS::ROUNDS_DEPLETED; return CITY_STATUS::CONTINUE; } void city::summarize() const { for(size_t cop_i = 0; cop_i < COPS_COUNT; cop_i++) { std::cout << "Police ID: " << cops[cop_i].get_id() << "\n\tConfiscated jewls amount: $" << cops[cop_i].get_inventoryValue() << "\n\tFinal number of robbers caught: " << cops[cop_i].get_robbersCaught() << "\n\n"; } for(size_t robber_i = 0; robber_i < ROBBERS_COUNT; robber_i++) { std::cout << (robbers[robber_i].isGreedy() ? "Greedy" : "Ordinary") << " Robber ID: " << robbers[robber_i].get_id() << "\n\tJewls picked up: " << robbers[robber_i].get_inventoryQty() << "\n\tTotal jewl worth: " << robbers[robber_i].get_inventoryValue() << "\n\n"; } std::cout << std::endl; } //I would have liked to set up a linked list for this as well, however I was running out of time. //Probably would have been nice to set up a generic linked list header type. void city::markJewlTaken(jewl &item) { size_t jewl_i; for(jewl_i = 0; jewls_available[jewl_i] != &item; jewl_i++); //find jewl in array //shift all jewls over by 1 at/after position of removed jewls for(; jewl_i < jewls_available_qty - 1; jewl_i++) jewls_available[jewl_i] = jewls_available[jewl_i + 1]; jewls_available_qty--; } void city::markJewlAvailable(jewl &item) { jewls_available[jewls_available_qty++] = &item; } void city::freeAllRobbers() { for(size_t robber_i = 0; robber_i < ROBBERS_COUNT; robber_i++) robbers[robber_i].free(); for(size_t cop_i = 0; cop_i < COPS_COUNT; cop_i++) cops[cop_i].robbersReleased(); } bool city::allRobbersArrested() const { for(size_t robber_i = 0; robber_i < ROBBERS_COUNT; robber_i++) if(robbers[robber_i].is_active()) return false; return true; }