Main Page | Class List | File List | Class Members | File Members

world.cpp

Go to the documentation of this file.
00001 00002 #include "world.h" 00003 #include "render.h" 00004 00005 00006 00007 #define process2entity(PROC) *static_cast<entity*>(&(PROC.getProgram())) 00008 #define vm2world(VM) *static_cast<world*>(&VM) 00009 00010 00011 entity::entity( uInt seed, 00012 const energy& e, 00013 const memory& m, 00014 const position& p, 00015 const direction& d ) 00016 : m_energy(e) 00017 , m_position(p) 00018 , m_direction(d) 00019 { 00020 resize(m); 00021 generate(seed); 00022 } 00023 00024 00025 00026 void entity::draw() 00027 { 00028 } 00029 00030 00031 00032 tile::tile(float cost, energy e) : m_cost(cost) 00033 , m_energy(e) 00034 , m_pEntity(NULL) 00035 { 00036 } 00037 00038 00039 00040 00041 world::world() : m_size(100,100) 00042 , m_absorbAmount(4) 00043 , m_energyPerUnitMemory(1) 00044 , m_maxEnergyPerTile(g_max_int) 00045 , m_maxCostPerTile(1.0f) 00046 , m_maxEnergyPerEntity(g_max_int) 00047 , m_maxMemoryPerEntity(10000) 00048 { 00049 } 00050 00051 00052 world::~world() 00053 { 00054 // delete all entites 00055 vv_tiles::iterator it = m_tiles.begin(); 00056 for( ; it!=m_tiles.end(); ++it) 00057 { 00058 v_tiles::iterator iit = it->begin(); 00059 for( ; iit!=it->end();++iit) 00060 { 00061 if(iit->m_pEntity) delete iit->m_pEntity; 00062 } 00063 } 00064 } 00065 00066 00067 void world::init() 00068 { 00069 // randomise the tile data 00070 m_tiles.resize(m_size.x); 00071 00072 vv_tiles::iterator it = m_tiles.begin(); 00073 v_tiles::iterator tit; 00074 for( ;it!= m_tiles.end(); ++it) 00075 { 00076 it->resize(m_size.y); 00077 00078 tit = it->begin(); 00079 for( ; tit!=it->end();++tit) 00080 { 00081 tit->m_cost = (float)rand()/RAND_MAX*m_maxCostPerTile; 00082 tit->m_energy = (float)rand()/RAND_MAX*m_maxEnergyPerTile; 00083 } 00084 } 00085 } 00086 00087 00088 instruction world::addInstruction(instructionPtr instrPtr) 00089 { 00090 return virtualMachine::addInstruction(instrPtr); 00091 } 00092 00093 00094 bool world::replaceInstruction(instruction instr, instructionPtr instrPtr) 00095 { 00096 return virtualMachine::replaceInstruction(instr, instrPtr); 00097 } 00098 00099 00100 uInt world::getNumInstructions() 00101 { 00102 return static_cast<uInt>(size()); 00103 } 00104 00105 00106 void world::step() 00107 { 00108 virtualMachine::step(); 00109 } 00110 00111 00112 void world::kill(entityId entId) 00113 { 00114 virtualMachine::kill(entId); 00115 } 00116 00117 00118 entity* world::spawnEntity( energy e, 00119 memory m, 00120 const position& p, 00121 const direction& d ) 00122 { 00123 // make sure the limits are not exceeded 00124 if(e>m_maxEnergyPerEntity) e = m_maxEnergyPerEntity; 00125 if(m>m_maxMemoryPerEntity) m = m_maxMemoryPerEntity; 00126 00127 static uInt seed = 0; 00128 00129 // check the position is valid and not inhabited 00130 if(p.x>=0 && p.x<m_size.x && p.y>=0 && p.y<m_size.y && !getEntityAtPos(p)) 00131 { 00132 // create an entity 00133 entity* pEntity = new entity(seed++, e, m, p, d); 00134 if(pEntity) 00135 { 00136 // start executing its program 00137 pEntity->m_pid = run(*pEntity); 00138 00139 // error check 00140 if(!pEntity->m_pid) 00141 { 00142 delete pEntity; 00143 pEntity = NULL; 00144 } 00145 00146 // add the entity to the tile 00147 m_tiles[p.x][p.y].m_pEntity = pEntity; 00148 00149 return(pEntity); 00150 } 00151 } 00152 00153 return NULL; 00154 } 00155 00156 00157 00158 entity* world::getEntity(entityId entId) 00159 { 00160 process* proc = getProcess(entId); 00161 if(proc) 00162 { 00163 return static_cast<entity*>(&(proc->getProgram())); 00164 } 00165 return NULL; 00166 } 00167 00168 00169 entity* world::getEntityAtPos(const position& p) 00170 { 00171 00172 if(p.x>=0 && p.x<m_size.x && p.y>=0 && p.y<m_size.y) 00173 { 00174 return m_tiles[p.x][p.y].m_pEntity; 00175 } 00176 00177 return NULL; 00178 } 00179 00180 00181 void world::moveEntity(entity& ent) 00182 { 00183 // get the position of the tile to move to 00184 position newPos = getPosInfrontOfEntity(ent, 1); 00185 // get the entity's current tile 00186 tile* currentTile = getTileAtPos(ent.m_position); 00187 // get the tile to move to 00188 tile* newTile = getTileAtPos(newPos); 00189 00190 ASSERT(currentTile, "entity must inhabit tile"); 00191 00192 // check if the tile exists and is emoty 00193 if(newTile && !getEntityAtPos(newPos)) 00194 { 00195 // remove the entity from the current tile 00196 currentTile->m_pEntity = NULL; 00197 // add it to the new tile 00198 newTile->m_pEntity = &ent; 00199 // update the entity's position 00200 ent.m_position = newPos; 00201 00202 // calculate the energy cost of moving 00203 // the entity 00204 energy cost = (energy)( 00205 ent.size()*( 00206 currentTile->m_cost-newTile->m_cost 00207 ) 00208 ); 00209 00210 // transfer that energy from the entity 00211 // to the tile it just vacated 00212 transferEnergy(cost, *currentTile, ent); 00213 } 00214 00215 ASSERT(ent.m_position.x>=0, "invalid coordinate"); 00216 ASSERT(ent.m_position.x<m_size.x, "invalid coordinate"); 00217 ASSERT(ent.m_position.y>=0, "invalid coordinate"); 00218 ASSERT(ent.m_position.y<m_size.y, "invalid coordinate"); 00219 } 00220 00221 00222 void world::rotateEntityLeft(entity& ent) 00223 { 00224 int tmp = ent.m_direction.x; 00225 ent.m_direction.x = -ent.m_direction.y; 00226 ent.m_direction.y = tmp; 00227 } 00228 00229 void world::rotateEntityRight(entity& ent) 00230 { 00231 int tmp = ent.m_direction.x; 00232 ent.m_direction.x = ent.m_direction.y; 00233 ent.m_direction.y = -tmp; 00234 } 00235 00236 00237 energy world::transferEnergy( energy amount, 00238 energy& a, 00239 energy& b ) 00240 { 00241 // fast out 00242 if(amount==0) return 0; 00243 00244 // make sure the amount to transfer 00245 // does not exceed the amount availavle 00246 if(amount>a) 00247 { 00248 amount = a; 00249 00250 } else if(-amount>b) 00251 { 00252 amount = -b; 00253 } 00254 00255 // transfer the energy 00256 a -= amount; 00257 b += amount; 00258 00259 // return the amount transfered 00260 return amount; 00261 } 00262 00263 energy world::transferEnergy( energy amount, 00264 tile& t, 00265 entity& ent ) 00266 { 00267 // fast out 00268 if(amount==0) return 0; 00269 00270 // make sure the maximums are not going to be 00271 // exceeded 00272 if(amount>0 && (amount+ent.m_energy)>m_maxEnergyPerEntity) 00273 { 00274 amount = m_maxEnergyPerEntity-ent.m_energy; 00275 00276 } else if((amount-t.m_energy)<-m_maxEnergyPerTile) 00277 { 00278 amount = -(m_maxEnergyPerTile-t.m_energy); 00279 } 00280 00281 // transfer the energy and return the amount 00282 // transfered 00283 return transferEnergy(amount, t.m_energy, ent.m_energy); 00284 } 00285 00286 00287 energy world::transferEnergy( energy amount, 00288 entity& entA, 00289 entity& entB ) 00290 { 00291 // fast out 00292 if(amount==0) return 0; 00293 00294 // make sure the maximums are not going to be 00295 // exceeded 00296 if(amount>0 && (amount+entB.m_energy)>m_maxEnergyPerEntity) 00297 { 00298 amount = m_maxEnergyPerEntity-entB.m_energy; 00299 00300 } else if((amount-entA.m_energy)<-m_maxEnergyPerEntity) 00301 { 00302 amount = -(m_maxEnergyPerEntity-entA.m_energy); 00303 } 00304 00305 // transfer the energy and return the amount 00306 // transfered 00307 return transferEnergy(amount, entA.m_energy, entB.m_energy); 00308 } 00309 00310 00311 energy world::transferEnergy(energy amount, entity& ent) 00312 { 00313 // get the tile infront of the entity 00314 position infront = getPosInfrontOfEntity(ent, 1); 00315 tile* tileInfront = getTileAtPos(infront); 00316 00317 // check if the tile exists 00318 if(tileInfront && tileInfront->m_pEntity) 00319 { 00320 // transfer the energy and return the amount 00321 // transfered 00322 return transferEnergy(amount, ent, *(tileInfront->m_pEntity)); 00323 } 00324 00325 return 0; 00326 } 00327 00328 00329 void world::absorbEnergy(entity& ent) 00330 { 00331 tile* currentTile = getTileAtPos(ent.m_position); 00332 ASSERT(currentTile, "entity must inhabit tile"); 00333 00334 // calculate the tile energy ratio 00335 energy halfMax = m_maxEnergyPerTile/2; 00336 float tileEnergyRatio = (float)(currentTile->m_energy-halfMax)/halfMax; 00337 00338 // calculate the energy to be transfered 00339 energy amount = m_absorbAmount*tileEnergyRatio; 00340 00341 // transfer the energy 00342 transferEnergy(amount, *currentTile, ent); 00343 } 00344 00345 00346 energy world::releaseEnergy(energy amount, entity& ent) 00347 { 00348 tile* currentTile = getTileAtPos(ent.m_position); 00349 ASSERT(currentTile, "entity must inhabit tile"); 00350 00351 // make sure entity's don't try anything too clever 00352 if(amount<=0) return 0; 00353 00354 // transfer the energy and return the amount 00355 // released 00356 return -transferEnergy(-amount, *currentTile, ent); 00357 } 00358 00359 00360 void world::transferMemory(address addr, program& a, program& b) 00361 { 00362 uInt sizeA = static_cast<uInt>(a.size()); 00363 uInt sizeB = static_cast<uInt>(b.size()); 00364 00365 // make sure the address is in range 00366 addr%=sizeA; 00367 00368 // up-size the destination 00369 b.resize(sizeB+sizeA-addr); 00370 00371 // copy the data 00372 for(address from=addr, to=sizeB; from<sizeA; ++from, ++to) 00373 { 00374 b[to] = a[from]; 00375 } 00376 00377 // down-size the source 00378 a.resize(addr); 00379 } 00380 00381 00382 void world::transferMemory(address addr, entity& entA, entity& entB) 00383 { 00384 int sizeA = static_cast<int>(entA.size()); 00385 int sizeB = static_cast<int>(entB.size()); 00386 00387 // fast out 00388 if(sizeA<=0) return; 00389 00390 00391 // make sure the address is in range 00392 addr%=sizeA; 00393 00394 memory amountToTransfer = sizeA-addr; 00395 00396 // make sure maximum will not be exceeded 00397 if(amountToTransfer+sizeB>m_maxMemoryPerEntity) 00398 { 00399 amountToTransfer = m_maxMemoryPerEntity-sizeB; 00400 } 00401 00402 // calculate the relative height of the tiles the 00403 // entites inhabit 00404 tile* tileA = getTileAtPos(entA.m_position); 00405 tile* tileB = getTileAtPos(entB.m_position); 00406 00407 ASSERT(tileA, "entity must inhabit tile"); 00408 ASSERT(tileB, "entity must inhabit tile"); 00409 00410 float deltaCost = tileB->m_cost-tileA->m_cost; 00411 00412 // calculate the transfer cost 00413 energy costToTransfer = (energy)(amountToTransfer*deltaCost); 00414 00415 if(costToTransfer>0) 00416 { 00417 // if the cost is positive then the entity doing 00418 // the transfer looses this energy to its tile 00419 costToTransfer = releaseEnergy(costToTransfer, entA); 00420 00421 } else if(costToTransfer<0) 00422 { 00423 // if the cost is negative then the entity gaining 00424 // the memory gains the energy 00425 costToTransfer = transferEnergy(costToTransfer, entB, entA); 00426 } else 00427 { 00428 // otherwise there is no cost 00429 deltaCost = 1.0f; 00430 } 00431 00432 // re-evaluate the cost to transfer the memory from 00433 // the energy (to avoid rounding errors) 00434 amountToTransfer = (memory)(costToTransfer/deltaCost); 00435 00436 // transfer the memory 00437 entB.resize(sizeB+amountToTransfer); 00438 00439 for(int from=sizeA-amountToTransfer, to=sizeB; from<sizeA; ++from, ++to) 00440 { 00441 entB[to] = entA[from]; 00442 } 00443 00444 entA.resize(sizeA-amountToTransfer); 00445 } 00446 00447 00448 void world::giveMemory(entity& ent, address addr) 00449 { 00450 position infront = getPosInfrontOfEntity(ent, 1); 00451 tile* tileInfront = getTileAtPos(infront); 00452 00453 if(tileInfront && tileInfront->m_pEntity) 00454 { 00455 // if the tile infront is inhabited then 00456 // transfer the memory to the entity in that tile 00457 transferMemory(addr, ent, *(tileInfront->m_pEntity)); 00458 00459 } else 00460 { 00461 // otherwise create a new entity in the tile 00462 entity* newEnt = spawnEntity(0, 0, infront, ent.m_direction); 00463 if(newEnt) 00464 { 00465 // and give it the memory 00466 transferMemory(addr, ent, *newEnt); 00467 } 00468 } 00469 } 00470 00471 00472 void world::takeMemory(entity& ent, address addr) 00473 { 00474 position infront = getPosInfrontOfEntity(ent, 1); 00475 tile* tileInfront = getTileAtPos(infront); 00476 00477 if(tileInfront && tileInfront->m_pEntity) 00478 { 00479 // if there is an entity, then grab the memory 00480 transferMemory(addr, *(tileInfront->m_pEntity), ent); 00481 } 00482 } 00483 00484 00485 void world::convertEnergyToMemory(entity& ent, energy amount) 00486 { 00487 // fast out 00488 if(ent.m_energy<=0) return; 00489 00490 // no funny stuff 00491 if(amount<0) amount = -amount; 00492 00493 memory current = static_cast<uInt>(ent.size()); 00494 00495 // make sure the amount of energy is in range 00496 amount%=ent.m_energy; 00497 00498 // calculate the energy as memory 00499 memory toAdd = amount/m_energyPerUnitMemory; 00500 00501 // fast out 00502 if(toAdd==0) return; 00503 00504 // make sure maximum will not be exceeded 00505 if(current + toAdd > m_maxMemoryPerEntity) 00506 { 00507 toAdd = m_maxMemoryPerEntity-current; 00508 } 00509 00510 // recalculate the amount from the memory 00511 // to avoid rounding errors 00512 amount = toAdd*m_energyPerUnitMemory; 00513 00514 // remove the energy 00515 ent.m_energy-=amount; 00516 00517 // add the memory and fill 00518 // it with null instructions 00519 ent.resize(current+toAdd, 0); 00520 } 00521 00522 00523 void world::convertMemoryToEnergy(entity& ent, address addr) 00524 { 00525 // fast out 00526 if(ent.size()==0) return; 00527 00528 // make sure the address is in range 00529 memory current = static_cast<uInt>(ent.size()); 00530 addr%=current; 00531 00532 // calculate the amount of memory to convert 00533 // and the amount of energy 00534 memory toConvert = current - addr; 00535 energy toAdd = toConvert*m_energyPerUnitMemory; 00536 00537 // make sure the maximum will not be exceeded 00538 if(toAdd+ent.m_energy+m_energyPerUnitMemory>m_maxEnergyPerEntity) 00539 { 00540 toAdd = m_maxEnergyPerEntity-ent.m_energy-m_energyPerUnitMemory; 00541 } 00542 00543 // add the energy 00544 ent.m_energy += toAdd; 00545 00546 // recalculate the memory from the energy 00547 toConvert = toAdd/m_energyPerUnitMemory; 00548 00549 // remove the memory 00550 ent.resize(current-toConvert); 00551 00552 // give any the energy not converted 00553 // back to the entity 00554 ent.m_energy+=toAdd%m_energyPerUnitMemory; 00555 } 00556 00557 00558 tile* world::getTileAtPos(const position& p) 00559 { 00560 if(p.x>=0 && p.x<m_size.x && p.y>=0 && p.y<m_size.y) 00561 { 00562 return &(m_tiles[p.x][p.y]); 00563 } 00564 return NULL; 00565 } 00566 00567 00568 position world::getPosInfrontOfEntity(entity& ent, uInt d) 00569 { 00570 position pos; 00571 pos.x = ent.m_position.x+d*ent.m_direction.x; 00572 pos.y = ent.m_position.y+d*ent.m_direction.y; 00573 return pos; 00574 } 00575 00576 00577 00578 00579 00580 void world::draw() 00581 { 00582 float tileSize = 1.0f/m_size.x; 00583 00584 vector2f pos, dim; 00585 00586 dim.x = dim.y = tileSize; 00587 float lineWidth = 1.0f; 00588 00589 colour4f lineColour(1.0f,1.0f,1.0f,1.0f); 00590 colour4f tileColour(0.0f,1.0f,0.0f,1.0f); 00591 colour4f entityColour(1.0f,0.0f,0.0f,1.0f); 00592 00593 vv_tiles::iterator xIt = m_tiles.begin(); 00594 v_tiles::iterator yIt; 00595 for( uInt x=0; xIt!=m_tiles.end(); ++xIt, ++x) 00596 { 00597 yIt = xIt->begin(); 00598 for( uInt y=0; yIt!=xIt->end(); ++yIt, ++y) 00599 { 00600 pos.x = x*tileSize; 00601 pos.y = y*tileSize; 00602 00603 if(yIt->m_pEntity==NULL) 00604 { 00605 tileColour.m_g = yIt->m_cost/m_maxCostPerTile; 00606 drawRectangle(&pos, &dim, NULL, NULL, &tileColour); 00607 } else 00608 { 00609 entityColour.m_b = (float)yIt->m_pEntity->m_energy/1000.0f; 00610 drawRectangle(&pos, &dim, NULL, NULL, &entityColour); 00611 } 00612 } 00613 } 00614 00615 drawGrid(m_size, NULL, NULL, &lineWidth, &lineColour, NULL); 00616 } 00617 00618 00619 00620 bool world::canBeExecuted(process& proc) 00621 { 00622 if(proc.isTerminated()) return false; 00623 00624 entity& ent = process2entity(proc); 00625 return ent.m_energy > 0; 00626 } 00627 00628 00629 00630 00631 void world::kill(process& proc) 00632 { 00633 entity* pEnt = &process2entity(proc); 00634 00635 00636 00637 tile* t = getTileAtPos(pEnt->m_position); 00638 ASSERT(t->m_pEntity==pEnt, "entity not in specified tile"); 00639 t->m_pEntity = NULL; 00640 t->m_energy += pEnt->m_energy; 00641 00642 #ifdef VERBOSE 00643 std::cout << "pid: " << proc.getId() << "\tkill\tE:" << pEnt->m_energy << "\tM:" << pEnt->size() << std::endl; 00644 #endif 00645 00646 delete pEnt; 00647 00648 00649 virtualMachine::kill(proc); 00650 00651 } 00652

Generated on Sun Mar 6 22:11:24 2005 for experiment03 by doxygen 1.3.7