BOOL TicTacToeGame::GetCellRect(HWND hWnd, int index, RECT *pRect) { RECT rcBoard; SetRectEmpty(pRect); if (index < 0 || index > 8) { return FALSE; } if (GetGameBoardRect(hWnd, &rcBoard)) { int column = index % 3; int row = index / 3; pRect->left = rcBoard.left + column * CELL_SIZE + 1; pRect->top = rcBoard.top + row * CELL_SIZE + 1; pRect->right = pRect->left + CELL_SIZE - 1; pRect->bottom = pRect->top + CELL_SIZE - 1; return TRUE; } return FALSE; }
int GetCellNumberFromPoint(HWND& hWnd, int x, int y) { POINT pt{ x, y }; RECT rc; if (GetGameBoardRect(hWnd, &rc) && PtInRect(&rc, pt)) // clicked inside game board { // Normalize point ( 0 to 3 * CELL_SIZE ) x = pt.x - rc.left; y = pt.y - rc.top; int column = x / CELL_SIZE; int row = y / CELL_SIZE; // convert to index ( 0 to 8 ) return column + row * 3; } return -1; // outside game board }
BOOL GetCellRect(HWND &hWnd, int index, RECT *pRect) { RECT rcBoard; if (GetGameBoardRect(hWnd, &rcBoard)) { //Convert index from 0 to 8 into (x, y) pair int x = index % 3; // Column number int y = index / 3; // Row number pRect->left = rcBoard.left + x * CELL_SIZE + 1; pRect->top = rcBoard.top + y * CELL_SIZE + 1; pRect->right = pRect->left + CELL_SIZE - 1; pRect->bottom = pRect->top + CELL_SIZE - 1; return TRUE; } return FALSE; }
int TicTacToeGame::GetCellNumberFromPoint(HWND hWnd, int xPos, int yPos) { POINT pt = { xPos, yPos }; RECT rc; if (GetGameBoardRect(hWnd, &rc)) { if (PtInRect(&rc, pt)) { xPos = pt.x - rc.left; yPos = pt.y - rc.top; int column = xPos / CELL_SIZE; int row = yPos / CELL_SIZE; return column + row * 3; } } return -1; //outside gameboard or failure }
int GetCellNumberFromPoint(HWND hwnd, int x, int y) { POINT pt = { x, y }; RECT rc; if (GetGameBoardRect(hwnd, &rc)) { if (PtInRect(&rc, pt)) { //user clicked inside x = pt.x - rc.left; y = pt.y - rc.top; int column = x / CELL_SIZE; int row = y / CELL_SIZE; return column + row * 3; } } return -1; }
BOOL GetCellRect (HWND hWnd, int index, RECT*pRect) { RECT rcBoard; SetRectEmpty(pRect); if (index < 0 || index >8) return FALSE; if (GetGameBoardRect(hWnd, &rcBoard)) { int y = index / 3; int x = index % 3; pRect->left = rcBoard.left + x*CELL_SIZE +1 ; pRect->top = rcBoard.top + y*CELL_SIZE+1 ; pRect->right = pRect->left + CELL_SIZE-1 ; pRect->bottom = pRect->top + CELL_SIZE-1 ; return TRUE; } return FALSE; }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { hbr1 = CreateSolidBrush(RGB(255, 0, 0)); hbr2 = CreateSolidBrush(RGB(0, 0, 255)); hIcon1 = LoadIcon(hInst, MAKEINTRESOURCE(ID_PLAYER1)); hIcon2 = LoadIcon(hInst, MAKEINTRESOURCE(ID_PLAYER2)); } break; case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case ID_FILE_NEWGAME: { int ret = MessageBox(hWnd, L"Are you sure you want to start a new game?", L"New Game", MB_YESNO | MB_ICONQUESTION); if (IDYES == ret) { //Reset and start a new game playerTurn = 1; winner = 0; ZeroMemory(gameBoard, sizeof(gameBoard)); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); } } break; case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_LBUTTONDOWN: { int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); if (0 == playerTurn) break; int index = GetCellNumberFromPoint(hWnd, xPos, yPos); HDC hdc = GetDC(hWnd); if (NULL != hdc) { // WCHAR temp[100]; //wsprintf(temp, L"Index = %d", index); // TextOut(hdc, xPos, yPos, temp, lstrlen(temp)); if (index != -1) { RECT rcCell; if ((0 == gameBoard[index]) && GetCellRect(hWnd, index, &rcCell)) { gameBoard[index] = playerTurn; //FillRect(hdc, &rcCell, (playerTurn==1) ? hbr1 : hbr2); DrawIconCentered(hdc, &rcCell, (playerTurn == 1) ? hIcon1 : hIcon2); winner = GetWinner(wins); if (winner == 1 || winner == 2) { MessageBox(hWnd, (winner == 1) ? L"Player 1 wins" : L"Player 2 wins", L"You win!",MB_OK | MB_ICONINFORMATION); playerTurn = 0; } else if (3==winner) { MessageBox(hWnd, L"No one wins!", L"It's a draw", MB_OK | MB_ICONEXCLAMATION); playerTurn = 0; } else if (winner == 0) { playerTurn = (playerTurn == 1) ? 2 : 1; } ShowTurn(hWnd, hdc); } } ReleaseDC(hWnd, hdc); } } break; case WM_GETMINMAXINFO: { MINMAXINFO * pMinMax = (MINMAXINFO*)lParam; pMinMax->ptMinTrackSize.x = CELL_SIZE * 5; pMinMax->ptMinTrackSize.y = CELL_SIZE * 5; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rc; if (GetGameBoardRect(hWnd, &rc)) { RECT rcClient; if (GetClientRect(hWnd, &rcClient)) { const WCHAR szPlayer1 [] = L"Player 1"; const WCHAR szPlayer2 []= L"Player 2"; SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, RGB(255, 255, 0)); TextOut(hdc, 16, 16, szPlayer1, 8); SetTextColor(hdc, RGB(0, 0, 255)); TextOut(hdc, rcClient.right-72, 16, szPlayer2, 8); ShowTurn(hWnd, hdc); } FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); //Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); } for (int i = 0; i < 4; i++) { DrawLine(hdc, rc.left+CELL_SIZE*i , rc.top, rc.left + CELL_SIZE*i, rc.bottom); DrawLine(hdc, rc.left, rc.top + CELL_SIZE*i, rc.right, rc.top+CELL_SIZE*i); } RECT rcCell; for (int i = 0; i < 9; i++) { if ((0!=gameBoard[i]) && GetCellRect(hWnd, i, &rcCell)) { //FillRect(hdc, &rcCell, (gameBoard[i]==2) ? hbr2 : hbr1); DrawIconCentered(hdc, &rcCell, (gameBoard[i] == 1) ? hIcon1 : hIcon2); } } EndPaint(hWnd, &ps); } break; case WM_DESTROY: DeleteObject(hbr1); DeleteObject(hbr2); DestroyIcon(hIcon1); DestroyIcon(hIcon2); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { hBrushP1 = CreateSolidBrush(colorPlayer1Bg); hBrushP2 = CreateSolidBrush(colorPlayer2Bg); hBrushPWin = CreateSolidBrush(RGB(200, 200, 0)); // neutral Yellow hIconP1 = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON_P1), IMAGE_ICON, SIGN_SIZE, SIGN_SIZE, LR_DEFAULTCOLOR); hIconP2 = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON_P2), IMAGE_ICON, SIGN_SIZE, SIGN_SIZE, LR_DEFAULTCOLOR); } break; case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case ID_FILE_NEWGAME: { if (IDYES == MessageBox(hWnd, L"Are you sure you want to start a new game?", L"New Game", MB_YESNO | MB_ICONQUESTION)) { // Reset and start new game; playerTurn = 1; winner = 0; std::fill(gameBoard, std::end(gameBoard), 0); // Force rePaint message InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); } } break; case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_GETMINMAXINFO: { MINMAXINFO *pMinMax = (MINMAXINFO*)lParam; pMinMax->ptMinTrackSize.x = CELL_SIZE * 4 - 25; pMinMax->ptMinTrackSize.y = CELL_SIZE * 4; } break; case WM_LBUTTONDOWN: { int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); if (playerTurn <= 0) break; int index = GetCellNumberFromPoint(hWnd, xPos, yPos); if (HDC hdc = GetDC(hWnd)) { //WCHAR temp[20]; //wsprintf(temp, L"Index = %d", index); //TextOut(hdc, xPos, yPos, temp, lstrlen(temp)); //Get cell dimension from Index if (index != -1) { RECT rcCell; if (!gameBoard[index] && GetCellRect(hWnd, index, &rcCell)) { gameBoard[index] = playerTurn; // set gameBoard status // draw gameBoard cells FillRect(hdc, &rcCell, playerTurn == 1 ? hBrushP1 : hBrushP2); DrawIconEx(hdc, rcCell.left + (CELL_SIZE - SIGN_SIZE)/2, rcCell.top + (CELL_SIZE - SIGN_SIZE) / 2, playerTurn == 1 ? hIconP1 : hIconP2, SIGN_SIZE, SIGN_SIZE, 0, nullptr, DI_NORMAL); winner = GetWinner(wins); // check for game winner if (winner > 0) { //We have a winner!!! ShowTurn(hWnd, hdc); // Display turn for (int winIndex : wins) { // draw winning gameBoard cells GetCellRect(hWnd, winIndex, &rcCell); FillRect(hdc, &rcCell, hBrushPWin); DrawIconEx(hdc, rcCell.left + (CELL_SIZE - SIGN_SIZE) / 2, rcCell.top + (CELL_SIZE - SIGN_SIZE) / 2, playerTurn == 1 ? hIconP1 : hIconP2, SIGN_SIZE, SIGN_SIZE, 0, nullptr, DI_NORMAL); } MessageBox( hWnd, winner == 1 ? L"Player 1 is the Winner!!!" : L"Player 2 is the Winner!!!", L"!!!Win!!!", MB_OK | MB_ICONINFORMATION); playerTurn = 0; } else if (winner == -1) { // It's a Tie!!! ShowTurn(hWnd, hdc); // Display turn MessageBox( hWnd, L"It's a Tie!!!", L"!!!Tie!!!", MB_OK | MB_ICONEXCLAMATION); playerTurn = 0; } else { playerTurn = playerTurn == 1 ? 2 : 1; ShowTurn(hWnd, hdc); // Display turn } } } ReleaseDC(hWnd, hdc); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rcClient; if (GetClientRect(hWnd, &rcClient)) { // Draw Player 1 and Player 2 text static const WCHAR player1Text[] = L"Player 1"; static const WCHAR player2Text[] = L"Player 2"; SetBkMode(hdc, TRANSPARENT); // transparent text background SetTextColor(hdc, colorPlayer1Text); // set text color to red for Player 1 TextOut(hdc, 16, 16, player1Text, std::size(player1Text)); SetTextColor(hdc, colorPlayer2Text); // set text color to blue for Player 2 TextOut(hdc, rcClient.right - 80, 16, player2Text, std::size(player2Text)); ShowTurn(hWnd, hdc); } RECT rc; if (GetGameBoardRect(hWnd, &rc)) { // Draw game board FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); for (unsigned int i = 0; i < 4; ++i) { // Draw vertical lines DrawLine(hdc, rc.left + CELL_SIZE * i, rc.top, rc.left + CELL_SIZE * i, rc.bottom); // Draw horizontal lines DrawLine(hdc, rc.left, rc.top + CELL_SIZE * i, rc.right, rc.top + CELL_SIZE * i); } RECT rcCell; for (unsigned int i = 0; i < std::size(gameBoard); ++i) // redraw gameBoard cells { if (gameBoard[i] && GetCellRect(hWnd, i, &rcCell)) { FillRect(hdc, &rcCell, gameBoard[i] == 1 ? hBrushP1 : hBrushP2); DrawIconEx(hdc, rcCell.left + (CELL_SIZE - SIGN_SIZE) / 2, rcCell.top + (CELL_SIZE - SIGN_SIZE) / 2, gameBoard[i] == 1 ? hIconP1 : hIconP2, SIGN_SIZE, SIGN_SIZE, 0, nullptr, DI_NORMAL); } } if (winner > 0) // redraw winning gameBoard cells { for (int winIndex : wins) { GetCellRect(hWnd, winIndex, &rcCell); FillRect(hdc, &rcCell, hBrushPWin); DrawIconEx(hdc, rcCell.left + (CELL_SIZE - SIGN_SIZE) / 2, rcCell.top + (CELL_SIZE - SIGN_SIZE) / 2, winner == 1 ? hIconP1 : hIconP2, SIGN_SIZE, SIGN_SIZE, 0, nullptr, DI_NORMAL); } } } EndPaint(hWnd, &ps); } break; case WM_DESTROY: DeleteObject(hBrushP1); DeleteObject(hBrushP2); //DeleteObject(hBrushPWin); DeleteObject(hIconP1); DeleteObject(hIconP1); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }