RoboSim.cpp
Go to the documentation of this file.
1 #include "RoboSim.hpp"
2 #include "player_config.hpp"
3 
4 #include <algorithm>
5 
6 using std::ceil;
7 using std::find;
8 using std::list;
9 using std::rand;
10 using std::to_string;
11 
12 using namespace robot_api;
13 
15 {
16  if(&potential_ally==&origin)
17  return false;
18 
19  switch(potential_ally.contents)
20  {
21  case ALLY:
22  return true;
23  case SELF:
24  if(!potential_ally.has_private_members || !origin.has_private_members)
25  return false;
26  if(potential_ally.occupant_data!=NULL && origin.occupant_data!=NULL &&
27  potential_ally.occupant_data->player==origin.occupant_data->player)
28  return true;
29  default:
30  return false;
31  }
32 }
33 
34 vector<vector<GridCell> > RoboSim::getSanitizedSubGrid(int x_left, int y_up, int x_right, int y_down, int player) const
35 {
36  const int x_length = x_right - x_left + 1;
37  const int y_height = y_down - y_up + 1;
38 
39  vector<vector<GridCell> > to_return(x_length,vector<GridCell>(y_height));
40  for(int i=x_left; i<=x_right; i++)
41  for(int j=y_up; j<=y_down; j++)
42  {
43  GridCell& sanitized = to_return[i-x_left][j-y_up];
44  sanitized = worldGrid[i][j];
45  if(sanitized.contents==SELF)
46  if(sanitized.occupant_data->player==player)
47  sanitized.contents = ALLY;
48  else
49  sanitized.contents = ENEMY;
50  sanitized.has_private_members = false;
51  sanitized.occupant_data = NULL;
52  sanitized.wallforthealth = 0;
53  }
54 
55  return to_return;
56 }
57 
58 Robot_Specs RoboSim::checkSpecsValid(Robot_Specs proposed, int player, int skill_points)
59 {
60  if(proposed.attack + proposed.defense + proposed.power + proposed.charge != skill_points)
61  throw RoboSimExecutionException("attempted to create invalid robot!",player);
62  else
63  return proposed;
64 }
65 
66 RoboSim::RoboSim(int initial_robots_per_combatant, int skill_points, int length, int width, int obstacles) :
67  worldGrid(length,vector<GridCell>(width)), turnOrder(RBP_NUM_PLAYERS*initial_robots_per_combatant),
68  turnOrder_pos(0)
69 {
70  for(int i=0; i<length; i++)
71  for(int j=0; j<width; j++)
72  {
73  worldGrid[i][j].x_coord = i;
74  worldGrid[i][j].y_coord = j;
75  worldGrid[i][j].contents = EMPTY;
76  }
77 
78  //Add robots for each combatant
79  for(int player=1; player<=RBP_NUM_PLAYERS; player++)
80  {
81  for(int i=0; i<initial_robots_per_combatant; i++)
82  {
83  int x_pos,y_pos;
84  do
85  {
86  x_pos = rand()%length;
87  y_pos = rand()%width;
88  } while(worldGrid[x_pos][y_pos].contents!=EMPTY);
89 
90  worldGrid[x_pos][y_pos].contents = SELF;
91  RobotData& data = turnOrder[(player-1)*initial_robots_per_combatant+i];
92  worldGrid[x_pos][y_pos].occupant_data = &data;
93  data.assoc_cell = &worldGrid[x_pos][y_pos];
94  data.robot = RBP_CALL_CONSTRUCTOR(player);
95  data.player = player;
96  vector<uint8_t> creation_message(64);
97  creation_message[1] = turnOrder_pos % 256;
98  creation_message[0] = turnOrder_pos / 256;
99  data.specs = checkSpecsValid(data.robot->createRobot(NULL, skill_points, creation_message), player, skill_points);
100  data.status.charge = data.status.health = data.specs.charge*10;
101 
102  data.status.defense_boost = 0;
103  data.whatBuilding = NOTHING;
104  data.investedPower = 0;
105  data.invested_assoc_cell = NULL;
106  }
107  }
108 
109  //Add obstacles to battlefield
110  for(int i=0; i<obstacles; i++)
111  {
112  int x_pos, y_pos;
113  do
114  {
115  x_pos = rand()%length;
116  y_pos = rand()%width;
117  } while(worldGrid[x_pos][y_pos].contents!=EMPTY);
118  worldGrid[x_pos][y_pos].contents = WALL;
119  worldGrid[x_pos][y_pos].wallforthealth = WALL_HEALTH;
120  }
121 }
122 
123 AttackResult RoboSim::RoboAPIImplementor::processAttack(int attack, GridCell& cell_to_attack, int power)
124 {
125  //Holds result of attack
126  AttackResult to_return = MISSED;
127 
128  //Calculate defense skill of opponent
129  int defense = 0;
130  switch(cell_to_attack.contents)
131  {
132  case SELF:
133  defense = cell_to_attack.occupant_data->specs.defense + cell_to_attack.occupant_data->status.defense_boost;
134  break;
135 
136  case FORT:
137  case WALL:
138  defense = 10;
139  break;
140  }
141 
142 
143  //If we hit, damage the opponent
144  if(calculateHit(attack,defense))
145  {
146  //We hit
147  to_return = HIT;
148 
149  if(cell_to_attack.occupant_data!=NULL)
150  {
151  //we're a robot
152  for(int i=0; true; i++)
153  if(&rsim.turnOrder[i]==cell_to_attack.occupant_data)
154  {
155  if((cell_to_attack.occupant_data->status.health-=power)<=0)
156  {
157  //We destroyed the opponent!
158  to_return = DESTROYED_TARGET;
159 
160  //Handle in-progress build, reusing setBuildTarget() to handle interruption of build due to death
161  RoboAPIImplementor(rsim,*(cell_to_attack.occupant_data)).setBuildTarget(NOTHING,cell_to_attack.occupant_data->invested_assoc_cell);
162 
163  //Handle cell
164  cell_to_attack.occupant_data = NULL;
165  cell_to_attack.contents = cell_to_attack.wallforthealth>0 ? FORT : EMPTY;
166 
167  //Handle turnOrder position
168  rsim.turnOrder.erase(rsim.turnOrder.begin()+i);
169  if(i<rsim.turnOrder_pos)
171  }
172  break;
173  }
174  }
175  else
176  if((cell_to_attack.wallforthealth-=power)<=0)
177  {
178  //We destroyed the target!
179  to_return = DESTROYED_TARGET;
180 
181  cell_to_attack.wallforthealth = 0;
182  cell_to_attack.contents = EMPTY;
183  }
184  }
185 
186  return to_return;
187 }
188 
190 {
191  //Lots of error checking here (as everywhere...)
192 
193  //Check that we're using a valid amount of power
194  if(power > actingRobot.status.power || power > actingRobot.specs.attack || power < 1)
195  throw RoboSimExecutionException("attempted melee attack with illegal power level",actingRobot.player,*actingRobot.assoc_cell);
196 
197  //Are cells adjacent?
198  if(!isAdjacent(adjacent_cell))
199  throw RoboSimExecutionException("attempted to melee attack nonadjacent cell",actingRobot.player,*actingRobot.assoc_cell);
200 
201  //Does cell exist in grid?
202  //(could put this in isAdjacent() method but want to give students more useful error messages)
203  if(adjacent_cell.x_coord > rsim.worldGrid.size() || adjacent_cell.y_coord > rsim.worldGrid[0].size() ||
204  adjacent_cell.x_coord < 0 || adjacent_cell.y_coord < 0)
205  throw RoboSimExecutionException("passed invalid cell coordinates to meleeAttack()",actingRobot.player,*actingRobot.assoc_cell,adjacent_cell);
206 
207  //Safe to use this now, checked for oob condition from student
208  GridCell& cell_to_attack = rsim.worldGrid[adjacent_cell.x_coord][adjacent_cell.y_coord];
209 
210  //Is there an enemy, fort, or wall at the cell's location?
211  switch(cell_to_attack.contents)
212  {
213  case EMPTY:
214  throw RoboSimExecutionException("attempted to attack empty cell",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
215  case BLOCKED:
216  throw RoboSimExecutionException("attempted to attack blocked tile",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
217  case SELF:
218  if(cell_to_attack.occupant_data->player==actingRobot.player)
219  throw RoboSimExecutionException("attempted to attack ally",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
220  break;
221  case CAPSULE:
222  throw RoboSimExecutionException("attempted to attack energy capsule",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
223  case ALLY:
224  throw RoboSimExecutionException("ERROR in RoboSim.RoboAPIImplementor.meleeAttack(). This is probably not the student's fault. Contact Patrick Simmons about this message. (Not the Doobie Brother...)");
225  }
226 
227  //Okay, if we haven't thrown an exception, the cell is valid to attack. Perform the attack.
228  //Update this robot's charge status and power status.
229  actingRobot.status.charge-=power;
230  actingRobot.status.power-=power;
231 
232  //Begin calculation of our attack power
233  int raw_attack = actingRobot.specs.attack;
234 
235  //If we're outside a fort attacking someone in the fort, range penalty applies
236  if(cell_to_attack.wallforthealth > 0 && cell_to_attack.occupant_data!=NULL)
237  raw_attack/=2;
238 
239  //Attack adds power of attack to raw skill
240  int attack = raw_attack + power;
241 
242  //Process attack
243  return processAttack(attack,cell_to_attack,power);
244 }
245 
247 {
248  //Lots of error checking here (as everywhere...)
249 
250  //Check that we're using a valid amount of power
251  if(power > actingRobot.status.power || power > actingRobot.specs.attack || power < 1)
252  throw RoboSimExecutionException("attempted ranged attack with illegal power level",actingRobot.player,*actingRobot.assoc_cell);
253 
254  //Does cell exist in grid?
255  //(could put this in isAdjacent() method but want to give students more useful error messages)
256  if(nonadjacent_cell.x_coord > rsim.worldGrid.size() || nonadjacent_cell.y_coord > rsim.worldGrid[0].size() ||
257  nonadjacent_cell.x_coord < 0 || nonadjacent_cell.y_coord < 0)
258  throw RoboSimExecutionException("passed invalid cell coordinates to rangedAttack()",actingRobot.player,*actingRobot.assoc_cell,nonadjacent_cell);
259 
260  //Are cells nonadjacent?
261  if(isAdjacent(nonadjacent_cell))
262  throw RoboSimExecutionException("attempted to range attack adjacent cell",actingRobot.player,*actingRobot.assoc_cell);
263 
264  //Safe to use this now, checked for oob condition from student
265  GridCell& cell_to_attack = rsim.worldGrid[nonadjacent_cell.x_coord][nonadjacent_cell.y_coord];
266 
267  //Do we have a "clear shot"?
268  list<GridCell*> shortest_path = robot_api::RobotUtility::findShortestPath(*actingRobot.assoc_cell,cell_to_attack,rsim.worldGrid);
269  if(!shortest_path.size()) //we don't have a clear shot
270  throw RoboSimExecutionException("attempted to range attack cell with no clear path",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
271  else if(shortest_path.size()>actingRobot.specs.defense) //out of range
272  throw RoboSimExecutionException("attempted to range attack cell more than (defense) tiles away",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
273 
274  //Is there an enemy, fort, or wall at the cell's location?
275  switch(cell_to_attack.contents)
276  {
277  case EMPTY:
278  throw RoboSimExecutionException("attempted to attack empty cell",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
279  case BLOCKED:
280  throw RoboSimExecutionException("attempted to attack blocked tile",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
281  case SELF:
282  if(cell_to_attack.occupant_data->player==actingRobot.player)
283  throw RoboSimExecutionException("attempted to attack ally",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
284  break;
285  case CAPSULE:
286  throw RoboSimExecutionException("attempted to attack energy capsule",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
287  case ALLY:
288  throw RoboSimExecutionException("ERROR in RoboSim.RoboAPIImplementor.rangedAttack(). This is probably not the student's fault. Contact Patrick Simmons about this message. (Not the Doobie Brother...)");
289  }
290 
291  //Okay, if we haven't thrown an exception, the cell is valid to attack. Perform the attack.
292  //Update this robot's charge status.
293  actingRobot.status.charge-=power;
294  actingRobot.status.power-=power;
295 
296  //Begin calculation of our attack power
297  int raw_attack = actingRobot.specs.attack/2;
298 
299  //Attack adds power of attack to raw skill
300  int attack = raw_attack + power;
301 
302  //Process attack
303  return processAttack(attack,cell_to_attack,power);
304 }
305 
307 {
308  //Error checking, *sigh*...
309 
310  //Does cell exist in grid?
311  if(cell.x_coord > rsim.worldGrid.size() || cell.y_coord > rsim.worldGrid[0].size() || cell.x_coord < 0 || cell.y_coord < 0)
312  throw RoboSimExecutionException("passed invalid cell coordinates to capsuleAttack()",actingRobot.player,*actingRobot.assoc_cell,cell);
313 
314  //Cell to attack
315  GridCell& cell_to_attack = rsim.worldGrid[cell.x_coord][cell.y_coord];
316 
317  //Do we have a capsule of this power rating?
318  auto capsule_it = find(actingRobot.status.capsules.begin(),actingRobot.status.capsules.end(),power_of_capsule);
319 
320  if(capsule_it==actingRobot.status.capsules.end())
321  throw RoboSimExecutionException(string("passed invalid power to capsuleAttack(): doesn't have capsule of power ")+to_string(power_of_capsule),actingRobot.player,*actingRobot.assoc_cell);
322 
323  //Can we use this capsule? (attack + defense >= power)
324  if(actingRobot.specs.attack + actingRobot.specs.defense < power_of_capsule)
325  throw RoboSimExecutionException("attempted to use capsule of greater power than attack+defense",actingRobot.player,*actingRobot.assoc_cell);
326 
327  //Can we hit the target? Range is power of capsule + defense.
328  list<GridCell*> shortest_path = robot_api::RobotUtility::findShortestPath(*actingRobot.assoc_cell,cell_to_attack,rsim.worldGrid);
329 
330  if(!shortest_path.size())
331  throw RoboSimExecutionException("no clear shot to target",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
332 
333  if(shortest_path.size() > power_of_capsule + actingRobot.specs.defense)
334  throw RoboSimExecutionException("target not in range",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
335 
336  //Is there an enemy, fort, or wall at the cell's location?
337  switch(cell_to_attack.contents)
338  {
339  case EMPTY:
340  throw RoboSimExecutionException("attempted to attack empty cell",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
341  case BLOCKED:
342  throw RoboSimExecutionException("attempted to attack blocked tile",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
343  case SELF:
344  if(cell_to_attack.occupant_data->player==actingRobot.player)
345  throw RoboSimExecutionException("attempted to attack ally",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
346  break;
347  case CAPSULE:
348  throw RoboSimExecutionException("attempted to attack energy capsule",actingRobot.player,*actingRobot.assoc_cell,cell_to_attack);
349  case ALLY:
350  throw RoboSimExecutionException("ERROR in RoboSim.RoboAPIImplementor.capsuleAttack(). This is probably not the student's fault. Contact Patrick Simmons about this message. (Not the Doobie Brother...)");
351  }
352 
353  /*Okay, if we're still here, we can use the capsule.
354  Need to delete capsule from robot status structure.
355  */
356  actingRobot.status.capsules.erase(capsule_it);
357 
358  //Process attack
359  return processAttack(actingRobot.specs.attack + power_of_capsule,cell_to_attack,(int)(ceil(0.1 * power_of_capsule * actingRobot.specs.attack)));
360 }
361 
363 {
364  if(steps<1)
365  return;
366 
367  int x_coord = actingRobot.assoc_cell->x_coord;
368  const int actor_x = x_coord;
369  int y_coord = actingRobot.assoc_cell->y_coord;
370  const int actor_y = y_coord;
371  switch(way)
372  {
373  case UP:
374  y_coord-=steps;
375  break;
376  case DOWN:
377  y_coord+=steps;
378  break;
379  case LEFT:
380  x_coord-=steps;
381  break;
382  case RIGHT:
383  x_coord+=steps;
384  break;
385  }
386 
387  //Is our destination in the map?
388  if(x_coord < 0 || x_coord > rsim.worldGrid.size() || y_coord < 0 || y_coord > rsim.worldGrid[0].size())
389  throw RoboSimExecutionException("attempted to move out of bounds",actingRobot.player,*actingRobot.assoc_cell);
390 
391  //Is our destination empty?
392  if(rsim.worldGrid[x_coord][y_coord].contents!=EMPTY && rsim.worldGrid[x_coord][y_coord].contents!=FORT)
393  throw RoboSimExecutionException("attempted to move onto illegal cell",actingRobot.player,*actingRobot.assoc_cell,rsim.worldGrid[x_coord][y_coord]);
394 
395  //Are we approaching the fort from the right angle?
396  if(rsim.worldGrid[x_coord][y_coord].contents==FORT && rsim.worldGrid[x_coord][y_coord].fort_orientation!=way)
397  throw RoboSimExecutionException("attempted to move onto a fort from an illegal direction",actingRobot.player,*actingRobot.assoc_cell,rsim.worldGrid[x_coord][y_coord]);
398 
399  //Okay, now we have to make sure each step is empty
400  const bool x_left = x_coord<actor_x;
401  const bool y_left = y_coord<actor_y;
402  if(x_coord!=actor_x)
403  {
404  for(int i=(x_left ? actor_x-1 : actor_x+1); i!=x_coord; i=(x_left ? i-1 : i+1))
405  if(rsim.worldGrid[i][y_coord].contents!=EMPTY)
406  throw RoboSimExecutionException("attempted to cross illegal cell",actingRobot.player,*actingRobot.assoc_cell,rsim.worldGrid[i][y_coord]);
407  }
408  else
409  {
410  for(int i=(y_left ? actor_y-1 : actor_y+1); i!=y_coord; i=(y_left ? i-1 : i+1))
411  if(rsim.worldGrid[x_coord][i].contents!=EMPTY)
412  throw RoboSimExecutionException("attempted to cross illegal cell",actingRobot.player,*actingRobot.assoc_cell,rsim.worldGrid[x_coord][i]);
413  }
414 
415  //Okay, now: do we have enough power/charge?
416  if(steps > actingRobot.status.power)
417  throw RoboSimExecutionException("attempted to move too far (not enough power)",actingRobot.player,*actingRobot.assoc_cell,rsim.worldGrid[x_coord][y_coord]);
418 
419  //Account for power cost
420  actingRobot.status.power-=steps;
421  actingRobot.status.charge-=steps;
422 
423  //Change position of robot.
424  actingRobot.assoc_cell->contents = EMPTY;
425  actingRobot.assoc_cell->occupant_data = NULL;
426  actingRobot.assoc_cell = &rsim.worldGrid[x_coord][y_coord];
427  actingRobot.assoc_cell->contents = SELF;
428  actingRobot.assoc_cell->occupant_data = &actingRobot;
429 }
430 
432 {
433  //Error checking, *sigh*...
434 
435  //Does cell exist in grid?
436  if(adjacent_cell.x_coord > rsim.worldGrid.size() || adjacent_cell.y_coord > rsim.worldGrid[0].size() || adjacent_cell.x_coord < 0 || adjacent_cell.y_coord < 0)
437  throw RoboSimExecutionException("passed invalid cell coordinates to pick_up_capsule()",actingRobot.player,*actingRobot.assoc_cell,adjacent_cell);
438 
439  //Cell in question
440  GridCell& gridCell = rsim.worldGrid[adjacent_cell.x_coord][adjacent_cell.y_coord];
441 
442  //Cell must be adjacent
443  if(!isAdjacent(adjacent_cell))
444  throw RoboSimExecutionException("attempted to pick up capsule in nonadjacent cell",actingRobot.player,*actingRobot.assoc_cell,gridCell);
445 
446  //We need at least one power.
447  if(actingRobot.status.power==0)
448  throw RoboSimExecutionException("attempted to pick up capsule with no power",actingRobot.player,*actingRobot.assoc_cell,gridCell);
449 
450  //Is there actually a capsule there?
451  if(gridCell.contents!=CAPSULE)
452  throw RoboSimExecutionException("attempted to pick up capsule from cell with no capsule",actingRobot.player,*actingRobot.assoc_cell,gridCell);
453 
454  //Do we have "room" for this capsule?
455  if(actingRobot.status.capsules.size()+1>actingRobot.specs.attack+actingRobot.specs.defense)
456  throw RoboSimExecutionException("attempted to pick up too many capsules",actingRobot.player,*actingRobot.assoc_cell,gridCell);
457 
458  //If still here, yes.
459 
460  //Decrement our power
461  actingRobot.status.power--;
462 
463  //Put capsule in our inventory, delete it from world
464  actingRobot.status.capsules.push_back(gridCell.capsule_power);
465  gridCell.contents = EMPTY;
466  gridCell.capsule_power = 0;
467 }
468 
469 void RoboSim::RoboAPIImplementor::drop_capsule(GridCell& adjacent_cell, int power_of_capsule)
470 {
471  //Error checking, *sigh*...
472  //Does cell exist in grid?
473  if(adjacent_cell.x_coord > rsim.worldGrid.size() || adjacent_cell.y_coord > rsim.worldGrid[0].size() || adjacent_cell.x_coord < 0 || adjacent_cell.y_coord < 0)
474  throw RoboSimExecutionException("passed invalid cell coordinates to pick_up_capsule()",actingRobot.player,*actingRobot.assoc_cell,adjacent_cell);
475 
476  //Cell in question
477  GridCell& gridCell = rsim.worldGrid[adjacent_cell.x_coord][adjacent_cell.y_coord];
478 
479  //Cell must be adjacent
480  if(!isAdjacent(adjacent_cell))
481  throw RoboSimExecutionException("attempted to pick up capsule in nonadjacent cell",actingRobot.player,*actingRobot.assoc_cell,gridCell);
482 
483  //Is the cell empty?
484  if(gridCell.contents!=EMPTY)
485  throw RoboSimExecutionException("attempted to place capsule in nonempty cell",actingRobot.player,*actingRobot.assoc_cell,gridCell);
486 
487  //Do we have such a capsule?
488  auto it = find(actingRobot.status.capsules.begin(),actingRobot.status.capsules.end(),power_of_capsule);
489  if(it==actingRobot.status.capsules.end())
490  throw RoboSimExecutionException(string("attempted to drop capsule with power ")+to_string(power_of_capsule)+", having no such capsule",actingRobot.player,*actingRobot.assoc_cell,gridCell);
491 
492  //Okay. We're good. Drop the capsule
493  gridCell.contents = CAPSULE;
494  gridCell.capsule_power = power_of_capsule;
495 
496  //Delete it from our inventory
497  actingRobot.status.capsules.erase(it);
498 }
499 
500 void RoboSim::RoboAPIImplementor::finalizeBuilding(vector<uint8_t> creation_message)
501 {
502  //Nothing to finalize if not building anything
503  if(actingRobot.whatBuilding==NOTHING)
504  return;
505 
506  //What do we have to finalize?
507  int capsule_power = actingRobot.investedPower/10;
508  switch(actingRobot.whatBuilding)
509  {
510  case WALL:
511  if(actingRobot.investedPower >= 50)
512  {
513  actingRobot.invested_assoc_cell->contents = WALL;
514  actingRobot.invested_assoc_cell->wallforthealth = WALL_HEALTH;
515  }
516  else
517  actingRobot.invested_assoc_cell->contents = EMPTY;
518  break;
519 
520  case FORT:
521  if(actingRobot.investedPower >= 75)
522  {
523  actingRobot.invested_assoc_cell->contents = FORT;
524  actingRobot.invested_assoc_cell->wallforthealth = WALL_HEALTH;
525  }
526  else
527  actingRobot.invested_assoc_cell->contents = EMPTY;
528  break;
529 
530  case CAPSULE:
531  if(capsule_power!=0)
532  {
533  if(actingRobot.status.capsules.size()+1>actingRobot.specs.attack+actingRobot.specs.defense)
534  throw RoboSimExecutionException("attempted to finish building capsule when already at max capsule capacity",actingRobot.player,*actingRobot.assoc_cell);
535  actingRobot.status.capsules.push_back(capsule_power);
536  }
537  break;
538 
539  case ROBOT:
540  int skill_points = actingRobot.investedPower/20;
541  if(skill_points!=0)
542  {
543  //Check creation message correct size
544  if(creation_message.size()!=0 && creation_message.size()!=64)
545  throw RoboSimExecutionException("passed incorrect sized creation message to setBuildTarget()",actingRobot.player,*actingRobot.assoc_cell,*actingRobot.invested_assoc_cell);
546 
547  //Set default creation message if we don't have one
548  if(!creation_message.size())
549  {
550  creation_message.resize(64);
551  creation_message[1] = rsim.turnOrder.size() % 256;
552  creation_message[0] = rsim.turnOrder.size() / 256;
553  }
554 
555  //Create the robot
556  actingRobot.invested_assoc_cell->contents = SELF;
557  Robot* robot;
558  try
559  {
560  robot = RBP_CALL_CONSTRUCTOR(actingRobot.player);
561  }
562  catch(...)
563  {
564  throw RoboSimExecutionException("something went wrong calling student's constructor", actingRobot.player,*actingRobot.assoc_cell, *actingRobot.invested_assoc_cell);
565  }
566  rsim.turnOrder.emplace_back();
567  RobotData& data = *(rsim.turnOrder.rbegin());
568  data.robot = robot;
569  data.assoc_cell = actingRobot.invested_assoc_cell;
570  actingRobot.invested_assoc_cell->occupant_data = &data;
571  data.player = actingRobot.player;
572  data.specs = checkSpecsValid(data.robot->createRobot(NULL, skill_points, creation_message), actingRobot.player, skill_points);
573  data.status.charge = data.status.health = data.specs.power*10;
574  }
575  else
576  actingRobot.invested_assoc_cell->contents = EMPTY;
577  break;
578  }
579 }
580 
581 void RoboSim::RoboAPIImplementor::setBuildTarget(BuildStatus status, GridCell* location, vector<uint8_t> message)
582 {
583  //If we're in the middle of building something, finalize it.
584  if(actingRobot.whatBuilding!=NOTHING)
585  finalizeBuilding(message);
586 
587  //Error checking, *sigh*...
588 
589  //Does cell exist in grid?
590  if(location!=NULL && (location->x_coord > rsim.worldGrid.size() || location->y_coord > rsim.worldGrid[0].size() || location->x_coord < 0 || location->y_coord < 0))
591  throw RoboSimExecutionException("passed invalid cell coordinates to setBuildTarget()",actingRobot.player,*actingRobot.assoc_cell,*location);
592 
593  //Cell in question
594  GridCell* gridCell = (location!=NULL ? &rsim.worldGrid[location->x_coord][location->y_coord] : NULL);
595 
596  //Update status
597  actingRobot.whatBuilding = status;
598  actingRobot.investedPower = 0;
599  actingRobot.invested_assoc_cell = gridCell;
600 
601  //CAN pass us null, so special-case it
602  if(location==NULL)
603  {
604  //We must be building capsule, then.
605  if(actingRobot.whatBuilding!=NOTHING && actingRobot.whatBuilding!=CAPSULE)
606  throw RoboSimExecutionException("passed null to setBuildTarget() location with non-null and non-capsule build target",actingRobot.player,*actingRobot.assoc_cell);
607  return;
608  }
609 
610  //If location NOT null, must not be building capsule
611  if(status == NOTHING || status == CAPSULE)
612  throw RoboSimExecutionException("attempted to target capsule or null building on non-null adjacent cell",actingRobot.player,*actingRobot.assoc_cell,*location);
613 
614  //Cell must be adjacent
615  if(!isAdjacent(*location))
616  throw RoboSimExecutionException("attempted to set build target to nonadjacent cell",actingRobot.player,*actingRobot.assoc_cell,*gridCell);
617 
618  //Is the cell empty?
619  if(gridCell->contents!=EMPTY)
620  throw RoboSimExecutionException("attempted to set build target to nonempty cell",actingRobot.player,*actingRobot.assoc_cell,*gridCell);
621 
622  //Okay, block off cell since we're building there now.
623  gridCell->contents = BLOCKED;
624 }
RobotData * occupant_data
Definition: robot_api.hpp:96
Definition: Robot.hpp:19
RoboSim(int initial_robots_per_combatant, int skill_points, int length, int width, int obstacles)
Definition: RoboSim.cpp:66
virtual Robot_Specs createRobot(WorldAPI *api, int skill_points, std::vector< std::uint8_t > message)=0
RoboAPIImplementor(RoboSim &rsim_, RobotData &actingRobot_)
Definition: RoboSim.hpp:104
#define RBP_CALL_CONSTRUCTOR(x)
int turnOrder_pos
Definition: RoboSim.hpp:45
GridObject contents
Definition: robot_api.hpp:83
#define RBP_NUM_PLAYERS
AttackResult capsuleAttack(int power_of_capsule, GridCell &cell)
Definition: RoboSim.cpp:306
Robot_Status status
Definition: robot_api.hpp:109
static const int WALL_HEALTH
Definition: RoboSim.hpp:39
AttackResult meleeAttack(int power, GridCell &adjacent_cell)
Definition: RoboSim.cpp:189
AttackResult rangedAttack(int power, GridCell &nonadjacent_cell)
Definition: RoboSim.cpp:246
BuildStatus whatBuilding
Definition: robot_api.hpp:114
vector< RobotData > turnOrder
Definition: RoboSim.hpp:44
void drop_capsule(GridCell &adjacent_cell, int power_of_capsule)
Definition: RoboSim.cpp:469
void move(int steps, Direction way)
Definition: RoboSim.cpp:362
static Robot_Specs checkSpecsValid(Robot_Specs proposed, int player, int skill_points)
Definition: RoboSim.cpp:58
AttackResult processAttack(int attack, GridCell &cell_to_attack, int power)
Definition: RoboSim.cpp:123
GridCell * assoc_cell
Definition: robot_api.hpp:107
bool operator()(const GridCell &potential_ally)
Definition: RoboSim.cpp:14
void setBuildTarget(BuildStatus status, GridCell *location)
Definition: RoboSim.hpp:181
vector< vector< GridCell > > worldGrid
Definition: RoboSim.hpp:43
void pick_up_capsule(GridCell &adjacent_cell)
Definition: RoboSim.cpp:431
static std::list< GridCell * > findShortestPath(GridCell &origin, const GridCell &target, std::vector< std::vector< GridCell > > &grid)
Definition: robot_api.cpp:29
vector< vector< GridCell > > getSanitizedSubGrid(int x_left, int y_up, int x_right, int y_down, int player) const
Definition: RoboSim.cpp:34
void finalizeBuilding(vector< uint8_t > creation_message)
Definition: RoboSim.cpp:500
GridCell * invested_assoc_cell
Definition: robot_api.hpp:116
bool calculateHit(int attack, int defense)
Definition: RoboSim.hpp:126