Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 9 của 9
  1. #1
    Ngày tham gia
    Sep 2015
    Đang ở
    Số 60 Thái Hà, Đống Đa, Hà Nội
    Bài viết
    0

    [Game Advanced Tetris và mã nguồn] - Những vấn đề xoay quanh CFW, API, MFC, CLR

    Khi bắt đầu đề tài này mình cũng mất đôi ba ngày tự trả lời cho các câu hỏi :
    _ Có nên thực hiện không khi trên DĐ cũng đã có các bạn khác thực hiện rồi dù bằng ngôn ngữ khác.
    _ Có phải vì các câu hỏi của các bạn khác mà vội vàng đưa ra đề tài chẳng có gì là mới mẻ.
    _ Nếu thực hiện, có phải là mình xem thường khả năng của các bạn khác.
    _ Cuối cùng, có thực sự giúp ích gì cho các bạn khác không.

    Sau gần một tuần đắn đo, mình quyết định sẽ khởi động dự án Advanced Tetris vì các ý sau :
    _ Thực tập lại cách viết mã theo tinh thần văn ôn võ luyện.
    _ Không trả lời trực tiếp các thắc mắc, mà để các bạn đã có câu hỏi trước đó tự tham khảo và áp dụng cho bài toán của riêng mình.
    _ Với các bạn đã đi trước, luôn mong muốn các góp ý trên tinh thần xây dựng. Nguồn động lực chính để mình viết và học hỏi là những kỹ thuật mới, những tư duy logic về thuật toán và về cấu trúc dữ liệu - Đây mới là cái đáng để quan tâm nhất. Một chương trình thì chỉ là một chương trình, ai viết cũng được, nhưng để bắt kịp những ý tưởng tiến bộ, thì một ngày đường ta sẽ học được một sàng khôn. Mong chờ những ý kiến xây dựng của các bạn.
    _ Bài viết hay dự án này không hề là khuôn mẫu, nó chỉ là tập hợp những kỹ thuật mình đã làm qua vì mục đích cơm áo gạo tiền, nó bừa bộn; nó lộn xộn; thậm chí nó có thể là một mớ hỗn độn những khai báo; những lớp; những hàm không đâu vào đâu. Mình sẽ cố gắng viết sao cho dễ hiểu nếu được, viết sao cho các bạn ở mức sơ khởi cũng có thể hiểu và có thể xây dựng các ứng dụng của riêng mình.

    Những thay đổi giữa Advanced Tetris so với Classic Tetris:
    • Cho phép người chơi chuyển bật cách chơi : Cổ điển - Cải tiến.Trong cách chơi cổ điển thì chỉ có khối gạch 4 viên gạch không có gì khác mà bạn đã từng chơi. Dùng các khối gạch có từ 1 đến 5 viên gạch trong cách chơi cải tiến.Trong cải tiến khối gạch chỉ có 1 viên gạch duy nhất sẽ có thêm chức năng cứu nguy - có khả năng di chuyển xuyên qua các viên gạch khác và điền vào lỗ trống.


    Bên dưới là phác thảo khung chơi và các kích thước cần để ý.

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Cấu tạo của các khối gạch
    Với số viên gạch trong khối gạch là từ 1 - 5, chúng ta sẽ có tất cả 28 kiểu khối gạch, trong mỗi kiểu sẽ có 1 hoặc 2 hoặc 4 bề mặt thể hiện.
    Thường thì chúng ta nghĩ ngay tới kiểu đặt một biến mảng tọa độ ô cho 1 khối gạch. Ví dụ như khối gạch chữ T có 4 ô có thể đặt như :

    POINT ptCellT[4] = { {x0,y0}, {x1,y1}, {x2,y2}, {x3,y3} };
    int iIndexRotate = 1;
    COLORREF crColor = RGB( rand()&255, rand()&255, rand()&255 );

    Cách đặt biến mảng như vậy không có gì sai cả, tuy nhiên sau này sẽ rất khó khăn khi đi vào tính toán, chúng ta sẽ quay lại ý này khi vào thực tế.
    Cách mình dùng ở đây là tạo mặt nạ cho tất cả các khối gạch và các mặt nạ xoay có thể có trong trò chơi.
    Các mặt nạ đều có cùng kích thước ngang dọc đều là một hằng, hình 2 phía bên dưới là mô tả cho 2 kiểu gạch



    Do chức năng Upload đang trục trặc nên chưa thể đưa mã nguồn lên, tạm thời mình phân tích dần dần, mã bằng chữ nên chắc chẳng thú vị gì nhỉ.

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Tạo khung cho ứng dụng
    Chắc các bạn đã từng chơi qua game dò mìn của Windows, có thủ thuật nhỏ mà mình muốn làm theo, đó là thay đổi kích thước cửa sổ bất kỳ khi nào người chơi thay đổi ma trận chơi.

    Tất cả các dự án mình đều viết trên VS2013, các bạn viết trên IDE khác thì có thể gặp sai khác cấu hình, nếu có thì các bạn nêu lên chúng ta cùng xem.

    Tạo khung cho các bạn chỉ học C:
    1. Khởi động VS2013.
    2. File -> New -> Project...
    3. Chọn Visual C++, chọn Empty Project
    4. Dặt tên cho dự án và nơi lưu trữ. Nhấn OK.
    5. Chọn tab Solution Explorer
    6. Nhấn chuột phải vào tên dự án -> Add -> New Item...
    7. Chọn Header File (.h)
    8. Nếu cần thì thay đổi tên tập tin .h xong rồi nhấn Add. IDE tạo ra tập tin .h và hiển thị để sẵn sàng soạn thảo.

    Làm lại từ bước 6 và trong bước 7 chọn C++ File (.cpp), đổi tên tập tin .cpp nếu cần. Nhấn Add

    Làm lại từ 6, mở rộng Visual C++ nếu nó chưa mở, chọn Resource, khung kế bên chọn Resource File (.rc), đổi tên tập tin nếu cần. Nhấn Add, IDE tạo cho chúng ta 2 tập tin .rc và .h

    Mình chọn tên cho 3 tập tin theo gợi ý của IDE là : Header.h và Source.cpp và Resource.rc. Resource.h đã được IDE thêm vào. Đây là 4 tập tin chính cho dự án.

    Tạo tài nguyên Icon cho dự án :
    1. Chọn tab Resource View
    2. Nhấn chuột phải vào Resource.rc -> Add Resource...
    3. Chọn Icon -> New.
    4. Nếu cần thì vẽ lại các Icon với các kích thước khác nhau, nhấn chuột phải vào định danh của icon rồi bật cửa sổ thuộc tính, thay định danh thành IDI_ICON_TETRIS.
    Tạo tài nguyên phím tắt cho dự án :
    1. Chọn tab Resource View
    2. Nhấn chuột phải vào Resource.rc -> Add Resource...
    3. Chọn Accelerator -> New, thay đổi định danh thành IDR_ACCEL_TETRIS
    4. Lần lượt tạo ra 6 phím tắt :
    _ ID : IDC_LEFT_ARROW, IDC_RIGHT_ARROW, IDC_UP_ARROW, IDC_DOWN_ARROW, IDC_ENTER, IDC_SPACE
    _ Modifier : None, None, None, None, None, None
    _ Key : VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_RETURN, VK_SPACE
    _ Type : VIRTKEY, VIRTKEY, VIRTKEY, VIRTKEY, VIRTKEY, VIRTKEY
    Tạo tài nguyên Bitmap để vẽ các nút nhấn:
    1. Sưu tầm hình vẽ button và chỉnh sửa tới kích thước 120x30 lưu thành tập tin Bitmap trong thư mục của dự án.
    2. Chọn tab Resource View
    3. Nhấn chuột phải vào Resource.rc -> Add Resource...
    4. Chọn Bitmap -> Import.
    5. điều hướng tới tập tin Bitmap ở trên -> Open
    6. Thay định danh thành IDB_BITMAP_BUTTON

    Khi chúng ta soạn thảo tài nguyên có thể IDE nhầm lẫn nên chúng ta phải qua bước kiểm tra tập tin Resource.h. Mở nó lên nếu không đúng hay bị chồng lấp thì sửa lại theo mã :

    Mã:
    //{{NO_DEPENDENCIES}}// Microsoft Visual C++ generated include file.// Used by Resource.rc//#define IDI_ICON_TETRIS                 101#define IDR_ACCEL_TETRIS                102#define IDB_BITMAP_BUTTON               103#define IDC_LEFT_ARROW                  40001#define IDC_RIGHT_ARROW                 40002#define IDC_UP_ARROW                    40003#define IDC_DOWN_ARROW                  40004#define IDC_ENTER                       40005#define IDC_SPACE                       40006 // Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        104#define _APS_NEXT_COMMAND_VALUE         40007#define _APS_NEXT_CONTROL_VALUE         1001#define _APS_NEXT_SYMED_VALUE           101#endif#endif
    Soạn thảo cho Header.h

    Mã:
    #include<Windows.h>#include<time.h>#include"resource.h"#define     APPNAME     (TEXT("Advanced Tetris"))                   // Tên lớp, tiêu đề#define     MYSUBKEY    (TEXT("SOFTWARE\\MHoang\\TetrisCFW"))       // Khóa con chứa dữ liệu thiết lập trước #define     NUMCOLS     15                                          // Số cột mặc định là 15#define     NUMROWS     20                                          // Số dòng mặc định là 20#define     CELLSIZE    30                                          // Kích thước cạnh 1 viên gạch mặc định là 30 pixels#define     ADVANCED    TRUE                                        // Chế độ chơi mặc định là cải tiến#define     DROPSPEED   1000                                        // Tốc độ rơi mặc định ban đầu của khối gạch ( 1 giây 1 lần) #define     SPACESIZE   40                                          // Khoảng cách dùng để canh chỉnh độ thưa dày giữa các đối tượng trên màn hình#define     CXCONTROL   120                                         // Chiều rộng các điều khiển#define     CYCONTROL   30                                          // Chiều cao các điều khiển button#define     NUMBLOCKS   28                                          // Số kiểu khối gạch#define     NEXTSIZE    20                                          // Kích thước cạnh của viên gạch vẽ trong khung chờ #define     ID_TIMER_DROP       1                                   // Định danh bộ định thời cho các khối gạch đi xuống #define     IDC_BUTTON_BOARD    1001#define     IDC_BUTTON_WAIT     1002#define     IDC_BUTTON_OPTIONS  1003#define     IDC_BUTTON_PAUSE    1004#define     IDC_BUTTON_START    1005 typedef struct{    int         cNumCols;       // Số cột    int         cNumRows;       // Số dòng    int         nCellSize;      // Kích thước cạnh viên gạch - cũng là kích thước ô trên ma trận chơi.} DOCUMENT;typedef DOCUMENT* PDOCUMENT; typedef struct{    HWND    hbtBoard;           // Khung chơi chính    HWND    hbtWait;            // Khối gạch chờ    HWND    hbtOptions;         // Nút nhấn Options    HWND    hbtPause;           // Nut nhấn Pause    HWND    hbtStart;           // Nút nhấn Start} CONTROLS;typedef CONTROLS* PCONTROLS; typedef struct{    HBITMAP hbmBackGround;      // Bitmap vẽ nền cửa sổ chính    HBITMAP hbmBackBoard;       // Bitmap vẽ nền cửa sổ hoạt động    HBITMAP hbmControl;         // Bitmap vẽ nền các nút nhấn} IMAGES;typedef IMAGES* PIMAGES;
    Mã tạm thời cho Source.cpp

    Mã:
    #include"Header.h" void ReadDwordFromRegistry(LPCTSTR pValueName, LPDWORD pdwValue){    DWORD       dw = REG_DWORD;    DWORD       dwSize = sizeof(DWORD);    RegGetValue(HKEY_CURRENT_USER, MYSUBKEY, pValueName, KEY_READ, &dw, pdwValue, &dwSize);}void WriteDwordToRegistry(LPCTSTR pValueName, LPDWORD pdwValue){    HKEY        hkey;    DWORD       dw;    if (RegCreateKeyEx(HKEY_CURRENT_USER, MYSUBKEY, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &dw) == ERROR_SUCCESS)    {        RegSetValueEx(hkey, pValueName, 0, REG_DWORD, (LPBYTE)pdwValue, sizeof(DWORD));        RegCloseKey(hkey);    }}void ReadStringFromRegistry(LPCTSTR pValueName, LPTSTR szString){    DWORD       dw = REG_SZ;    DWORD       dwSize = MAX_PATH;    RegGetValue(HKEY_CURRENT_USER, MYSUBKEY, pValueName, KEY_READ, &dw, (LPBYTE)szString, &dwSize);}void WriteStringToRegistry(LPCTSTR pValueName, LPCTSTR szString){    HKEY        hkey;    DWORD       dw;    if (RegCreateKeyEx(HKEY_CURRENT_USER, MYSUBKEY, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &dw) == ERROR_SUCCESS)    {        RegSetValueEx(hkey, pValueName, 0, REG_SZ, (LPBYTE)szString, strlen(szString) + 1);        RegCloseKey(hkey);    }}void LoadSetupFromRegistry(PDOCUMENT pDocument){    ReadDwordFromRegistry(TEXT("NumCols"), (PDWORD)&pDocument->cNumCols);    ReadDwordFromRegistry(TEXT("NumRows"), (PDWORD)&pDocument->cNumRows);    ReadDwordFromRegistry(TEXT("CellSize"), (PDWORD)&pDocument->nCellSize);}void SaveSetupToRegistry(PDOCUMENT pDocument){    WriteDwordToRegistry(TEXT("NumCols"), (PDWORD)&pDocument->cNumCols);    WriteDwordToRegistry(TEXT("NumRows"), (PDWORD)&pDocument->cNumRows);    WriteDwordToRegistry(TEXT("CellSize"), (PDWORD)&pDocument->nCellSize);} void CalculateBoardSize(PDOCUMENT pDocument, int & cxBoardWindow, int & cyBoardWindow){    RECT    r = { 0, 0, pDocument->cNumCols * pDocument->nCellSize, pDocument->cNumRows * pDocument->nCellSize };     AdjustWindowRectEx(&r, 0, FALSE, WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME);    cxBoardWindow = r.right - r.left;    cyBoardWindow = r.bottom - r.top;}void CalculateWindowSize(int & cxWindow, int & cyWindow, int & cxClient, int & cyClient){    RECT    r = { 0, 0, cxClient, cyClient };     AdjustWindowRect(&r, WS_CAPTION, FALSE);    cxWindow = r.right - r.left;    cyWindow = r.bottom - r.top;}void CalculateWorkArea(int & cxWork, int & cyWork){    RECT    r;     SystemParametersInfo(SPI_GETWORKAREA, NULL, &r, NULL);    cxWork = r.right - r.left;    cyWork = r.bottom - r.top;} void DrawCells(HDC hdcMem, PDOCUMENT pDocument){}void DrawBlockDrop(HDC hdcMem, PDOCUMENT pDocument){}void DrawButtonBoard(LPDRAWITEMSTRUCT pd, HBITMAP hbmBoard, PDOCUMENT pDocument){    HDC     hdcMem, hdcImage;    HBITMAP hbm;    int     cxClientBoard, cyClientBoard;     hdcMem = CreateCompatibleDC(pd->hDC);    hdcImage = CreateCompatibleDC(pd->hDC);    cxClientBoard = pd->rcItem.right - pd->rcItem.left;    cyClientBoard = pd->rcItem.bottom - pd->rcItem.top;    hbm = CreateCompatibleBitmap(pd->hDC, cxClientBoard, cyClientBoard);    SelectObject(hdcMem, hbm);    SelectObject(hdcImage, hbmBoard);    BitBlt(hdcMem, 0, 0, cxClientBoard, cyClientBoard, hdcImage, 0, 0, SRCCOPY);    DeleteDC(hdcImage);    // Vẽ lên hdcMem tất cả các viên gạch và khối gạch đang hoạt động ở đây    DrawCells(hdcMem, pDocument);    DrawBlockDrop(hdcMem, pDocument);    // Xong    BitBlt(pd->hDC, 0, 0, cxClientBoard, cyClientBoard, hdcMem, 0, 0, SRCCOPY);    DeleteObject(hbm);    DeleteDC(hdcMem);}void DrawButtonNext(LPDRAWITEMSTRUCT pd, PDOCUMENT pDocument){    HDC     hdcMem;    HBITMAP hbm;    int     cxClientWait, cyClientWait;     hdcMem = CreateCompatibleDC(pd->hDC);    cxClientWait = pd->rcItem.right - pd->rcItem.left;    cyClientWait = pd->rcItem.bottom - pd->rcItem.top;    hbm = CreateCompatibleBitmap(pd->hDC, cxClientWait, cyClientWait);    SelectObject(hdcMem, hbm);    // Tô màu    FillRect(hdcMem, &pd->rcItem, (HBRUSH)GetStockObject(GRAY_BRUSH));    // Vẽ khối gạch chờ lên ở đây    // Vẽ từ DC ảo lên DC button    BitBlt(pd->hDC, 0, 0, cxClientWait, cyClientWait, hdcMem, 0, 0, SRCCOPY);    DeleteObject(hbm);    DeleteDC(hdcMem);}void DrawButtonOptions(LPDRAWITEMSTRUCT pd, HBITMAP hbmControl, PDOCUMENT pDocument){    HDC     hdcMem;     hdcMem = CreateCompatibleDC(pd->hDC);    SelectObject(hdcMem, hbmControl);    BitBlt(pd->hDC, 0, 0, pd->rcItem.right - pd->rcItem.left, pd->rcItem.bottom - pd->rcItem.top, hdcMem, 0, 0, SRCCOPY);    DeleteDC(hdcMem);    // Vẽ chữ     SetTextColor(pd->hDC, RGB(0, 0, 255));    SetBkMode(pd->hDC, TRANSPARENT);    DrawText(pd->hDC, TEXT("Options ..."), -1, &pd->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);}void DrawButtonPause(LPDRAWITEMSTRUCT pd, HBITMAP hbmControl, PDOCUMENT pDocument){    HDC     hdcMem;     hdcMem = CreateCompatibleDC(pd->hDC);    SelectObject(hdcMem, hbmControl);    BitBlt(pd->hDC, 0, 0, pd->rcItem.right - pd->rcItem.left, pd->rcItem.bottom - pd->rcItem.top, hdcMem, 0, 0, SRCCOPY);    DeleteDC(hdcMem);    // Vẽ chữ     SetTextColor(pd->hDC, RGB(0, 0, 255));    SetBkMode(pd->hDC, TRANSPARENT);    DrawText(pd->hDC, TEXT("Pause"), -1, &pd->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);}void DrawButtonStart(LPDRAWITEMSTRUCT pd, HBITMAP hbmControl, PDOCUMENT pDocument){    HDC     hdcMem;     hdcMem = CreateCompatibleDC(pd->hDC);    SelectObject(hdcMem, hbmControl);    BitBlt(pd->hDC, 0, 0, pd->rcItem.right - pd->rcItem.left, pd->rcItem.bottom - pd->rcItem.top, hdcMem, 0, 0, SRCCOPY);    DeleteDC(hdcMem);    // Vẽ chữ     SetTextColor(pd->hDC, RGB(0, 0, 255));    SetBkMode(pd->hDC, TRANSPARENT);    DrawText(pd->hDC, TEXT("Start"), -1, &pd->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);} void OnCreate(HINSTANCE hInstance, HWND hParent, PDOCUMENT pDocument, PCONTROLS pControls, PIMAGES pImages){    int     cxBoardClient, cyBoardClient, cxBoardWindow, cyBoardWindow;    int     cxClient, cyClient, cxWindow, cyWindow;    int     cxWork, cyWork;     // Tạo ra các cửa sổ con    pControls->hbtBoard = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME, TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hParent, (HMENU)IDC_BUTTON_BOARD, hInstance, NULL);    pControls->hbtWait = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME, TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hParent, (HMENU)IDC_BUTTON_WAIT, hInstance, NULL);    pControls->hbtOptions = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hParent, (HMENU)IDC_BUTTON_OPTIONS, hInstance, NULL);    pControls->hbtPause = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hParent, (HMENU)IDC_BUTTON_PAUSE, hInstance, NULL);    pControls->hbtStart = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hParent, (HMENU)IDC_BUTTON_START, hInstance, NULL);    // Tạo tài liệu mặc định    srand((unsigned int)time(0));    pDocument->cNumCols = NUMCOLS;    pDocument->cNumRows = NUMROWS;    pDocument->nCellSize = CELLSIZE;    // Nạp chồng tài liệu từ Registry    LoadSetupFromRegistry(pDocument);    // Tính toán các kích thước của Board    cxBoardClient = pDocument->cNumCols * pDocument->nCellSize;    cyBoardClient = pDocument->cNumRows * pDocument->nCellSize;    CalculateBoardSize(pDocument, cxBoardWindow, cyBoardWindow);    // Tính toán các kích thước của cửa sổ chính    cxClient = SPACESIZE + cxBoardWindow + SPACESIZE + CXCONTROL + SPACESIZE;    cyClient = SPACESIZE + cyBoardWindow + SPACESIZE;    CalculateWindowSize(cxWindow, cyWindow, cxClient, cyClient);    // Tính toán các kích thước vùng làm việc    CalculateWorkArea(cxWork, cyWork);    // Di chuyển tất cả các cửa sổ    MoveWindow(hParent, (cxWork - cxWindow) / 2, (cyWork - cyWindow) / 2, cxWindow, cyWindow, FALSE);    MoveWindow(pControls->hbtBoard, SPACESIZE, SPACESIZE, cxBoardWindow, cyBoardWindow, FALSE);    MoveWindow(pControls->hbtWait, SPACESIZE + cxBoardWindow + SPACESIZE, SPACESIZE, CXCONTROL, CXCONTROL, FALSE);    MoveWindow(pControls->hbtOptions, SPACESIZE + cxBoardWindow + SPACESIZE, SPACESIZE + cyBoardWindow - 110, CXCONTROL, CYCONTROL, FALSE);    MoveWindow(pControls->hbtPause, SPACESIZE + cxBoardWindow + SPACESIZE, SPACESIZE + cyBoardWindow - 70, CXCONTROL, CYCONTROL, FALSE);    MoveWindow(pControls->hbtStart, SPACESIZE + cxBoardWindow + SPACESIZE, SPACESIZE + cyBoardWindow - 30, CXCONTROL, CYCONTROL, FALSE);    // Tạo ra tất cả các bitmap với kích thước vừa với cửa sổ    pImages->hbmControl = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP_BUTTON));    pImages->hbmBackBoard = (HBITMAP)LoadImage(NULL, TEXT("BackBoard.bmp"), IMAGE_BITMAP, cxBoardClient, cyBoardClient, LR_LOADFROMFILE);    pImages->hbmBackGround = (HBITMAP)LoadImage(NULL, TEXT("BackGround.bmp"), IMAGE_BITMAP, cxClient, cyClient, LR_LOADFROMFILE);}void OnDrawItem(LPDRAWITEMSTRUCT pd, PIMAGES pImages, PDOCUMENT pDocument){    switch (pd->CtlID)    {    case IDC_BUTTON_BOARD:      DrawButtonBoard(pd, pImages->hbmBackBoard, pDocument); break;    case IDC_BUTTON_WAIT:       DrawButtonNext(pd, pDocument); break;    case IDC_BUTTON_OPTIONS:    DrawButtonOptions(pd, pImages->hbmControl, pDocument); break;    case IDC_BUTTON_PAUSE:      DrawButtonPause(pd, pImages->hbmControl, pDocument); break;    case IDC_BUTTON_START:      DrawButtonStart(pd, pImages->hbmControl, pDocument); break;    }}void OnPaint(HWND hwnd, HBITMAP hbmBackGround){    HDC             hdc, hdcMem;    PAINTSTRUCT     ps;    RECT            r;     GetClientRect(hwnd, &r);    hdc = BeginPaint(hwnd, &ps);    if (hbmBackGround)    {        hdcMem = CreateCompatibleDC(hdc);        SelectObject(hdcMem, hbmBackGround);        BitBlt(hdc, 0, 0, r.right - r.left, r.bottom - r.top, hdcMem, 0, 0, SRCCOPY);        DeleteDC(hdcMem);    }    EndPaint(hwnd, &ps);}void OnClose(HWND hwnd){    if (IDYES == MessageBox(hwnd, TEXT("Are you quit ?"), APPNAME, MB_YESNO | MB_ICONQUESTION))        DestroyWindow(hwnd);}void OnDestroy(PDOCUMENT pDocument){    PostQuitMessage(0);}void OnOptionsClick(){}void OnPauseClick(){}void OnStartClick(HWND hwnd, PDOCUMENT pDocument, PCONTROLS pControls){}void OnDropTimer(){}void OnLeftArrow(PDOCUMENT pDocument, PCONTROLS pControls){}void OnRightArrow(PDOCUMENT pDocument, PCONTROLS pControls){}void OnDownArrow(PDOCUMENT pDocument, PCONTROLS pControls){}void OnUpArrow(PDOCUMENT pDocument){}void OnEnterPress(){}void OnSpacePress(){} LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam){    static  HINSTANCE   hInstance;    static  CONTROLS    Controls;    static  IMAGES      Images;    static  DOCUMENT    Document;     switch (message)    {    case WM_CREATE:        hInstance = ((LPCREATESTRUCT)lparam)->hInstance;        OnCreate(hInstance, hwnd, &Document, &Controls, &Images);        return 0;    case WM_CLOSE:      OnClose(hwnd); return 0;    case WM_DESTROY:    OnDestroy(&Document); return 0;    case WM_DRAWITEM:   OnDrawItem((LPDRAWITEMSTRUCT)lparam, &Images, &Document); return 0;    case WM_PAINT:      OnPaint(hwnd, Images.hbmBackGround); return 0;    case WM_TIMER:        switch (wparam)        {        case ID_TIMER_DROP:            OnDropTimer();            break;        }        return 0;    case WM_COMMAND:        switch (LOWORD(wparam))        {        case IDC_BUTTON_OPTIONS:            if (HIWORD(wparam) == BN_CLICKED)                OnOptionsClick();            return 0;        case IDC_BUTTON_PAUSE:            return 0;        case IDC_BUTTON_START:            if (HIWORD(wparam) == BN_CLICKED)                OnStartClick(hwnd, &Document, &Controls);            return 0;        case IDC_ENTER:            OnEnterPress();            return 0;        case IDC_SPACE:            OnSpacePress();            return 0;        case IDC_LEFT_ARROW:    OnLeftArrow(&Document, &Controls); return 0;        case IDC_RIGHT_ARROW:   OnRightArrow(&Document, &Controls); return 0;        case IDC_DOWN_ARROW:    OnDownArrow(&Document, &Controls); return 0;        case IDC_UP_ARROW:      OnUpArrow(&Document); return 0;        }        break;    }    return DefWindowProc(hwnd, message, wparam, lparam);}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow){    WNDCLASS        w;    HACCEL          hAccel;    HWND            hwnd;    MSG             msg;     w.cbClsExtra = 0;    w.cbWndExtra = 0;    w.hbrBackground = NULL;    w.hCursor = LoadCursor(NULL, IDC_ARROW);    w.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON_TETRIS));    w.hInstance = hInstance;    w.lpfnWndProc = WndProc;    w.lpszClassName = APPNAME;    w.lpszMenuName = NULL;    w.style = CS_HREDRAW | CS_VREDRAW;    if (!RegisterClass(&w))    {        MessageBox(NULL, TEXT("Error : RegisterClass"), APPNAME, MB_OK | MB_ICONINFORMATION);        return 0;    }    hwnd = CreateWindow(APPNAME, APPNAME, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);    if (!hwnd)    {        MessageBox(NULL, TEXT("Error : CreateWindow"), APPNAME, MB_OK | MB_ICONINFORMATION);        return 0;    }    ShowWindow(hwnd, nShow);    UpdateWindow(hwnd);    hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCEL_TETRIS));    while (GetMessage(&msg, NULL, 0, 0))    {        if (!TranslateAccelerator(hwnd, hAccel, &msg))        {            TranslateMessage(&msg);            DispatchMessage(&msg);        }    }    return msg.wParam;}
    Nếu bạn sưu tầm và đặt một tập tin BackBoard.bmp ngay sát tập tin thực thi khi đã biên dịch thì nền của Khung hoạt động sẽ hiển thị nó.
    Tương tự nếu có BackGround.bmp thì nền cửa sổ chính sẽ được vẽ từ nó.

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Tạm gác qua phần diễn đạt thuật toán, mình đưa cái [Advanced Tetris.exe] lên các bạn chạy và kiểm lỗi giùm.
    Các bạn nhớ sưu tầm 2 tập tin "BackBoard.bmp" và "BackGround.bmp" và để sát .exe đã được bung ra cho nó bớt khô cứng nhé.
    Mã nguồn sẽ đưa lên sau khi tiếp thu ý kiến của các bạn và làm gọn lại.


  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Có phiên bản cho linux không? Tui dùng linux. [IMG]images/smilies/icon_question.gif[/IMG]

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Ổ cứng của mình đã format theo cách dồn hết vào 1 phân vùng nên không cài thêm Linux được. Tuy nhiên phần mã các giải thuật sẽ có thể giúp bạn tự chuyển sang con Chim cánh cụt, sau này có điều kiện thì mình sẽ thực hiện bổ sung. Chào bạn.

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Cấu tạo của các khối gạch - tiếp theo

    Với cấu tạo của một khối gạch như vậy, chúng ta thấy chỉ cần một số int32 là đủ nắm giữ bố cục của một bề mặt của 1 khối gạch. Do đó cỏ thể dùng mảng 4 phấn tử int32 để lưu trữ 4 bề mặt có thể có của khối gạch bất kỳ, và một hằng mảng toàn cục g_BlockImage[28][4] là có thể truy vết bất kỳ khối gạch nào.

    Mã:
    #define     NUMBLOCKS           28                      // Số kiểu khối gạchconst   int     g_BlockImage[NUMBLOCKS][4] = {    0x00001000, 0x00001000, 0x00001000, 0x00001000,     // 0: Block 1 viên duy nhất    0x00003180, 0x00003180, 0x00003180, 0x00003180,     // 1: Block 4 viên tạo hình vuông    0x00003C00, 0x00021084, 0x00003C00, 0x00021084,     // 2: Block 4 viên tạo que    0x000030C0, 0x00023100, 0x000030C0, 0x00023100,     // 3: Block 4 viên chữ Z    0x00001980, 0x00043080, 0x00001980, 0x00043080,     // 4: Block 4 viên chữ S    0x00003900, 0x00061080, 0x00013800, 0x000210C0,     // 5: Block 4 viên chữ L    0x00003840, 0x00021180, 0x00043800, 0x00031080,     // 6: Block 4 viên Chữ J    0x00003880, 0x00023080, 0x00023800, 0x00021880,     // 7: Block 4 viên chữ T    0x00023880, 0x00023880, 0x00023880, 0x00023880,     // 8: Block 5 viên chữ thập    0x00001800, 0x00001080, 0x00001800, 0x00001080,     // 9: Block 2 viên tạo que    0x00003800, 0x00021080, 0x00003800, 0x00021080,     // 10: Block 3 viên tạo que    0x00007C00, 0x00421084, 0x00007C00, 0x00421084,     // 11: Block 5 viên tạo que    0x000070C0, 0x00423100, 0x00061C00, 0x00011884,     // 12: Block 5 viên chữ Z thừa    0x00001D80, 0x00043084, 0x00037000, 0x00421840,     // 13: Block 5 viên chữ S thừa    0x00001880, 0x00003080, 0x00023000, 0x00021800,     // 14: Block 3 viên tạo góc vuông    0x00072100, 0x00070840, 0x000109C0, 0x000421C0,     // 15: Block 5 viên tạo góc vuông    0x00003D00, 0x00061084, 0x00017800, 0x004210C0,     // 16: Block 5 viên chữ L thừa    0x00007840, 0x00421180, 0x00043C00, 0x00031084,     // 17: Block 5 viên chữ J thừa    0x00071080, 0x00013840, 0x000211C0, 0x00043900,     // 18: Block 5 viên chữ T    0x000038C0, 0x00023180, 0x00063800, 0x00031880,     // 19: Block 5 viên hình vuông đính kèm trên trái    0x00003980, 0x00063080, 0x00033800, 0x000218C0,     // 20: Block 5 viên hình vuông đính kèm trên phải    0x00003C80, 0x00023084, 0x00027800, 0x00421880,     // 21: Block 5 viên Chữ T lệch trái    0x00007880, 0x00423080, 0x00023C00, 0x00021884,     // 22: Block 5 viên Chữ T lệch phải    0x000310C0, 0x00003940, 0x00061180, 0x00053800,     // 23: Block 5 viên chữ C    0x000610C0, 0x00013900, 0x000610C0, 0x00013900,     // 24: Block 5 viên Chữ Z lớn    0x00031180, 0x00043840, 0x00031180, 0x00043840,     // 25: Block 5 viên Chữ S lớn    0x000230C0, 0x00023900, 0x00061880, 0x00013880,     // 26: Block 5 viên Chữ L đeo ba lô    0x00021980, 0x00043880, 0x00033080, 0x00023840,     // 27: Block 5 viên Chữ J đeo ba lô};
    Ở đây mình đặt kiểu khối chỉ có duy nhất 1 viên ở chỉ mục 0 vì các mục đích riêng của nó, các kiểu khối gạch Classic ở sát nhau với mục đích dễ dàng tạo ngẫu nhiên khi người chơi chơi ở chế độ cổ điển. Những số nguyên viết dưới dạng thập lục có thể gây bối rối đôi chút, nên mình giải thích thêm từ đâu có nó.
    Ví dụ lấy khối gạch kiểu chữ C ở chỉ mục 23, các mặt nạ bit như sau :

    Mã:
    //  0, 0, 0, 0, 0,      // Nhị phân : 00000 00110 00100 00110 00000 = 0 0000 0011 0001 0000 1100 0000 = 0x310C0//  0, 0, 1, 1, 0,//  0, 0, 1, 0, 0,//  0, 0, 1, 1, 0,//  0, 0, 0, 0, 0, //  0, 0, 0, 0, 0,      // Nhị phân : 00000 00000 01110 01010 00000 = 0 0000 0000 0011 1001 0100 0000 = 0x3940//  0, 0, 0, 0, 0,//  0, 1, 1, 1, 0,//  0, 1, 0, 1, 0,//  0, 0, 0, 0, 0, //  0, 0, 0, 0, 0,      // Nhị phân : 00000 01100 00100 01100 00000 = 0 0000 0110 0001 0001 1000 0000 = 0x61180//  0, 1, 1, 0, 0,//  0, 0, 1, 0, 0,//  0, 1, 1, 0, 0,//  0, 0, 0, 0, 0, //  0, 0, 0, 0, 0,      // Nhị phân : 00000 01010 01110 00000 00000 = 0 0000 0101 0011 1000 0000 0000 = 0x53800//  0, 1, 0, 1, 0,//  0, 1, 1, 1, 0,//  0, 0, 0, 0, 0,//  0, 0, 0, 0, 0,
    Các bạn cũng thấy các khối gạch như 1 viên duy nhất, 4 viên hình vuông hay 5 viên chữ thập cũng đều có 4 bề mặt cho dù các bề mặt là giống nhau. Đây là cách thức rút gọn về 1 đến 2 vòng lặp lồng nhau là có thể truy xuất các "thuộc tính" của khối gạch - nó có thể rút gọn khoảng chừng 2 - 3 trang màn hình so với cách dùng switch-case như có bạn nào đó từng nêu vấn đề ở mấy ngày trước.

    Khi tạo các mặt nạ các bạn nên cẩn thận vì nó không trực quan, cái .exe ở trên chạy không đúng với bề mặt thứ 2 chữ L và bề mặt thứ 3 của 5 viên góc vuông (do sơ ý khi đánh máy).

    - - - Nội dung đã được cập nhật ngày 07-10-2015 lúc 11:21 AM - - -

    Cấu tạo các mặt nạ xoay

    Khi xoay 1 khối gạch theo 1 góc vuông 90 độ, chúng ta phải bảo đảm rằng khi quay nó sẽ không bị vướng vào các viên gạch có sẵn trên ma trận chơi. Cũng như ở trên, mình thấy tạo sẵn các mặt nạ xoay vẫn là tốt hơn đi kiểm tra từng viên trong khối. Sau đây là hằng mảng toàn cục khác lưu trữ tất cả các mặt nạ xoay của các kiểu khối gạch


    Mã:
    const   int     g_RotateMask[NUMBLOCKS][4] = {    0x00000000, 0x00000000, 0x00000000, 0x00000000,     // 0: RotateMask 1 viên duy nhất    0x00000000, 0x00000000, 0x00000000, 0x00000000,     // 1: RotateMask 4 viên tạo hình vuông    0x000600E7, 0x00042C63, 0x000600E7, 0x00042C63,     // 2: RotateMask 4 viên tạo que    0x00060100, 0x000400C0, 0x00060100, 0x000400C0,     // 3: RotateMask 4 viên chữ Z    0x00042000, 0x00000940, 0x00042000, 0x00000940,     // 4: RotateMask 4 viên chữ S    0x000600C0, 0x00012900, 0x000600C0, 0x00012900,     // 5: RotateMask 4 viên chữ L    0x00060180, 0x00052800, 0x000300C0, 0x00002940,     // 6: RotateMask 4 viên Chữ J    0x00060140, 0x00050900, 0x000500C0, 0x00012140,     // 7: RotateMask 4 viên chữ T    0x00050140, 0x00050140, 0x00050140, 0x00050140,     // 8: RotateMask 5 viên chữ thập    0x000000C0, 0x00000840, 0x000000C0, 0x00000840,     // 9: RotateMask 2 viên tạo que    0x000600C0, 0x00012900, 0x000600C0, 0x00012900,     // 10: RotateMask 3 viên tạo que    0x01CE00E7, 0x018C6C63, 0x01CE00E7, 0x018C6C63,     // 11: RotateMask 5 viên tạo que    0x01CE0100, 0x00358C00, 0x000100E7, 0x00006358,     // 12: RotateMask 5 viên chữ Z thừa    0x00042067, 0x00034318, 0x01CC0840, 0x00318580,     // 13: RotateMask 5 viên chữ S thừa    0x00002040, 0x00060100, 0x00050800, 0x000100C0,     // 14: RotateMask 3 viên tạo góc vuông    0x00000840, 0x00000180, 0x00042000, 0x00030000,     // 15: RotateMask 5 viên tạo góc vuông    0x000600E7, 0x00016B18, 0x01CE00C0, 0x0031AD00,     // 16: RotateMask 5 viên chữ L thừa    0x01CE0180, 0x0035AC00, 0x000300E7, 0x00006B58,     // 17: RotateMask 5 viên chữ J thừa    0x00002940, 0x00060180, 0x00052800, 0x000300C0,     // 18: RotateMask 5 viên chữ T    0x00060100, 0x00050800, 0x000100C0, 0x000020C0,     // 19: RotateMask 5 viên hình vuông đính kèm trên trái    0x00060040, 0x00010900, 0x000400C0, 0x00012100,     // 20: RotateMask 5 viên hình vuông đính kèm bên phải    0x00060167, 0x00054B18, 0x01CD00C0, 0x0031A540,     // 21: RotateMask 5 viên Chữ T lệch trái    0x01CE0140, 0x00358D00, 0x000500E7, 0x00016358,     // 22: RotateMask 5 viên Chữ T lệch phải    0x00002900, 0x00060080, 0x00012800, 0x000200C0,     // 23: RotateMask 5 viên chữ C    0x00012900, 0x000600C0, 0x00012900, 0x000600C0,     // 24: RotateMask 5 viên Chữ Z lớn    0x00042840, 0x00600180, 0x00042840, 0x00600180,     // 25: RotateMask 5 viên Chữ S lớn    0x00050900, 0x000500C0, 0x00012140, 0x00060140,     // 26: RotateMask 5 viên Chữ L đeo ba lô    0x00052040, 0x00030140, 0x00040940, 0x00050180,     // 27: RotateMask 5 viên Chữ J đeo ba lô};
    Mình lấy ví dụ khối có chỉ mục 16 : 5 viên hình chữ L thừa (thừa 1 viên trên đầu) để diễn tả cách tạo mặt nạ

    Mã:
    //  Bề mặt cần xoay       //  Bề mặt xoay tới       //  Mặt nạ xoay         //  Nhị phân                     Thập lục //  Face0//  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      // 00000 01100 00000 00111 00111 =  0x600E7//  0, 0, 0, 0, 0,      //  0, 1, 1, 0, 0,      //  0, 1, 1, 0, 0,      //  0, 1, 1, 1, 1,      //  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,//  0, 1, 0, 0, 0,      //  0, 0, 1, 0, 0,      //  0, 0, 1, 1, 1,//  0, 0, 0, 0, 0,      //  0, 0, 1, 0, 0,      //  0, 0, 1, 1, 1, //  Face1//  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      // 00000 00010 11010 11000 11000 =  0x16B18//  0, 1, 1, 0, 0,      //  0, 0, 0, 1, 0,      //  0, 0, 0, 1, 0,//  0, 0, 1, 0, 0,      //  1, 1, 1, 1, 0,      //  1, 1, 0, 1, 0,//  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,      //  1, 1, 0, 0, 0,//  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,      //  1, 1, 0, 0, 0, //  Face2//  0, 0, 0, 0, 0,      //  0, 0, 1, 0, 0,      //  1, 1, 1, 0, 0,      // 11100 11100 00000 00110 00000 =  0x1CE00C0//  0, 0, 0, 1, 0,      //  0, 0, 1, 0, 0,      //  1, 1, 1, 0, 0,//  1, 1, 1, 1, 0,      //  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,//  0, 0, 0, 0, 0,      //  0, 0, 1, 1, 0,      //  0, 0, 1, 1, 0,//  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0, //  Face3//  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 1, 1,      // 00011 00011 01011 01000 00000 =  0x31AD00//  0, 0, 1, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 1, 1,//  0, 0, 1, 0, 0,      //  0, 1, 1, 1, 1,      //  0, 1, 0, 1, 1,//  0, 0, 1, 1, 0,      //  0, 1, 0, 0, 0,      //  0, 1, 0, 0, 0,//  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,      //  0, 0, 0, 0, 0,
    Cho dù các viên gạch có xoay cũng như không chúng ta vẫn đưa đầy đủ các mặt nạ nhưng đặt về 0 cho chúng.
    Chú ý là không phải tất cả các bề mặt đều xoay theo chiều kim đồng hồ. Có những bề mặt như của chữ Z thì từ face0 -> face1 theo kim đồng hồ nhưng từ face1 -> face2 lại quay ngược lại, thực chất face0 và face2 đều là một.
    Cũng như các mặt nạ bề mặt, các mặt nạ xoay khi tạo ra cũng rất dễ nhầm lẫn, chưa chắc trong mảng trên mình đã đặt cờ bit đúng toàn bộ, các bạn phát hiện sai sót thì báo nhé.

    - - - Nội dung đã được cập nhật ngày 07-10-2015 lúc 03:32 PM - - -

    Cấu tạo mảng hiển thị và mảng thực tế



    Trong số rất nhiều cách làm việc với mảng, để tránh vượt quá chỉ mục mảng mà không cần kiểm tra chặt chẽ biến chạy, nhiều người thường dùng phương pháp đơn giản là bao thêm bên ngoài mảng một số các phần tử cộng thêm, khi đó mảng thực tế sẽ phình to hơn mảng ban đầu nhưng bù lại mã sẽ gọn gàng hơn.

    Trong hình trên mô tả cho ngữ cảnh : Người chơi chọn lựa khung chơi là 10x10, chương trình làm việc thực tế trên khung chơi 16x16, một khối gạch Z lúc mới đưa vào chỉ "thò một chân" và phần còn lại "còn trong bóng tối". Thiết kế theo kiểu này có người gọi là mailbox, có người gọi là lính canh - gọi là gì thì nó cũng giúp chúng ta nhiều.

    Khối gạch trong trò chơi chỉ di chuyển 3 hướng : sang phải; sang trái; xuống dưới. Khi đáp ứng các chỉ lệnh điều khiển từ người dùng, chương trình cần phải xem khối gạch có vượt ra các biên đó không nên tất cả các "lính canh" luôn luôn được đặt về 1 - Nghĩa là nơi đây đang có gạch không thể di chuyển đè lên được. Phần bên trên không cần thiết phải "canh" hết, ngay khi chỉ "thò một chân" thôi, người chơi vần di chuyển khối gạch sang trái hay phải bình thường.

    Ngoài ra trong khi đáp ứng di chuyển của người chơi, chương trình cũng phải xác định xem các di chuyển có đè qua các viên gạch đã có sẵn trong phần hiển thị không. Một lần nữa, chúng ta lại dùng mặt nạ để thao tác.

    Một khối gạch có 5x5=25 ô nên mặt nạ cũng chỉ áp đặt cho mảng con 5x5 trong mảng thực tế. Lấy lại hình trên, ta thấy viên gạch Z đang được định vị với 4 ô có màu cam, nhưng thực chất mặt nạ của nó đang chiếm trọn ma trận từ hàng 0->4 và từ cột 6->10, nếu cho nó di chuyển xuống 1 hàng thì mặt nạ của nó nằm từ hàng 1->5 và cột vẫn giữ nguyên. Từ đây chúng ta chỉ cần nhận mặt nạ từ hàng 1->5 và cột từ 6->10 của mảng thực, sau đó một phép toán thao tác Bit & là biết ngay di chuyển được hay không.

    Bên dưới là các mặt nạ cho từng ô chính xác trong từng khối gạch hay trong một mảng 5x5 xác định của mảng thực

    Mã:
    #define     CELLSPERBLOCK       25                      // Số ô trên 1 khốiconst   int     g_BrickMask[CELLSPERBLOCK] = {    0x01000000,     // 0000 0001 0000 0000 0000 0000 0000 0000 - Mặt nạ ô 0    0x00800000,     // 0000 0000 1000 0000 0000 0000 0000 0000 - Mặt nạ ô 1    0x00400000,     // 0000 0000 0100 0000 0000 0000 0000 0000 - Mặt nạ ô 2    0x00200000,     // 0000 0000 0010 0000 0000 0000 0000 0000 - Mặt nạ ô 3    0x00100000,     // 0000 0000 0001 0000 0000 0000 0000 0000 - Mặt nạ ô 4    0x00080000,     // 0000 0000 0000 1000 0000 0000 0000 0000 - Mặt nạ ô 5    0x00040000,     // 0000 0000 0000 0100 0000 0000 0000 0000 - Mặt nạ ô 6    0x00020000,     // 0000 0000 0000 0010 0000 0000 0000 0000 - Mặt nạ ô 7    0x00010000,     // 0000 0000 0000 0001 0000 0000 0000 0000 - Mặt nạ ô 8    0x00008000,     // 0000 0000 0000 0000 1000 0000 0000 0000 - Mặt nạ ô 9    0x00004000,     // 0000 0000 0000 0000 0100 0000 0000 0000 - Mặt nạ ô 10    0x00002000,     // 0000 0000 0000 0000 0010 0000 0000 0000 - Mặt nạ ô 11    0x00001000,     // 0000 0000 0000 0000 0001 0000 0000 0000 - Mặt nạ ô 12    0x00000800,     // 0000 0000 0000 0000 0000 1000 0000 0000 - Mặt nạ ô 13    0x00000400,     // 0000 0000 0000 0000 0000 0100 0000 0000 - Mặt nạ ô 14    0x00000200,     // 0000 0000 0000 0000 0000 0010 0000 0000 - Mặt nạ ô 15    0x00000100,     // 0000 0000 0000 0000 0000 0001 0000 0000 - Mặt nạ ô 16    0x00000080,     // 0000 0000 0000 0000 0000 0000 1000 0000 - Mặt nạ ô 17    0x00000040,     // 0000 0000 0000 0000 0000 0000 0100 0000 - Mặt nạ ô 18    0x00000020,     // 0000 0000 0000 0000 0000 0000 0010 0000 - Mặt nạ ô 19    0x00000010,     // 0000 0000 0000 0000 0000 0000 0001 0000 - Mặt nạ ô 20    0x00000008,     // 0000 0000 0000 0000 0000 0000 0000 1000 - Mặt nạ ô 21    0x00000004,     // 0000 0000 0000 0000 0000 0000 0000 0100 - Mặt nạ ô 22    0x00000002,     // 0000 0000 0000 0000 0000 0000 0000 0010 - Mặt nạ ô 23    0x00000001,     // 0000 0000 0000 0000 0000 0000 0000 0001 - Mặt nạ ô 24};
    và mã để nhận mặt nạ Cell sẽ có dạng : Giả sử pCell là con trỏ tới mảng thực 2 chiều, thuộc tính bBrick là có gạch hay không.

    Mã:
        int     i, j, n = 0, iMask = 0;     for (i = iRow; i < iRow + 5; i++)    for (j = iCol; j < iCol + 5; j++)    {        if (pCell[i][j].bBrick)            iMask |= g_BrickMask[n];        n++;    }    return iMask;
    Phép toán |= sẽ dựng một cờ Bit tương ứng với vị trí ô vào mặt nạ.
    Và bên dưới là mã xem có di chuyển được không sẽ có dạng

    Mã:
        if ( ! (iMask & matnakhoigach) )                            // Không có sự chồng lấp - có thể di chuyển xuống dưới    {        // Thuộc tính dòng của khối gạch ++;        // Cập nhật màn hình    }
    Dùng các mặt nạ và các toán hạng thao tác bit có thể gây nhức đầu cho các bạn mới, nhưng nếu quen với nó, bạn sẽ lại không muốn dùng các cách khác đâu, tin mình đi !

  8. #8
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bên dưới là mã nguồn dự án Advanced Tetris được viết theo OOP kết hợp với API, mời các bạn chạy thử.

  9. #9
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Cảm ơn bạn về bài viết nó rất hay... mình đang cần đến nó.

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •