Bonjour,
Pour la lisibilité du code on parle souvent des commentaires, du nommage des variables et des classes, de la responsabilité unique, etc...
Mais je n'ai pas souvent entendu parler du choix de la représentation de données.
Ces derniers jours, je travaillais sur un algorithme assez compliqué et j'ai décidé de faire deux classes :
TheoricPlinthGroup qui représente un groupe de socle et TheoricPlinth qui représente un socle.
J'ai essayé de faire en sorte d'accéder le plus facilement aux données en les "dupliquant", en effet, un TheoricPlinth possède la liste de ses TheoricPlinth voisins et TheoricPlinthGroup possède la liste de ses TheoricPlinthGroup voisins ainsi que la liste des TheoricPlinth qui constituent ses frontières avec un autre groupe.
Lorsque j'ai dû codé cela, ça a été un véritable enfer, pleins de bugs à corriger, etc... :
J'ai alors décidé de changer la représentation de ces donnée en ne créant qu'une instance de TheoricPlinthGroup qui stocke un vector de ThPlinth et ces ThPlinth contiennent l'identifiant du groupe auquels ils sont liés.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 class TheoricPlinthGroup { public: enum TeamType {EMPTY, FRIEND, ADV, ADV_LIVING}; TheoricPlinthGroup(PlinthGroup * group, std::map<PlinthGroup *, TheoricPlinthGroup *> & link, TeamType team, std::set<TheoricPlinthGroup *> & m_list_th_plg); TheoricPlinthGroup(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> &frontier, std::set<TheoricPlinthGroup *> & list_th_plg); TheoricPlinthGroup(std::set<TheoricPlinthGroup *> & list_th_plg) : m_team(EMPTY), m_list_th_plg(list_th_plg){ m_list_th_plg.insert(this); } ~TheoricPlinthGroup(void); static void eatADV(std::set<TheoricPlinthGroup *> &list_th_plg); static void eat(TheoricPlinthGroup * ); static void fill(std::set<TheoricPlinth *> &plinth_to_fill); TeamType team(void){ return m_team; } TheoricPlinthGroup * add(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> &frontier, bool handleSplit = true); static TheoricPlinthGroup * fusion(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> &frontier, std::set<TheoricPlinthGroup *> & neight_frontier); void movePlinthTo(TheoricPlinth *, TheoricPlinthGroup *, bool handleSplit); std::set<TheoricPlinthGroup *> & list(void){ return m_list_th_plg; } static bool canLive(std::set<TheoricPlinthGroup *> & list_th_plg); private : std::set<TheoricPlinthGroup *> m_neigths; TeamType m_team; std::map<TheoricPlinthGroup *, std::set<TheoricPlinth *> > m_frontiers; std::set<TheoricPlinth *> m_pls; std::set<TheoricPlinthGroup *> & m_list_th_plg; }; class TheoricPlinth { public : typedef std::set<TheoricPlinth *> Frontier; TheoricPlinth(Plinth * pl, TheoricPlinthGroup * gp); Frontier *getFrontierWith(TheoricPlinthGroup *gp); ~TheoricPlinth(void); void eraseMe(TheoricPlinthGroup *); void fill(void); TheoricPlinthGroup * group(void){ return m_group; } void newGroupIs(TheoricPlinthGroup * gp); std::set<TheoricPlinthGroup *> frontier(void); bool isEye(void); private: Plinth * m_pl; TheoricPlinthGroup * m_group; std::map<TheoricPlinthGroup *, Frontier> m_frontier; static std::map<Plinth *, TheoricPlinth *> m_real_pls; }; TheoricPlinthGroup::TheoricPlinthGroup(PlinthGroup * group, std::map<PlinthGroup *, TheoricPlinthGroup *> & link, TeamType team, std::set<TheoricPlinthGroup *> & list_th_plg) : m_team(team), m_list_th_plg(list_th_plg) { m_list_th_plg.insert(this); //add plinths for(Plinth * pl : group->plinths() ) m_pls.insert(new TheoricPlinth(pl, this) ); //add neightbourghood. for( PlinthGroup * plg : group->neightbourg() ) { auto it = link.find(plg); if( it != link.end() ) { m_neigths.insert(it->second); it->second->m_neigths.insert(this); // search frontier for( TheoricPlinth * th_pl : m_pls) { TheoricPlinth::Frontier * frontier = th_pl->getFrontierWith(it->second); if( frontier ) { m_frontiers[it->second].insert(th_pl); for( TheoricPlinth * t_th_pl : *frontier ) it->second->m_frontiers[this].insert(t_th_pl); } } } } link[group] = this; } TheoricPlinthGroup::TheoricPlinthGroup(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> & frontier, std::set<TheoricPlinthGroup *> &list_th_plg) : m_team(FRIEND), m_list_th_plg(list_th_plg) { m_list_th_plg.insert(this); add(th_pl, frontier); } TheoricPlinthGroup * TheoricPlinthGroup::add(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> & frontier, bool handleSplit) { th_pl->group()->movePlinthTo(th_pl, this, handleSplit); m_pls.insert(th_pl); m_neigths.insert(frontier.begin(), frontier.end() ); for( TheoricPlinthGroup * th_plg : frontier) { m_frontiers[th_plg].insert(th_pl ); } return this; } void TheoricPlinthGroup::movePlinthTo(TheoricPlinth * pl, TheoricPlinthGroup * plg, bool handleSplit) { //update frontier group adj. std::cerr << m_neigths.size() << std::endl; for( TheoricPlinthGroup * th_plg : m_neigths) { bool isNeight = false; std::set<TheoricPlinth *> list = m_frontiers[this]; for(TheoricPlinth * th_pl : list) { if( th_pl->getFrontierWith(this)->count(pl) ) { if( th_pl->getFrontierWith(this)->size() == 1 ) { auto it = th_plg->m_frontiers.find(this); it->second.erase(th_pl); if( ! it->second.size() ) th_plg->m_frontiers.erase(it); } else isNeight = true; if( th_plg != plg) th_plg->m_frontiers[plg].insert( th_pl); } else isNeight = true; // other } if( ! isNeight ) { th_plg->m_neigths.erase(this); m_neigths.erase(th_plg); } } auto it = m_frontiers.begin(); auto end = m_frontiers.end(); while( it != end ) { auto t_it = it->second.find(pl); if( t_it != it->second.end() ) { it->second.erase(t_it); } if( ! it->second.size() ) it = m_frontiers.erase(it); else ++it; } TheoricPlinthGroup * oldGroup = pl->group(); std::cerr << oldGroup << ":" << this << std::endl; pl->newGroupIs(plg); if(handleSplit) { auto tbase = pl->getFrontierWith(this); std::set<TheoricPlinth *> base(tbase->begin(), tbase->end() ); std::cerr << base.size() << std::endl; if(base.size() > 1) { bool oneGroup = true; while( ! base.empty() ) { std::set<TheoricPlinth *> linkedPlinth; std::stack<TheoricPlinth *> plinthToHandle; plinthToHandle.push( *base.begin() ); linkedPlinth.insert( *base.begin() ); base.erase( base.begin() ); std::cerr << "new size" << base.size() << std::endl; while( ! plinthToHandle.empty() ) { TheoricPlinth * th_pl = plinthToHandle.top(); plinthToHandle.pop(); for(TheoricPlinth * temp : *th_pl->getFrontierWith(oldGroup) ) { if(linkedPlinth.find(temp) == linkedPlinth.end() ) { linkedPlinth.insert(temp); plinthToHandle.push(temp); base.erase(temp); std::cerr << "new size" << base.size() << std::endl; if( oneGroup && base.empty() ) goto end; } } } if( ! oneGroup) { TheoricPlinthGroup * newG = new TheoricPlinthGroup(oldGroup->m_list_th_plg); for(TheoricPlinth * plinth : linkedPlinth) { std::set<TheoricPlinthGroup *> front = plinth->frontier(); front.erase(newG); newG->add(plinth, front,false); } } else oneGroup = false; } end : ;//there is only one group } } m_pls.erase(pl); if( m_pls.size() == 0) delete this; } TheoricPlinthGroup * TheoricPlinthGroup::fusion(TheoricPlinth * th_pl, std::set<TheoricPlinthGroup *> &frontier, std::set<TheoricPlinthGroup *> & neight_frontier) { TheoricPlinthGroup * finalGroup = *neight_frontier.begin(); finalGroup->add(th_pl, frontier); // pas très optimisé mais la flemme (et puis c'est trop enquiquinant :cry:) for(TheoricPlinthGroup * th_plg : neight_frontier) { while( ! th_plg->m_pls.empty() ) { TheoricPlinth * pl = *th_plg->m_pls.begin(); std::set<TheoricPlinthGroup * > t_frontier; for( TheoricPlinthGroup * plg : pl->frontier() ) { if(plg != finalGroup) t_frontier.insert(plg); } finalGroup->add(pl, t_frontier, false ); } } return finalGroup; } TheoricPlinthGroup::~TheoricPlinthGroup(void) { m_list_th_plg.erase(this); for( TheoricPlinthGroup * th_plg : m_neigths ) { for( TheoricPlinth * th_pl : th_plg->m_frontiers[this] ) { th_pl->eraseMe(this); } th_plg->m_neigths.erase(this); th_plg->m_frontiers.erase(this); } for( auto * pl : m_pls) { delete pl; } } // if there is more than 2 team, we eat group in randomly order so the result can be false. void TheoricPlinthGroup::eatADV(std::set<TheoricPlinthGroup *> & list_th_plg) { std::set<TheoricPlinthGroup *> groupToDestroy; std::set<TheoricPlinthGroup *> createdGroup; for( TheoricPlinthGroup * th_plg : list_th_plg ) { if( th_plg->m_team == ADV || th_plg->m_team == ADV_LIVING) { eat(th_plg); if( th_plg->m_team != ADV) groupToDestroy.insert(th_plg); } } list_th_plg.insert(createdGroup.begin(), createdGroup.end()); for( TheoricPlinthGroup * th_plg : groupToDestroy ) { delete th_plg; } } void TheoricPlinthGroup::eat(TheoricPlinthGroup * th_plg ) { std::set<TheoricPlinthGroup *> neights = th_plg->m_neigths; std::cerr << "size neights " << neights.size(); for( TheoricPlinthGroup * neight : neights) { if(neight->m_team == EMPTY) { std::set<TheoricPlinth *> plinth_to_fill = neight->m_frontiers[th_plg]; neight->fill( plinth_to_fill); } } th_plg->m_team = EMPTY; } void TheoricPlinthGroup::fill(std::set<TheoricPlinth *> & plinth_to_fill ) { std::cerr << "plTo " << plinth_to_fill.size() << std::endl; for( TheoricPlinth * plinth : plinth_to_fill) { plinth->fill(); } } bool TheoricPlinthGroup::canLive(std::set<TheoricPlinthGroup *> & list_th_plg) { std::set<TheoricPlinth *> frontier; for( TheoricPlinthGroup * th_plg : list_th_plg ) { if( th_plg->team() == FRIEND) continue; for( auto pair : th_plg->m_frontiers ) frontier.insert(pair.second.begin(), pair.second.end()); } std::cerr << "frontier size() :" <<frontier.size() << std::endl; std::set<TheoricPlinth *> eyes; while(frontier.size() + eyes.size() >= 2 && std::count_if(list_th_plg.begin(), list_th_plg.end(), [](TheoricPlinthGroup * th_plg) { return th_plg->team() == TheoricPlinthGroup::EMPTY; } ) ) { bool modification = false; auto it = frontier.begin(); while(it != frontier.end() ) { // search obvious eyes if( (*it)->isEye() ) { modification = true; eyes.insert(*it); if(eyes.size() >= 2) return true; // fill empty neightbourg for( TheoricPlinthGroup * temp_th_plg : (*it)->frontier() ) { if( temp_th_plg->team() == TheoricPlinthGroup::EMPTY) { TheoricPlinth * adj_plinth = *(*it)->getFrontierWith(temp_th_plg)->begin(); adj_plinth->fill(); frontier.erase(adj_plinth); } } it = frontier.erase(it); } else ++it; } //TODO // TODO //TODO fill some empty cases if( ! modification) // if we can't do anything else, they can live for this time. return true; std::cerr << "un tour un !" << std::endl; } return false; } std::map<Plinth *, TheoricPlinth *> TheoricPlinth::m_real_pls; TheoricPlinth::TheoricPlinth(Plinth * pl, TheoricPlinthGroup * gp) : m_pl(pl), m_group(gp) { m_real_pls[pl] = this; for( Plinth * t_pl : pl->neigthbourg() ) { auto it = m_real_pls.find(t_pl); if( it != m_real_pls.end() ) { m_frontier.insert( std::pair<TheoricPlinthGroup *, std::set<TheoricPlinth *> >(it->second->m_group, std::set<TheoricPlinth *>() ) ); auto & set = m_frontier[it->second->m_group]; set.insert(it->second); it->second->m_frontier[m_group].insert(this); } } } TheoricPlinth::~TheoricPlinth(void) { m_real_pls.erase(m_pl); } void TheoricPlinth::eraseMe(TheoricPlinthGroup * th_plg) { m_frontier.erase(th_plg); } TheoricPlinth::Frontier * TheoricPlinth::getFrontierWith(TheoricPlinthGroup * gp) { auto it = m_frontier.find(gp); if( it == m_frontier.end() ) return nullptr; return &it->second; } void TheoricPlinth::fill(void) { if( m_group->team() != TheoricPlinthGroup::EMPTY) return; std::set<TheoricPlinthGroup *> friend_neight; std::set<TheoricPlinthGroup *> frontier; for( auto it : m_frontier) { frontier.insert(it.first); if( it.first->team() == TheoricPlinthGroup::FRIEND ) friend_neight.insert(it.first); } if(friend_neight.size() == 0) //create. { new TheoricPlinthGroup(this, frontier, m_group->list() ); } else if( friend_neight.size() == 1) //add (*(friend_neight.begin()))->add(this, frontier); else //fusion TheoricPlinthGroup::fusion(this, frontier, friend_neight); } void TheoricPlinth::newGroupIs(TheoricPlinthGroup *gp) { for( auto pair : m_frontier) { for(TheoricPlinth * adj : pair.second) { auto it = adj->m_frontier[m_group].find(this); adj->m_frontier[m_group].erase(it); adj->m_frontier[gp].insert(this); } } m_group = gp; } std::set<TheoricPlinthGroup *> TheoricPlinth::frontier(void) { std::set<TheoricPlinthGroup *> front; for(auto pair : m_frontier) { front.insert(pair.first); } return front; } bool TheoricPlinth::isEye(void) { if(m_frontier.size() == 1 && m_frontier.begin()->first->team() == TheoricPlinthGroup::FRIEND) return true; TheoricPlinth * pl = nullptr; auto fr = frontier(); auto it = fr.begin(); auto end = fr.end(); while( !pl && it != end) { if( (*it)->team() == TheoricPlinthGroup::EMPTY) { Frontier * fr = getFrontierWith(*it); if( fr->size() > 1) return false; pl = *fr->begin(); } ++it; } if( !pl ) return false; auto frontier1 = frontier(); auto frontier2 = pl->frontier() ; if( std::includes(frontier2.begin(), frontier2.end(), frontier1.begin(), frontier1.end()) ) return true; return false; }
Il y a déjà moins de duplication d'informations, il y a déjà beaucoup moins d'instance mais à première vue les informations sont "plus difficile d'accès".
Mais à ma grande surprise, l'implémentation des fonctions a été beaucoup plus facile :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635 class PlinthGroup; class TheoricPlinthGroup { private : struct ThPlinth; public : //first bit is for frontier (1) or not(0) -only into ThPlinth- typedef uint16_t IdPlinth; // first bit is for empty (0) or plain (1) -only into ThPlinth- typedef uint16_t IdGp; TheoricPlinthGroup(const PlinthGroup & plinth, const std::set<PlinthGroup *> &living, unsigned int nbMaxPlinth); bool canLive(void); ThPlinth & getPlinth(IdPlinth id){ return m_plinths[id & MASK_ID]; } const ThPlinth & getPlinth(IdPlinth id) const { return m_plinths[id & MASK_ID]; } IdGp newIdGp(void){ return gpMax++; } void changeGpTo( IdGp old, IdGp current ); bool isObviousEye(void); void setNewGroupTo(const std::set<IdPlinth> & plinths); bool groupHasOtherNeightbourg(IdGp gp, std::set<IdPlinth> & neightbourg ); private: void createPlinths(const PlinthGroup & plinth, const std::set<PlinthGroup *> &living); void buildGroupAndNeightbourghood(void); IdGp gpMax = 0; enum{ MASK_ID = 0x7FFF, MASK_EMPTY = 0x8000, MASK_FRONTIER = 0x8000}; struct ThPlinth { public : class Iterator; Iterator begin(); Iterator end(); IdPlinth id(void) const { return m_id & MASK_ID; } bool empty(void) const { return ! (m_group & MASK_EMPTY); } IdGp group(void) const { return m_group & MASK_ID; } IdPlinth operator[](unsigned int i) const{ return m_neightbourg[i] & MASK_ID; } unsigned int nbNeightbourg(void) const { return m_neightbourg.size(); } void create(const std::set<IdPlinth> & neights, IdPlinth id, bool empty, TheoricPlinthGroup * th_plg); void setGroup(IdGp id){ m_group = (id & MASK_ID) | (m_group & MASK_EMPTY) ; } void setFrontier(IdPlinth id); void makeEye(void); void fill(void); bool isObviousEye(void) const; std::set<IdPlinth> searchAllPlinthLinkTo(IdPlinth id, std::set<IdPlinth> exceptions = std::set<IdPlinth>()) const; bool cantBeEye(void); static bool areNeightbourg(ThPlinth & a, ThPlinth & b); static bool areNeightbourg(IdPlinth id_a, IdPlinth id_b, TheoricPlinthGroup &th_plg); bool isFrontier(void){ return m_id & MASK_FRONTIER; } bool isFrontierWith(unsigned int index){ return m_neightbourg[index] & MASK_FRONTIER; } private : std::vector<IdPlinth> m_neightbourg; IdGp m_group; IdPlinth m_id; TheoricPlinthGroup * m_th_plg; }; std::vector< ThPlinth > m_plinths; }; class TheoricPlinthGroup::ThPlinth::Iterator { public : TheoricPlinthGroup::IdPlinth operator*(void) const{ return (*m_ptr)[m_position]; } Iterator & operator++(void); Iterator & operator--(void); Iterator & operator-=(unsigned int i); Iterator & operator+=(unsigned int i); bool operator==(const Iterator & other) const { return m_ptr == other.m_ptr && m_position == other.m_position; } bool operator!=(const Iterator & other) const { return ! (*this == other); } uint8_t operator-(const Iterator & other) const{ return m_position - other.m_position; } private: ThPlinth * m_ptr; uint8_t m_position; Iterator(ThPlinth * ptr, int8_t position) : m_ptr(ptr), m_position(position) {} friend class ThPlinth; public : bool isFrontierWith(void){ return m_ptr->isFrontierWith(m_position); } }; TheoricPlinthGroup::TheoricPlinthGroup(const PlinthGroup & plg, const std::set<PlinthGroup *> & living, unsigned int nbMaxPlinth) : m_plinths(nbMaxPlinth) { createPlinths(plg, living); std::set<IdPlinth> id; int i = 0, j = 0; for( ThPlinth & th_pl : m_plinths ) { if( th_pl.empty() ) { id.insert( th_pl.id() ); ++i; } ++j; } buildGroupAndNeightbourghood(); } void TheoricPlinthGroup::createPlinths(const PlinthGroup & plg, const std::set<PlinthGroup *> &living) { std::map<Plinth *, IdPlinth> getPlinth; std::stack<Plinth *> toHandle; const auto player = plg.player(); Plinth * first = *plg.plinths().begin(); IdPlinth id = 0; toHandle.push(first); getPlinth[first] = id++; while( ! toHandle.empty() ) { Plinth * handled = toHandle.top(); toHandle.pop(); bool nextAdv = false; std::set<IdPlinth> neights; for( Plinth * neight : handled->neigthbourg() ) { if(neight->group()->player() && neight->group()->player() != player) { nextAdv = true; // we do not handle adv. living group. if( living.find(neight->group()) != living.end() ) continue; } //add to be handle auto it = getPlinth.find(neight); if( it == getPlinth.end() ) { getPlinth[neight] = id; neights.insert(id); toHandle.push(neight); ++id; } else neights.insert(it->second); } IdPlinth myId = getPlinth[handled]; bool empty = (! nextAdv) && handled->group()->player() != player; m_plinths[myId].create(neights, myId, empty, this); } } void TheoricPlinthGroup::buildGroupAndNeightbourghood(void) { std::set<IdPlinth> notHandled; for(IdPlinth id = 0; id != m_plinths.size() ; ++id) notHandled.insert(id); while( ! notHandled.empty() ) { // we create a new group std::stack<IdPlinth> toHandle; auto it = notHandled.begin(); toHandle.push( *it ); notHandled.erase(it); while( ! toHandle.empty() ) { IdPlinth handled = toHandle.top(); toHandle.pop(); ThPlinth & pl = m_plinths[handled]; for( IdPlinth neightbourg : pl) { if( m_plinths[neightbourg].empty() != pl.empty() )//frontier { pl.setFrontier(neightbourg); } else //same group { if( notHandled.find(neightbourg) != notHandled.end() ) { toHandle.push(neightbourg); notHandled.erase(neightbourg); } } } pl.setGroup(gpMax); } gpMax++; // we created a new group } } bool TheoricPlinthGroup::canLive(void) { unsigned int nbEye = 0; std::set<IdPlinth> emptyPlinth; for( ThPlinth & th_pl : m_plinths ) if( th_pl.empty() ) emptyPlinth.insert( th_pl.id() ); bool modify = true; auto canLive = [&emptyPlinth, &nbEye, this]() { int nbPossibleEye = emptyPlinth.size(); if(emptyPlinth.size() == 2) { if( ThPlinth::areNeightbourg( *(emptyPlinth.begin()), // first PlinthId *(++emptyPlinth.begin()), // second PlinthId *this) ) nbPossibleEye--; } return nbPossibleEye + nbEye >= 2; }; while( canLive() && modify ) { modify = false; auto it = emptyPlinth.begin(); while( it != emptyPlinth.end() ) { ThPlinth & th_pl = m_plinths[*it]; if( th_pl.isObviousEye() ) { modify = true; th_pl.makeEye(); nbEye++; for(IdPlinth neight : th_pl) emptyPlinth.erase(neight); if(nbEye >= 2) return true; } else { bool test = th_pl.cantBeEye(); if( test ) { th_pl.fill(); emptyPlinth.erase(th_pl.id()); modify = true; } } ++it; } } return ! modify; // modify = false <=> emptyPlinth.size() + nbEye < 2 <=> can't live } void TheoricPlinthGroup::changeGpTo( IdGp old, IdGp current ) { for( ThPlinth & plinth : m_plinths ) { if( plinth.group() == old ) plinth.setGroup(current); } } void TheoricPlinthGroup::setNewGroupTo(const std::set<IdPlinth> & plinths) { IdGp newId = newIdGp(); for( IdPlinth id : plinths ) m_plinths[id & MASK_ID].setGroup(newId); } bool TheoricPlinthGroup::groupHasOtherNeightbourg(IdGp gp, std::set<IdPlinth> & neightbourg ) { for( ThPlinth & th_pl : m_plinths ) { if( th_pl.group() == (gp & MASK_ID) && th_pl.isFrontier() ) // we search the group's plinths { auto it = th_pl.begin(); auto end = th_pl.end(); auto it_set = neightbourg.begin(); auto it_end = neightbourg.end(); while( it != end ) { if( it.isFrontierWith() ) { while(it_set != it_end && *it > *it_set ) it_set++;//TODO if( it_set == it_end || *it != *it_set) return true;// if the frontier isn't in neightbourg, return true } ++it; } } } return false; } void TheoricPlinthGroup::ThPlinth::setFrontier(TheoricPlinthGroup::IdPlinth id) { m_id |= TheoricPlinthGroup::MASK_FRONTIER; int i = 0; while( (m_neightbourg[i] & TheoricPlinthGroup::MASK_ID) != id ) ++i; m_neightbourg[i] | TheoricPlinthGroup::MASK_FRONTIER; } bool TheoricPlinthGroup::ThPlinth::isObviousEye(void) const { std::set<TheoricPlinthGroup::IdPlinth> gp_adj; bool haveOneFriend = false; TheoricPlinthGroup::IdPlinth theFriend = 0; for( TheoricPlinthGroup::IdPlinth id : m_neightbourg ) { if( ! id & TheoricPlinthGroup::MASK_FRONTIER ) // a friend { if( haveOneFriend ) // a return into a for isn't "good" but I'm too lazy to make a while loop. return false; else { haveOneFriend = true; theFriend = id; } } else { gp_adj.insert(id); } } if( gp_adj.size() <= 1 ) return true; if( haveOneFriend ) { auto & first = m_th_plg->getPlinth(theFriend).m_neightbourg; return std::includes( first.begin(), first.end(), gp_adj.begin(), gp_adj.end(), [](const TheoricPlinthGroup::IdPlinth & a, const TheoricPlinthGroup::IdPlinth & b) { return (a & TheoricPlinthGroup::MASK_ID) < (b & TheoricPlinthGroup::MASK_ID); } ); } return false; } void TheoricPlinthGroup::ThPlinth::makeEye(void) { for( TheoricPlinthGroup::IdPlinth id : *this ) { m_th_plg->getPlinth(id).fill(); } } void TheoricPlinthGroup::ThPlinth::fill(void) { if( m_group & TheoricPlinthGroup::MASK_EMPTY ) // if already plain return; bool frontier = false; int i = 0; TheoricPlinthGroup::IdPlinth oldGroup = m_group; std::set<TheoricPlinthGroup::IdPlinth> plinthInTheFirstEmptyGroup; for( IdPlinth & id : m_neightbourg ) { ThPlinth & th_pl = m_th_plg->getPlinth(id); if( id & TheoricPlinthGroup::MASK_FRONTIER ) // plain neightbourg { if( oldGroup == m_group ) // add a plinth to a group { m_group = th_pl.m_group; } else m_th_plg->changeGpTo(th_pl.m_group, m_group);// "fusion" } else { frontier = true; // we don't care about that but if I decide to use it instead of PlinthGroup, I will need // it. if( (oldGroup & TheoricPlinthGroup::MASK_ID) == m_th_plg->getPlinth(id).group() ) { if( plinthInTheFirstEmptyGroup.empty() ) { plinthInTheFirstEmptyGroup = \ searchAllPlinthLinkTo(id, {m_id} ); } else if( plinthInTheFirstEmptyGroup.find(id) == plinthInTheFirstEmptyGroup.end() ) { // create a new group, this plinth isn't link to the old group. m_th_plg->setNewGroupTo( searchAllPlinthLinkTo(id, {m_id}) ); } } // else the group has already handled. } // a frontier become an unfrontier and an unfrontier becaume a frontier. id = ( id & TheoricPlinthGroup::MASK_ID ) | ( ~id & TheoricPlinthGroup::MASK_FRONTIER ); bool adj_frontier = false; for( IdPlinth & id_pl : th_pl.m_neightbourg ) { // for update frontier flag if( ( id_pl & TheoricPlinthGroup::MASK_ID ) == ( m_id & TheoricPlinthGroup::MASK_ID ) ) id_pl = ( id_pl & TheoricPlinthGroup::MASK_ID ) | ( ~id_pl & TheoricPlinthGroup::MASK_FRONTIER ); // for update frontier flag if( id_pl & TheoricPlinthGroup::MASK_FRONTIER ) adj_frontier = true; } //update frontier flag th_pl.m_id = ( th_pl.m_id & TheoricPlinthGroup::MASK_ID ) | (adj_frontier ? TheoricPlinthGroup::MASK_FRONTIER : 0); i++; } // create a new group if( m_group == oldGroup ) m_group = m_th_plg->newIdGp() | TheoricPlinthGroup::MASK_EMPTY; // update frontier flag m_id = ( m_id & TheoricPlinthGroup::MASK_ID ) | (frontier ? TheoricPlinthGroup::MASK_FRONTIER : 0); } void TheoricPlinthGroup::ThPlinth::create(const std::set<TheoricPlinthGroup::IdPlinth> & neights, TheoricPlinthGroup::IdPlinth id, bool empty, TheoricPlinthGroup *th_plg) { m_id = id; m_neightbourg.insert(m_neightbourg.begin(), neights.begin(), neights.end() ); m_group = empty ? 0 : TheoricPlinthGroup::MASK_EMPTY; m_th_plg = th_plg; } std::set<TheoricPlinthGroup::IdPlinth> \ TheoricPlinthGroup::ThPlinth::searchAllPlinthLinkTo(TheoricPlinthGroup::IdPlinth id, std::set<TheoricPlinthGroup::IdPlinth> exceptions) const { std::set<TheoricPlinthGroup::IdPlinth> found; found.insert(id & TheoricPlinthGroup::MASK_ID); std::stack<TheoricPlinthGroup::IdPlinth> toHandle; toHandle.push(id & TheoricPlinthGroup::MASK_ID); while( ! toHandle.empty() ) { TheoricPlinthGroup::IdPlinth handled = toHandle.top(); toHandle.pop(); TheoricPlinthGroup::ThPlinth & plinth = m_th_plg->getPlinth(handled); for( TheoricPlinthGroup::IdPlinth neight : plinth.m_neightbourg ) { TheoricPlinthGroup::IdPlinth neight_id = neight & TheoricPlinthGroup::MASK_ID; if( ! neight & TheoricPlinthGroup::MASK_FRONTIER //if friend && found.find(neight_id) == found.end() && exceptions.find(neight_id) == exceptions.end() ) { found.insert(neight_id); toHandle.push(neight_id); } } } return found; } bool TheoricPlinthGroup::ThPlinth::areNeightbourg(ThPlinth & a, ThPlinth & b) { for( IdPlinth id : a) if(id == b.id() ) return true; // not good to do return false; } bool TheoricPlinthGroup::ThPlinth::areNeightbourg(IdPlinth id_a, IdPlinth id_b, TheoricPlinthGroup & th_plg) { return areNeightbourg(th_plg.getPlinth(id_a), th_plg.getPlinth(id_b)); } bool TheoricPlinthGroup::ThPlinth::cantBeEye(void) // bug { std::set<TheoricPlinthGroup::IdPlinth> empty; std::list<std::set<TheoricPlinthGroup::IdGp> > plain; plain.push_back(std::set<TheoricPlinthGroup::IdGp>()); for( TheoricPlinthGroup::IdPlinth id : m_neightbourg ) if( ! (id & TheoricPlinthGroup::MASK_FRONTIER) ) // if same group empty.insert(id & TheoricPlinthGroup::MASK_ID); else plain.front().insert(id & TheoricPlinthGroup::MASK_ID); bool oneIsPlain = false; for( TheoricPlinthGroup::IdPlinth id : empty) { plain.push_back( std::set<TheoricPlinthGroup::IdGp>() ); TheoricPlinthGroup::ThPlinth & th_pl = m_th_plg->getPlinth(id); for( TheoricPlinthGroup::IdPlinth id_pl : th_pl.m_neightbourg ) { if( id_pl & TheoricPlinthGroup::MASK_FRONTIER ) // if plain { plain.back().insert(id_pl & TheoricPlinthGroup::MASK_ID); oneIsPlain = true; } } } if( ! oneIsPlain ) return false; // fusion the plain set which are one same plain group auto it = plain.begin(); while( it != plain.end() ) { auto it2 = it; while( ++it2 != plain.end() ) { auto it_a = it->begin(); auto end_a = it->end(); auto it_b = it2->begin(); auto end_b = it2->end(); while( it_a != end_a ) { while( it_b != end_b && *it_b < *it_a) ++it_b; if( *it_a == *it_b) break; it_a++; } if( it_a != end_a ) { for( TheoricPlinthGroup::IdGp id_gp : *it ) it2->insert(id_gp); it = plain.erase(it); break; } } ++it; } empty.insert(m_id & TheoricPlinthGroup::MASK_ID); for( std::set<TheoricPlinthGroup::IdGp> & listGroup : plain ) { bool hasOtherFreedom = false; for( TheoricPlinthGroup::IdGp group : listGroup ) { // search frontier if( m_th_plg->groupHasOtherNeightbourg(group, empty) ) hasOtherFreedom = true; } if( ! hasOtherFreedom ) return true; } // 1 - L2 = trouver voisins "vides" // 2 - (L1) = liste des voisins "plein" et des voisins "pleins" des voisins "vides" // -- plusieurs listes selon les fusions // 3 - L3 = Chercher la liste des voisins vides de L1. // Si L3 = L2 : remplir return false; } TheoricPlinthGroup::ThPlinth::Iterator TheoricPlinthGroup::ThPlinth::begin() { return Iterator(this, 0); } TheoricPlinthGroup::ThPlinth::Iterator TheoricPlinthGroup::ThPlinth::end() { return Iterator(this, m_neightbourg.size() ); } TheoricPlinthGroup::ThPlinth::Iterator & TheoricPlinthGroup::ThPlinth::Iterator::operator++(void) { if( m_position == m_ptr->nbNeightbourg() ) return *this; m_position++; return *this; } TheoricPlinthGroup::ThPlinth::Iterator & TheoricPlinthGroup::ThPlinth::Iterator::operator--(void) { if( m_position == m_ptr->nbNeightbourg() ) return *this; if( m_position) m_position--; else m_position = m_ptr->nbNeightbourg(); return *this; } TheoricPlinthGroup::ThPlinth::Iterator & \ TheoricPlinthGroup::ThPlinth::Iterator::operator-=(unsigned int i) { if( m_position == m_ptr->nbNeightbourg() ) return *this; if( m_position >= i) m_position-= i; else m_position = m_ptr->nbNeightbourg(); return *this; } TheoricPlinthGroup::ThPlinth::Iterator & \ TheoricPlinthGroup::ThPlinth::Iterator::operator+=(unsigned int i) { if( m_position + i >= m_ptr->nbNeightbourg() ) return *this; m_position++; return *this; }
Mais question serait alors la suivante :
Comment choisir efficacement la représentation de ses données, l'architecture du "modèle" ?
Partager