1
/////////////////////////////////////////////////////////////////////////////
3
// Purpose: Life! game logic
4
// Author: Guillermo Rodriguez Garcia, <guille@iies.es>
7
// RCS-ID: $Id: game.cpp 60926 2009-06-06 23:16:27Z VZ $
8
// Copyright: (c) 2000, Guillermo Rodriguez Garcia
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
// ==========================================================================
13
// headers, declarations, constants
14
// ==========================================================================
16
// For compilers that support precompilation, includes "wx/wx.h".
17
#include "wx/wxprec.h"
28
#include "wx/module.h"
31
#include <string.h> // for memset
34
#define CELLSARRAYSIZE 1024 // static array for BeginFind & co.
35
#define ALLOCBOXES 16 // number of cellboxes to alloc at once
36
#define MAXDEAD 8 // tics before removing cellbox from list
39
// ==========================================================================
41
// ==========================================================================
43
#define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
45
#define HASHSIZE 16384 // hash table size (do not change!)
46
#define CELLBOX 8 // cells in a cellbox (do not change!)
53
inline bool IsAlive(int dx, int dy) const;
54
inline bool SetCell(int dx, int dy, bool alive);
57
wxInt32 m_x, m_y; // position in universe
58
wxUint32 m_live1, m_live2; // alive cells (1 bit per cell)
59
wxUint32 m_old1, m_old2; // old values for m_live1, 2
60
wxUint32 m_on[8]; // neighbouring info
61
wxUint32 m_dead; // been dead for n generations
62
LifeCellBox *m_up, *m_dn, *m_lf, *m_rt; // neighbour CellBoxes
63
LifeCellBox *m_prev, *m_next; // in linked list
64
LifeCellBox *m_hprev, *m_hnext; // in hash table
69
// Returns whether cell dx, dy in this box is alive
71
bool LifeCellBox::IsAlive(int dx, int dy) const
74
return (m_live2 & 1 << ((dy - 4) * 8 + dx)) ? true : false ;
76
return (m_live1 & 1 << ((dy) * 8 + dx)) ? true : false ;
80
// Sets cell dx, dy in this box to 'alive', returns true if
81
// the previous value was different, false if it was the same.
83
bool LifeCellBox::SetCell(int dx, int dy, bool alive)
85
if (IsAlive(dx, dy) != alive)
88
m_live2 ^= 1 << ((dy - 4) * 8 + dx);
90
m_live1 ^= 1 << ((dy) * 8 + dx);
92
// reset this here to avoid updating problems
102
// ==========================================================================
104
// ==========================================================================
106
// --------------------------------------------------------------------------
108
// --------------------------------------------------------------------------
112
// pattern description
113
m_name = wxEmptyString;
114
m_rules = wxEmptyString;
115
m_description = wxEmptyString;
119
m_boxes = new LifeCellBox *[HASHSIZE];
122
for (int i = 0; i < HASHSIZE; i++)
125
// state vars for BeginFind & FindMore
126
m_cells = new LifeCell[CELLSARRAYSIZE];
141
// Clears the board, freeing all storage.
147
// clear the hash table pointers
148
for (int i = 0; i < HASHSIZE; i++)
161
// free available boxes
172
m_name = wxEmptyString;
173
m_rules = wxEmptyString;
174
m_description = wxEmptyString;
178
// --------------------------------------------------------------------------
179
// Test and set individual cells
180
// --------------------------------------------------------------------------
183
// Returns whether cell (x, y) is alive.
185
bool Life::IsAlive(wxInt32 x, wxInt32 y)
187
LifeCellBox *c = LinkBox(x, y, false);
189
return (c && c->IsAlive( x - c->m_x, y - c->m_y ));
193
// Sets or clears cell (x, y), according to the 'alive' param.
195
void Life::SetCell(wxInt32 x, wxInt32 y, bool alive)
197
LifeCellBox *c = LinkBox(x, y);
198
wxUint32 dx = x - c->m_x;
199
wxUint32 dy = y - c->m_y;
201
if (c->SetCell(dx, dy, alive))
210
void Life::SetPattern(const LifePattern& pattern)
212
wxArrayString data = pattern.m_shape;
218
for (size_t n = 0; n < data.GetCount(); n++)
222
if ( (line.GetChar(0) != wxT('*')) &&
223
(line.GetChar(0) != wxT('.')) )
225
// assume that it is a digit or a minus sign
226
line.BeforeFirst(wxT(' ')).ToLong(&x);
227
line.AfterFirst(wxT(' ')).ToLong(&y);
232
for (size_t k = 0; k < line.Len(); k++)
233
SetCell(x + k, y, line.GetChar(k) == wxT('*'));
239
m_name = pattern.m_name;
240
m_rules = pattern.m_rules;
241
m_description = pattern.m_description;
244
// --------------------------------------------------------------------------
245
// Cellbox management functions
246
// --------------------------------------------------------------------------
249
// Creates a box in x, y, either taking it from the list
250
// of available boxes, or allocating a new one.
252
LifeCellBox* Life::CreateBox(wxInt32 x, wxInt32 y, wxUint32 hv)
256
// if there are no available boxes, alloc a few more
258
for (int i = 1; i <= ALLOCBOXES; i++)
260
c = new LifeCellBox();
264
// TODO: handle memory errors. Note that right now, if we
265
// couldn't allocate at least one cellbox, we will crash
266
// before leaving CreateBox(). Probably we should try to
267
// allocate some boxes *before* the m_available list goes
268
// empty, so that we have a margin to handle errors
270
wxLogFatalError(_("Out of memory! Aborting..."));
275
c->m_next = m_available;
279
// take a cellbox from the list of available boxes
281
m_available = c->m_next;
284
memset((void *) c, 0, sizeof(LifeCellBox));
288
// insert c in the list
291
if (c->m_next) c->m_next->m_prev = c;
293
// insert c in the hash table
294
c->m_hnext = m_boxes[hv];
296
if (c->m_hnext) c->m_hnext->m_hprev = c;
302
// Returns a pointer to the box (x, y); if it didn't exist yet,
303
// it returns NULL or creates a new one, depending on the value
304
// of the 'create' parameter.
306
LifeCellBox* Life::LinkBox(wxInt32 x, wxInt32 y, bool create)
315
// search in the hash table
316
for (c = m_boxes[hv]; c; c = c->m_hnext)
317
if ((c->m_x == x) && (c->m_y == y)) return c;
319
// if not found, and (create == true), create a new one
320
return create? CreateBox(x, y, hv) : (LifeCellBox*) NULL;
324
// Removes this box from the list and the hash table and
325
// puts it in the list of available boxes.
327
void Life::KillBox(LifeCellBox *c)
329
wxUint32 hv = HASH(c->m_x, c->m_y);
331
// remove from the list
333
c->m_prev->m_next = c->m_next;
337
// remove from the hash table
338
if (c != m_boxes[hv])
339
c->m_hprev->m_hnext = c->m_hnext;
341
m_boxes[hv] = c->m_hnext;
344
if (c->m_next) c->m_next->m_prev = c->m_prev;
345
if (c->m_hnext) c->m_hnext->m_hprev = c->m_hprev;
346
if (c->m_up) c->m_up->m_dn = NULL;
347
if (c->m_dn) c->m_dn->m_up = NULL;
348
if (c->m_lf) c->m_lf->m_rt = NULL;
349
if (c->m_rt) c->m_rt->m_lf = NULL;
351
// append to the list of available boxes
352
c->m_next = m_available;
356
// --------------------------------------------------------------------------
358
// --------------------------------------------------------------------------
360
LifeCell Life::FindCenter()
369
for (c = m_head; c; c = c->m_next)
379
sx = (sx / n) + CELLBOX / 2;
380
sy = (sy / n) + CELLBOX / 2;
384
cell.i = (wxInt32) sx;
385
cell.j = (wxInt32) sy;
389
LifeCell Life::FindNorth()
391
wxInt32 x = 0, y = 0;
395
for (c = m_head; c; c = c->m_next)
396
if (!c->m_dead && ((first) || (c->m_y < y)))
404
cell.i = first? 0 : x + CELLBOX / 2;
405
cell.j = first? 0 : y + CELLBOX / 2;
409
LifeCell Life::FindSouth()
411
wxInt32 x = 0, y = 0;
415
for (c = m_head; c; c = c->m_next)
416
if (!c->m_dead && ((first) || (c->m_y > y)))
424
cell.i = first? 0 : x + CELLBOX / 2;
425
cell.j = first? 0 : y + CELLBOX / 2;
429
LifeCell Life::FindWest()
431
wxInt32 x = 0, y = 0;
435
for (c = m_head; c; c = c->m_next)
436
if (!c->m_dead && ((first) || (c->m_x < x)))
444
cell.i = first? 0 : x + CELLBOX / 2;
445
cell.j = first? 0 : y + CELLBOX / 2;
449
LifeCell Life::FindEast()
451
wxInt32 x = 0, y = 0;
455
for (c = m_head; c; c = c->m_next)
456
if (!c->m_dead && ((first) || (c->m_x > x)))
464
cell.i = first? 0 : x + CELLBOX / 2;
465
cell.j = first? 0 : y + CELLBOX / 2;
469
// --------------------------------------------------------------------------
471
// --------------------------------------------------------------------------
474
// Post eight cells to the cell arrays - leave out the fourth
475
// argument (or pass 0, the default value) to post alive cells
476
// only, else it will post cells which have changed.
478
void Life::DoLine(wxInt32 x, wxInt32 y, wxUint32 live, wxUint32 old)
480
wxUint32 diff = (live ^ old) & 0xff;
484
for (wxInt32 k = 8; k; k--, x++)
488
m_cells[m_ncells].i = x;
489
m_cells[m_ncells].j = y;
496
void Life::BeginFind(wxInt32 x0, wxInt32 y0, wxInt32 x1, wxInt32 y1, bool changed)
498
// TODO: optimize for the case where the maximum number of
499
// cellboxes that fit in the specified viewport is smaller
500
// than the current total of boxes; iterating over the list
501
// should then be faster than searching in the hash table.
503
m_x0 = m_x = x0 & 0xfffffff8;
504
m_y0 = m_y = y0 & 0xfffffff8;
505
m_x1 = (x1 + 7) & 0xfffffff8;
506
m_y1 = (y1 + 7) & 0xfffffff8;
512
bool Life::FindMore(LifeCell *cells[], size_t *ncells)
520
for ( ; m_y <= m_y1; m_y += 8, m_x = m_x0)
521
for ( ; m_x <= m_x1; m_x += 8)
523
if ((c = LinkBox(m_x, m_y, false)) == NULL)
526
// check whether there is enough space left in the array
527
if (m_ncells > (CELLSARRAYSIZE - 64))
533
DoLine(m_x, m_y , c->m_live1, c->m_old1 );
534
DoLine(m_x, m_y + 1, c->m_live1 >> 8, c->m_old1 >> 8 );
535
DoLine(m_x, m_y + 2, c->m_live1 >> 16, c->m_old1 >> 16);
536
DoLine(m_x, m_y + 3, c->m_live1 >> 24, c->m_old1 >> 24);
537
DoLine(m_x, m_y + 4, c->m_live2, c->m_old2 );
538
DoLine(m_x, m_y + 5, c->m_live2 >> 8, c->m_old2 >> 8 );
539
DoLine(m_x, m_y + 6, c->m_live2 >> 16, c->m_old2 >> 16);
540
DoLine(m_x, m_y + 7, c->m_live2 >> 24, c->m_old2 >> 24);
545
for ( ; m_y <= m_y1; m_y += 8, m_x = m_x0)
546
for ( ; m_x <= m_x1; m_x += 8)
548
if ((c = LinkBox(m_x, m_y, false)) == NULL)
551
// check whether there is enough space left in the array
552
if (m_ncells > (CELLSARRAYSIZE - 64))
558
DoLine(m_x, m_y , c->m_live1 );
559
DoLine(m_x, m_y + 1, c->m_live1 >> 8 );
560
DoLine(m_x, m_y + 2, c->m_live1 >> 16);
561
DoLine(m_x, m_y + 3, c->m_live1 >> 24);
562
DoLine(m_x, m_y + 4, c->m_live2 );
563
DoLine(m_x, m_y + 5, c->m_live2 >> 8 );
564
DoLine(m_x, m_y + 6, c->m_live2 >> 16);
565
DoLine(m_x, m_y + 7, c->m_live2 >> 24);
574
// --------------------------------------------------------------------------
576
// --------------------------------------------------------------------------
578
extern unsigned char *g_tab;
583
// Advance one step in evolution :-)
587
LifeCellBox *c, *up, *dn, *lf, *rt;
588
wxUint32 t1, t2, t3, t4;
589
bool changed = false;
594
// Compute neighbours of each cell
596
// WARNING: unrolled loops and lengthy code follows!
602
if (! (c->m_live1 || c->m_live2))
613
t1 = c->m_live1 & 0x000000ff;
618
up = LinkBox(c->m_x, c->m_y - 8);
624
c->m_on[0] += g_tab2[t1];
628
t1 = (c->m_live2 & 0xff000000) >> 24;
633
dn = LinkBox(c->m_x, c->m_y + 8);
639
c->m_on[7] += g_tab2[t1];
650
lf = LinkBox(c->m_x - 8, c->m_y);
657
lf->m_up = LinkBox(c->m_x - 8, c->m_y - 8);
660
lf->m_up->m_on[7] += 0x10000000;
661
lf->m_on[0] += 0x10000000;
662
lf->m_on[1] += 0x10000000;
666
lf->m_on[0] += 0x10000000;
667
lf->m_on[1] += 0x10000000;
668
lf->m_on[2] += 0x10000000;
672
lf->m_on[1] += 0x10000000;
673
lf->m_on[2] += 0x10000000;
674
lf->m_on[3] += 0x10000000;
678
lf->m_on[2] += 0x10000000;
679
lf->m_on[3] += 0x10000000;
680
lf->m_on[4] += 0x10000000;
687
lf = LinkBox(c->m_x - 8, c->m_y);
692
lf->m_on[3] += 0x10000000;
693
lf->m_on[4] += 0x10000000;
694
lf->m_on[5] += 0x10000000;
698
lf->m_on[4] += 0x10000000;
699
lf->m_on[5] += 0x10000000;
700
lf->m_on[6] += 0x10000000;
704
lf->m_on[5] += 0x10000000;
705
lf->m_on[6] += 0x10000000;
706
lf->m_on[7] += 0x10000000;
712
lf->m_dn = LinkBox(c->m_x - 8, c->m_y + 8);
715
lf->m_on[6] += 0x10000000;
716
lf->m_on[7] += 0x10000000;
717
lf->m_dn->m_on[0] += 0x10000000;
726
rt = LinkBox(c->m_x + 8, c->m_y);
733
rt->m_up = LinkBox(c->m_x + 8, c->m_y - 8);
736
rt->m_up->m_on[7] += 0x00000001;
737
rt->m_on[0] += 0x00000001;
738
rt->m_on[1] += 0x00000001;
742
rt->m_on[0] += 0x00000001;
743
rt->m_on[1] += 0x00000001;
744
rt->m_on[2] += 0x00000001;
748
rt->m_on[1] += 0x00000001;
749
rt->m_on[2] += 0x00000001;
750
rt->m_on[3] += 0x00000001;
754
rt->m_on[2] += 0x00000001;
755
rt->m_on[3] += 0x00000001;
756
rt->m_on[4] += 0x00000001;
763
rt = LinkBox(c->m_x + 8, c->m_y);
768
rt->m_on[3] += 0x00000001;
769
rt->m_on[4] += 0x00000001;
770
rt->m_on[5] += 0x00000001;
774
rt->m_on[4] += 0x00000001;
775
rt->m_on[5] += 0x00000001;
776
rt->m_on[6] += 0x00000001;
780
rt->m_on[5] += 0x00000001;
781
rt->m_on[6] += 0x00000001;
782
rt->m_on[7] += 0x00000001;
788
rt->m_dn = LinkBox(c->m_x + 8, c->m_y + 8);
791
rt->m_on[6] += 0x00000001;
792
rt->m_on[7] += 0x00000001;
793
rt->m_dn->m_on[0] += 0x00000001;
799
for (i = 1; i <= 3; i++)
801
t1 = ((c->m_live1) >> (i * 8)) & 0x000000ff;
804
c->m_on[i - 1] += g_tab1[t1];
805
c->m_on[i ] += g_tab2[t1];
806
c->m_on[i + 1] += g_tab1[t1];
809
for (i = 0; i <= 2; i++)
811
t1 = ((c->m_live2) >> (i * 8)) & 0x000000ff;
814
c->m_on[i + 3] += g_tab1[t1];
815
c->m_on[i + 4] += g_tab2[t1];
816
c->m_on[i + 5] += g_tab1[t1];
841
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 ) & 0xf) ];
842
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 4 ) & 0xf) ] << 4;
844
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 8 ) & 0xf) ] << 8;
845
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 12) & 0xf) ] << 12;
847
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 16) & 0xf) ] << 16;
848
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 20) & 0xf) ] << 20;
850
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 24) & 0xf) ] << 24;
851
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 28) & 0xf) ] << 28;
857
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 ) & 0xf) ];
858
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 4 ) & 0xf) ] << 4;
860
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 8 ) & 0xf) ] << 8;
861
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 12) & 0xf) ] << 12;
863
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 16) & 0xf) ] << 16;
864
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 20) & 0xf) ] << 20;
866
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 24) & 0xf) ] << 24;
867
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 28) & 0xf) ] << 28;
869
c->m_on[0] = c->m_on[1] = c->m_on[2] = c->m_on[3] =
870
c->m_on[4] = c->m_on[5] = c->m_on[6] = c->m_on[7] = 0;
878
t1_ = (t1 & 0x55555555) + (t1 >> 1 & 0x55555555);
879
t1_ = (t1_ & 0x33333333) + (t1_ >> 2 & 0x33333333);
881
t2_ = (t2 & 0x55555555) + (t2 >> 1 & 0x55555555);
882
t2_ = (t2_ & 0x33333333) + (t2_ >> 2 & 0x33333333) + t1_;
883
t2_ = (t2_ & 0x0F0F0F0F) + (t2_ >> 4 & 0x0F0F0F0F);
884
t2_ = (t2_ & 0x00FF00FF) + (t2_ >> 8 & 0x00FF00FF);
886
m_numcells += (t2_ & 0xFF) + (t2_ >> 16 & 0xFF);
888
// Original, slower code
889
for (int i = 0; i < 32; i++)
891
if (t1 & (1 << i)) m_numcells++;
892
if (t2 & (1 << i)) m_numcells++;
896
changed |= ((t1 ^ c->m_old1) || (t2 ^ c->m_old2));
898
// mark, and discard if necessary, dead boxes
906
LifeCellBox *aux = c->m_next;
907
if (c->m_dead++ > MAXDEAD)
917
// ==========================================================================
919
// ==========================================================================
921
// A module to pregenerate lookup tables without having to do it
922
// from the application.
924
class LifeModule: public wxModule
926
DECLARE_DYNAMIC_CLASS(LifeModule)
934
IMPLEMENT_DYNAMIC_CLASS(LifeModule, wxModule)
936
bool LifeModule::OnInit()
939
g_tab = new unsigned char [0xfffff];
941
if (!g_tab) return false;
943
for (wxUint32 i = 0; i < 0xfffff; i++)
945
wxUint32 val = i >> 4;
946
wxUint32 old = i & 0x0000f;
949
for (int j = 0; j < 4; j++)
953
if (((val & 0xf) == 3) || (((val & 0xf) == 2) && (old & 0x1)))
960
g_tab[i] = (unsigned char) live;
966
void LifeModule::OnExit()
972
// This table converts from number of neighbors (like in on[]) to
973
// bits, for a set of four cells. It takes as index a five-digit
974
// hexadecimal value (0xNNNNB) where Ns hold number of neighbors
975
// for each cell and B holds their previous state.
977
unsigned char *g_tab;
979
// This table converts from bits (like in live1, live2) to number
980
// of neighbors for each cell in the upper or lower row.
1242
// This table converts from bits (like in live1, live2) to number
1243
// of neighbors for each cell in the same row (excluding ourselves)