]>
git.saurik.com Git - wxWidgets.git/blob - samples/life/life.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: The game of life, created by J. H. Conway
4 // Author: Guillermo Rodriguez Garcia, <guille@iies.es>
8 // Copyright: (c) 2000, Guillermo Rodriguez Garcia
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ==========================================================================
14 // ==========================================================================
16 // minimum and maximum table size, in each dimension
21 #define ADD_TOOL(a, b, c, d) \
22 toolBar->AddTool(a, b, wxNullBitmap, FALSE, -1, -1, (wxObject *)0, c, d)
25 ((LifeFrame *) wxGetApp().GetTopWindow())
27 // --------------------------------------------------------------------------
29 // --------------------------------------------------------------------------
32 #pragma implementation "life.cpp"
33 #pragma interface "life.cpp"
36 // for compilers that support precompilation, includes "wx/wx.h".
37 #include "wx/wxprec.h"
43 // for all others, include the necessary headers
48 #include "wx/statline.h"
49 #include "wx/spinctrl.h"
51 // --------------------------------------------------------------------------
53 // --------------------------------------------------------------------------
55 #if defined(__WXGTK__) || defined(__WXMOTIF__)
56 // the application icon
57 #include "mondrian.xpm"
59 // bitmap buttons for the toolbar
60 #include "bitmaps/reset.xpm"
61 #include "bitmaps/play.xpm"
62 #include "bitmaps/stop.xpm"
65 // --------------------------------------------------------------------------
67 // --------------------------------------------------------------------------
75 class LifeNewGameDialog
;
76 class LifeSamplesDialog
;
78 // --------------------------------------------------------------------------
80 // --------------------------------------------------------------------------
87 Life(int width
, int height
);
89 void Create(int width
, int height
);
93 inline int GetWidth() const { return m_width
; };
94 inline int GetHeight() const { return m_height
; };
95 inline bool IsAlive(int x
, int y
) const;
96 inline bool HasChanged(int x
, int y
) const;
99 void SetBorderWrap(bool on
);
103 void SetCell(int x
, int y
, bool alive
= TRUE
);
104 void SetShape(LifeShape
&shape
);
109 CELL_DEAD
= 0x0000, // is dead
110 CELL_ALIVE
= 0x0001, // is alive
111 CELL_MARK
= 0x0002, // will change / has changed
115 int GetNeighbors(int x
, int y
) const;
116 inline void SetCell(int x
, int y
, Cell status
);
128 LifeShape::LifeShape(wxString name
,
130 int width
, int height
, char *data
,
131 int fieldWidth
= 20, int fieldHeight
= 20,
139 m_fieldWidth
= fieldWidth
;
140 m_fieldHeight
= fieldHeight
;
154 // --------------------------------------------------------------------------
156 // --------------------------------------------------------------------------
159 class LifeCanvas
: public wxScrolledWindow
163 LifeCanvas(wxWindow
* parent
, Life
* life
, bool interactive
= TRUE
);
168 void DrawEverything(bool force
= FALSE
);
169 void DrawCell(int i
, int j
);
170 void DrawCell(int i
, int j
, wxDC
&dc
);
171 inline int CellToCoord(int i
) const { return (i
* m_cellsize
); };
172 inline int CoordToCell(int x
) const { return ((x
>= 0)? (x
/ m_cellsize
) : -1); };
175 void OnPaint(wxPaintEvent
& event
);
176 void OnMouse(wxMouseEvent
& event
);
177 void OnSize(wxSizeEvent
& event
);
180 // any class wishing to process wxWindows events must use this macro
181 DECLARE_EVENT_TABLE()
196 MouseStatus m_status
;
201 class LifeTimer
: public wxTimer
208 class LifeFrame
: public wxFrame
216 void UpdateInfoText();
219 void OnMenu(wxCommandEvent
& event
);
220 void OnNewGame(wxCommandEvent
& event
);
221 void OnSamples(wxCommandEvent
& event
);
225 void OnSlider(wxScrollEvent
& event
);
228 // any class wishing to process wxWindows events must use this macro
229 DECLARE_EVENT_TABLE()
233 LifeCanvas
*m_canvas
;
234 wxStaticText
*m_text
;
240 // Life new game dialog
241 class LifeNewGameDialog
: public wxDialog
245 LifeNewGameDialog(wxWindow
*parent
, int *w
, int *h
);
248 void OnOK(wxCommandEvent
& event
);
251 // any class wishing to process wxWindows events must use this macro
252 DECLARE_EVENT_TABLE();
256 wxSpinCtrl
*m_spinctrlw
;
257 wxSpinCtrl
*m_spinctrlh
;
260 // Life sample configurations dialog
261 class LifeSamplesDialog
: public wxDialog
265 LifeSamplesDialog(wxWindow
*parent
);
266 ~LifeSamplesDialog();
272 void OnListBox(wxCommandEvent
&event
);
275 // any class wishing to process wxWindows events must use this macro
276 DECLARE_EVENT_TABLE();
281 LifeCanvas
*m_canvas
;
286 class LifeApp
: public wxApp
289 virtual bool OnInit();
292 // --------------------------------------------------------------------------
294 // --------------------------------------------------------------------------
296 // IDs for the controls and the menu commands
299 // menu items and toolbar buttons
310 // speed selection slider
313 // listbox in samples dialog
318 // built-in sample games
319 #include "samples.inc"
321 // --------------------------------------------------------------------------
322 // event tables and other macros for wxWindows
323 // --------------------------------------------------------------------------
326 BEGIN_EVENT_TABLE(LifeFrame
, wxFrame
)
327 EVT_MENU (ID_NEWGAME
, LifeFrame::OnNewGame
)
328 EVT_MENU (ID_SAMPLES
, LifeFrame::OnSamples
)
329 EVT_MENU (ID_ABOUT
, LifeFrame::OnMenu
)
330 EVT_MENU (ID_EXIT
, LifeFrame::OnMenu
)
331 EVT_MENU (ID_CLEAR
, LifeFrame::OnMenu
)
332 EVT_MENU (ID_START
, LifeFrame::OnMenu
)
333 EVT_MENU (ID_STEP
, LifeFrame::OnMenu
)
334 EVT_MENU (ID_STOP
, LifeFrame::OnMenu
)
335 EVT_MENU (ID_WRAP
, LifeFrame::OnMenu
)
336 EVT_COMMAND_SCROLL (ID_SLIDER
, LifeFrame::OnSlider
)
339 BEGIN_EVENT_TABLE(LifeCanvas
, wxScrolledWindow
)
340 EVT_PAINT ( LifeCanvas::OnPaint
)
341 EVT_SIZE ( LifeCanvas::OnSize
)
342 EVT_MOUSE_EVENTS ( LifeCanvas::OnMouse
)
345 BEGIN_EVENT_TABLE(LifeNewGameDialog
, wxDialog
)
346 EVT_BUTTON (wxID_OK
, LifeNewGameDialog::OnOK
)
349 BEGIN_EVENT_TABLE(LifeSamplesDialog
, wxDialog
)
350 EVT_LISTBOX (ID_LISTBOX
, LifeSamplesDialog::OnListBox
)
354 // Create a new application object
355 IMPLEMENT_APP(LifeApp
)
357 // ==========================================================================
359 // ==========================================================================
361 // --------------------------------------------------------------------------
363 // --------------------------------------------------------------------------
365 // `Main program' equivalent: the program execution "starts" here
366 bool LifeApp::OnInit()
368 // create the main application window
369 LifeFrame
*frame
= new LifeFrame();
371 // show it and tell the application that it's our main window
375 // enter the main message loop and run the app
379 // --------------------------------------------------------------------------
381 // --------------------------------------------------------------------------
384 LifeFrame::LifeFrame() : wxFrame((wxFrame
*)0, -1, _("Life!"), wxPoint(50, 50))
387 SetIcon(wxICON(mondrian
));
390 wxMenu
*menuFile
= new wxMenu("", wxMENU_TEAROFF
);
391 wxMenu
*menuGame
= new wxMenu("", wxMENU_TEAROFF
);
393 menuFile
->Append(ID_NEWGAME
, _("New game..."), _("Start a new game"));
394 menuFile
->Append(ID_SAMPLES
, _("Sample game..."), _("Select a sample configuration"));
395 menuFile
->AppendSeparator();
396 menuFile
->Append(ID_ABOUT
, _("&About...\tCtrl-A"), _("Show about dialog"));
397 menuFile
->AppendSeparator();
398 menuFile
->Append(ID_EXIT
, _("E&xit\tAlt-X"), _("Quit this program"));
400 menuGame
->Append(ID_CLEAR
, _("&Clear\tCtrl-C"), _("Clear game field"));
401 menuGame
->Append(ID_START
, _("&Start\tCtrl-S"), _("Start"));
402 menuGame
->Append(ID_STEP
, _("&Next\tCtrl-N"), _("Single step"));
403 menuGame
->Append(ID_STOP
, _("S&top\tCtrl-T"), _("Stop"));
404 menuGame
->Enable(ID_STOP
, FALSE
);
405 menuGame
->AppendSeparator();
406 menuGame
->Append(ID_WRAP
, _("&Wraparound\tCtrl-W"), _("Wrap around borders"), TRUE
);
407 menuGame
->Check (ID_WRAP
, TRUE
);
410 wxMenuBar
*menuBar
= new wxMenuBar();
411 menuBar
->Append(menuFile
, _("&File"));
412 menuBar
->Append(menuGame
, _("&Game"));
416 wxBitmap tbBitmaps
[3];
418 tbBitmaps
[0] = wxBITMAP(reset
);
419 tbBitmaps
[1] = wxBITMAP(play
);
420 tbBitmaps
[2] = wxBITMAP(stop
);
422 wxToolBar
*toolBar
= CreateToolBar();
423 toolBar
->SetMargins(5, 5);
424 toolBar
->SetToolBitmapSize(wxSize(16, 16));
425 ADD_TOOL(ID_CLEAR
, tbBitmaps
[0], _("Clear"), _("Clear game board"));
426 ADD_TOOL(ID_START
, tbBitmaps
[1], _("Start"), _("Start"));
427 ADD_TOOL(ID_STOP
, tbBitmaps
[2], _("Stop"), _("Stop"));
428 toolBar
->EnableTool(ID_STOP
, FALSE
);
433 SetStatusText(_("Welcome to Life!"));
436 wxPanel
*panel
= new wxPanel(this, -1);
439 m_life
= new Life(20, 20);
440 m_canvas
= new LifeCanvas(panel
, m_life
);
441 m_timer
= new LifeTimer();
444 m_text
= new wxStaticText(panel
, -1, "");
448 wxSlider
*slider
= new wxSlider(panel
, ID_SLIDER
, 5, 1, 10,
449 wxDefaultPosition
, wxSize(200, -1), wxSL_HORIZONTAL
| wxSL_AUTOTICKS
);
452 wxBoxSizer
*sizer
= new wxBoxSizer(wxVERTICAL
);
453 sizer
->Add(new wxStaticLine(panel
, -1), 0, wxGROW
| wxCENTRE
);
454 sizer
->Add(m_canvas
, 1, wxGROW
| wxCENTRE
| wxALL
, 5);
455 sizer
->Add(new wxStaticLine(panel
, -1), 0, wxGROW
| wxCENTRE
);
456 sizer
->Add(m_text
, 0, wxCENTRE
| wxTOP
, 5);
457 sizer
->Add(slider
, 0, wxCENTRE
| wxALL
, 5);
458 panel
->SetSizer(sizer
);
459 panel
->SetAutoLayout(TRUE
);
461 sizer
->SetSizeHints(this);
464 LifeFrame::~LifeFrame()
470 void LifeFrame::UpdateInfoText()
474 msg
.Printf(_("Generation: %u, Interval: %u ms"), m_tics
, m_interval
);
475 m_text
->SetLabel(msg
);
479 void LifeFrame::OnMenu(wxCommandEvent
& event
)
481 switch (event
.GetId())
483 case ID_START
: OnStart(); break;
484 case ID_STEP
: OnTimer(); break;
485 case ID_STOP
: OnStop(); break;
488 bool checked
= GetMenuBar()->GetMenu(1)->IsChecked(ID_WRAP
);
489 m_life
->SetBorderWrap(checked
);
496 m_canvas
->DrawEverything(TRUE
);
497 m_canvas
->Refresh(FALSE
);
505 _("This is the about dialog of the Life! sample.\n"
506 "(c) 2000 Guillermo Rodriguez Garcia"),
508 wxOK
| wxICON_INFORMATION
,
514 // TRUE is to force the frame to close
521 void LifeFrame::OnNewGame(wxCommandEvent
& WXUNUSED(event
))
523 int w
= m_life
->GetWidth();
524 int h
= m_life
->GetHeight();
526 // stop if it was running
530 LifeNewGameDialog
dialog(this, &w
, &h
);
533 if (dialog
.ShowModal() == wxID_OK
)
536 if (w
>= LIFE_MIN
&& w
<= LIFE_MAX
&&
537 h
>= LIFE_MIN
&& h
<= LIFE_MAX
)
541 m_life
->Create(w
, h
);
552 msg
.Printf(_("Both dimensions must be within %u and %u.\n"),
554 wxMessageBox(msg
, _("Error!"), wxOK
| wxICON_EXCLAMATION
, this);
559 void LifeFrame::OnSamples(wxCommandEvent
& WXUNUSED(event
))
561 // stop if it was running
565 LifeSamplesDialog
dialog(this);
568 if (dialog
.ShowModal() == wxID_OK
)
570 int result
= dialog
.GetValue();
575 int gw
= g_shapes
[result
].m_fieldWidth
;
576 int gh
= g_shapes
[result
].m_fieldHeight
;
577 int wrap
= g_shapes
[result
].m_wrap
;
579 // set wraparound (don't ask the user)
580 m_life
->SetBorderWrap(wrap
);
581 GetMenuBar()->GetMenu(1)->Check(ID_WRAP
, wrap
);
583 // need to resize the game field?
584 if (gw
> m_life
->GetWidth() || gh
> m_life
->GetHeight())
587 s
.Printf(_("Your game field is too small for this configuration.\n"
588 "It is recommended to resize it to %u x %u. Proceed?\n"),
591 if (wxMessageBox(s
, _("Question"), wxYES_NO
| wxICON_QUESTION
, this) == wxYES
)
594 m_life
->Create(gw
, gh
);
599 m_life
->SetShape(g_shapes
[result
]);
601 // tell the canvas about the change
609 void LifeFrame::OnStart()
613 GetToolBar()->EnableTool(ID_START
, FALSE
);
614 GetToolBar()->EnableTool(ID_STOP
, TRUE
);
615 GetMenuBar()->GetMenu(1)->Enable(ID_START
, FALSE
);
616 GetMenuBar()->GetMenu(1)->Enable(ID_STEP
, FALSE
);
617 GetMenuBar()->GetMenu(1)->Enable(ID_STOP
, TRUE
);
619 m_timer
->Start(m_interval
);
624 void LifeFrame::OnStop()
628 GetToolBar()->EnableTool(ID_START
, TRUE
);
629 GetToolBar()->EnableTool(ID_STOP
, FALSE
);
630 GetMenuBar()->GetMenu(1)->Enable(ID_START
, TRUE
);
631 GetMenuBar()->GetMenu(1)->Enable(ID_STEP
, TRUE
);
632 GetMenuBar()->GetMenu(1)->Enable(ID_STOP
, FALSE
);
639 void LifeFrame::OnTimer()
641 if (m_life
->NextTic())
647 m_canvas
->DrawEverything();
648 m_canvas
->Refresh(FALSE
);
651 void LifeFrame::OnSlider(wxScrollEvent
& event
)
653 m_interval
= event
.GetPosition() * 100;
655 // restart timer if running, to set the new interval
659 m_timer
->Start(m_interval
);
665 // --------------------------------------------------------------------------
667 // --------------------------------------------------------------------------
669 void LifeTimer::Notify()
671 GET_FRAME()->OnTimer();
674 // --------------------------------------------------------------------------
676 // --------------------------------------------------------------------------
678 // canvas constructor
679 LifeCanvas::LifeCanvas(wxWindow
*parent
, Life
*life
, bool interactive
)
680 : wxScrolledWindow(parent
, -1, wxPoint(0, 0), wxSize(100, 100))
683 m_interactive
= interactive
;
689 LifeCanvas::~LifeCanvas()
694 void LifeCanvas::Reset()
699 m_status
= MOUSE_NOACTION
;
700 m_width
= CellToCoord(m_life
->GetWidth()) + 1;
701 m_height
= CellToCoord(m_life
->GetHeight()) + 1;
702 m_bmp
= new wxBitmap(m_width
, m_height
);
703 wxCoord w
= GetClientSize().GetX();
704 wxCoord h
= GetClientSize().GetY();
705 m_xoffset
= (w
> m_width
)? ((w
- m_width
) / 2) : 0;
706 m_yoffset
= (h
> m_height
)? ((h
- m_height
) / 2) : 0;
709 DrawEverything(TRUE
);
710 SetScrollbars(10, 10, (m_width
+ 9) / 10, (m_height
+ 9) / 10);
713 void LifeCanvas::DrawEverything(bool force
)
717 dc
.SelectObject(*m_bmp
);
721 for (int j
= 0; j
< m_life
->GetHeight(); j
++)
722 for (int i
= 0; i
< m_life
->GetWidth(); i
++)
723 if (force
|| m_life
->HasChanged(i
, j
))
726 // bounding rectangle (always drawn - better than clipping region)
727 dc
.SetPen(*wxBLACK_PEN
);
728 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
729 dc
.DrawRectangle(0, 0, m_width
, m_height
);
732 dc
.SelectObject(wxNullBitmap
);
735 void LifeCanvas::DrawCell(int i
, int j
)
739 dc
.SelectObject(*m_bmp
);
742 dc
.SetClippingRegion(1, 1, m_width
- 2, m_height
- 2);
746 dc
.SelectObject(wxNullBitmap
);
749 void LifeCanvas::DrawCell(int i
, int j
, wxDC
&dc
)
751 if (m_life
->IsAlive(i
, j
))
753 dc
.SetPen(*wxBLACK_PEN
);
754 dc
.SetBrush(*wxBLACK_BRUSH
);
755 dc
.DrawRectangle(CellToCoord(i
),
762 dc
.SetPen(*wxLIGHT_GREY_PEN
);
763 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
764 dc
.DrawRectangle(CellToCoord(i
),
768 dc
.SetPen(*wxWHITE_PEN
);
769 dc
.SetBrush(*wxWHITE_BRUSH
);
770 dc
.DrawRectangle(CellToCoord(i
) + 1,
778 void LifeCanvas::OnPaint(wxPaintEvent
& event
)
783 wxRegionIterator
upd(GetUpdateRegion());
784 wxCoord x
, y
, w
, h
, xx
, yy
;
787 memdc
.SelectObject(*m_bmp
);
795 CalcUnscrolledPosition(x
, y
, &xx
, &yy
);
797 dc
.Blit(x
, y
, w
, h
, &memdc
, xx
- m_xoffset
, yy
- m_yoffset
);
801 memdc
.SelectObject(wxNullBitmap
);
805 void LifeCanvas::OnMouse(wxMouseEvent
& event
)
810 int x
, y
, xx
, yy
, i
, j
;
812 // which cell are we pointing at?
815 CalcUnscrolledPosition(x
, y
, &xx
, &yy
);
816 i
= CoordToCell( xx
- m_xoffset
);
817 j
= CoordToCell( yy
- m_yoffset
);
819 // adjust x, y to point to the upper left corner of the cell
820 CalcScrolledPosition( CellToCoord(i
) + m_xoffset
,
821 CellToCoord(j
) + m_yoffset
,
824 // set cursor shape and statusbar text
825 if (i
< 0 || i
>= m_life
->GetWidth() ||
826 j
< 0 || j
>= m_life
->GetHeight())
828 GET_FRAME()->SetStatusText(wxEmptyString
, 1);
829 SetCursor(*wxSTANDARD_CURSOR
);
834 msg
.Printf(_("Cell: (%u, %u)"), i
, j
);
835 GET_FRAME()->SetStatusText(msg
, 1);
836 SetCursor(*wxCROSS_CURSOR
);
840 if (!event
.LeftIsDown())
842 m_status
= MOUSE_NOACTION
;
844 else if (i
>= 0 && i
< m_life
->GetWidth() &&
845 j
>= 0 && j
< m_life
->GetHeight())
847 bool alive
= m_life
->IsAlive(i
, j
);
849 // if just pressed, update status
850 if (m_status
== MOUSE_NOACTION
)
851 m_status
= (alive
? MOUSE_ERASING
: MOUSE_DRAWING
);
853 // toggle cell and refresh if needed
854 if (((m_status
== MOUSE_ERASING
) && alive
) ||
855 ((m_status
== MOUSE_DRAWING
) && !alive
))
857 wxRect
rect(x
, y
, m_cellsize
+ 1, m_cellsize
+ 1);
858 m_life
->SetCell(i
, j
, !alive
);
860 Refresh(FALSE
, &rect
);
865 void LifeCanvas::OnSize(wxSizeEvent
& event
)
867 wxCoord w
= event
.GetSize().GetX();
868 wxCoord h
= event
.GetSize().GetY();
869 m_xoffset
= (w
> m_width
)? ((w
- m_width
) / 2) : 0;
870 m_yoffset
= (h
> m_height
)? ((h
- m_height
) / 2) : 0;
872 // allow default processing
876 // --------------------------------------------------------------------------
878 // --------------------------------------------------------------------------
880 LifeNewGameDialog::LifeNewGameDialog(wxWindow
*parent
, int *w
, int *h
)
881 : wxDialog(parent
, -1,
885 wxDEFAULT_DIALOG_STYLE
| wxDIALOG_MODAL
)
891 m_spinctrlw
= new wxSpinCtrl( this, -1 );
892 m_spinctrlw
->SetValue(*m_w
);
893 m_spinctrlw
->SetRange(LIFE_MIN
, LIFE_MAX
);
895 m_spinctrlh
= new wxSpinCtrl( this, -1 );
896 m_spinctrlh
->SetValue(*m_h
);
897 m_spinctrlh
->SetRange(LIFE_MIN
, LIFE_MAX
);
900 wxBoxSizer
*inputsizer1
= new wxBoxSizer( wxHORIZONTAL
);
901 inputsizer1
->Add( new wxStaticText(this, -1, _("Width")), 1, wxCENTRE
| wxLEFT
, 20);
902 inputsizer1
->Add( m_spinctrlw
, 2, wxCENTRE
| wxLEFT
| wxRIGHT
, 20 );
904 wxBoxSizer
*inputsizer2
= new wxBoxSizer( wxHORIZONTAL
);
905 inputsizer2
->Add( new wxStaticText(this, -1, _("Height")), 1, wxCENTRE
| wxLEFT
, 20);
906 inputsizer2
->Add( m_spinctrlh
, 2, wxCENTRE
| wxLEFT
| wxRIGHT
, 20 );
908 wxBoxSizer
*topsizer
= new wxBoxSizer( wxVERTICAL
);
909 topsizer
->Add( CreateTextSizer(_("Enter board dimensions")), 0, wxALL
, 10 );
910 topsizer
->Add( new wxStaticLine(this, -1), 0, wxGROW
| wxLEFT
| wxRIGHT
| wxBOTTOM
, 10);
911 topsizer
->Add( inputsizer1
, 1, wxGROW
| wxLEFT
| wxRIGHT
, 5 );
912 topsizer
->Add( inputsizer2
, 1, wxGROW
| wxLEFT
| wxRIGHT
, 5 );
913 topsizer
->Add( new wxStaticLine(this, -1), 0, wxGROW
| wxLEFT
| wxRIGHT
| wxTOP
, 10);
914 topsizer
->Add( CreateButtonSizer(wxOK
| wxCANCEL
), 0, wxCENTRE
| wxALL
, 10);
919 topsizer
->SetSizeHints(this);
924 void LifeNewGameDialog::OnOK(wxCommandEvent
& WXUNUSED(event
))
926 *m_w
= m_spinctrlw
->GetValue();
927 *m_h
= m_spinctrlh
->GetValue();
932 // --------------------------------------------------------------------------
934 // --------------------------------------------------------------------------
936 LifeSamplesDialog::LifeSamplesDialog(wxWindow
*parent
)
937 : wxDialog(parent
, -1,
941 wxDEFAULT_DIALOG_STYLE
| wxDIALOG_MODAL
)
945 // create and populate the list of available samples
946 m_list
= new wxListBox( this, ID_LISTBOX
,
950 wxLB_SINGLE
| wxLB_NEEDED_SB
| wxLB_HSCROLL
);
952 for (unsigned i
= 0; i
< (sizeof(g_shapes
) / sizeof(LifeShape
)); i
++)
953 m_list
->Append(g_shapes
[i
].m_name
);
956 wxStaticBox
*statbox
= new wxStaticBox( this, -1, _("Description"));
957 m_life
= new Life( 16, 16 );
958 m_life
->SetShape(g_shapes
[0]);
959 m_canvas
= new LifeCanvas( this, m_life
, FALSE
);
960 m_text
= new wxTextCtrl( this, -1,
964 wxTE_MULTILINE
| wxTE_READONLY
);
967 wxStaticBoxSizer
*sizer1
= new wxStaticBoxSizer( statbox
, wxVERTICAL
);
968 sizer1
->Add( m_canvas
, 2, wxGROW
| wxCENTRE
| wxALL
, 5);
969 sizer1
->Add( m_text
, 1, wxGROW
| wxCENTRE
| wxALL
, 5 );
971 wxBoxSizer
*sizer2
= new wxBoxSizer( wxHORIZONTAL
);
972 sizer2
->Add( m_list
, 0, wxGROW
| wxCENTRE
| wxALL
, 5 );
973 sizer2
->Add( sizer1
, 1, wxGROW
| wxCENTRE
| wxALL
, 5 );
975 wxBoxSizer
*sizer3
= new wxBoxSizer( wxVERTICAL
);
976 sizer3
->Add( CreateTextSizer(_("Select one configuration")), 0, wxALL
, 10 );
977 sizer3
->Add( new wxStaticLine(this, -1), 0, wxGROW
| wxLEFT
| wxRIGHT
, 10 );
978 sizer3
->Add( sizer2
, 1, wxGROW
| wxCENTRE
| wxALL
, 5 );
979 sizer3
->Add( new wxStaticLine(this, -1), 0, wxGROW
| wxLEFT
| wxRIGHT
, 10 );
980 sizer3
->Add( CreateButtonSizer(wxOK
| wxCANCEL
), 0, wxCENTRE
| wxALL
, 10 );
985 sizer3
->SetSizeHints(this);
990 LifeSamplesDialog::~LifeSamplesDialog()
996 int LifeSamplesDialog::GetValue()
1001 void LifeSamplesDialog::OnListBox(wxCommandEvent
& event
)
1003 if (event
.GetSelection() != -1)
1005 m_value
= m_list
->GetSelection();
1006 m_text
->SetValue(g_shapes
[ event
.GetSelection() ].m_desc
);
1007 m_life
->SetShape(g_shapes
[ event
.GetSelection() ]);
1009 m_canvas
->DrawEverything(TRUE
); // force redraw everything
1010 m_canvas
->Refresh(FALSE
); // do not erase background
1014 // --------------------------------------------------------------------------
1016 // --------------------------------------------------------------------------
1018 Life::Life(int width
, int height
)
1022 Create(width
, height
);
1030 void Life::Create(int width
, int height
)
1032 wxASSERT(width
> 0 && height
> 0);
1036 m_cells
= new Cell
[m_width
* m_height
];
1040 void Life::Destroy()
1047 for (int i
= 0; i
< m_width
* m_height
; i
++)
1048 m_cells
[i
] = CELL_DEAD
;
1051 bool Life::IsAlive(int x
, int y
) const
1053 wxASSERT(x
>= 0 && y
>= 0 && x
< m_width
&& y
< m_height
);
1055 return (m_cells
[y
* m_width
+ x
] & CELL_ALIVE
);
1058 bool Life::HasChanged(int x
, int y
) const
1060 wxASSERT(x
>= 0 && y
>= 0 && x
< m_width
&& y
< m_height
);
1062 return (m_cells
[y
* m_width
+ x
] & CELL_MARK
) != 0;
1065 void Life::SetBorderWrap(bool on
)
1070 void Life::SetCell(int x
, int y
, bool alive
)
1072 wxASSERT(x
>= 0 && y
>= 0 && x
< m_width
&& y
< m_height
);
1074 m_cells
[y
* m_width
+ x
] = (alive
? CELL_ALIVE
: CELL_DEAD
);
1077 void Life::SetShape(LifeShape
& shape
)
1079 wxASSERT((m_width
>= shape
.m_width
) && (m_height
>= shape
.m_height
));
1081 int x0
= (m_width
- shape
.m_width
) / 2;
1082 int y0
= (m_height
- shape
.m_height
) / 2;
1083 char *p
= shape
.m_data
;
1086 for (int j
= y0
; j
< y0
+ shape
.m_height
; j
++)
1087 for (int i
= x0
; i
< x0
+ shape
.m_width
; i
++)
1088 SetCell(i
, j
, *(p
++) == '*');
1091 bool Life::NextTic()
1096 /* 1st pass. Find and mark deaths and births for this generation.
1099 * An organism with <= 1 neighbors will die due to isolation.
1100 * An organism with >= 4 neighbors will die due to starvation.
1101 * New organisms are born in cells with exactly 3 neighbors.
1103 for (j
= 0; j
< m_height
; j
++)
1104 for (i
= 0; i
< m_width
; i
++)
1106 int neighbors
= GetNeighbors(i
, j
);
1107 bool alive
= IsAlive(i
, j
);
1109 /* Set CELL_MARK if this cell must change, clear it
1110 * otherwise. We cannot toggle the CELL_ALIVE bit yet
1111 * because all deaths and births are simultaneous (it
1112 * would affect neighbouring cells).
1114 if ((!alive
&& neighbors
== 3) ||
1115 (alive
&& (neighbors
<= 1 || neighbors
>= 4)))
1116 m_cells
[j
* m_width
+ i
] |= CELL_MARK
;
1118 m_cells
[j
* m_width
+ i
] &= ~CELL_MARK
;
1121 /* 2nd pass. Stabilize.
1123 for (j
= 0; j
< m_height
; j
++)
1124 for (i
= 0; i
< m_width
; i
++)
1126 /* Toggle CELL_ALIVE for those cells marked in the
1127 * previous pass. Do not clear the CELL_MARK bit yet;
1128 * it is useful to know which cells have changed and
1129 * thus must be updated in the screen.
1131 if (m_cells
[j
* m_width
+ i
] & CELL_MARK
)
1133 m_cells
[j
* m_width
+ i
] ^= CELL_ALIVE
;
1138 return (changed
!= 0);
1141 int Life::GetNeighbors(int x
, int y
) const
1143 wxASSERT(x
>= 0 && y
>= 0 && x
< m_width
&& y
< m_height
);
1147 int i0
= (x
)? (x
- 1) : 0;
1148 int j0
= (y
)? (y
- 1) : 0;
1149 int i1
= (x
< (m_width
- 1))? (x
+ 1) : (m_width
- 1);
1150 int j1
= (y
< (m_height
- 1))? (y
+ 1) : (m_height
- 1);
1152 if (m_wrap
&& ( !x
|| !y
|| x
== (m_width
- 1) || y
== (m_height
- 1)))
1154 // this is an outer cell and wraparound is on
1155 for (int j
= y
- 1; j
<= y
+ 1; j
++)
1156 for (int i
= x
- 1; i
<= x
+ 1; i
++)
1157 if (IsAlive( ((i
< 0)? (i
+ m_width
) : (i
% m_width
)),
1158 ((j
< 0)? (j
+ m_height
) : (j
% m_height
)) ))
1163 // this is an inner cell, or wraparound is off
1164 for (int j
= j0
; j
<= j1
; j
++)
1165 for (int i
= i0
; i
<= i1
; i
++)
1170 // do not count ourselves
1171 if (IsAlive(x
, y
)) neighbors
--;
1176 void Life::SetCell(int x
, int y
, Cell status
)
1178 wxASSERT(x
>= 0 && y
>= 0 && x
< m_width
&& y
< m_height
);
1180 m_cells
[y
* m_width
+ x
] = status
;