# Linux 核心專題: 重作 kxo > 執行人: qianzsh > [專題解說錄影](https://youtu.be/paZvdJajvqc) ## 任務描述 以[第三份作業](https://hackmd.io/@sysprog/linux2025-kxo)為基礎、搭配課堂討論和觀摩學員的成果,重新投入 kxo 開發。過程中可參照其他學員的成果 (但要實驗和指出其缺失),務必清楚標示。 ## TODO: 重作 kxo #### 利用位元遮罩判斷 4 x 4 棋局的勝負 ```c static const uint32_t move_masks[16] = { 0x80008000, 0x40000800, 0x20000080, 0x10000008, 0x08004000, 0x04000400, 0x02000040, 0x01000004, 0x00802000, 0x00400200, 0x00200020, 0x00100002, 0x00081000, 0x00040100, 0x00020010, 0x00010001, }; ``` 用十六進位 32 位元無號整數來表示,由最高位元開始依序往下會對應到的是十六宮格中由上到下、由左到右的橫一、橫二、橫三、橫四、直一、直二、直三、直四,而一組 nibble 中的 4 個位元則代表了該位置是否已經被放置。以左上角第一個位置做說明,對於橫一、直一來講,就會是 1000 ,其餘皆為 0000 ,因此整體就會是 0x80008000 。 ```c #define ROW_COL(n) ( ((n) & ((n)>>1) & ((n)>>2) & ((n)>>3)) & 1u ) ``` 此段程式碼是用來決定是否已經存在一方獲勝的狀態,也就是上述八種可能有其中一種被填滿,舉例來說,如果是橫二被填滿,那當下的狀態應為 0x0f004444 ,換句話說,被填滿的那條線必定會出現 f(0b1111) 這個數字,因此我對一組 nibble 的每個 bit 做 bitwise-AND 運算,若為 1 則代表有存在著連線,否則會 return 0 ,值得注意的是以上設計的遮罩與獲勝的判斷皆未考慮右斜和左斜 。 ```c #define CHECK_WIN(addbits, check) do { \ uint32_t _b = (addbits); \ (check) |= ((_b & 0x84218421) == 0x84218421); \ (check) |= ((_b & 0x12481248) == 0x12481248); \ (check) |= ROW_COL((_b >> 28) & 0xF); \ (check) |= ROW_COL((_b >> 24) & 0xF); \ (check) |= ROW_COL((_b >> 20) & 0xF); \ (check) |= ROW_COL((_b >> 16) & 0xF); \ (check) |= ROW_COL((_b >> 12) & 0xF); \ (check) |= ROW_COL((_b >> 8) & 0xF); \ (check) |= ROW_COL((_b >> 4) & 0xF); \ (check) |= ROW_COL((_b >> 0) & 0xF); \ } while (0) ``` 由於設計的位元遮罩其中任意的兩個數值加總不會有進位,舉例來說落子在棋盤的左上與右上,位元遮罩的加總為 0x80008000 + 0x10000008 = 0x90008008 ,把每個 bit 單獨來看,可以發現只有第一個 bit 有兩個非零的數值相加,用二進位表示: 1000 + 0001 = 1001 ,沒有進位,所以目前落子情況的加總數值可以對右斜與左協的加總先做 bitwise-AND 運算,來判斷是否為右斜或左協。 :::danger 改進漢語書寫 ::: #### 棋盤繪製從核心模式,移植到使用者層級 將 kxo/main.c 中 `draw_board` 函式移植到 kxo/xo-user.c 後,更改 `main` 函式,在讀取核心 kfifo 棋盤字串的資料後,才呼叫 `draw_board` 函式繪製棋盤。 ```c read(device_fd, display_buf, DRAWBUFFER_SIZE); display_buf[DRAWBUFFER_SIZE - 1] = '\0'; draw_board(display_buf); printf("%s", draw_buffer); ``` :::danger 描述未能與程式碼匹配。 ::: ## TODO: 觀摩其他學員的成果 > 紀錄你的啟發、你進行的驗證,以及後續改進