Conway’s Game Of Life

康威生命游戏

main.cpp

#include "ConwaysGameOfLife.h"

// ReSharper disable once CppParameterMayBeConst
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int)
{
	ConwayGameOfLife cgl(hInstance);
	cgl.Initialize();
	cgl.GenerateGosperGliderGun(0, 0);
	cgl.GenerateGosperGliderGun(30, 0);
	cgl.GenerateGosperGliderGun(60, 0);
	cgl.Run();
	return 0;
}

ConwaysGameOfLife.h

#pragma once

#include "WindowsApp.h"

#include <vector>

enum class CellStatus { Dead, Alive };

class ConwayGameOfLife final : public WindowsApp
{
	const static int NumRows = 108;
	const static int NumCols = 192;
	const static int SizePerCell = 10;
	const static int Width = NumCols * SizePerCell;
	const static int Height = NumRows * SizePerCell;

	using Row = std::vector<CellStatus>;
	using Map = std::vector<Row>;

	Map map = Map(NumRows, Row(NumCols, CellStatus::Dead));
	HBITMAP bmp{};
	HDC cdc{};
	bool pause = false;
	void* pBits = nullptr;
	int32_t fpsLimit = 60;

	void SetCell(int row, int col, CellStatus status);

	static CellStatus GetCell(const Map& map, int row, int col);

	static int GetCount(const Map& map, int row, int col);

	static CellStatus DeadOrAlive(const Map& map, int row, int col);

	static void DrawAliveCell(HDC dc, int x, int y);

	void InitializeBmp();
	void Update();
	void Draw();

public:
	explicit ConwayGameOfLife(HINSTANCE hInstance);

	void GenerateGosperGliderGun(int offsetRow, int offsetCol);
	void Initialize() override;
	void Run() override;
	LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) override;
};

ConwaysGameOfLife.cpp

#include "ConwaysGameOfLife.h"

#include <string>
#include <array>

void ConwayGameOfLife::SetCell(const int row, const int col, const CellStatus status)
{
	map.at((row + NumRows) % NumRows).at((col + NumCols) % NumCols) = status;
}

CellStatus ConwayGameOfLife::GetCell(const Map& map, const int row, const int col)
{
	return map.at((row + NumRows) % NumRows).at((col + NumCols) % NumCols);
}

int ConwayGameOfLife::GetCount(const Map& map, const int row, const int col)
{
	auto count = 0;
	std::array<int, 3> ops{ -1, 0, 1 };
	for (auto colOp : ops)
	{
		for (auto rowOp : ops)
		{
			if (rowOp || colOp)
			{
				count += static_cast<int>(GetCell(map, row + rowOp, col + colOp));
			}
		}
	}
	return count;
}

CellStatus ConwayGameOfLife::DeadOrAlive(const Map& map, const int row, const int col)
{
	const auto count = GetCount(map, row, col);
	return CellStatus(!static_cast<int>(map.at(row).at(col)) && count == 3
		|| static_cast<int>(map.at(row).at(col)) && (count == 2 || count == 3));
}

void ConwayGameOfLife::Update()
{
	const auto prevMap = map;
	for (auto row = 0; row < NumRows; row++)
	{
		for (auto col = 0; col < NumCols; col++)
		{
			map.at(row).at(col) = DeadOrAlive(prevMap, row, col);
		}
	}
}

void ConwayGameOfLife::Draw()
{
	memset(pBits, RGB(255, 255, 255), Width * Height * sizeof int32_t);
	for (auto row = 0; row < NumRows; row++)
	{
		for (auto col = 0; col < NumCols; col++)
		{
			if (static_cast<int>(map.at(row).at(col)))
			{
				DrawAliveCell(cdc, row, col);
			}
		}
	}
	const auto hdc = GetDC(hWnd);
	BitBlt(hdc, 0, 0, Width, Height, cdc, 0, 0, SRCCOPY);
	ReleaseDC(hWnd, hdc);
}

void ConwayGameOfLife::InitializeBmp()
{
	BITMAPINFO bi;
	ZeroMemory(&bi, sizeof bi);
	bi.bmiHeader.biSize = sizeof bi.bmiHeader;
	bi.bmiHeader.biBitCount = sizeof(DWORD) * 8;
	bi.bmiHeader.biCompression = BI_RGB;
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biWidth = Width;
	bi.bmiHeader.biHeight = Height;
	const auto hdc = GetDC(hWnd);
	bmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &pBits, nullptr, 0);
	cdc = CreateCompatibleDC(hdc);
	SelectObject(cdc, bmp);
	ReleaseDC(hWnd, hdc);
}

// ReSharper disable once CppParameterMayBeConst
void ConwayGameOfLife::DrawAliveCell(HDC dc, const int x, const int y)
{
	RECT rc;
	SetRect(&rc, y * SizePerCell, x * SizePerCell, (y + 1) * SizePerCell, (x + 1) * SizePerCell);
	FillRect(dc, &rc,
		static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
}

// ReSharper disable once CppParameterMayBeConst
ConwayGameOfLife::ConwayGameOfLife(HINSTANCE hInstance) : WindowsApp(hInstance)
{
}

void ConwayGameOfLife::GenerateGosperGliderGun(const int offsetRow, const int offsetCol)
{
	SetCell(5 + offsetRow, 1 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 1 + offsetCol, CellStatus::Alive);
	SetCell(5 + offsetRow, 2 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 2 + offsetCol, CellStatus::Alive);
	SetCell(5 + offsetRow, 11 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 11 + offsetCol, CellStatus::Alive);
	SetCell(7 + offsetRow, 11 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 12 + offsetCol, CellStatus::Alive);
	SetCell(8 + offsetRow, 12 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 13 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 14 + offsetCol, CellStatus::Alive);
	SetCell(9 + offsetRow, 13 + offsetCol, CellStatus::Alive);
	SetCell(9 + offsetRow, 14 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 15 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 16 + offsetCol, CellStatus::Alive);
	SetCell(8 + offsetRow, 16 + offsetCol, CellStatus::Alive);
	SetCell(5 + offsetRow, 17 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 17 + offsetCol, CellStatus::Alive);
	SetCell(7 + offsetRow, 17 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 18 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 21 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 21 + offsetCol, CellStatus::Alive);
	SetCell(5 + offsetRow, 21 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 22 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 22 + offsetCol, CellStatus::Alive);
	SetCell(5 + offsetRow, 22 + offsetCol, CellStatus::Alive);
	SetCell(2 + offsetRow, 23 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 23 + offsetCol, CellStatus::Alive);
	SetCell(1 + offsetRow, 25 + offsetCol, CellStatus::Alive);
	SetCell(2 + offsetRow, 25 + offsetCol, CellStatus::Alive);
	SetCell(6 + offsetRow, 25 + offsetCol, CellStatus::Alive);
	SetCell(7 + offsetRow, 25 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 35 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 35 + offsetCol, CellStatus::Alive);
	SetCell(3 + offsetRow, 36 + offsetCol, CellStatus::Alive);
	SetCell(4 + offsetRow, 36 + offsetCol, CellStatus::Alive);
}

void ConwayGameOfLife::Initialize()
{
	RECT rect{ 0,0,Width,Height };
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false);
	InitializeWindows(rect.right - rect.left, rect.bottom - rect.top, L"Conway's Game of Life");
	InitializeBmp();
	Draw();
}

void ConwayGameOfLife::Run()
{
	uint64_t countsPerSec;
	QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&countsPerSec));
	const auto secondsPerCount = 1.0 / static_cast<double>(countsPerSec);
	
	uint64_t currTime = 0;
	QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime));
	auto prevTime = currTime;
	
	uint64_t fpsLimitCurrTime = 0;
	auto fpsLimitPrevTime = fpsLimitCurrTime;
	
	MSG msg{};
	auto fps = 0;
	while (msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != 0)
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			if (!pause)
			{
				QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&currTime));
				if (static_cast<double>(currTime - prevTime)* secondsPerCount >= 1.)
				{
					prevTime = currTime;
					SetWindowText(hWnd, 
						(std::to_wstring(fps) + L"fps" 
							+ L" | Current Fps Limit:" + std::to_wstring(fpsLimit) 
							+ L"fps | space:pause | +/-:Fps Limit+/-10 | *:Fps Unlimited").c_str());
					fps = 0;
				}
				fpsLimitCurrTime = currTime;
				if (static_cast<double>(fpsLimitCurrTime - fpsLimitPrevTime)* secondsPerCount >= 1. / fpsLimit)
				{
					fpsLimitPrevTime = fpsLimitCurrTime;
					fps++;
					Update();
					Draw();
				}
			}
		}
	}
}

// ReSharper disable once CppParameterMayBeConst
LRESULT ConwayGameOfLife::WndProc(HWND hWnd, const UINT message, const WPARAM wParam, const LPARAM lParam)
{
	switch (message)
	{
	case WM_KEYDOWN: if (wParam == VK_SPACE)
		{
			pause = !pause;
		}
		else if (wParam == VK_ESCAPE)
		{
			DestroyWindow(hWnd);
		}
		else if (wParam == VK_ADD)
		{
			fpsLimit += 10;
		}
		else if (wParam == VK_SUBTRACT)
		{
			fpsLimit -= 10;
		}
		else if (wParam == VK_MULTIPLY)
		{
			fpsLimit = -10;
		}
		return 0;
	case WM_DESTROY: PostQuitMessage(0);
		return 0;
	default: return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

WindowsApp.h

#pragma once

#include <Windows.h>
#include <string>

class WindowsApp
{
protected:
	HINSTANCE hInstance;
	HWND hWnd{};

	static WindowsApp* app;
public:
	explicit WindowsApp(HINSTANCE hInstance);
	virtual ~WindowsApp();

	WindowsApp(const WindowsApp& windowsApp) = delete;
	WindowsApp(WindowsApp&& windowsApp) = delete;

	WindowsApp* operator=(const WindowsApp& windowsApp) = delete;
	WindowsApp* operator=(WindowsApp&& windowsApp) = delete;

	void InitializeWindows(int width, int height, const std::wstring& title);
	virtual void Initialize() = 0;
	virtual void Run() = 0;
	virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) = 0;

	static WindowsApp* GetApp();
};

WindowsApp.cpp

#include "WindowsApp.h"

// ReSharper disable once CppParameterMayBeConst
LRESULT WINAPI MainWndProc(HWND hWnd, const UINT message, const WPARAM wParam, const LPARAM lParam)
{
	return WindowsApp::GetApp()->WndProc(hWnd, message, wParam, lParam);
}

WindowsApp* WindowsApp::app = nullptr;

// ReSharper disable once CppParameterMayBeConst
WindowsApp::WindowsApp(HINSTANCE hInstance) : hInstance(hInstance)
{
	app = this;
}

WindowsApp::~WindowsApp() = default;

void WindowsApp::InitializeWindows(const int width, const int height, const std::wstring& title)
{
	WNDCLASS wc =
	{
		CS_HREDRAW | CS_VREDRAW,
		MainWndProc,
		0,
		0,
		hInstance,
		LoadIcon(hInstance, IDI_APPLICATION),
		LoadCursor(nullptr, IDC_ARROW),
		static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)),
		nullptr,
		L"main"
	};
	RegisterClass(&wc);
	hWnd = CreateWindow(
		L"main",
		title.c_str(),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		width,
		height,
		nullptr,
		nullptr,
		hInstance,
		nullptr);
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
}

WindowsApp* WindowsApp::GetApp()
{
	return app;
}

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注