Conway's Life

Conway's Life
#define STRICT
#include <windows.h>
#include <stdio.h>
#define LIFE 1
#define DEATH 0
#define COLUMNS 100
#define ROWS 100
#define TILEWIDTH 8
#define TILEHEIGHT 8
#define TIMER 100 // milliseconds
#define TOGGLEGO 1001
#define CLEAR 1002
#define DRAW WM_USER + 1
class CField
{
private:
char F1[COLUMNS][ROWS];
char F2[COLUMNS][ROWS];
public:
char *Source;
char *Dest;
RECT rect[COLUMNS][ROWS];
CField(void)
{
this->Source = *F1;
this->Dest = *F2;
for(int x = 0; x < COLUMNS; x ++)
{
for(int y = 0; y < ROWS; y ++)
{
SetRect(&rect[x][y], (x+1) * TILEWIDTH, (y+1) * TILEHEIGHT, (x+2) * TILEWIDTH - 1, (y+2) * TILEHEIGHT - 1);
F1[x][y] = 0;
F2[x][y] = 0;
}
}
}
BOOL GetCellValue(int x, int y)
{
return *(this->Source + ((y * ROWS) + x) * sizeof(char));
}
void SetSourceCellValue(int x, int y, BOOL bVal)
{
*(this->Source + ((y * ROWS) + x) * sizeof(char)) = bVal;
}
void SetDestCellValue(int x, int y, BOOL bVal)
{
*(this->Dest + ((y * ROWS) + x) * sizeof(char)) = bVal;
}
void ToggleCell(POINT *p)
{
int x, y;
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
if(PtInRect(&rect[x][y], *p))
{
SetSourceCellValue(x, y, 1 - GetCellValue(x, y));
return;
}
}
}
}
void CalcField(void)
{
char *temp;
for(int x = 0; x < COLUMNS; x ++)
{
for(int y = 0; y < ROWS; y ++)
{
int iNeighbours = 0;
int iXStart;
int iXStop;
int ix;
iXStart = x - 1 + (x == 0);
iXStop = x + 2 - (x == COLUMNS);
for(ix = iXStart; ix < iXStop; ix ++)
{
iNeighbours += GetCellValue(ix, y);
if(y > 0)
{
iNeighbours += GetCellValue(ix, y-1);
}
if(y < ROWS)
{
iNeighbours += GetCellValue(ix, y+1);
}
}
iNeighbours -= GetCellValue(x, y);
SetDestCellValue(x, y, (BOOL)(GetCellValue(x, y) && (iNeighbours == 2) || (iNeighbours == 3)));
}
}
// Swap pointers
temp = this->Source;
this->Source = this->Dest;
this->Dest = temp;
}
};
// -------------
// | 0 | 1 | 2 |
// -------------
// | 3 | X | 4 |
// -------------
// | 5 | 6 | 7 |
//
// Death
// If an occupied cell has 0, 1, 4, 5, 6, 7, or 8 occupied neighbors, the organism dies (0, 1: of loneliness; 4 thru 8: of overcrowding).
// Survival
// If an occupied cell has two or three neighbors, the organism survives to the next generation.
// Birth
// If an unoccupied cell has three occupied neighbors, it becomes occupied.
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
CREATESTRUCT *cs;
PAINTSTRUCT ps;
int x, y;
static char MenuText[2][6] = {"GO", "STOP"};
static int iWidth, iHeight;
static HBRUSH hBrush[2], hBkGndBrush;
static HPEN hPen;
static HDC hdcMem;
static HBITMAP hBitmap = NULL;
static BOOL bGo = FALSE;
static CField *field;
HMENU hMenu;
static RECT rect;
POINT p;
HDC hdc;
switch (iMsg)
{
case WM_CREATE :
cs = (LPCREATESTRUCT)lParam;
field = new CField();
hPen = CreatePen(1, 1, RGB(200, 200, 200));
hBrush[0] = CreateSolidBrush(RGB(64, 64, 64));
hBrush[1] = CreateSolidBrush(RGB(64, 255, 64));
hBkGndBrush = CreateSolidBrush(RGB(192, 192, 192));
iWidth = (COLUMNS+2) * TILEWIDTH + 5;
iHeight = (ROWS+2) * TILEHEIGHT + 50;
hdc = GetDC(hwnd);
hdcMem = CreateCompatibleDC(hdc);
ReleaseDC(hwnd, hdc);
// init tiles
GetClientRect(GetDesktopWindow(), &rect);
MoveWindow(hwnd, ((rect.right/2) - (iWidth/2)), ((rect.bottom/2) - (iHeight/2)), iWidth, iHeight, TRUE);
break;
case WM_KEYUP:
if(!bGo && wParam == VK_SPACE)
{
PostMessage(hwnd, DRAW, 0, 0);
}
break;
case WM_RBUTTONUP :
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
hMenu = CreatePopupMenu();
AppendMenu (hMenu, MF_STRING, TOGGLEGO, MenuText[bGo]);
if(!bGo)
{
AppendMenu (hMenu, MF_STRING, CLEAR, "Clear Screen");
}
ClientToScreen (hwnd, (LPPOINT)&p);
TrackPopupMenu (hMenu, 0, p.x, p.y, 0, hwnd, NULL);
DestroyMenu (hMenu);
break;
case WM_LBUTTONUP :
if(!bGo)
{
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
field->ToggleCell(&p);
PostMessage(hwnd, DRAW, 0, 0);
}
break;
case WM_SIZE :
if(hBitmap != NULL)
{
DeleteObject(hBitmap);
}
GetClientRect(hwnd, &rect);
hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
SelectObject(hdcMem, hBitmap);
ReleaseDC(hwnd, hdc);
break;
case WM_COMMAND :
switch(LOWORD(wParam))
{
case TOGGLEGO :
if(bGo)
{
KillTimer(hwnd, 1);
bGo = FALSE;
}
else
{
SetTimer(hwnd, 1, TIMER, NULL);
bGo = TRUE;
}
break;
case CLEAR :
if(!bGo)
{
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
field->SetSourceCellValue(x, y, FALSE);
}
}
}
PostMessage(hwnd, DRAW, 0, 0);
break;
}
break;
case WM_ERASEBKGND :
return FALSE;
case WM_TIMER :
field->CalcField();
PostMessage(hwnd, DRAW, 0, 0);
break;
case DRAW :
FillRect(hdcMem, &rect, hBkGndBrush);
SelectObject(hdcMem, hPen);
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
RECT r;
CopyRect(&r, &field->rect[x][y]);
FillRect(hdcMem, &field->rect[x][y], hBrush[field->GetCellValue(x,y)]);
}
}
hdc = GetDC(hwnd);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdc);
break;
case WM_PAINT :
FillRect(hdcMem, &rect, hBkGndBrush);
SelectObject(hdcMem, hPen);
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
FillRect(hdcMem, &field->rect[x][y], hBrush[field->GetCellValue(x, y)]);
}
}
BeginPaint(hwnd, &ps);
BitBlt(ps.hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE :
DestroyWindow(hwnd);
break;
case WM_DESTROY :
DeleteObject(hBrush[0]);
DeleteObject(hBrush[1]);
DeleteObject(hBkGndBrush);
DeleteObject(hPen);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
char szAppName[] = "Conway's Life";
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName,
szAppName,
WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
200, 200, 500, 500,
NULL, NULL, hInstance, szCmdLine);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
#include <windows.h>
#include <stdio.h>
#define LIFE 1
#define DEATH 0
#define COLUMNS 100
#define ROWS 100
#define TILEWIDTH 8
#define TILEHEIGHT 8
#define TIMER 100 // milliseconds
#define TOGGLEGO 1001
#define CLEAR 1002
#define DRAW WM_USER + 1
class CField
{
private:
char F1[COLUMNS][ROWS];
char F2[COLUMNS][ROWS];
public:
char *Source;
char *Dest;
RECT rect[COLUMNS][ROWS];
CField(void)
{
this->Source = *F1;
this->Dest = *F2;
for(int x = 0; x < COLUMNS; x ++)
{
for(int y = 0; y < ROWS; y ++)
{
SetRect(&rect[x][y], (x+1) * TILEWIDTH, (y+1) * TILEHEIGHT, (x+2) * TILEWIDTH - 1, (y+2) * TILEHEIGHT - 1);
F1[x][y] = 0;
F2[x][y] = 0;
}
}
}
BOOL GetCellValue(int x, int y)
{
return *(this->Source + ((y * ROWS) + x) * sizeof(char));
}
void SetSourceCellValue(int x, int y, BOOL bVal)
{
*(this->Source + ((y * ROWS) + x) * sizeof(char)) = bVal;
}
void SetDestCellValue(int x, int y, BOOL bVal)
{
*(this->Dest + ((y * ROWS) + x) * sizeof(char)) = bVal;
}
void ToggleCell(POINT *p)
{
int x, y;
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
if(PtInRect(&rect[x][y], *p))
{
SetSourceCellValue(x, y, 1 - GetCellValue(x, y));
return;
}
}
}
}
void CalcField(void)
{
char *temp;
for(int x = 0; x < COLUMNS; x ++)
{
for(int y = 0; y < ROWS; y ++)
{
int iNeighbours = 0;
int iXStart;
int iXStop;
int ix;
iXStart = x - 1 + (x == 0);
iXStop = x + 2 - (x == COLUMNS);
for(ix = iXStart; ix < iXStop; ix ++)
{
iNeighbours += GetCellValue(ix, y);
if(y > 0)
{
iNeighbours += GetCellValue(ix, y-1);
}
if(y < ROWS)
{
iNeighbours += GetCellValue(ix, y+1);
}
}
iNeighbours -= GetCellValue(x, y);
SetDestCellValue(x, y, (BOOL)(GetCellValue(x, y) && (iNeighbours == 2) || (iNeighbours == 3)));
}
}
// Swap pointers
temp = this->Source;
this->Source = this->Dest;
this->Dest = temp;
}
};
// -------------
// | 0 | 1 | 2 |
// -------------
// | 3 | X | 4 |
// -------------
// | 5 | 6 | 7 |
//
// Death
// If an occupied cell has 0, 1, 4, 5, 6, 7, or 8 occupied neighbors, the organism dies (0, 1: of loneliness; 4 thru 8: of overcrowding).
// Survival
// If an occupied cell has two or three neighbors, the organism survives to the next generation.
// Birth
// If an unoccupied cell has three occupied neighbors, it becomes occupied.
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
CREATESTRUCT *cs;
PAINTSTRUCT ps;
int x, y;
static char MenuText[2][6] = {"GO", "STOP"};
static int iWidth, iHeight;
static HBRUSH hBrush[2], hBkGndBrush;
static HPEN hPen;
static HDC hdcMem;
static HBITMAP hBitmap = NULL;
static BOOL bGo = FALSE;
static CField *field;
HMENU hMenu;
static RECT rect;
POINT p;
HDC hdc;
switch (iMsg)
{
case WM_CREATE :
cs = (LPCREATESTRUCT)lParam;
field = new CField();
hPen = CreatePen(1, 1, RGB(200, 200, 200));
hBrush[0] = CreateSolidBrush(RGB(64, 64, 64));
hBrush[1] = CreateSolidBrush(RGB(64, 255, 64));
hBkGndBrush = CreateSolidBrush(RGB(192, 192, 192));
iWidth = (COLUMNS+2) * TILEWIDTH + 5;
iHeight = (ROWS+2) * TILEHEIGHT + 50;
hdc = GetDC(hwnd);
hdcMem = CreateCompatibleDC(hdc);
ReleaseDC(hwnd, hdc);
// init tiles
GetClientRect(GetDesktopWindow(), &rect);
MoveWindow(hwnd, ((rect.right/2) - (iWidth/2)), ((rect.bottom/2) - (iHeight/2)), iWidth, iHeight, TRUE);
break;
case WM_KEYUP:
if(!bGo && wParam == VK_SPACE)
{
PostMessage(hwnd, DRAW, 0, 0);
}
break;
case WM_RBUTTONUP :
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
hMenu = CreatePopupMenu();
AppendMenu (hMenu, MF_STRING, TOGGLEGO, MenuText[bGo]);
if(!bGo)
{
AppendMenu (hMenu, MF_STRING, CLEAR, "Clear Screen");
}
ClientToScreen (hwnd, (LPPOINT)&p);
TrackPopupMenu (hMenu, 0, p.x, p.y, 0, hwnd, NULL);
DestroyMenu (hMenu);
break;
case WM_LBUTTONUP :
if(!bGo)
{
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
field->ToggleCell(&p);
PostMessage(hwnd, DRAW, 0, 0);
}
break;
case WM_SIZE :
if(hBitmap != NULL)
{
DeleteObject(hBitmap);
}
GetClientRect(hwnd, &rect);
hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
SelectObject(hdcMem, hBitmap);
ReleaseDC(hwnd, hdc);
break;
case WM_COMMAND :
switch(LOWORD(wParam))
{
case TOGGLEGO :
if(bGo)
{
KillTimer(hwnd, 1);
bGo = FALSE;
}
else
{
SetTimer(hwnd, 1, TIMER, NULL);
bGo = TRUE;
}
break;
case CLEAR :
if(!bGo)
{
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
field->SetSourceCellValue(x, y, FALSE);
}
}
}
PostMessage(hwnd, DRAW, 0, 0);
break;
}
break;
case WM_ERASEBKGND :
return FALSE;
case WM_TIMER :
field->CalcField();
PostMessage(hwnd, DRAW, 0, 0);
break;
case DRAW :
FillRect(hdcMem, &rect, hBkGndBrush);
SelectObject(hdcMem, hPen);
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
RECT r;
CopyRect(&r, &field->rect[x][y]);
FillRect(hdcMem, &field->rect[x][y], hBrush[field->GetCellValue(x,y)]);
}
}
hdc = GetDC(hwnd);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdc);
break;
case WM_PAINT :
FillRect(hdcMem, &rect, hBkGndBrush);
SelectObject(hdcMem, hPen);
for(x = 0; x < COLUMNS; x ++)
{
for(y = 0; y < ROWS; y ++)
{
FillRect(hdcMem, &field->rect[x][y], hBrush[field->GetCellValue(x, y)]);
}
}
BeginPaint(hwnd, &ps);
BitBlt(ps.hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE :
DestroyWindow(hwnd);
break;
case WM_DESTROY :
DeleteObject(hBrush[0]);
DeleteObject(hBrush[1]);
DeleteObject(hBkGndBrush);
DeleteObject(hPen);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
char szAppName[] = "Conway's Life";
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName,
szAppName,
WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
200, 200, 500, 500,
NULL, NULL, hInstance, szCmdLine);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}