Я пишу игру пятнашки на с++. Сделай цифры на плитках желтыми: ```#include <windows.h> #include <windowsx.h> #include <commctrl.h> #include <fstream> #include <string> #include <ctime> #include <tchar.h> #include <shellapi.h> #include <locale> #include <codecvt> using namespace std; // Constants #define GRID_SIZE 4 #define TILE_SIZE 100 // Initial tile size #define IDC_TIMER 1 #define TIMER_INTERVAL 1000 // 1 second #define IDC_NEW_GAME 1001 #define IDC_VIEW_RESULTS 1002 // Global Variables HINSTANCE hInst; HWND hWndMain; HWND hButtonNewGame; HWND hButtonViewResults; HFONT hFontVerdana; int board[GRID_SIZE][GRID_SIZE]; RECT tileRects[GRID_SIZE][GRID_SIZE]; unsigned int elapsedSeconds = 0; unsigned int moveCount = 0; bool timerRunning = false; int emptyRow, emptyCol; int windowWidth = GRID_SIZE * TILE_SIZE; int windowHeight = GRID_SIZE * TILE_SIZE + 80; // Extra space for timer and buttons const TCHAR* resultsFileName = _T("results.txt"); // Function Declarations LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void InitializeGame(); void DrawBoard(HDC hdc); void ShuffleBoard(); bool IsSolved(); void SaveResult(); void UpdateTileRects(); void MoveTile(int row, int col); void StartTimer(); void StopTimer(); void DrawTimer(HDC hdc); void AddControls(HWND hWnd); void ShowResults(); void UpdateButtonPositions(); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // Register Window Class WNDCLASS wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = _T("FifteenPuzzleClass"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClass(&wc); // Create Window hInst = hInstance; hWndMain = CreateWindow( _T("FifteenPuzzleClass"), _T("Пятнашки"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, windowWidth, windowHeight, NULL, NULL, hInstance, NULL); if (!hWndMain) return 0; // Message Loop MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } void InitializeGame() { // Initialize font LOGFONT lf = {}; lf.lfHeight = -30; // Размер шрифта lstrcpy(lf.lfFaceName, _T("Verdana")); hFontVerdana = CreateFontIndirect(&lf); // Initialize board ShuffleBoard(); // Initialize timer and moves elapsedSeconds = 0; moveCount = 0; timerRunning = true; // Start timer SetTimer(hWndMain, IDC_TIMER, TIMER_INTERVAL, NULL); // Update tile rectangles UpdateTileRects(); // Force redraw InvalidateRect(hWndMain, NULL, TRUE); } void ShuffleBoard() { // Initialize the board to the solved state int value = 1; for (int row = 0; row < GRID_SIZE; row++) { for (int col = 0; col < GRID_SIZE; col++) { if (row == GRID_SIZE - 1 && col == GRID_SIZE - 1) { board[row][col] = 0; // Пустая клетка emptyRow = row; emptyCol = col; } else { board[row][col] = value++; } } } // Perform random moves to shuffle the board srand((unsigned int)time(NULL)); int moves = 1000; // Количество случайных ходов for (int i = 0; i < moves; i++) { int direction = rand() % 4; int newRow = emptyRow; int newCol = emptyCol; switch (direction) { case 0: // Вверх newRow = emptyRow - 1; break; case 1: // Вниз newRow = emptyRow + 1; break; case 2: // Влево newCol = emptyCol - 1; break; case 3: // Вправо newCol = emptyCol + 1; break; } if (newRow >= 0 && newRow < GRID_SIZE && newCol >= 0 && newCol < GRID_SIZE) { // Swap the empty space with the adjacent tile board[emptyRow][emptyCol] = board[newRow][newCol]; board[newRow][newCol] = 0; emptyRow = newRow; emptyCol = newCol; } } } void UpdateTileRects() { RECT clientRect; GetClientRect(hWndMain, &clientRect); int tileWidth = (clientRect.right - clientRect.left) / GRID_SIZE; int tileHeight = (clientRect.bottom - clientRect.top - 80) / GRID_SIZE; // Отступ для таймера и кнопок for (int row = 0; row < GRID_SIZE; row++) { for (int col = 0; col < GRID_SIZE; col++) { tileRects[row][col].left = col * tileWidth; tileRects[row][col].top = row * tileHeight + 80; // Смещение для таймера и кнопок tileRects[row][col].right = tileRects[row][col].left + tileWidth; tileRects[row][col].bottom = tileRects[row][col].top + tileHeight; } } } void DrawBoard(HDC hdc) { // Fill the background to avoid artifacts RECT clientRect; GetClientRect(hWndMain, &clientRect); HBRUSH hBrushBG = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); FillRect(hdc, &clientRect, hBrushBG); DeleteObject(hBrushBG); // Draw the timer and move counter DrawTimer(hdc); // Draw the tiles SelectObject(hdc, hFontVerdana); SetBkMode(hdc, TRANSPARENT); for (int row = 0; row < GRID_SIZE; row++) { for (int col = 0; col < GRID_SIZE; col++) { RECT rect = tileRects[row][col]; if (board[row][col] != 0) { // Draw tile rectangle HBRUSH hBrush = CreateSolidBrush(RGB(200, 200, 255)); // Светло-синий цвет FillRect(hdc, &rect, hBrush); DeleteObject(hBrush); // Draw tile number TCHAR text[3]; _stprintf_s(text, _T("%d"), board[row][col]); SetTextColor(hdc, RGB(0, 0, 0)); DrawText(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } else { // Empty space HBRUSH hBrush = CreateSolidBrush(RGB(240, 240, 240)); // Серый цвет FillRect(hdc, &rect, hBrush); DeleteObject(hBrush); } // Draw tile border FrameRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); } } } void DrawTimer(HDC hdc) { RECT rect; GetClientRect(hWndMain, &rect); rect.bottom = 40; rect.top = 0; // Fill the background of the timer area to erase previous text HBRUSH hBrushBG = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); FillRect(hdc, &rect, hBrushBG); DeleteObject(hBrushBG); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, RGB(0, 0, 0)); TCHAR text[100]; _stprintf_s(text, _T("Время: %u сек     Ходы: %u"), elapsedSeconds, moveCount); DrawText(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } void MoveTile(int row, int col) { if ((abs(emptyRow - row) == 1 && emptyCol == col) || (abs(emptyCol - col) == 1 && emptyRow == row)) { // Swap tile with empty space board[emptyRow][emptyCol] = board[row][col]; board[row][col] = 0; emptyRow = row; emptyCol = col; // Increment move count moveCount++; InvalidateRect(hWndMain, NULL, TRUE); if (IsSolved()) { StopTimer(); SaveResult();    // Сохраняем результат перед сообщением // Диалоговое окно с вопросом int response = MessageBox(hWndMain, _T("Вы выиграли!\nХотите начать новую игру?"), _T("Поздравляем"), MB_YESNO | MB_ICONQUESTION); if (response == IDYES) { // Start a new game InitializeGame(); } else { // Close the program PostQuitMessage(0); } } } } bool IsSolved() { int expected = 1; for (int row = 0; row < GRID_SIZE; row++) { for (int col = 0; col < GRID_SIZE; col++) { if (row == GRID_SIZE - 1 && col == GRID_SIZE - 1) { // Последняя клетка должна быть пустой if (board[row][col] != 0) return false; } else { if (board[row][col] != expected) return false; expected++; } } } return true; } void SaveResult() { wofstream outFile(resultsFileName, ios::app); if (outFile.is_open()) { // Устанавливаем локаль на UTF-8 outFile.imbue(locale(locale::empty(), new codecvt_utf8<wchar_t>)); outFile << _T("Время: ") << elapsedSeconds << _T(" сек; "); outFile << _T("Ходы: ") << moveCount << _T("\n"); outFile.close(); } } void StartTimer() { elapsedSeconds = 0; timerRunning = true; SetTimer(hWndMain, IDC_TIMER, TIMER_INTERVAL, NULL); } void StopTimer() { timerRunning = false; KillTimer(hWndMain, IDC_TIMER); } void AddControls(HWND hWnd) { // Создаем кнопку "Новая игра" hButtonNewGame = CreateWindow( _T("BUTTON"), _T("Новая игра"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 10, 40, 100, 30, hWnd, (HMENU)IDC_NEW_GAME, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); // Создаем кнопку "Результаты" hButtonViewResults = CreateWindow( _T("BUTTON"), _T("Результаты"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 120, 40, 100, 30, hWnd, (HMENU)IDC_VIEW_RESULTS, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); } void UpdateButtonPositions() { RECT clientRect; GetClientRect(hWndMain, &clientRect); int buttonWidth = 100; int buttonHeight = 30; int spacing = 10; // Позиционируем кнопки относительно ширины окна int totalButtonsWidth = buttonWidth * 2 + spacing; int startX = (clientRect.right - totalButtonsWidth) / 2; int y = 40; MoveWindow(hButtonNewGame, startX, y, buttonWidth, buttonHeight, TRUE); MoveWindow(hButtonViewResults, startX + buttonWidth + spacing, y, buttonWidth, buttonHeight, TRUE); } void ShowResults() { // Открываем results.txt с помощью приложения по умолчанию ShellExecute(NULL, _T("open"), resultsFileName, NULL, NULL, SW_SHOWNORMAL); } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: hWndMain = hWnd; // Ensure hWndMain is set AddControls(hWnd); InitializeGame(); break; case WM_SIZE: UpdateTileRects(); UpdateButtonPositions(); InvalidateRect(hWnd, NULL, TRUE); break; case WM_GETMINMAXINFO: { MINMAXINFO* pMinMax = (MINMAXINFO*)lParam; pMinMax->ptMinTrackSize.x = 800; // Минимальная ширина pMinMax->ptMinTrackSize.y = 400; // Минимальная высота } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); DrawBoard(hdc); EndPaint(hWnd, &ps); } break; case WM_LBUTTONDOWN: { int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam); // Determine which tile was clicked for (int row = 0; row < GRID_SIZE; row++) { for (int col = 0; col < GRID_SIZE; col++) { RECT rect = tileRects[row][col]; if (PtInRect(&rect, POINT{ x, y })) { MoveTile(row, col); goto AfterMove; // Выходим из циклов после хода } } } AfterMove: break; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_NEW_GAME: // Start a new game StopTimer(); InitializeGame(); break; case IDC_VIEW_RESULTS: // Open results.txt ShowResults(); break; } break; case WM_TIMER: if (wParam == IDC_TIMER && timerRunning) { elapsedSeconds++; // Invalidate only the timer area to avoid unnecessary redraws RECT rect; GetClientRect(hWnd, &rect); rect.bottom = 40; // The area where the timer and moves are displayed InvalidateRect(hWnd, &rect, FALSE); } break; case WM_DRAWITEM: { LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)lParam; if (lpDrawItem->hwndItem == hButtonNewGame || lpDrawItem->hwndItem == hButtonViewResults) { // Fill the button background HBRUSH hBrush = CreateSolidBrush(RGB(200, 162, 200)); // Цвет c8a2c8 FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, hBrush); DeleteObject(hBrush); // Set text color SetTextColor(lpDrawItem->hDC, RGB(0, 0, 0)); SetBkMode(lpDrawItem->hDC, TRANSPARENT); // Draw the button text TCHAR buttonText[50]; GetWindowText(lpDrawItem->hwndItem, buttonText, sizeof(buttonText) / sizeof(TCHAR)); DrawText(lpDrawItem->hDC, buttonText, -1, &lpDrawItem->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // Draw the button frame DrawEdge(lpDrawItem->hDC, &lpDrawItem->rcItem, (lpDrawItem->itemState & ODS_SELECTED) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT); return TRUE; } break; } case WM_DESTROY: if (hFontVerdana) DeleteObject(hFontVerdana); PostQuitMessage(0); break; case WM_KEYDOWN: if (wParam == VK_ESCAPE) { // Start a new game StopTimer(); InitializeGame(); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }```