A Simulation Program of The
Game of Life
A sample program of The Game of Life was developed in Visual C++/CLR. The rules of the game (A. Bellos, 2015, Alex through the looking glass: How life reflects numbers, and numbers reflect life. Bloomsbury Publishing PLC, p. 262) are as follows.
Individuals are represented as small squares arranged in 2 dimensional array.
When an individual is dead, it will be live at the next time only if exactly three neighbors are live.
When an individual is live, it will be live at the next time if two or three neighbors are live, and will be dead otherwise.
Program files are compressed in GameOfLife.zip. The files in the .zip file can be extracted by clicking on the filename of the downloaded .zip file with the right button of a mouse (Figure 1).
From the menu items presented, choose gExtract Allch
Figure 1
When the project is opened by Visual Studio 2013, an error may be shown. But, this type of error can be neglected.
Detailed information about this is prepared at the website
http://mcn-www.jwu.ac.jp/~yokamoto/openwww/cpp/VCppGraphics/
When the program starts, a form shown in Figure 2 will appear.
Figure 2
Clicking or dragging on the pictureBox with the left button of the mouse makes the white square black. Black and White mean live and dead, respectively.
Clicking or dragging with the right button changes the black squares that are clicked or dragged to white ones.
After setting the pattern (Figure 3), click the Go button.
Figure 3
Simulation will start (Figure 4).
Figure 4
If you click the Stop button, simulation will stop. The form will be like Figures 2 or 3, i.e., the Stop button hides and the Go button appears. You can re-draw a pattern. After that, you can re-start the simulation by clicking the Go button.
Program files are as follows.
MyForm.h
#pragma
once
#include "common.h";
namespace GameOfLife
{
@@@E
@@@E
@@@E
#pragma
endregion
private: System::Void buttonClose_Click(System::Object^ sender, System::EventArgs^ e) {
this->Close();
}
int
pw, ph;
Graphics ^ g;
Brush ^ bshWt;
Brush ^ bshBk;
int
ms_st;
int
cell_ck0, cell_ck1;
int
cond_set;
private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
pw = 500;
ph = 500;
pictureBox1->Width
= pw;
pictureBox1->Height
= ph;
pictureBox1->Image
= gcnew Bitmap(pw, ph);
g
= Graphics::FromImage(pictureBox1->Image);
bshWt = gcnew
SolidBrush(Color::White);
g->FillRectangle(bshWt, 0, 0, pw, ph);
bshBk = gcnew
SolidBrush(Color::Black);
ms_st = 0;
for (int st
= 0; st < 2; st++)
for (int i
= 0; i <= 101; i++)
for (int j = 0; j <= 101; j++)
cell_state[st][i][j] = dead;
cell_ck0
= 0;
cell_ck1
= 1;
cond_set = 0;
buttonStop->Hide();
}
private:
System::Void pictureBox1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
int ix, iy;
if (cond_set == 0){
ix = (e->Location.X / 5);
iy = (e->Location.Y
/ 5);
if ((ix >= 0) && (ix < 100) && (iy >= 0) && (iy <
100)){
if
(e->Button == System::Windows::Forms::MouseButtons::Left){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshBk, ix * 5, iy * 5, 5, 5);
ms_st = 1;
cell_state[cell_ck0][ix
+ 1][iy + 1] = live;
}
else
if (e->Button == System::Windows::Forms::MouseButtons::Right){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshWt, ix * 5, iy * 5, 5, 5);
ms_st = -1;
cell_state[cell_ck0][ix
+ 1][iy + 1] = dead;
}
}
}
}
private:
System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
if (cond_set == 0){
if (ms_st == 1){
int ix, iy;
ix = (e->Location.X / 5);
iy = (e->Location.Y
/ 5);
if ((ix >= 0) && (ix < 100) && (iy >= 0) && (iy <
100)){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshBk, ix * 5, iy * 5, 5, 5);
cell_state[cell_ck0][ix
+ 1][iy + 1] = live;
}
}
else if (ms_st == -1){
int ix, iy;
ix = (e->Location.X / 5);
iy = (e->Location.Y
/ 5);
if ((ix >= 0) && (ix < 100) && (iy >= 0) && (iy <
100)){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshWt, ix * 5, iy * 5, 5, 5);
cell_state[cell_ck0][ix
+ 1][iy + 1] = dead;
}
}
}
}
private:
System::Void pictureBox1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
if (cond_set == 0){
ms_st = 0;
}
}
private: System::Void buttonGo_Click(System::Object^ sender, System::EventArgs^ e) {
buttonStop->Show();
cond_set = 1;
timer1->Enabled
= true;
buttonGo->Hide();
}
private: System::Void buttonStop_Click(System::Object^ sender, System::EventArgs^ e) {
buttonGo->Show();
cond_set = 0;
timer1->Enabled
= false;
buttonStop->Hide();
}
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
timer1->Enabled
= false;
for (int i
= 1; i <= 100; i++)
for (int j = 1; j <= 100; j++){
int count = 0;
for (int i1 = -1; i1 <= 1;
i1++)
for (int j1 = -1; j1 <= 1;
j1++){
if ((i1 != 0) || (j1 != 0)){
if (cell_state[cell_ck0][i + i1][j + j1] == live)
count++;
}
}
switch (cell_state[cell_ck0][i][j]){
case dead: if (count == 3){
cell_state[cell_ck1][i][j] = live;
}
else{
cell_state[cell_ck1][i][j] = dead;
}
break;
case live: if ((2 <= count) && (count <= 3)){
cell_state[cell_ck1][i][j] = live;
}
else{
cell_state[cell_ck1][i][j] = dead;
}
break;
default:
break;
}
}
pictureBox1->Image
= pictureBox1->Image;
for (int i
= 0; i < 100; i++)
for (int j = 0; j < 100; j++)
if (cell_state[cell_ck1][i + 1][j + 1] == live){
g->FillRectangle(bshBk, i * 5, j * 5, 5, 5);
}
else{
g->FillRectangle(bshWt, i * 5, j * 5, 5, 5);
}
if (cell_ck1 == 0){
cell_ck1
= 1;
cell_ck0
= 0;
}
else{
cell_ck1
= 0;
cell_ck0
= 1;
}
timer1->Enabled
= true;
}
};
}
The three dimensional array cell_state, which stores patterns of states of individuals, is declared and defined in the separate files common.h and common.cpp from the header file MyForm.h to avoid errors.
If the array were declared as a member of the ref class form or out of the declaration of the ref class form, the compiler would produce an error message.
common.h
#ifndef COMMON_CK
#define
COMMON_CK
enum
Cell_st {live, dead, other};
extern Cell_st
cell_state[2][102][102];
#endif
common.cpp
#include "common.h"
Cell_st cell_state[2][102][102];
Effects of drawing with Graphics handle g may fail to be reflected on the display, when drawing is done after some interval of the previous setting of the Image property.
To avoid this unsuccessful drawing after some interval of the previous setting of the Image property, set the Image property of the pictureBox again before drawing.
Before drawing, setting the Image property like this
pictureBox1->Image = pictureBox1->Image;
will make effects of drawing to be reflected on the display.
Speed of the simulation is controlled by a timer component.