RoboSim.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "robot_api.hpp"
4 #include "Robot.hpp"
5 
6 using namespace robot_api;
7 
12 #include <cmath>
13 #include <cstdlib>
14 #include <vector>
15 #include <list>
16 
17 using std::abs;
18 using std::min;
19 using std::vector;
20 
21 class RoboSim
22 {
23 public:
24 
27  {
28  private:
29  const GridCell& origin;
30 
31  public:
32  SimGridAllyDeterminant(const GridCell& origin_) : origin(origin_) { }
33 
34  bool operator()(const GridCell& potential_ally);
35  };
36 
37 private:
38  //RoboSim environmental constants
39  static const int WALL_HEALTH = 10;
40  static const int WALL_DEFENSE = 10;
41 
42  //RoboSim execution data (world grid, turn order, GUI reference, etc.)
43  vector<vector<GridCell> > worldGrid;
44  vector<RobotData> turnOrder;
46 
47 public:
49  vector<vector<GridCell> >& getWorldGrid() { return worldGrid; }
50 
54  int getOccupantPlayer(const GridCell& cell) const
55  {
56  return cell.occupant_data->player;
57  }
58 
59 private:
68  vector<vector<GridCell> > getSanitizedSubGrid(int x_left, int y_up, int x_right, int y_down, int player) const;
69 
70  static Robot_Specs checkSpecsValid(Robot_Specs proposed, int player, int skill_points);
71 
72 public:
82  RoboSim(int initial_robots_per_combatant, int skill_points, int length, int width, int obstacles);
83 
92 private:
94  {
95  private:
98 
99  public:
104  RoboAPIImplementor(RoboSim& rsim_, RobotData& actingRobot_) : rsim(rsim_), actingRobot(actingRobot_) { };
105 
111  private:
112  bool isAdjacent(GridCell adjacent_cell)
113  {
114  return (abs(actingRobot.assoc_cell->x_coord-adjacent_cell.x_coord)==1 &&
115  actingRobot.assoc_cell->y_coord == adjacent_cell.y_coord ||
116  abs(actingRobot.assoc_cell->y_coord-adjacent_cell.y_coord)==1 &&
117  actingRobot.assoc_cell->x_coord == adjacent_cell.x_coord);
118  }
119 
126  bool calculateHit(int attack, int defense)
127  {
128  int luckOfAttacker = rand()%10;
129  return luckOfAttacker+attack-defense>=5;
130  }
131 
138  AttackResult processAttack(int attack, GridCell& cell_to_attack, int power);
139 
140  public:
141  AttackResult meleeAttack(int power, GridCell& adjacent_cell);
142  AttackResult rangedAttack(int power, GridCell& nonadjacent_cell);
143  AttackResult capsuleAttack(int power_of_capsule, GridCell& cell);
144 
145  void defend(int power)
146  {
147  //Error checking
148  if(power < 0 || power > actingRobot.specs.defense || power > actingRobot.specs.power || power > actingRobot.status.charge)
149  throw RoboSimExecutionException("attempted to defend with negative power",actingRobot.player, *actingRobot.assoc_cell);
150 
151  //This one's easy
152  actingRobot.status.charge-=power;
153  actingRobot.status.defense_boost+=power;
154  }
155 
156  void move(int steps, Direction way);
157  void pick_up_capsule(GridCell& adjacent_cell);
158  void drop_capsule(GridCell& adjacent_cell, int power_of_capsule);
159 
161  {
162  return actingRobot.whatBuilding;
163  }
164 
166  {
167  return actingRobot.invested_assoc_cell;
168  }
169 
171  {
172  return actingRobot.investedPower;
173  }
174 
175  private:
176 
177  void finalizeBuilding(vector<uint8_t> creation_message);
178 
179  public:
180 
181  void setBuildTarget(BuildStatus status, GridCell* location)
182  {
183  setBuildTarget(status,location,vector<uint8_t>());
184  }
185 
186  void setBuildTarget(BuildStatus status, GridCell* location, vector<uint8_t> message);
187 
188  void build(int power)
189  {
190  if(power > actingRobot.status.power || power < 0)
191  throw RoboSimExecutionException("attempted to apply invalid power to build task",actingRobot.player,*actingRobot.assoc_cell);
192  actingRobot.status.charge-=power;
193  actingRobot.status.power-=power;
194  actingRobot.investedPower+=power;
195  }
196 
197  void repair(int power)
198  {
199  if(power > actingRobot.status.power || power < 0)
200  throw RoboSimExecutionException("attempted to apply invalid power to repair task",actingRobot.player,*actingRobot.assoc_cell);
201  actingRobot.status.charge-=power;
202  actingRobot.status.power-=power;
203  actingRobot.status.health+=power/2;
204 
205  //Can't have more health than charge skill*10
206  if(actingRobot.status.health > actingRobot.specs.charge*10)
207  actingRobot.status.health = actingRobot.specs.charge*10;
208  }
209 
210  void charge(int power, GridCell& ally)
211  {
212  //Check that we're using a valid amount of power
213  if(power > actingRobot.status.power || power < 1)
214  throw RoboSimExecutionException("attempted charge with illegal power level",actingRobot.player,*actingRobot.assoc_cell);
215 
216  //Are cells adjacent?
217  if(!isAdjacent(ally))
218  throw RoboSimExecutionException("attempted to charge nonadjacent cell",actingRobot.player,*actingRobot.assoc_cell);
219 
220  //Does cell exist in grid?
221  //(could put this in isAdjacent() method but want to give students more useful error messages)
222  if(ally.x_coord > rsim.worldGrid.size() || ally.y_coord > rsim.worldGrid[0].size() || ally.x_coord < 0 || ally.y_coord < 0)
223  throw RoboSimExecutionException("passed invalid cell coordinates to charge()",actingRobot.player,*actingRobot.assoc_cell,ally);
224 
225  //Safe to use this now, checked for oob condition from student
226  GridCell& allied_cell = rsim.worldGrid[ally.x_coord][ally.y_coord];
227 
228  //Is there an ally in that cell?
229  if(allied_cell.contents!=SELF || allied_cell.occupant_data->player!=actingRobot.player)
230  throw RoboSimExecutionException("attempted to charge non-ally, or cell with no robot in it",actingRobot.player,*actingRobot.assoc_cell,allied_cell);
231 
232  //Perform the charge
233  actingRobot.status.power-=power;
234  actingRobot.status.charge-=power;
235  allied_cell.occupant_data->status.charge+=power;
236  }
237 
238  void sendMessage(vector<uint8_t> message, int power)
239  {
240  if(power < 1 || power > 2)
241  throw RoboSimExecutionException("attempted to send message with invalid power", actingRobot.player,*actingRobot.assoc_cell);
242 
243  if(message.size()!=64)
244  throw RoboSimExecutionException("attempted to send message byte array of incorrect length", actingRobot.player,*actingRobot.assoc_cell);
245 
246  GridCell* target = NULL;
247  if(power==1)
248  {
249  target = RobotUtility::findNearestAlly(*actingRobot.assoc_cell,rsim.worldGrid);
250  if(target!=NULL)
251  {
252  /*There's a way to "cheat" here and set up a power-free comm channel
253  *between two allied robots. If you can find it ... let me know, and
254  *you'll get extra credit :). Additional credit for a bugfix.*/
255  target->occupant_data->buffered_radio.push_back(message);
256  }
257  return;
258  }
259  else //power==2
260  for(RobotData& x : rsim.turnOrder)
261  if(&x!=&actingRobot && x.player==actingRobot.player)
262  x.buffered_radio.push_back(message);
263  }
264 
265  //It's a wonderful day in the neighborhood...
266  vector<vector<GridCell> > getVisibleNeighborhood()
267  {
268  //YAY! No parameters means NO ERROR CHECKING! YAY!
269  const int range = actingRobot.specs.defense;
270  const int xloc = actingRobot.assoc_cell->x_coord;
271  const int yloc = actingRobot.assoc_cell->y_coord;
272  const int x_left = (xloc - range < 0) ? 0 : (xloc - range);
273  const int x_right = (xloc + range > rsim.worldGrid.size()-1) ? (rsim.worldGrid.size()-1) : (xloc + range);
274  const int y_up = (yloc - range < 0) ? 0 : (yloc - range);
275  const int y_down = (yloc + range > rsim.worldGrid[0].size() - 1) ? (rsim.worldGrid[0].size()-1) : (yloc + range);
276  vector<vector<GridCell> > to_return = rsim.getSanitizedSubGrid(x_left,y_up,x_right,y_down,actingRobot.player);
277 
278  //Set associated cell to SELF instead of ALLY
279  to_return[xloc - x_left][yloc - y_up].contents=SELF;
280  return to_return;
281  }
282 
283  vector<vector<GridCell> > getWorld(int power)
284  {
285  if(power!=3)
286  throw RoboSimExecutionException("tried to get world with invalid power (not equal to 3)",actingRobot.player,*actingRobot.assoc_cell);
287 
288  vector<vector<GridCell> > to_return = rsim.getSanitizedSubGrid(0,0,rsim.worldGrid.size()-1,rsim.worldGrid[0].size()-1,actingRobot.player);
289 
290  //Set self to self instead of ally
291  to_return[actingRobot.assoc_cell->x_coord][actingRobot.assoc_cell->y_coord].contents=SELF;
292  return to_return;
293  }
294 
295  void scanEnemy(Robot_Specs& enemySpecs, Robot_Status& enemyStatus, GridCell toScan)
296  {
297  if(toScan.x_coord < 0 || toScan.x_coord >= rsim.worldGrid.size() || toScan.y_coord < 0 || toScan.y_coord >= rsim.worldGrid[0].size() || actingRobot.status.power==0)
298  throw RoboSimExecutionException("Invalid parameters passed to scanEnemy()",actingRobot.player,*actingRobot.assoc_cell);
299 
300  GridCell& cell = rsim.worldGrid[toScan.x_coord][toScan.y_coord];
301 
302  //Are we within range?
303  if(abs(actingRobot.assoc_cell->x_coord - cell.x_coord) > actingRobot.specs.defense || abs(actingRobot.assoc_cell->y_coord - cell.y_coord) > actingRobot.specs.defense)
304  throw RoboSimExecutionException("attempted to scan farther than range", actingRobot.player, *actingRobot.assoc_cell);
305 
306  //Is there a robot in this cell?
307  if(cell.contents != SELF)
308  throw RoboSimExecutionException("attempted to scan invalid cell (no robot in cell)", actingRobot.player, *actingRobot.assoc_cell, cell);
309 
310  //Register cost
311  actingRobot.status.power--;
312  actingRobot.status.charge--;
313 
314  //Okay, we're good. Fill in the data.
315  enemySpecs.attack = cell.occupant_data->specs.attack;
316  enemySpecs.defense = cell.occupant_data->specs.defense;
317  enemySpecs.power = cell.occupant_data->specs.power;
318  enemySpecs.charge = cell.occupant_data->specs.charge;
319  enemyStatus.power = cell.occupant_data->status.power;
320  enemyStatus.charge = cell.occupant_data->status.charge;
321  enemyStatus.health = cell.occupant_data->status.health;
322  enemyStatus.defense_boost = cell.occupant_data->status.defense_boost;
323  enemyStatus.capsules = cell.occupant_data->status.capsules;
324  }
325  };
326 
327 public:
333  {
334  for(turnOrder_pos=0; turnOrder_pos<turnOrder.size(); turnOrder_pos++)
335  {
336  //References to robot's data
337  RobotData& data = turnOrder[turnOrder_pos];
338  RoboAPIImplementor student_api(*this,data);
339 
340  //Charge robot an amount of charge equal to charge skill
341  data.status.charge = min(data.status.charge + data.specs.charge, data.specs.charge*10);
342 
343  /*We can spend up to status.power power this turn, but
344  *no more than our current charge level*/
345  data.status.power = min(data.specs.power, data.status.charge);
346 
347  //Defense boost reset to zero at beginning of turn
348  data.status.defense_boost = 0;
349 
350  //Clone status for student
351  Robot_Status clonedStatus = data.status;
352 
353  //Run student code
354  data.robot->act(student_api,clonedStatus,data.buffered_radio);
355  data.buffered_radio.clear();
356  }
357 
358  int player = turnOrder[0].player;
359  for(int i=1; i<turnOrder.size(); i++)
360  if(turnOrder[i].player!=player)
361  return -1;
362  return player;
363  }
364 };
SimGridAllyDeterminant(const GridCell &origin_)
Definition: RoboSim.hpp:32
RobotData * occupant_data
Definition: robot_api.hpp:96
vector< vector< uint8_t > > buffered_radio
Definition: robot_api.hpp:119
vector< vector< GridCell > > getVisibleNeighborhood()
Definition: RoboSim.hpp:266
RoboAPIImplementor(RoboSim &rsim_, RobotData &actingRobot_)
Definition: RoboSim.hpp:104
int turnOrder_pos
Definition: RoboSim.hpp:45
GridObject contents
Definition: robot_api.hpp:83
void repair(int power)
Definition: RoboSim.hpp:197
vector< vector< GridCell > > getWorld(int power)
Definition: RoboSim.hpp:283
Robot_Status status
Definition: robot_api.hpp:109
int x
Definition: test.cpp:1
BuildStatus getBuildStatus()
Definition: RoboSim.hpp:160
void scanEnemy(Robot_Specs &enemySpecs, Robot_Status &enemyStatus, GridCell toScan)
Definition: RoboSim.hpp:295
BuildStatus whatBuilding
Definition: robot_api.hpp:114
bool isAdjacent(GridCell adjacent_cell)
Definition: RoboSim.hpp:112
vector< RobotData > turnOrder
Definition: RoboSim.hpp:44
void charge(int power, GridCell &ally)
Definition: RoboSim.hpp:210
vector< vector< GridCell > > & getWorldGrid()
Definition: RoboSim.hpp:49
int getOccupantPlayer(const GridCell &cell) const
Definition: RoboSim.hpp:54
int executeSingleTimeStep()
Definition: RoboSim.hpp:332
void build(int power)
Definition: RoboSim.hpp:188
GridCell * assoc_cell
Definition: robot_api.hpp:107
GridCell * getBuildTarget()
Definition: RoboSim.hpp:165
void setBuildTarget(BuildStatus status, GridCell *location)
Definition: RoboSim.hpp:181
vector< vector< GridCell > > worldGrid
Definition: RoboSim.hpp:43
void defend(int power)
Definition: RoboSim.hpp:145
vector< vector< GridCell > > getSanitizedSubGrid(int x_left, int y_up, int x_right, int y_down, int player) const
Definition: RoboSim.cpp:34
static GridCell * findNearestAlly(GridCell &origin, std::vector< std::vector< GridCell > > &grid)
Definition: robot_api.cpp:19
GridCell * invested_assoc_cell
Definition: robot_api.hpp:116
void sendMessage(vector< uint8_t > message, int power)
Definition: RoboSim.hpp:238
bool calculateHit(int attack, int defense)
Definition: RoboSim.hpp:126
virtual void act(WorldAPI &api, Robot_Status status, std::vector< std::vector< std::uint8_t > > received_radio)=0