contributed by <Andrushika
>
commit d203aed
在 xo-user.c
中加入以下程式碼:
static int draw_board(const char *table)
{
printf("\n\n");
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
printf(" %c ", table[i * BOARD_SIZE + j]);
if (j < BOARD_SIZE - 1)
printf("|");
}
printf("\n");
if (i < BOARD_SIZE - 1) {
for (int j = 0; j < BOARD_SIZE * 4 - 1; j++) {
printf("-");
}
printf("\n");
}
}
printf("\n");
}
此處有對原本的 draw_board
做了一些修改,稍微提高了程式的可讀性;但因為用到了乘法運算,效能可能沒有原本來得好,是可以改進的地方。
這樣一來,在 kernel level 中就無須在乎棋盤的繪製方式,只要專注於將 data 本身利用 kfifo 傳輸給 user level 就好。
接下來修改 main.c
中原本的 produce_board
,可以直接使用 table
的內容,並更名為更貼近實際行為的 send_board_data
:
/* send chess board data into the kfifo buffer */
static void send_board_data(void)
{
unsigned int len = kfifo_in(&rx_fifo, table, N_GRIDS);
if (unlikely(len < N_GRIDS) && printk_ratelimit())
pr_warn("%s: %zu bytes dropped\n", __func__, N_GRIDS - len);
pr_debug("kxo: %s: in %u/%u bytes\n", __func__, len, kfifo_len(&rx_fifo));
}
而在 drawboard_work_func
和 timer_handler
中,不再需要 draw_board
:
static void drawboard_work_func(struct work_struct *w)
{
int cpu;
/* This code runs from a kernel thread, so softirqs and hard-irqs must
* be enabled.
*/
WARN_ON_ONCE(in_softirq());
WARN_ON_ONCE(in_interrupt());
/* Pretend to simulate access to per-CPU data, disabling preemption
* during the pr_info().
*/
cpu = get_cpu();
pr_info("kxo: [CPU#%d] %s\n", cpu, __func__);
put_cpu();
read_lock(&attr_obj.lock);
if (attr_obj.display == '0') {
read_unlock(&attr_obj.lock);
return;
}
read_unlock(&attr_obj.lock);
/* Store data to the kfifo buffer */
mutex_lock(&producer_lock);
mutex_lock(&consumer_lock);
send_board_data();
mutex_unlock(&producer_lock);
mutex_unlock(&consumer_lock);
wake_up_interruptible(&rx_wait);
}
在實作時遇到一個問題:無論進行了幾局遊戲,games_count
始終為 0(即 history 停留在第一局遊戲)。
因此,在 track_moves
加上以下程式碼進行偵錯:
static void track_moves(const char *board)
{
printf("track_moves called. Current move_count: %d, game_count: %d\n", move_count, game_count);
bool reset_detected = is_board_reset(board);
printf("is_board_reset returned: %s\n", reset_detected ? "true" : "false");
/* Check if the board has been reset */
if (reset_detected) {
printf("Reset detected!\n");
if (move_count > 0) {
printf("Saving game %d with %d moves.\n", game_count + 1, move_count);
save_current_game();
printf("After save_current_game, game_count is now: %d\n", game_count);
}else{
printf("Reset detected, but move_count is 0, not saving.\n");
}
move_count = 0;
printf("move_count reset to 0.\n");
memcpy(prev_board, board, N_GRIDS);
return;
}
/* Track new moves */
for (int i = 0; i < N_GRIDS; i++) {
if (board[i] != ' ' && prev_board[i] == ' ') {
if (move_count < N_GRIDS) {
current_moves[move_count].pos = i;
current_moves[move_count].player = board[i];
move_count++;
}
}
}
/* Update previous board state */
memcpy(prev_board, board, N_GRIDS);
}
之後發現,即使棋局已經分出勝負並重置,is_board_reset
都未被觸發,導致不同棋局的 moves
仍會繼續被寫在同一條 game
紀錄中:
track_moves called. Current move_count: 0, game_count: 0
track_moves called. Current move_count: 1, game_count: 0
track_moves called. Current move_count: 1, game_count: 0
track_moves called. Current move_count: 2, game_count: 0
track_moves called. Current move_count: 3, game_count: 0
track_moves called. Current move_count: 3, game_count: 0
track_moves called. Current move_count: 4, game_count: 0
track_moves called. Current move_count: 5, game_count: 0
track_moves called. Current move_count: 5, game_count: 0
track_moves called. Current move_count: 6, game_count: 0
track_moves called. Current move_count: 6, game_count: 0
track_moves called. Current move_count: 6, game_count: 0
track_moves called. Current move_count: 6, game_count: 0
track_moves called. Current move_count: 7, game_count: 0
track_moves called. Current move_count: 8, game_count: 0
track_moves called. Current move_count: 8, game_count: 0
track_moves called. Current move_count: 9, game_count: 0
track_moves called. Current move_count: 10, game_count: 0
is_board_reset returned: false
原本的 is_board_reset
是這樣實作的:
static bool is_board_empty(const char *board)
{
return memcmp(board, empty_board, N_GRIDS) == 0;
}
static bool is_board_reset(const char *board)
{
if (!is_board_empty(board))
return false;
if (is_board_empty(prev_board))
return false;
return true;
}
此處使用了檢查棋盤是否為 empty 的方式進行判斷;然而,因為每次的畫面更新都是在 AI 決策完才被觸發,所以不會有 is_board_reset
條件成立的時候。
降低核心和使用者層級溝通的成本
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up