Conway's Life

Conway's Life
Conway's Life

This is a sample of a life simulation program. Just type "Conway's Life" in Google. Use rightmousebutton to start or stop the simulation. I made it as simple as possible. I suggest you play with the defines to create bigger grids or slow things down. With 340 lines of code and an executable size of 40k it's small and easy to understand and modify. Download source
#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;
}
Articles
info@win32apicode.com