康威生命游戏
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; }