Skip to content

Instantly share code, notes, and snippets.

@arhadthedev
Last active April 19, 2016 16:11
Show Gist options
  • Save arhadthedev/e7683da755a99db769d59527b09539c7 to your computer and use it in GitHub Desktop.
Save arhadthedev/e7683da755a99db769d59527b09539c7 to your computer and use it in GitHub Desktop.
Шаблон для игры «Жизнь» Александру
#include <windows.h>
#include <Strsafe.h>
#include <bitset>
// **********************************************
//
// Логика приложения
//
// **********************************************
template <size_t Rows, size_t Columns>
class GameOfLife
{
public:
GameOfLife()
: _currentField(0), _generation(1)
{
for(size_t i = 0; i < _fields[0].size(); ++i)
_fields[0].set(i, std::rand() & 1);
}
unsigned generation() const
{
return _generation;
}
size_t rows() const
{
return Rows;
}
size_t columns() const
{
return Columns;
}
const std::bitset<Rows * Columns>& field() const
{
return _fields[_currentField];
}
void process()
{
++_generation;
std::bitset<Rows * Columns>& prevGenField = _fields[_currentField];
_currentField = (_currentField == 0) ? 1 : 0;
std::bitset<Rows * Columns>& nextGenField = _fields[_currentField];
for(size_t row = 0; row < Rows; ++row)
{
for(size_t column = 0; column < Columns; ++column)
{
// Считаем количество соседей очередной клетки. Воспользуемся
// тем свойством, что true преобразуется в 1, а false - в 0.
const unsigned neightboursCount
= prevGenField.test(CellByPosition(row - 1, column - 1))
+ prevGenField.test(CellByPosition(row - 1, column ))
+ prevGenField.test(CellByPosition(row - 1, column + 1))
+ prevGenField.test(CellByPosition(row, column - 1))
+ prevGenField.test(CellByPosition(row, column ))
+ prevGenField.test(CellByPosition(row, column + 1))
+ prevGenField.test(CellByPosition(row + 1, column - 1))
+ prevGenField.test(CellByPosition(row + 1, column ))
+ prevGenField.test(CellByPosition(row + 1, column + 1));
// 1. "Мёртвая" клетка с тремя соседями "оживает".
// 2. "Живая" клетка с менее, чем двумя, или более, чем тремя,
// соседями "умирает".
const bool wasAlive
= prevGenField.test(CellByPosition(row, column));
const bool isAlive = (neightboursCount == 3)
|| (neightboursCount == 2 && wasAlive);
nextGenField.set(CellByPosition(row, column), isAlive);
}
}
}
private:
static size_t CellByPosition(size_t row, size_t column)
{
const size_t warpedRow = row % Rows;
const size_t warpedColumn = column % Columns;
return warpedRow * Columns + warpedColumn;
}
std::bitset<Rows * Columns> _fields[2];
size_t _currentField;
unsigned _generation;
};
// **********************************************
//
// Графический интерфейс пользователя
//
// **********************************************
#define UNUSED(expr) (void)(expr)
// ==============================================
enum
{
MainWindowStyle = WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
Interval = 500,
CellSize = 10,
FieldRows = 20,
FieldColumns = 40
};
static GameOfLife<FieldRows, FieldColumns> game;
// ==============================================
static LRESULT CALLBACK MainWindowProc
(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
const HDC hdc = BeginPaint(hWnd, &ps);
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
size_t i = 0;
const std::bitset<FieldRows * FieldColumns>& field = game.field();
for(size_t row = 0; row < game.rows(); ++row)
{
for(size_t column = 0; column < game.columns(); ++column)
{
if(field.test(i++))
{
Rectangle(
hdc,
column * CellSize,
row * CellSize,
column * CellSize + CellSize,
row * CellSize + CellSize
);
}
}
}
EndPaint(hWnd, &ps);
return 0;
}
case WM_TIMER:
{
game.process();
TCHAR captionBuffer[sizeof(TEXT("Generation: \0\0\0\0\0\0\0"))];
StringCbPrintf(
captionBuffer,
sizeof captionBuffer,
TEXT("Generation: %u"),
game.generation()
);
SetWindowText(hWnd, captionBuffer);
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
static HWND createWindow(HINSTANCE hInstance)
{
WNDCLASS cls;
cls.style = 0;
cls.lpfnWndProc = MainWindowProc;
cls.cbClsExtra = 0;
cls.cbWndExtra = 0;
cls.hInstance = hInstance;
cls.hIcon = NULL;
cls.hCursor = LoadCursor(NULL, IDC_ARROW);
cls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
cls.lpszMenuName = NULL;
cls.lpszClassName = TEXT("MainWindowClass");
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = game.columns() * CellSize;
rect.bottom = game.rows() * CellSize;
AdjustWindowRect(&rect, MainWindowStyle, false);
return CreateWindow(
MAKEINTATOM(RegisterClass(&cls)),
TEXT("Generation: 1"),
MainWindowStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
NULL,
NULL,
hInstance,
NULL
);
}
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
UNUSED(hPrevInstance);
UNUSED(lpCmdLine);
const HWND mainWindow = createWindow(hInstance);
SetTimer(mainWindow, 0, Interval, NULL);
ShowWindow(mainWindow, nCmdShow);
MSG msg;
bool process = true;
while(process)
{
const WINBOOL bRet = GetMessage(&msg, NULL, 0, 0);
if(bRet > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else if(bRet < 0)
{
const TCHAR Message[]
= TEXT("Error occured while processing a window message");
DWORD dummy;
const HANDLE winStdErr = GetStdHandle(STD_ERROR_HANDLE);
WriteConsole(winStdErr, Message, sizeof Message, &dummy, NULL);
}
else
process = false;
}
return msg.wParam;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment