wxwidgets/demos/life/game.cpp
Vadim Zeitlin 4f4c5fcfdf Use nullptr instead of NULL in the code and documentation
This is a combination of running clang-tidy with modernize-use-nullptr
check for some ports (GTK, X11, OSX) and manual changes to the ports for
which it couldn't be used easily (MSW, DFB) and also manually updating
the docs.

Also replace NULL with null or nullptr in the comments as this is more
consistent with the use of nullptr in the code and makes it simpler to
grep for the remaining occurrences of NULL itself.

And also use null in the assert messages.

Only a few occurrences of "NULL" are still left in non-C files, mostly
corresponding to unclear comments or string output which it might not be
safe to change.
2022-10-18 01:25:25 +02:00

1499 lines
34 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: game.cpp
// Purpose: Life! game logic
// Author: Guillermo Rodriguez Garcia, <guille@iies.es>
// Modified by:
// Created: Jan/2000
// Copyright: (c) 2000, Guillermo Rodriguez Garcia
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ==========================================================================
// headers, declarations, constants
// ==========================================================================
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/log.h"
#include "wx/module.h"
#include "game.h"
#include <string.h> // for memset
#define CELLSARRAYSIZE 1024 // static array for BeginFind & co.
#define ALLOCBOXES 16 // number of cellboxes to alloc at once
#define MAXDEAD 8 // tics before removing cellbox from list
// ==========================================================================
// CellBox
// ==========================================================================
#define HASH(x, y) (((x >> 3) & 0x7f) << 7) + ((y >> 3) & 0x7f)
#define HASHSIZE 16384 // hash table size (do not change!)
#define CELLBOX 8 // cells in a cellbox (do not change!)
class LifeCellBox
{
public:
// members
inline bool IsAlive(int dx, int dy) const;
inline bool SetCell(int dx, int dy, bool alive);
// attributes
wxInt32 m_x, m_y; // position in universe
wxUint32 m_live1, m_live2; // alive cells (1 bit per cell)
wxUint32 m_old1, m_old2; // old values for m_live1, 2
wxUint32 m_on[8]; // neighbouring info
wxUint32 m_dead; // been dead for n generations
LifeCellBox *m_up, *m_dn, *m_lf, *m_rt; // neighbour CellBoxes
LifeCellBox *m_prev, *m_next; // in linked list
LifeCellBox *m_hprev, *m_hnext; // in hash table
};
// IsAlive:
// Returns whether cell dx, dy in this box is alive
//
bool LifeCellBox::IsAlive(int dx, int dy) const
{
if (dy > 3)
return (m_live2 & 1 << ((dy - 4) * 8 + dx)) ? true : false ;
else
return (m_live1 & 1 << ((dy) * 8 + dx)) ? true : false ;
}
// SetCell:
// Sets cell dx, dy in this box to 'alive', returns true if
// the previous value was different, false if it was the same.
//
bool LifeCellBox::SetCell(int dx, int dy, bool alive)
{
if (IsAlive(dx, dy) != alive)
{
if (dy > 3)
m_live2 ^= 1 << ((dy - 4) * 8 + dx);
else
m_live1 ^= 1 << ((dy) * 8 + dx);
// reset this here to avoid updating problems
m_dead = 0;
return true;
}
else
return false;
}
// ==========================================================================
// Life
// ==========================================================================
// --------------------------------------------------------------------------
// Ctor and dtor
// --------------------------------------------------------------------------
Life::Life()
{
// pattern description
m_name = wxEmptyString;
m_rules = wxEmptyString;
m_description = wxEmptyString;
// pattern data
m_numcells = 0;
m_boxes = new LifeCellBox *[HASHSIZE];
m_head = nullptr;
m_available = nullptr;
for (int i = 0; i < HASHSIZE; i++)
m_boxes[i] = nullptr;
// state vars for BeginFind & FindMore
m_cells = new LifeCell[CELLSARRAYSIZE];
m_ncells = 0;
m_findmore = false;
m_changed = false;
}
Life::~Life()
{
Clear();
delete[] m_boxes;
delete[] m_cells;
}
// Clear:
// Clears the board, freeing all storage.
//
void Life::Clear()
{
LifeCellBox *c, *nc;
// clear the hash table pointers
for (int i = 0; i < HASHSIZE; i++)
m_boxes[i] = nullptr;
// free used boxes
c = m_head;
while (c)
{
nc = c->m_next;
delete c;
c = nc;
}
m_head = nullptr;
// free available boxes
c = m_available;
while (c)
{
nc = c->m_next;
delete c;
c = nc;
}
m_available = nullptr;
// reset state
m_name = wxEmptyString;
m_rules = wxEmptyString;
m_description = wxEmptyString;
m_numcells = 0;
}
// --------------------------------------------------------------------------
// Test and set individual cells
// --------------------------------------------------------------------------
// IsAlive:
// Returns whether cell (x, y) is alive.
//
bool Life::IsAlive(wxInt32 x, wxInt32 y)
{
LifeCellBox *c = LinkBox(x, y, false);
return (c && c->IsAlive( x - c->m_x, y - c->m_y ));
}
// SetCell:
// Sets or clears cell (x, y), according to the 'alive' param.
//
void Life::SetCell(wxInt32 x, wxInt32 y, bool alive)
{
LifeCellBox *c = LinkBox(x, y);
wxUint32 dx = x - c->m_x;
wxUint32 dy = y - c->m_y;
if (c->SetCell(dx, dy, alive))
{
if (alive)
m_numcells++;
else
m_numcells--;
}
}
void Life::SetPattern(const LifePattern& pattern)
{
wxArrayString data = pattern.m_shape;
wxString line;
long x = 0,
y = 0;
Clear();
for (size_t n = 0; n < data.GetCount(); n++)
{
line = data[n];
if ( (line.GetChar(0) != wxT('*')) &&
(line.GetChar(0) != wxT('.')) )
{
// assume that it is a digit or a minus sign
line.BeforeFirst(wxT(' ')).ToLong(&x);
line.AfterFirst(wxT(' ')).ToLong(&y);
}
else
{
// pattern data
for (size_t k = 0; k < line.Len(); k++)
SetCell(x + k, y, line.GetChar(k) == wxT('*'));
y++;
}
}
m_name = pattern.m_name;
m_rules = pattern.m_rules;
m_description = pattern.m_description;
}
// --------------------------------------------------------------------------
// Cellbox management functions
// --------------------------------------------------------------------------
// CreateBox:
// Creates a box in x, y, either taking it from the list
// of available boxes, or allocating a new one.
//
LifeCellBox* Life::CreateBox(wxInt32 x, wxInt32 y, wxUint32 hv)
{
LifeCellBox *c;
// if there are no available boxes, alloc a few more
if (!m_available)
for (int i = 1; i <= ALLOCBOXES; i++)
{
c = new LifeCellBox();
if (!c)
{
// TODO: handle memory errors. Note that right now, if we
// couldn't allocate at least one cellbox, we will crash
// before leaving CreateBox(). Probably we should try to
// allocate some boxes *before* the m_available list goes
// empty, so that we have a margin to handle errors
// gracefully.
wxLogFatalError(_("Out of memory! Aborting..."));
// NOTREACHED
}
c->m_next = m_available;
m_available = c;
}
// take a cellbox from the list of available boxes
c = m_available;
m_available = c->m_next;
// reset everything
memset((void *) c, 0, sizeof(LifeCellBox));
c->m_x = x;
c->m_y = y;
// insert c in the list
c->m_next = m_head;
m_head = c;
if (c->m_next) c->m_next->m_prev = c;
// insert c in the hash table
c->m_hnext = m_boxes[hv];
m_boxes[hv] = c;
if (c->m_hnext) c->m_hnext->m_hprev = c;
return c;
}
// LinkBox:
// Returns a pointer to the box (x, y); if it didn't exist yet,
// it returns nullptr or creates a new one, depending on the value
// of the 'create' parameter.
//
LifeCellBox* Life::LinkBox(wxInt32 x, wxInt32 y, bool create)
{
wxUint32 hv;
LifeCellBox *c;
x &= 0xfffffff8;
y &= 0xfffffff8;
hv = HASH(x, y);
// search in the hash table
for (c = m_boxes[hv]; c; c = c->m_hnext)
if ((c->m_x == x) && (c->m_y == y)) return c;
// if not found, and (create == true), create a new one
return create? CreateBox(x, y, hv) : nullptr;
}
// KillBox:
// Removes this box from the list and the hash table and
// puts it in the list of available boxes.
//
void Life::KillBox(LifeCellBox *c)
{
wxUint32 hv = HASH(c->m_x, c->m_y);
// remove from the list
if (c != m_head)
c->m_prev->m_next = c->m_next;
else
m_head = c->m_next;
// remove from the hash table
if (c != m_boxes[hv])
c->m_hprev->m_hnext = c->m_hnext;
else
m_boxes[hv] = c->m_hnext;
// update neighbours
if (c->m_next) c->m_next->m_prev = c->m_prev;
if (c->m_hnext) c->m_hnext->m_hprev = c->m_hprev;
if (c->m_up) c->m_up->m_dn = nullptr;
if (c->m_dn) c->m_dn->m_up = nullptr;
if (c->m_lf) c->m_lf->m_rt = nullptr;
if (c->m_rt) c->m_rt->m_lf = nullptr;
// append to the list of available boxes
c->m_next = m_available;
m_available = c;
}
// --------------------------------------------------------------------------
// Navigation
// --------------------------------------------------------------------------
LifeCell Life::FindCenter()
{
double sx, sy;
int n;
sx = 0.0;
sy = 0.0;
n = 0;
LifeCellBox *c;
for (c = m_head; c; c = c->m_next)
if (!c->m_dead)
{
sx += c->m_x;
sy += c->m_y;
n++;
}
if (n > 0)
{
sx = (sx / n) + CELLBOX / 2;
sy = (sy / n) + CELLBOX / 2;
}
LifeCell cell;
cell.i = (wxInt32) sx;
cell.j = (wxInt32) sy;
return cell;
}
LifeCell Life::FindNorth()
{
wxInt32 x = 0, y = 0;
bool first = true;
LifeCellBox *c;
for (c = m_head; c; c = c->m_next)
if (!c->m_dead && ((first) || (c->m_y < y)))
{
x = c->m_x;
y = c->m_y;
first = false;
}
LifeCell cell;
cell.i = first? 0 : x + CELLBOX / 2;
cell.j = first? 0 : y + CELLBOX / 2;
return cell;
}
LifeCell Life::FindSouth()
{
wxInt32 x = 0, y = 0;
bool first = true;
LifeCellBox *c;
for (c = m_head; c; c = c->m_next)
if (!c->m_dead && ((first) || (c->m_y > y)))
{
x = c->m_x;
y = c->m_y;
first = false;
}
LifeCell cell;
cell.i = first? 0 : x + CELLBOX / 2;
cell.j = first? 0 : y + CELLBOX / 2;
return cell;
}
LifeCell Life::FindWest()
{
wxInt32 x = 0, y = 0;
bool first = true;
LifeCellBox *c;
for (c = m_head; c; c = c->m_next)
if (!c->m_dead && ((first) || (c->m_x < x)))
{
x = c->m_x;
y = c->m_y;
first = false;
}
LifeCell cell;
cell.i = first? 0 : x + CELLBOX / 2;
cell.j = first? 0 : y + CELLBOX / 2;
return cell;
}
LifeCell Life::FindEast()
{
wxInt32 x = 0, y = 0;
bool first = true;
LifeCellBox *c;
for (c = m_head; c; c = c->m_next)
if (!c->m_dead && ((first) || (c->m_x > x)))
{
x = c->m_x;
y = c->m_y;
first = false;
}
LifeCell cell;
cell.i = first? 0 : x + CELLBOX / 2;
cell.j = first? 0 : y + CELLBOX / 2;
return cell;
}
// --------------------------------------------------------------------------
// FindMore & co.
// --------------------------------------------------------------------------
// DoLine:
// Post eight cells to the cell arrays - leave out the fourth
// argument (or pass 0, the default value) to post alive cells
// only, else it will post cells which have changed.
//
void Life::DoLine(wxInt32 x, wxInt32 y, wxUint32 live, wxUint32 old)
{
wxUint32 diff = (live ^ old) & 0xff;
if (!diff) return;
for (wxInt32 k = 8; k; k--, x++)
{
if (diff & 0x01)
{
m_cells[m_ncells].i = x;
m_cells[m_ncells].j = y;
m_ncells++;
}
diff >>= 1;
}
}
void Life::BeginFind(wxInt32 x0, wxInt32 y0, wxInt32 x1, wxInt32 y1, bool changed)
{
// TODO: optimize for the case where the maximum number of
// cellboxes that fit in the specified viewport is smaller
// than the current total of boxes; iterating over the list
// should then be faster than searching in the hash table.
m_x0 = m_x = x0 & 0xfffffff8;
m_y0 = m_y = y0 & 0xfffffff8;
m_x1 = (x1 + 7) & 0xfffffff8;
m_y1 = (y1 + 7) & 0xfffffff8;
m_findmore = true;
m_changed = changed;
}
bool Life::FindMore(LifeCell *cells[], size_t *ncells)
{
LifeCellBox *c;
*cells = m_cells;
m_ncells = 0;
if (m_changed)
{
for ( ; m_y <= m_y1; m_y += 8, m_x = m_x0)
for ( ; m_x <= m_x1; m_x += 8)
{
if ((c = LinkBox(m_x, m_y, false)) == nullptr)
continue;
// check whether there is enough space left in the array
if (m_ncells > (CELLSARRAYSIZE - 64))
{
*ncells = m_ncells;
return false;
}
DoLine(m_x, m_y , c->m_live1, c->m_old1 );
DoLine(m_x, m_y + 1, c->m_live1 >> 8, c->m_old1 >> 8 );
DoLine(m_x, m_y + 2, c->m_live1 >> 16, c->m_old1 >> 16);
DoLine(m_x, m_y + 3, c->m_live1 >> 24, c->m_old1 >> 24);
DoLine(m_x, m_y + 4, c->m_live2, c->m_old2 );
DoLine(m_x, m_y + 5, c->m_live2 >> 8, c->m_old2 >> 8 );
DoLine(m_x, m_y + 6, c->m_live2 >> 16, c->m_old2 >> 16);
DoLine(m_x, m_y + 7, c->m_live2 >> 24, c->m_old2 >> 24);
}
}
else
{
for ( ; m_y <= m_y1; m_y += 8, m_x = m_x0)
for ( ; m_x <= m_x1; m_x += 8)
{
if ((c = LinkBox(m_x, m_y, false)) == nullptr)
continue;
// check whether there is enough space left in the array
if (m_ncells > (CELLSARRAYSIZE - 64))
{
*ncells = m_ncells;
return false;
}
DoLine(m_x, m_y , c->m_live1 );
DoLine(m_x, m_y + 1, c->m_live1 >> 8 );
DoLine(m_x, m_y + 2, c->m_live1 >> 16);
DoLine(m_x, m_y + 3, c->m_live1 >> 24);
DoLine(m_x, m_y + 4, c->m_live2 );
DoLine(m_x, m_y + 5, c->m_live2 >> 8 );
DoLine(m_x, m_y + 6, c->m_live2 >> 16);
DoLine(m_x, m_y + 7, c->m_live2 >> 24);
}
}
*ncells = m_ncells;
m_findmore = false;
return true;
}
// --------------------------------------------------------------------------
// Evolution engine
// --------------------------------------------------------------------------
extern unsigned char *g_tab;
extern int g_tab1[];
extern int g_tab2[];
// NextTic:
// Advance one step in evolution :-)
//
bool Life::NextTic()
{
LifeCellBox *c, *up, *dn, *lf, *rt;
wxUint32 t1, t2;
bool changed = false;
m_numcells = 0;
// Stage 1:
// Compute neighbours of each cell
//
// WARNING: unrolled loops and lengthy code follows!
//
c = m_head;
while (c)
{
if (! (c->m_live1 || c->m_live2))
{
c = c->m_next;
continue;
}
up = c->m_up;
dn = c->m_dn;
lf = c->m_lf;
rt = c->m_rt;
// up
t1 = c->m_live1 & 0x000000ff;
if (t1)
{
if (!up)
{
up = LinkBox(c->m_x, c->m_y - 8);
up->m_dn = c;
}
t2 = g_tab1[t1];
up->m_on[7] += t2;
c->m_on[1] += t2;
c->m_on[0] += g_tab2[t1];
}
// down
t1 = (c->m_live2 & 0xff000000) >> 24;
if (t1)
{
if (!dn)
{
dn = LinkBox(c->m_x, c->m_y + 8);
dn->m_up = c;
}
t2 = g_tab1[t1];
dn->m_on[0] += t2;
c->m_on[6] += t2;
c->m_on[7] += g_tab2[t1];
}
t1 = c->m_live1;
t2 = c->m_live2;
// left
if (t1 & 0x01010101)
{
if (!lf)
{
lf = LinkBox(c->m_x - 8, c->m_y);
lf->m_rt = c;
}
if (t1 & 0x00000001)
{
if (!lf->m_up)
{
lf->m_up = LinkBox(c->m_x - 8, c->m_y - 8);
lf->m_up->m_dn = lf;
}
lf->m_up->m_on[7] += 0x10000000;
lf->m_on[0] += 0x10000000;
lf->m_on[1] += 0x10000000;
}
if (t1 & 0x00000100)
{
lf->m_on[0] += 0x10000000;
lf->m_on[1] += 0x10000000;
lf->m_on[2] += 0x10000000;
}
if (t1 & 0x00010000)
{
lf->m_on[1] += 0x10000000;
lf->m_on[2] += 0x10000000;
lf->m_on[3] += 0x10000000;
}
if (t1 & 0x01000000)
{
lf->m_on[2] += 0x10000000;
lf->m_on[3] += 0x10000000;
lf->m_on[4] += 0x10000000;
}
}
if (t2 & 0x01010101)
{
if (!lf)
{
lf = LinkBox(c->m_x - 8, c->m_y);
lf->m_rt = c;
}
if (t2 & 0x00000001)
{
lf->m_on[3] += 0x10000000;
lf->m_on[4] += 0x10000000;
lf->m_on[5] += 0x10000000;
}
if (t2 & 0x00000100)
{
lf->m_on[4] += 0x10000000;
lf->m_on[5] += 0x10000000;
lf->m_on[6] += 0x10000000;
}
if (t2 & 0x00010000)
{
lf->m_on[5] += 0x10000000;
lf->m_on[6] += 0x10000000;
lf->m_on[7] += 0x10000000;
}
if (t2 & 0x01000000)
{
if (!lf->m_dn)
{
lf->m_dn = LinkBox(c->m_x - 8, c->m_y + 8);
lf->m_dn->m_up = lf;
}
lf->m_on[6] += 0x10000000;
lf->m_on[7] += 0x10000000;
lf->m_dn->m_on[0] += 0x10000000;
}
}
// right
if (t1 & 0x80808080)
{
if (!rt)
{
rt = LinkBox(c->m_x + 8, c->m_y);
rt->m_lf = c;
}
if (t1 & 0x00000080)
{
if (!rt->m_up)
{
rt->m_up = LinkBox(c->m_x + 8, c->m_y - 8);
rt->m_up->m_dn = rt;
}
rt->m_up->m_on[7] += 0x00000001;
rt->m_on[0] += 0x00000001;
rt->m_on[1] += 0x00000001;
}
if (t1 & 0x00008000)
{
rt->m_on[0] += 0x00000001;
rt->m_on[1] += 0x00000001;
rt->m_on[2] += 0x00000001;
}
if (t1 & 0x00800000)
{
rt->m_on[1] += 0x00000001;
rt->m_on[2] += 0x00000001;
rt->m_on[3] += 0x00000001;
}
if (t1 & 0x80000000)
{
rt->m_on[2] += 0x00000001;
rt->m_on[3] += 0x00000001;
rt->m_on[4] += 0x00000001;
}
}
if (t2 & 0x80808080)
{
if (!rt)
{
rt = LinkBox(c->m_x + 8, c->m_y);
rt->m_lf = c;
}
if (t2 & 0x00000080)
{
rt->m_on[3] += 0x00000001;
rt->m_on[4] += 0x00000001;
rt->m_on[5] += 0x00000001;
}
if (t2 & 0x00008000)
{
rt->m_on[4] += 0x00000001;
rt->m_on[5] += 0x00000001;
rt->m_on[6] += 0x00000001;
}
if (t2 & 0x00800000)
{
rt->m_on[5] += 0x00000001;
rt->m_on[6] += 0x00000001;
rt->m_on[7] += 0x00000001;
}
if (t2 & 0x80000000)
{
if (!rt->m_dn)
{
rt->m_dn = LinkBox(c->m_x + 8, c->m_y + 8);
rt->m_dn->m_up = rt;
}
rt->m_on[6] += 0x00000001;
rt->m_on[7] += 0x00000001;
rt->m_dn->m_on[0] += 0x00000001;
}
}
// inner cells
int i;
for (i = 1; i <= 3; i++)
{
t1 = ((c->m_live1) >> (i * 8)) & 0x000000ff;
if (t1)
{
c->m_on[i - 1] += g_tab1[t1];
c->m_on[i ] += g_tab2[t1];
c->m_on[i + 1] += g_tab1[t1];
}
}
for (i = 0; i <= 2; i++)
{
t1 = ((c->m_live2) >> (i * 8)) & 0x000000ff;
if (t1)
{
c->m_on[i + 3] += g_tab1[t1];
c->m_on[i + 4] += g_tab2[t1];
c->m_on[i + 5] += g_tab1[t1];
}
}
c->m_up = up;
c->m_dn = dn;
c->m_lf = lf;
c->m_rt = rt;
c = c->m_next;
}
// Stage 2:
// Stabilize
//
c = m_head;
while (c)
{
t1 = 0;
t2 = 0;
wxUint32 t3, t4;
t3 = c->m_live1;
c->m_old1 = t3;
t4 = c->m_on[0];
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 ) & 0xf) ];
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 4 ) & 0xf) ] << 4;
t4 = c->m_on[1];
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 8 ) & 0xf) ] << 8;
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 12) & 0xf) ] << 12;
t4 = c->m_on[2];
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 16) & 0xf) ] << 16;
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 20) & 0xf) ] << 20;
t4 = c->m_on[3];
t1 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 24) & 0xf) ] << 24;
t1 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 28) & 0xf) ] << 28;
t3 = c->m_live2;
c->m_old2 = t3;
t4 = c->m_on[4];
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 ) & 0xf) ];
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 4 ) & 0xf) ] << 4;
t4 = c->m_on[5];
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 8 ) & 0xf) ] << 8;
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 12) & 0xf) ] << 12;
t4 = c->m_on[6];
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 16) & 0xf) ] << 16;
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 20) & 0xf) ] << 20;
t4 = c->m_on[7];
t2 |= g_tab[ ((t4 & 0x0000ffff) << 4 ) + ((t3 >> 24) & 0xf) ] << 24;
t2 |= g_tab[ ((t4 & 0xffff0000) >> 12) + ((t3 >> 28) & 0xf) ] << 28;
c->m_on[0] = c->m_on[1] = c->m_on[2] = c->m_on[3] =
c->m_on[4] = c->m_on[5] = c->m_on[6] = c->m_on[7] = 0;
c->m_live1 = t1;
c->m_live2 = t2;
// count alive cells
#if 1
wxUint32 t1_, t2_;
t1_ = (t1 & 0x55555555) + (t1 >> 1 & 0x55555555);
t1_ = (t1_ & 0x33333333) + (t1_ >> 2 & 0x33333333);
t2_ = (t2 & 0x55555555) + (t2 >> 1 & 0x55555555);
t2_ = (t2_ & 0x33333333) + (t2_ >> 2 & 0x33333333) + t1_;
t2_ = (t2_ & 0x0F0F0F0F) + (t2_ >> 4 & 0x0F0F0F0F);
t2_ = (t2_ & 0x00FF00FF) + (t2_ >> 8 & 0x00FF00FF);
m_numcells += (t2_ & 0xFF) + (t2_ >> 16 & 0xFF);
#else
// Original, slower code
for (int i = 0; i < 32; i++)
{
if (t1 & (1 << i)) m_numcells++;
if (t2 & (1 << i)) m_numcells++;
}
#endif
changed |= ((t1 ^ c->m_old1) || (t2 ^ c->m_old2));
// mark, and discard if necessary, dead boxes
if (t1 || t2)
{
c->m_dead = 0;
c = c->m_next;
}
else
{
LifeCellBox *aux = c->m_next;
if (c->m_dead++ > MAXDEAD)
KillBox(c);
c = aux;
}
}
return changed;
}
// ==========================================================================
// LifeModule
// ==========================================================================
// A module to pregenerate lookup tables without having to do it
// from the application.
class LifeModule: public wxModule
{
wxDECLARE_DYNAMIC_CLASS(LifeModule);
public:
LifeModule() {}
bool OnInit() override;
void OnExit() override;
};
wxIMPLEMENT_DYNAMIC_CLASS(LifeModule, wxModule);
bool LifeModule::OnInit()
{
// see below
g_tab = new unsigned char [0xfffff];
if (!g_tab) return false;
for (wxUint32 i = 0; i < 0xfffff; i++)
{
wxUint32 val = i >> 4;
wxUint32 old = i & 0x0000f;
wxUint32 live = 0;
for (int j = 0; j < 4; j++)
{
live >>= 1;
if (((val & 0xf) == 3) || (((val & 0xf) == 2) && (old & 0x1)))
live |= 0x8;
old >>= 1;
val >>= 4;
}
g_tab[i] = (unsigned char) live;
}
return true;
}
void LifeModule::OnExit()
{
delete [] g_tab;
}
// This table converts from number of neighbors (like in on[]) to
// bits, for a set of four cells. It takes as index a five-digit
// hexadecimal value (0xNNNNB) where Ns hold number of neighbors
// for each cell and B holds their previous state.
//
unsigned char *g_tab;
// This table converts from bits (like in live1, live2) to number
// of neighbors for each cell in the upper or lower row.
//
int g_tab1[]=
{
0x00000000,
0x00000011,
0x00000111,
0x00000122,
0x00001110,
0x00001121,
0x00001221,
0x00001232,
0x00011100,
0x00011111,
0x00011211,
0x00011222,
0x00012210,
0x00012221,
0x00012321,
0x00012332,
0x00111000,
0x00111011,
0x00111111,
0x00111122,
0x00112110,
0x00112121,
0x00112221,
0x00112232,
0x00122100,
0x00122111,
0x00122211,
0x00122222,
0x00123210,
0x00123221,
0x00123321,
0x00123332,
0x01110000,
0x01110011,
0x01110111,
0x01110122,
0x01111110,
0x01111121,
0x01111221,
0x01111232,
0x01121100,
0x01121111,
0x01121211,
0x01121222,
0x01122210,
0x01122221,
0x01122321,
0x01122332,
0x01221000,
0x01221011,
0x01221111,
0x01221122,
0x01222110,
0x01222121,
0x01222221,
0x01222232,
0x01232100,
0x01232111,
0x01232211,
0x01232222,
0x01233210,
0x01233221,
0x01233321,
0x01233332,
0x11100000,
0x11100011,
0x11100111,
0x11100122,
0x11101110,
0x11101121,
0x11101221,
0x11101232,
0x11111100,
0x11111111,
0x11111211,
0x11111222,
0x11112210,
0x11112221,
0x11112321,
0x11112332,
0x11211000,
0x11211011,
0x11211111,
0x11211122,
0x11212110,
0x11212121,
0x11212221,
0x11212232,
0x11222100,
0x11222111,
0x11222211,
0x11222222,
0x11223210,
0x11223221,
0x11223321,
0x11223332,
0x12210000,
0x12210011,
0x12210111,
0x12210122,
0x12211110,
0x12211121,
0x12211221,
0x12211232,
0x12221100,
0x12221111,
0x12221211,
0x12221222,
0x12222210,
0x12222221,
0x12222321,
0x12222332,
0x12321000,
0x12321011,
0x12321111,
0x12321122,
0x12322110,
0x12322121,
0x12322221,
0x12322232,
0x12332100,
0x12332111,
0x12332211,
0x12332222,
0x12333210,
0x12333221,
0x12333321,
0x12333332,
0x11000000,
0x11000011,
0x11000111,
0x11000122,
0x11001110,
0x11001121,
0x11001221,
0x11001232,
0x11011100,
0x11011111,
0x11011211,
0x11011222,
0x11012210,
0x11012221,
0x11012321,
0x11012332,
0x11111000,
0x11111011,
0x11111111,
0x11111122,
0x11112110,
0x11112121,
0x11112221,
0x11112232,
0x11122100,
0x11122111,
0x11122211,
0x11122222,
0x11123210,
0x11123221,
0x11123321,
0x11123332,
0x12110000,
0x12110011,
0x12110111,
0x12110122,
0x12111110,
0x12111121,
0x12111221,
0x12111232,
0x12121100,
0x12121111,
0x12121211,
0x12121222,
0x12122210,
0x12122221,
0x12122321,
0x12122332,
0x12221000,
0x12221011,
0x12221111,
0x12221122,
0x12222110,
0x12222121,
0x12222221,
0x12222232,
0x12232100,
0x12232111,
0x12232211,
0x12232222,
0x12233210,
0x12233221,
0x12233321,
0x12233332,
0x22100000,
0x22100011,
0x22100111,
0x22100122,
0x22101110,
0x22101121,
0x22101221,
0x22101232,
0x22111100,
0x22111111,
0x22111211,
0x22111222,
0x22112210,
0x22112221,
0x22112321,
0x22112332,
0x22211000,
0x22211011,
0x22211111,
0x22211122,
0x22212110,
0x22212121,
0x22212221,
0x22212232,
0x22222100,
0x22222111,
0x22222211,
0x22222222,
0x22223210,
0x22223221,
0x22223321,
0x22223332,
0x23210000,
0x23210011,
0x23210111,
0x23210122,
0x23211110,
0x23211121,
0x23211221,
0x23211232,
0x23221100,
0x23221111,
0x23221211,
0x23221222,
0x23222210,
0x23222221,
0x23222321,
0x23222332,
0x23321000,
0x23321011,
0x23321111,
0x23321122,
0x23322110,
0x23322121,
0x23322221,
0x23322232,
0x23332100,
0x23332111,
0x23332211,
0x23332222,
0x23333210,
0x23333221,
0x23333321,
0x23333332
};
// This table converts from bits (like in live1, live2) to number
// of neighbors for each cell in the same row (excluding ourselves)
//
int g_tab2[]=
{
0x00000000,
0x00000010,
0x00000101,
0x00000111,
0x00001010,
0x00001020,
0x00001111,
0x00001121,
0x00010100,
0x00010110,
0x00010201,
0x00010211,
0x00011110,
0x00011120,
0x00011211,
0x00011221,
0x00101000,
0x00101010,
0x00101101,
0x00101111,
0x00102010,
0x00102020,
0x00102111,
0x00102121,
0x00111100,
0x00111110,
0x00111201,
0x00111211,
0x00112110,
0x00112120,
0x00112211,
0x00112221,
0x01010000,
0x01010010,
0x01010101,
0x01010111,
0x01011010,
0x01011020,
0x01011111,
0x01011121,
0x01020100,
0x01020110,
0x01020201,
0x01020211,
0x01021110,
0x01021120,
0x01021211,
0x01021221,
0x01111000,
0x01111010,
0x01111101,
0x01111111,
0x01112010,
0x01112020,
0x01112111,
0x01112121,
0x01121100,
0x01121110,
0x01121201,
0x01121211,
0x01122110,
0x01122120,
0x01122211,
0x01122221,
0x10100000,
0x10100010,
0x10100101,
0x10100111,
0x10101010,
0x10101020,
0x10101111,
0x10101121,
0x10110100,
0x10110110,
0x10110201,
0x10110211,
0x10111110,
0x10111120,
0x10111211,
0x10111221,
0x10201000,
0x10201010,
0x10201101,
0x10201111,
0x10202010,
0x10202020,
0x10202111,
0x10202121,
0x10211100,
0x10211110,
0x10211201,
0x10211211,
0x10212110,
0x10212120,
0x10212211,
0x10212221,
0x11110000,
0x11110010,
0x11110101,
0x11110111,
0x11111010,
0x11111020,
0x11111111,
0x11111121,
0x11120100,
0x11120110,
0x11120201,
0x11120211,
0x11121110,
0x11121120,
0x11121211,
0x11121221,
0x11211000,
0x11211010,
0x11211101,
0x11211111,
0x11212010,
0x11212020,
0x11212111,
0x11212121,
0x11221100,
0x11221110,
0x11221201,
0x11221211,
0x11222110,
0x11222120,
0x11222211,
0x11222221,
0x01000000,
0x01000010,
0x01000101,
0x01000111,
0x01001010,
0x01001020,
0x01001111,
0x01001121,
0x01010100,
0x01010110,
0x01010201,
0x01010211,
0x01011110,
0x01011120,
0x01011211,
0x01011221,
0x01101000,
0x01101010,
0x01101101,
0x01101111,
0x01102010,
0x01102020,
0x01102111,
0x01102121,
0x01111100,
0x01111110,
0x01111201,
0x01111211,
0x01112110,
0x01112120,
0x01112211,
0x01112221,
0x02010000,
0x02010010,
0x02010101,
0x02010111,
0x02011010,
0x02011020,
0x02011111,
0x02011121,
0x02020100,
0x02020110,
0x02020201,
0x02020211,
0x02021110,
0x02021120,
0x02021211,
0x02021221,
0x02111000,
0x02111010,
0x02111101,
0x02111111,
0x02112010,
0x02112020,
0x02112111,
0x02112121,
0x02121100,
0x02121110,
0x02121201,
0x02121211,
0x02122110,
0x02122120,
0x02122211,
0x02122221,
0x11100000,
0x11100010,
0x11100101,
0x11100111,
0x11101010,
0x11101020,
0x11101111,
0x11101121,
0x11110100,
0x11110110,
0x11110201,
0x11110211,
0x11111110,
0x11111120,
0x11111211,
0x11111221,
0x11201000,
0x11201010,
0x11201101,
0x11201111,
0x11202010,
0x11202020,
0x11202111,
0x11202121,
0x11211100,
0x11211110,
0x11211201,
0x11211211,
0x11212110,
0x11212120,
0x11212211,
0x11212221,
0x12110000,
0x12110010,
0x12110101,
0x12110111,
0x12111010,
0x12111020,
0x12111111,
0x12111121,
0x12120100,
0x12120110,
0x12120201,
0x12120211,
0x12121110,
0x12121120,
0x12121211,
0x12121221,
0x12211000,
0x12211010,
0x12211101,
0x12211111,
0x12212010,
0x12212020,
0x12212111,
0x12212121,
0x12221100,
0x12221110,
0x12221201,
0x12221211,
0x12222110,
0x12222120,
0x12222211,
0x12222221
};