會有這個程式,是因為聽一位朋友說,他光是寫井字遊戲的AI就用掉近千行。
然後當時我就宣稱不用一百行,我就能做出含視窗介面、AI 和流程控制的井字遊戲。
後來當然寫出來了,事實上大概有七成都是寫 GUI 跑不掉的程式碼,其餘是遊戲流程控制,只有藍色的部分是井字遊戲的 AI。

這種程式碼純粹為了好玩,完全不值得學習。小朋友請勿模仿,叔叔是有練過的

編譯時記得要開 Win32 專案


#include <windows.h>
#include <tchar.h>

int AI(unsigned bb1, unsigned bb2, unsigned& mv, int d) {
   const unsigned link3[8] = {7, 56, 448, 73, 146, 292, 84, 273};
   unsigned em = ~(bb1 | bb2) & 511u, i, u;
   int s = -d-1, t;
   for (i = 0; i < 8; ++i)
      if (link3[i] == (bb2 & link3[i])) return s;
   if (em == 0 || d == 0) return 0;
   for (; em && (u = em & -em); em ^= u) 
      ((t = -AI(bb2, bb1 | u, i, d-1)) > s) && (s = t, mv = u);
   return s;
}

const TCHAR* symbol[2] = {_T("O"), _T("X")};
const TCHAR* outcome[] = {_T("You LOSE!!"), _T("Draw Game!!"), _T("@@")};
HWND hWnd, hBtn[9];
unsigned bb[2] = {0}, side = 0;

LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
   unsigned idx = LOWORD(wParam) - 101, mv;
   if (msg == WM_COMMAND && 0 <= idx && idx < 9) {
      if ((mv = 1u << idx) & ~(bb[0] | bb[1])) {
         bb[side] |= mv;
         SetWindowText(hBtn[idx], symbol[side]);
         int val = -AI(bb[side^1], bb[side], mv=0, 10);
         switch (mv != 0) {
         case 1:
            bb[side^1] |= mv;
            for (idx = 0; mv>>=1; idx++);
            SetWindowText(hBtn[idx], symbol[side^1]);
            val = AI(bb[side], bb[side^1], mv=0, 1);
            if (mv) break;
         default:
            MessageBox(hWnd, outcome[((val>0)-(val<0))+1], _T("Game"), MB_OK);
            for (int i = 0; i < 9; ++i) 
               SetWindowText(hBtn[i], _T(" "));
            if ((bb[1] = 0) || (bb[0] = (side^=1)))
               SetWindowText(hBtn[0], symbol[side^1]);            
         }
      }
   } else if (msg == WM_DESTROY) {
      PostQuitMessage(0);
   }
   return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR arg, int mode) {
   MSG msg;
   WNDCLASSEX wincls = {sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW, 
      &WinProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
      LoadCursor(NULL, IDC_ARROW), (HBRUSH) (COLOR_BTNFACE+1), 0,
      _T("OOXX"),LoadIcon(NULL, IDI_APPLICATION)};

   if (!RegisterClassEx(&wincls)) return 0;
   
   hWnd = CreateWindowEx(0, _T("OOXX"), _T("Game"),
         (WS_OVERLAPPEDWINDOW|WS_VISIBLE), 
         CW_USEDEFAULT, CW_USEDEFAULT, 40*4, 40*5,
         HWND_DESKTOP, NULL, hInst, NULL);

   for (int i = 0; i < 9; ++i) {
      hBtn[i] = CreateWindowEx(0, _T("button"), _T(" "), 
            BS_FLAT |WS_CHILD|WS_VISIBLE, 
            (i/3)*40+16, (i%3)*40+16, 40, 40, 
            hWnd, HMENU(101+i), hInst, 0);
   }

   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   return msg.wParam;
}

novus 發表在 痞客邦 PIXNET 留言(4) 人氣()


留言列表 (4)

發表留言
  • damody
  • 真的有練過= =a
    問一下,請問你是先寫清淅版再改成短碼,還是直接寫短碼呀?
  • 一開始就直接寫短
    不過並不是像現在這個樣子
    依賴副作用的部分大都是後來幾個循環再改出來的

    我還在想如果不用win32 api能不能寫短一點?
    我猜 wxWidgets 是不太可能


    novus 於 2010/07/03 10:47 回覆

  • novus
  • 剛發現一個bug,然後又想到一件事

    那就是反正使用者不可能會贏
    所以把這部分的判斷拿掉應該可以在省個5、6行吧
  • 悄悄話
  • 季寶貝
  • 我想請問一下,AI的部分看不懂可以解說一下嗎?