---
# System prepended metadata

title: 2025q1 Homework3

---

# 2025q1 Homework3 (kxo)
contributed by < [BennyWang1007](https://github.com/BennyWang1007) >

:::danger
注意書寫規範！
:::

{%hackmd NrmQUGbRQWemgwPfhzXj6g %}

## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

$ lscpu
架構：                    x86_64
  CPU 作業模式：          32-bit, 64-bit
  Address sizes:          39 bits physical, 48 bits virtual
  Byte Order:             Little Endian
CPU(s):                   20
  On-line CPU(s) list:    0-19
供應商識別號：            GenuineIntel
  Model name:             12th Gen Intel(R) Core(TM) i7-12700H
    CPU 家族：            6
    型號：                154
    每核心執行緒數：      2
    每通訊端核心數：      14
    Socket(s):            1
    製程：                3
    CPU(s) scaling MHz:   23%
    CPU max MHz:          4700.0000
    CPU min MHz:          400.0000
    BogoMIPS:             5376.00
Virtualization features:  
  虛擬：                  VT-x
Caches (sum of all):      
  L1d:                    544 KiB (14 instances)
  L1i:                    704 KiB (14 instances)
  L2:                     11.5 MiB (8 instances)
  L3:                     24 MiB (1 instance)
NUMA:                     
  NUMA 節點：             1
  NUMA node0 CPU(s)：     0-19
```

## 提昇版面傳輸速率
> commit [a285e0b](https://github.com/BennyWang1007/kxo/commit/a285e0b6c4357f61fb89feb32edd014db73a525c)
> commit [a969618](https://github.com/BennyWang1007/kxo/commit/a969618ac5660c8be92a2078c4fce7d889357629)

我將原先傳輸 DRAWBUFFER_SIZE bytes 的資料改為 BOARD_DATA_SIZE bytes，定義如下。

```diff
- #define DRAWBUFFER_SIZE                                                 \
-     ((BOARD_SIZE * (BOARD_SIZE + 1) << 1) + (BOARD_SIZE * BOARD_SIZE) + \
-     ((BOARD_SIZE << 1) + 1) + 1)
- static char draw_buffer[DRAWBUFFER_SIZE];

+ #define BOARD_HISTORY_ELE_BITS 2
+ #define BOARD_HISTORY_SIZE \
+     (BOARD_SIZE * BOARD_SIZE * BOARD_HISTORY_ELE_BITS / 8)
+ static char board_data[BOARD_DATA_SIZE] = {0};
```

:::danger
注意書寫規範，中英文間用一個半形空白字元區隔
:::

每一個位置皆只使用兩bit來傳輸，比起原先的 `char` 加空格，省去了許多不必要的資訊。
當版面夠大時（$BOARD\_SIZE\to\infty$），節省了 $\lim\limits_{n\to\infty} (1-\dfrac{n^2/4}{3n^2+4n+2}) \approx 91.7％$ 的資料大小。

:::danger
提供更多數學分析
:::

## 顯示歷史紀錄
> commit [797c875](https://github.com/BennyWang1007/kxo/commit/797c875c3b115d76051bafb72cce31a03ef77d4b)

首先我定義了 type `board_history_t` 作為一盤棋的紀錄。

```c
#define BOARD_HISTORY_SIZE \
    ((BOARD_SIZE * BOARD_SIZE * BOARD_SIZE_SQUARE_LOG2 + 7) / 8)

typedef struct board_history {
     char moves[BOARD_HISTORY_SIZE];
     size_t length;
 } board_history_t;
```
（記 BOARD\_SIZE = n）
其中我定義座標 $A1 = 0, A2 = 1, ..., B1 = n, ...$。一盤棋最多會有 $n^2$ 步，因此需要 $\lceil \dfrac {n^2 \cdot \lceil log_2(n^2) \rceil} {8}\rceil$ bytes 的大小來存取資料，並預留一個 byte 存 `\0`。

接著我讓程式在進行移動時用 `history_append_move()`將資料存入歷史紀錄，並用 `history_next_board()` 在該盤棋結束時將地址一向下一個紀錄。

最後我定義了 kxo 的 `ioctl` interface 讓使用者能夠向 kernel 獲取儲存的歷史盤面，如以下用法。

```c
ioctl(device_fd, KXO_GET_BOARD_HISTORY, &histories);
```

程式輸出如下。

```shell
Stopping the kernel space tic-tac-toe game...
Game history:
Moves: B2 -> D2 -> B1 -> C2 -> D2 -> B1 -> C2 -> B1
Moves: A2 -> D2 -> B2 -> D2 -> A2
Moves: C2 -> D1 -> C2 -> D2 -> D2 -> D2 -> B1
Moves: A2 -> D2 -> B2 -> D2 -> A2
Moves: C2 -> D2 -> D2 -> B1 -> C2 -> B2 -> D2
```

## 顯示時間
> commit [1777226](https://github.com/BennyWang1007/kxo/commit/1777226249e8e8614b07b385a7de88b15322d022)

在每次繪製盤面後，使用 `gettimeofday()` 以及 `localtime()` 來將時間轉換為當地時間，最後用 `strftime()` 將時間轉為字串並顯示出來，範例如下。

```shell
O . . . 
. X . . 
. . . O 
. . . . 
Current time: 2025-04-19 02:58:22
```

## 修正執行多次 xo-user 會卡死
> commit [ef3fa9b](https://github.com/BennyWang1007/kxo/commit/ef3fa9ba63684e7f2d3b60dd656ec8e090326b20)

在結束 `kxo` 時會將 `attr_obj.end` 設為 `1`，但在下次呼叫 `kxo` 時並未重置而造成卡死。因此我將初始化從 `kxo_init()` 移至 `kxo_open()` 以保證每次執行都有全新的初始值。

