1 /*
2 * cPopulationCell.cc
3 * Avida
4 *
5 * Called "pop_cell.cc" prior to 12/5/05.
6 * Copyright 1999-2011 Michigan State University. All rights reserved.
7 * Copyright 1993-2003 California Institute of Technology.
8 *
9 *
10 * This file is part of Avida.
11 *
12 * Avida is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
14 *
15 * Avida is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License along with Avida.
19 * If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include "cPopulationCell.h"
24
25 #include "cDoubleSum.h"
26 #include "nHardware.h"
27 #include "cOrganism.h"
28 #include "cWorld.h"
29 #include "cEnvironment.h"
30 #include "cPopulation.h"
31 #include "cDeme.h"
32
33 #include <cmath>
34 #include <iterator>
35
36 using namespace std;
37
38
cPopulationCell(const cPopulationCell & in_cell)39 cPopulationCell::cPopulationCell(const cPopulationCell& in_cell)
40 : m_world(in_cell.m_world)
41 , m_organism(in_cell.m_organism)
42 , m_hardware(in_cell.m_hardware)
43 , m_inputs(in_cell.m_inputs)
44 , m_cell_id(in_cell.m_cell_id)
45 , m_deme_id(in_cell.m_deme_id)
46 , m_cell_data(in_cell.m_cell_data)
47 , m_spec_state(in_cell.m_spec_state)
48 , can_input(false)
49 , can_output(false)
50 , m_hgt(0)
51 {
52 // Copy the mutation rates into a new structure
53 m_mut_rates = new cMutationRates(*in_cell.m_mut_rates);
54
55 // Copy the connection list
56 tConstListIterator<cPopulationCell> conn_it(in_cell.m_connections);
57 cPopulationCell* test_cell;
58 while ((test_cell = const_cast<cPopulationCell*>(conn_it.Next()))) m_connections.PushRear(test_cell);
59
60 // copy the hgt information, if needed.
61 if(in_cell.m_hgt) {
62 InitHGTSupport();
63 *m_hgt = *in_cell.m_hgt;
64 }
65 }
66
operator =(const cPopulationCell & in_cell)67 void cPopulationCell::operator=(const cPopulationCell& in_cell)
68 {
69 if(this != &in_cell) {
70 m_world = in_cell.m_world;
71 m_organism = in_cell.m_organism;
72 m_hardware = in_cell.m_hardware;
73 m_inputs = in_cell.m_inputs;
74 m_cell_id = in_cell.m_cell_id;
75 m_deme_id = in_cell.m_deme_id;
76 m_cell_data = in_cell.m_cell_data;
77 m_spec_state = in_cell.m_spec_state;
78 can_input = in_cell.can_input;
79 can_output = in_cell.can_output;
80
81 // Copy the mutation rates, constructing the structure as necessary
82 if (m_mut_rates == NULL)
83 m_mut_rates = new cMutationRates(*in_cell.m_mut_rates);
84 else
85 m_mut_rates->Copy(*in_cell.m_mut_rates);
86
87 // Copy the connection list
88 tConstListIterator<cPopulationCell> conn_it(in_cell.m_connections);
89 cPopulationCell* test_cell;
90 while ((test_cell = const_cast<cPopulationCell*>(conn_it.Next()))) m_connections.PushRear(test_cell);
91
92 // copy hgt information, if needed.
93 delete m_hgt;
94 m_hgt = 0;
95 if(in_cell.m_hgt) {
96 InitHGTSupport();
97 *m_hgt = *in_cell.m_hgt;
98 }
99 }
100 }
101
Setup(cWorld * world,int in_id,const cMutationRates & in_rates,int x,int y)102 void cPopulationCell::Setup(cWorld* world, int in_id, const cMutationRates& in_rates, int x, int y)
103 {
104 m_world = world;
105 m_cell_id = in_id;
106 m_x = x;
107 m_y = y;
108 m_deme_id = -1;
109 m_cell_data.contents = 0;
110 m_cell_data.org_id = -1;
111 m_cell_data.update = -1;
112 m_cell_data.territory = -1;
113 m_spec_state = 0;
114
115 if (m_mut_rates == NULL)
116 m_mut_rates = new cMutationRates(in_rates);
117 else
118 m_mut_rates->Copy(in_rates);
119 }
120
Rotate(cPopulationCell & new_facing)121 void cPopulationCell::Rotate(cPopulationCell& new_facing)
122 {
123 // @CAO Note, this breaks avida if new_facing is not in connection_list
124
125 //@AWC if this cell contains a migrant then we assume new_facing is not in the connection list and bail out ...
126 if(IsMigrant()){
127 UnsetMigrant(); //@AWC -- unset the migrant flag for the next time this cell is used
128 return;
129 }
130
131 #ifdef DEBUG
132 int scan_count = 0;
133 #endif
134 while (m_connections.GetFirst() != &new_facing) {
135 m_connections.CircNext();
136 #ifdef DEBUG
137 assert(++scan_count < m_connections.GetSize());
138 #endif
139 }
140 }
141
142 /*! This method recursively builds a set of cells that neighbor this cell, out to
143 the given depth. The set must be passed in by-reference, as calls to this method
144 must share a common set of already-visited cells.
145 */
GetNeighboringCells(std::set<cPopulationCell * > & cell_set,int depth) const146 void cPopulationCell::GetNeighboringCells(std::set<cPopulationCell*>& cell_set, int depth) const {
147 typedef std::set<cPopulationCell*> cell_set_t;
148
149 // For each cell in our connection list...
150 tConstListIterator<cPopulationCell> i(m_connections);
151 while(!i.AtEnd()) {
152 // store the cell pointer, and check to see if we've already visited that cell...
153 cPopulationCell* cell = i.Next();
154 assert(cell != 0); // cells should never be null.
155 std::pair<cell_set_t::iterator, bool> ins = cell_set.insert(cell);
156 // and if so, recurse to it...
157 if(ins.second && (depth > 1)) {
158 cell->GetNeighboringCells(cell_set, depth-1);
159 }
160 }
161 }
162
163 /*! Recursively build a set of occupied cells that neighbor this one, out to the given depth.
164 */
GetOccupiedNeighboringCells(std::set<cPopulationCell * > & occupied_cell_set,int depth) const165 void cPopulationCell::GetOccupiedNeighboringCells(std::set<cPopulationCell*>& occupied_cell_set, int depth) const {
166 // we'll do this the easy way, and just filter the neighbor set.
167 std::set<cPopulationCell*> cell_set;
168 GetNeighboringCells(cell_set, depth);
169 for(std::set<cPopulationCell*>::iterator i=cell_set.begin(); i!=cell_set.end(); ++i) {
170 if((*i)->IsOccupied()) {
171 occupied_cell_set.insert(*i);
172 }
173 }
174 }
175
GetOccupiedNeighboringCells(Apto::Array<cPopulationCell * > & occupied_cells) const176 void cPopulationCell::GetOccupiedNeighboringCells(Apto::Array<cPopulationCell*>& occupied_cells) const
177 {
178 occupied_cells.Resize(m_connections.GetSize());
179 int occupied_count = 0;
180
181 tLWConstListIterator<cPopulationCell> i(m_connections);
182 while(!i.AtEnd()) {
183 cPopulationCell* cell = i.Next();
184 assert(cell); // cells should never be null.
185 if (cell->IsOccupied()) occupied_cells[occupied_count++] = cell;
186 }
187
188 occupied_cells.Resize(occupied_count);
189 }
190
191
192 /*! These values are chosen so as to make loops on the facing 'easy'.
193 111 = NE
194 101 = E
195 100 = SE
196 000 = S
197 001 = SW
198 011 = W
199 010 = NW
200 110 = N
201
202 Facing is determined by the relative positions of this cell and the cell that
203 is currently faced. Note that we cannot differentiate between directions on a 2x2
204 torus.
205 */
GetFacing()206 int cPopulationCell::GetFacing()
207 {
208 // This whole function is a hack.
209 cPopulationCell* faced = ConnectionList().GetFirst();
210
211 int x=0,y=0,lr=0,du=0;
212 faced->GetPosition(x,y);
213
214 if((x==m_x-1) || (x>m_x+1))
215 lr = -1; //left
216 else if((x==m_x+1) || (x<m_x-1))
217 lr = 1; //right
218
219 if((y==m_y-1) || (y>m_y+1))
220 du = -1; //up
221 else if((y==m_y+1) || (y<m_y-1))
222 du = 1; //down
223
224 // This is hackish.
225 // If you change these return values then the directional send tasks, like sent-north, need to be updated.
226 if(lr==0 && du==-1) return 0; //N
227 else if(lr==-1 && du==-1) return 1; //NW
228 else if(lr==-1 && du==0) return 3; //W
229 else if(lr==-1 && du==1) return 2; //SW
230 else if(lr==0 && du==1) return 6; //S
231 else if(lr==1 && du==1) return 7; //SE
232 else if(lr==1 && du==0) return 5; //E
233 else if(lr==1 && du==-1) return 4; //NE
234
235 assert(false);
236
237 return 0;
238 }
239
GetFacedDir()240 int cPopulationCell::GetFacedDir()
241 {
242 const int facing = GetFacing();
243 int faced_dir = 0;
244 if (facing == 0) faced_dir = 0; //N
245 else if (facing == 1) faced_dir = 7; //NW
246 else if (facing == 3) faced_dir = 6; //W
247 else if (facing == 2) faced_dir = 5; //SW
248 else if (facing == 6) faced_dir = 4; //S
249 else if (facing == 7) faced_dir = 3; //SE
250 else if (facing == 5) faced_dir = 2; //E
251 else if (facing == 4) faced_dir = 1; //NE
252 return faced_dir;
253
254 }
ResetInputs(cAvidaContext & ctx)255 void cPopulationCell::ResetInputs(cAvidaContext& ctx)
256 {
257 m_world->GetEnvironment().SetupInputs(ctx, m_inputs);
258 }
259
260
InsertOrganism(cOrganism * new_org,cAvidaContext & ctx)261 void cPopulationCell::InsertOrganism(cOrganism* new_org, cAvidaContext& ctx)
262 {
263 assert(new_org != NULL);
264 assert(m_organism == NULL);
265
266 // Adjust this cell's attributes to account for the new organism.
267 m_organism = new_org;
268 m_hardware = &new_org->GetHardware();
269 m_world->GetStats().AddSpeculativeWaste(m_spec_state);
270 m_spec_state = 0;
271
272 // Adjust the organism's attributes to match this cell.
273 m_organism->GetOrgInterface().SetCellID(m_cell_id);
274 m_organism->GetOrgInterface().SetDemeID(m_deme_id);
275
276 // If this organism is new, set the previously-seen cell id
277 if(m_organism->GetOrgInterface().GetPrevSeenCellID() == -1) {
278 m_organism->GetOrgInterface().SetPrevSeenCellID(m_cell_id);
279 }
280
281 if(m_world->GetConfig().ENERGY_ENABLED.Get() == 1 && m_world->GetConfig().FRAC_ENERGY_TRANSFER.Get() > 0.0) {
282 // uptake all the cells energy
283 double uptake_energy = UptakeCellEnergy(1.0, ctx);
284 if(uptake_energy != 0.0) {
285 // update energy and merit
286 cPhenotype& phenotype = m_organism->GetPhenotype();
287 phenotype.ReduceEnergy(-1.0 * uptake_energy);
288 phenotype.SetMerit(cMerit(phenotype.ConvertEnergyToMerit(phenotype.GetStoredEnergy() * phenotype.GetEnergyUsageRatio())));
289 }
290 }
291 }
292
RemoveOrganism(cAvidaContext & ctx)293 cOrganism * cPopulationCell::RemoveOrganism(cAvidaContext& ctx)
294 {
295 if (m_organism == NULL) return NULL; // Nothing to do!
296
297 // For the moment, the cell doesn't keep track of much...
298 cOrganism * out_organism = m_organism;
299 if(m_world->GetConfig().ENERGY_ENABLED.Get() == 1 && m_world->GetConfig().FRAC_ENERGY_TRANSFER.Get() > 0.0
300 && m_world->GetConfig().FRAC_ENERGY_DECAY_AT_DEME_BIRTH.Get() != 1.0) { // hack
301 m_world->GetPopulation().GetDeme(m_deme_id).GiveBackCellEnergy(m_cell_id, m_organism->GetPhenotype().GetStoredEnergy() * m_world->GetConfig().FRAC_ENERGY_TRANSFER.Get(), ctx);
302 }
303 m_organism = NULL;
304 m_hardware = NULL;
305 return out_organism;
306 }
307
UptakeCellEnergy(double frac_to_uptake,cAvidaContext & ctx)308 double cPopulationCell::UptakeCellEnergy(double frac_to_uptake, cAvidaContext& ctx) {
309 assert(0.0 <= frac_to_uptake);
310 assert(frac_to_uptake <= 1.0);
311
312 double cell_energy = m_world->GetPopulation().GetDeme(m_deme_id).GetAndClearCellEnergy(m_cell_id, ctx);
313 double uptakeAmount = cell_energy * frac_to_uptake;
314 cell_energy -= uptakeAmount;
315 m_world->GetPopulation().GetDeme(m_deme_id).GiveBackCellEnergy(m_cell_id, cell_energy, ctx);
316 return uptakeAmount;
317 }
318
319
320 // -------- Avatar support --------
321 /* In the avatar system, each cell contains an array of organism pointers to tie the cell
322 * back to all organisms with avatars in that cell. Each organism then contains a list
323 * (in cPopulationInterface) of all it's avatars and the cell for each avatar.
324 * Currently there are two supported avatar types, input and output,
325 * which are also used as predators and prey, respectively.
326 */
327
328 // Adds an organism to the cell's input avatars, then keeps the list mixed by swapping the new avatar into a random position in the array
AddInputAV(cOrganism * org)329 void cPopulationCell::AddInputAV(cOrganism* org)
330 {
331 m_av_inputs.Push(org);
332 // Swaps the added avatar into a random position in the array
333 int loc = m_world->GetRandom().GetUInt(0, m_av_inputs.GetSize());
334 cOrganism* exist_org = m_av_inputs[loc];
335 m_av_inputs.Swap(loc, m_av_inputs.GetSize() - 1);
336 exist_org->SetAVInIndex(m_av_inputs.GetSize() - 1);
337 org->SetAVInIndex(loc);
338 }
339
340 // Adds an organism to the cell's output avatars, then keeps the list mixed by swapping the new avatar into a random position in the array
AddOutputAV(cOrganism * org)341 void cPopulationCell::AddOutputAV(cOrganism* org)
342 {
343 m_av_outputs.Push(org);
344 // Swaps the added avatar into a random position in the array
345 int loc = m_world->GetRandom().GetUInt(0, m_av_outputs.GetSize());
346 cOrganism* exist_org = m_av_outputs[loc];
347 m_av_outputs.Swap(loc, m_av_outputs.GetSize() - 1);
348 exist_org->SetAVOutIndex(m_av_outputs.GetSize() - 1);
349 org->SetAVOutIndex(loc);
350 }
351
352 // Removes the organism from the cell's input avatars (predator)
RemoveInputAV(cOrganism * org)353 void cPopulationCell::RemoveInputAV(cOrganism* org)
354 {
355 assert(HasInputAV());
356 assert(m_av_inputs[org->GetAVInIndex()] == org);
357 unsigned int last = GetNumAVInputs() - 1;
358 cOrganism* exist_org = m_av_inputs[last];
359 exist_org->SetAVInIndex(org->GetAVInIndex());
360 m_av_inputs.Swap(org->GetAVInIndex(), last);
361 m_av_inputs.Pop();
362 }
363
364 // Removes the organism from the cell's output avatars (prey)
RemoveOutputAV(cOrganism * org)365 void cPopulationCell::RemoveOutputAV(cOrganism* org)
366 {
367 assert(HasOutputAV());
368 assert(m_av_outputs[org->GetAVOutIndex()] == org);
369 unsigned int last = GetNumAVOutputs() - 1;
370 cOrganism* exist_org = m_av_outputs[last];
371 exist_org->SetAVOutIndex(org->GetAVOutIndex());
372 m_av_outputs.Swap(org->GetAVOutIndex(), last);
373 m_av_outputs.Pop();
374 }
375
376 // Returns whether a cell has an output AV that the org will be able to receive messages from.
HasOutputAV(cOrganism * org)377 bool cPopulationCell::HasOutputAV(cOrganism* org)
378 {
379 // No output avatars
380 if (!HasOutputAV()) return false;
381 // If org can talk to itself, any avatar in the cell works
382 if (m_world->GetConfig().SELF_COMMUNICATION.Get()) return true;
383
384 // If no self-messaging, is there an output avatar for another organism in the cell
385 for (int i = 0; i < GetNumAVOutputs(); i++) {
386 if (m_av_outputs[i] != org) {
387 return true;
388 }
389 }
390 return false;
391 }
392
393 // Randomly returns an avatar from the cell, all avatars equally likely
394 //********** DO NOT CALL FROM VIEWER OR DATA COLLECTION, DOING SO WILL AFFECT RESULTS DURING RUN **********
GetRandAV() const395 cOrganism* cPopulationCell::GetRandAV() const
396 {
397 if (HasAV()) {
398 int rand = m_world->GetRandom().GetUInt(0, GetNumAV());
399 if (rand < GetNumAVInputs()) {
400 return m_av_inputs[rand];
401 }
402 else {
403 return m_av_outputs[rand - GetNumAVInputs()];
404 }
405 }
406 return NULL;
407 }
408
409 // Returns the first predator (input) avatar, which should be random as the list is mixed whenever a new avatar is added
GetRandPredAV() const410 cOrganism* cPopulationCell::GetRandPredAV() const
411 {
412 if (HasInputAV()) {
413 return m_av_inputs[0];
414 }
415 return NULL;
416 }
417
418 // Returns the first prey (output) avatar, which should be random as the list is mixed whenever a new avatar is added
GetRandPreyAV() const419 cOrganism* cPopulationCell::GetRandPreyAV() const
420 {
421 if (HasOutputAV()) {
422 return m_av_outputs[0];
423 }
424 return NULL;
425 }
426
427 // Returns all input avatars (organisms) contained in the cell
GetCellInputAVs()428 tArray<cOrganism*> cPopulationCell::GetCellInputAVs()
429 {
430 assert(HasInputAV());
431 tArray<cOrganism*> avatar_inputs;
432 avatar_inputs.Resize(GetNumAVInputs());
433 for (int i = 0; i < GetNumAVInputs(); i++) {
434 avatar_inputs[i] = m_av_inputs[i];
435 }
436 return avatar_inputs;
437 }
438
439 // Returns all output avatars (organisms) contained in the cell
GetCellOutputAVs()440 tArray<cOrganism*> cPopulationCell::GetCellOutputAVs()
441 {
442 assert(HasOutputAV());
443 tArray<cOrganism*> avatar_outputs;
444 avatar_outputs.Resize(GetNumAVOutputs());
445 for (int i = 0; i < GetNumAVOutputs(); i++) {
446 avatar_outputs[i] = m_av_outputs[i];
447 }
448 return avatar_outputs;
449 }
450
451 // Returns all avatars (organisms) contained in the cell
GetCellAVs()452 tArray<cOrganism*> cPopulationCell::GetCellAVs()
453 {
454 assert(HasAV());
455 tArray<cOrganism*> avatars;
456 const int num_outputs = GetNumAVOutputs();
457 avatars.Resize(GetNumAV());
458 for (int i = 0; i < GetNumAVOutputs(); i++) {
459 avatars[i] = m_av_outputs[i];
460 }
461 for (int i = 0; i < GetNumAVInputs(); i++) {
462 avatars[i + num_outputs] = m_av_inputs[i];
463 }
464 return avatars;
465 }
466
467
468
469 /*! Diffuse genome fragments from this cell to its neighbors.
470
471 NOTE: This method is for OUTGOING diffusion only.
472
473 There are many possible ways in which genome fragments could be diffused. We'll
474 put in the framework to support those other mechanisms, but we're not going to
475 worry about this until we need it. Not terribly interested in recreating an
476 artificial chemistry here...
477 */
DiffuseGenomeFragments()478 void cPopulationCell::DiffuseGenomeFragments() {
479 InitHGTSupport();
480
481 switch(m_world->GetConfig().HGT_DIFFUSION_METHOD.Get()) {
482 case 0: { // none
483 break;
484 }
485 default: {
486 m_world->GetDriver().RaiseFatalException(-1, "Unrecognized diffusion type in cPopulationCell::DiffuseGenomeFragments().");
487 }
488 }
489 }
490
491 /*! Add fragments from the passed-in genome to the HGT fragments contained in this cell.
492
493 Split the passed-in genome into fragments according to a normal distribution specified
494 by HGT_FRAGMENT_SIZE_MEAN and HGT_FRAGMENT_SIZE_VARIANCE. These fragments are added
495 to this cell's fragment list.
496
497 As a safety measure, we also remove old fragments to conserve memory. Specifically, we
498 remove old fragments until at most HGT_MAX_FRAGMENTS_PER_CELL fragments remain.
499 */
AddGenomeFragments(cAvidaContext & ctx,const Sequence & genome)500 void cPopulationCell::AddGenomeFragments(cAvidaContext& ctx, const Sequence& genome) {
501 assert(genome.GetSize()>0); // oh, sweet sanity.
502 InitHGTSupport();
503
504 m_world->GetPopulation().AdjustHGTResource(ctx, genome.GetSize());
505
506
507 cGenomeUtil::RandomSplit(ctx,
508 m_world->GetConfig().HGT_FRAGMENT_SIZE_MEAN.Get(),
509 m_world->GetConfig().HGT_FRAGMENT_SIZE_VARIANCE.Get(),
510 genome,
511 m_hgt->fragments);
512
513 // pop off the front of this cell's buffer until we have <= HGT_MAX_FRAGMENTS_PER_CELL.
514 while(m_hgt->fragments.size()>(unsigned int)m_world->GetConfig().HGT_MAX_FRAGMENTS_PER_CELL.Get()) {
515 m_world->GetPopulation().AdjustHGTResource(ctx, -m_hgt->fragments.front().GetSize());
516 m_hgt->fragments.pop_front();
517 }
518 }
519
520 /*! Retrieve the number of genome fragments currently found in this cell.
521 */
CountGenomeFragments() const522 unsigned int cPopulationCell::CountGenomeFragments() const {
523 if(IsHGTInitialized()) {
524 return m_hgt->fragments.size();
525 } else {
526 return 0;
527 }
528 }
529
530 /*! Remove and return a random genome fragment.
531 */
PopGenomeFragment()532 Sequence cPopulationCell::PopGenomeFragment() {
533 assert(m_hgt!=0);
534 fragment_list_type::iterator i = m_hgt->fragments.begin();
535 std::advance(i, m_world->GetRandom().GetUInt(0, m_hgt->fragments.size()));
536 Sequence tmp = *i;
537 m_hgt->fragments.erase(i);
538 return tmp;
539 }
540
541 /*! Retrieve the list of fragments from this cell.
542 */
GetFragments()543 cPopulationCell::fragment_list_type& cPopulationCell::GetFragments() {
544 InitHGTSupport();
545 return m_hgt->fragments;
546 }
547
548 /*! Clear all fragments from this cell, adjust resources as required.
549 */
ClearFragments(cAvidaContext & ctx)550 void cPopulationCell::ClearFragments(cAvidaContext& ctx) {
551 InitHGTSupport();
552 for(fragment_list_type::iterator i=m_hgt->fragments.begin(); i!=m_hgt->fragments.end(); ++i) {
553 m_world->GetPopulation().AdjustHGTResource(ctx, -i->GetSize());
554 }
555 m_hgt->fragments.clear();
556 }
557
SetCellData(int data,int org_id)558 void cPopulationCell::SetCellData(int data, int org_id)
559 {
560 m_cell_data.contents = data;
561 m_cell_data.org_id = org_id;
562 m_cell_data.update = m_world->GetStats().GetUpdate();
563 if (m_organism != NULL) {
564 if (m_organism->HasOpinion()) {
565 m_cell_data.territory = m_organism->GetOpinion().first;
566 }
567 m_cell_data.forager = m_organism->GetForageTarget();
568 }
569 }
570
ClearCellData()571 void cPopulationCell::ClearCellData()
572 {
573 m_cell_data.contents = 0;
574 m_cell_data.org_id = -1;
575 m_cell_data.update = -1;
576 m_cell_data.territory = -1;
577 m_cell_data.forager = -99;
578 }
579
UpdateCellDataExpired()580 void cPopulationCell::UpdateCellDataExpired()
581 {
582 const int expiration = m_world->GetConfig().MARKING_EXPIRE_DATE.Get();
583 const int update = m_world->GetStats().GetUpdate();
584 const int ud_marked = m_cell_data.update;
585 // update only if marked, can expire, and enough time has passed
586 if (ud_marked != -1 && expiration != -1 && (expiration < (update - ud_marked))) ClearCellData();
587 }
588