-
-// --------------------------------------------------------------------------
-// LifeNewGameDialog
-// --------------------------------------------------------------------------
-
-LifeNewGameDialog::LifeNewGameDialog(wxWindow *parent, int *w, int *h)
- : wxDialog(parent, -1,
- _("New game"),
- wxDefaultPosition,
- wxDefaultSize,
- wxDEFAULT_DIALOG_STYLE | wxDIALOG_MODAL)
-{
- m_w = w;
- m_h = h;
-
- // spin ctrls
- m_spinctrlw = new wxSpinCtrl( this, -1 );
- m_spinctrlw->SetValue(*m_w);
- m_spinctrlw->SetRange(LIFE_MIN, LIFE_MAX);
-
- m_spinctrlh = new wxSpinCtrl( this, -1 );
- m_spinctrlh->SetValue(*m_h);
- m_spinctrlh->SetRange(LIFE_MIN, LIFE_MAX);
-
- // component layout
- wxBoxSizer *inputsizer1 = new wxBoxSizer( wxHORIZONTAL );
- inputsizer1->Add( new wxStaticText(this, -1, _("Width")), 1, wxCENTRE | wxLEFT, 20);
- inputsizer1->Add( m_spinctrlw, 2, wxCENTRE | wxLEFT | wxRIGHT, 20 );
-
- wxBoxSizer *inputsizer2 = new wxBoxSizer( wxHORIZONTAL );
- inputsizer2->Add( new wxStaticText(this, -1, _("Height")), 1, wxCENTRE | wxLEFT, 20);
- inputsizer2->Add( m_spinctrlh, 2, wxCENTRE | wxLEFT | wxRIGHT, 20 );
-
- wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
- topsizer->Add( CreateTextSizer(_("Enter board dimensions")), 0, wxALL, 10 );
- topsizer->Add( new wxStaticLine(this, -1), 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 10);
- topsizer->Add( inputsizer1, 1, wxGROW | wxLEFT | wxRIGHT, 5 );
- topsizer->Add( inputsizer2, 1, wxGROW | wxLEFT | wxRIGHT, 5 );
- topsizer->Add( new wxStaticLine(this, -1), 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 10);
- topsizer->Add( CreateButtonSizer(wxOK | wxCANCEL), 0, wxCENTRE | wxALL, 10);
-
- // activate
- SetSizer(topsizer);
- SetAutoLayout(TRUE);
- topsizer->SetSizeHints(this);
- topsizer->Fit(this);
- Centre(wxBOTH);
-}
-
-void LifeNewGameDialog::OnOK(wxCommandEvent& WXUNUSED(event))
-{
- *m_w = m_spinctrlw->GetValue();
- *m_h = m_spinctrlh->GetValue();
-
- EndModal(wxID_OK);
-}
-
-// --------------------------------------------------------------------------
-// LifeSamplesDialog
-// --------------------------------------------------------------------------
-
-LifeSamplesDialog::LifeSamplesDialog(wxWindow *parent)
- : wxDialog(parent, -1,
- _("Sample games"),
- wxDefaultPosition,
- wxDefaultSize,
- wxDEFAULT_DIALOG_STYLE | wxDIALOG_MODAL)
-{
- m_value = 0;
-
- // create and populate the list of available samples
- m_list = new wxListBox( this, ID_LISTBOX,
- wxDefaultPosition,
- wxDefaultSize,
- 0, NULL,
- wxLB_SINGLE | wxLB_NEEDED_SB | wxLB_HSCROLL );
-
- for (unsigned i = 0; i < (sizeof(g_shapes) / sizeof(LifeShape)); i++)
- m_list->Append(g_shapes[i].m_name);
-
- // descriptions
- wxStaticBox *statbox = new wxStaticBox( this, -1, _("Description"));
- m_life = new Life( 16, 16 );
- m_life->SetShape(g_shapes[0]);
- m_canvas = new LifeCanvas( this, m_life, FALSE );
- m_text = new wxTextCtrl( this, -1,
- g_shapes[0].m_desc,
- wxDefaultPosition,
- wxSize(300, 60),
- wxTE_MULTILINE | wxTE_READONLY);
-
- // layout components
- wxStaticBoxSizer *sizer1 = new wxStaticBoxSizer( statbox, wxVERTICAL );
- sizer1->Add( m_canvas, 2, wxGROW | wxCENTRE | wxALL, 5);
- sizer1->Add( m_text, 1, wxGROW | wxCENTRE | wxALL, 5 );
-
- wxBoxSizer *sizer2 = new wxBoxSizer( wxHORIZONTAL );
- sizer2->Add( m_list, 0, wxGROW | wxCENTRE | wxALL, 5 );
- sizer2->Add( sizer1, 1, wxGROW | wxCENTRE | wxALL, 5 );
-
- wxBoxSizer *sizer3 = new wxBoxSizer( wxVERTICAL );
- sizer3->Add( CreateTextSizer(_("Select one configuration")), 0, wxALL, 10 );
- sizer3->Add( new wxStaticLine(this, -1), 0, wxGROW | wxLEFT | wxRIGHT, 10 );
- sizer3->Add( sizer2, 1, wxGROW | wxCENTRE | wxALL, 5 );
- sizer3->Add( new wxStaticLine(this, -1), 0, wxGROW | wxLEFT | wxRIGHT, 10 );
- sizer3->Add( CreateButtonSizer(wxOK | wxCANCEL), 0, wxCENTRE | wxALL, 10 );
-
- // activate
- SetSizer(sizer3);
- SetAutoLayout(TRUE);
- sizer3->SetSizeHints(this);
- sizer3->Fit(this);
- Centre(wxBOTH);
-}
-
-LifeSamplesDialog::~LifeSamplesDialog()
-{
- m_canvas->Destroy();
- delete m_life;
-}
-
-int LifeSamplesDialog::GetValue()
-{
- return m_value;
-}
-
-void LifeSamplesDialog::OnListBox(wxCommandEvent& event)
-{
- if (event.GetSelection() != -1)
- {
- m_value = m_list->GetSelection();
- m_text->SetValue(g_shapes[ event.GetSelection() ].m_desc);
- m_life->SetShape(g_shapes[ event.GetSelection() ]);
-
- m_canvas->DrawEverything(TRUE); // force redraw everything
- m_canvas->Refresh(FALSE); // do not erase background
- }
-}
-
-// --------------------------------------------------------------------------
-// Life
-// --------------------------------------------------------------------------
-
-Life::Life(int width, int height)
-{
- m_wrap = TRUE;
- m_cells = NULL;
- Create(width, height);
-}
-
-Life::~Life()
-{
- Destroy();
-}
-
-void Life::Create(int width, int height)
-{
- wxASSERT(width > 0 && height > 0);
-
- m_width = width;
- m_height = height;
- m_cells = new Cell[m_width * m_height];
- Clear();
-}
-
-void Life::Destroy()
-{
- delete[] m_cells;
-}
-
-void Life::Clear()
-{
- for (int i = 0; i < m_width * m_height; i++)
- m_cells[i] = CELL_DEAD;
-}
-
-bool Life::IsAlive(int x, int y) const
-{
- wxASSERT(x >= 0 && y >= 0 && x < m_width && y < m_height);
-
- return (m_cells[y * m_width + x] & CELL_ALIVE);
-}
-
-bool Life::HasChanged(int x, int y) const
-{
- wxASSERT(x >= 0 && y >= 0 && x < m_width && y < m_height);
-
- return (m_cells[y * m_width + x] & CELL_MARK) != 0;
-}
-
-void Life::SetBorderWrap(bool on)
-{
- m_wrap = on;
-}
-
-void Life::SetCell(int x, int y, bool alive)
-{
- wxASSERT(x >= 0 && y >= 0 && x < m_width && y < m_height);
-
- m_cells[y * m_width + x] = (alive? CELL_ALIVE : CELL_DEAD);
-}
-
-void Life::SetShape(LifeShape& shape)
-{
- wxASSERT((m_width >= shape.m_width) && (m_height >= shape.m_height));
-
- int x0 = (m_width - shape.m_width) / 2;
- int y0 = (m_height - shape.m_height) / 2;
- char *p = shape.m_data;
-
- Clear();
- for (int j = y0; j < y0 + shape.m_height; j++)
- for (int i = x0; i < x0 + shape.m_width; i++)
- SetCell(i, j, *(p++) == '*');
-}
-
-bool Life::NextTic()
-{
- long changed = 0;
- int i, j;
-
- /* 1st pass. Find and mark deaths and births for this generation.
- *
- * Rules:
- * An organism with <= 1 neighbors will die due to isolation.
- * An organism with >= 4 neighbors will die due to starvation.
- * New organisms are born in cells with exactly 3 neighbors.
- */
- for (j = 0; j < m_height; j++)
- for (i = 0; i < m_width; i++)
- {
- int neighbors = GetNeighbors(i, j);
- bool alive = IsAlive(i, j);
-
- /* Set CELL_MARK if this cell must change, clear it
- * otherwise. We cannot toggle the CELL_ALIVE bit yet
- * because all deaths and births are simultaneous (it
- * would affect neighbouring cells).
- */
- if ((!alive && neighbors == 3) ||
- (alive && (neighbors <= 1 || neighbors >= 4)))
- m_cells[j * m_width + i] |= CELL_MARK;
- else
- m_cells[j * m_width + i] &= ~CELL_MARK;
- }
-
- /* 2nd pass. Stabilize.
- */
- for (j = 0; j < m_height; j++)
- for (i = 0; i < m_width; i++)
- {
- /* Toggle CELL_ALIVE for those cells marked in the
- * previous pass. Do not clear the CELL_MARK bit yet;
- * it is useful to know which cells have changed and
- * thus must be updated in the screen.
- */
- if (m_cells[j * m_width + i] & CELL_MARK)
- {
- m_cells[j * m_width + i] ^= CELL_ALIVE;
- changed++;
- }
- }
-
- return (changed != 0);
-}
-
-int Life::GetNeighbors(int x, int y) const
-{
- wxASSERT(x >= 0 && y >= 0 && x < m_width && y < m_height);
-
- int neighbors = 0;
-
- int i0 = (x)? (x - 1) : 0;
- int j0 = (y)? (y - 1) : 0;
- int i1 = (x < (m_width - 1))? (x + 1) : (m_width - 1);
- int j1 = (y < (m_height - 1))? (y + 1) : (m_height - 1);
-
- if (m_wrap && ( !x || !y || x == (m_width - 1) || y == (m_height - 1)))
- {
- // this is an outer cell and wraparound is on
- for (int j = y - 1; j <= y + 1; j++)
- for (int i = x - 1; i <= x + 1; i++)
- if (IsAlive( ((i < 0)? (i + m_width ) : (i % m_width)),
- ((j < 0)? (j + m_height) : (j % m_height)) ))
- neighbors++;
- }
- else
- {
- // this is an inner cell, or wraparound is off
- for (int j = j0; j <= j1; j++)
- for (int i = i0; i <= i1; i++)
- if (IsAlive(i, j))
- neighbors++;
- }
-
- // do not count ourselves
- if (IsAlive(x, y)) neighbors--;
-
- return neighbors;
-}
-
-void Life::SetCell(int x, int y, Cell status)
-{
- wxASSERT(x >= 0 && y >= 0 && x < m_width && y < m_height);
-
- m_cells[y * m_width + x] = status;
-}
-