Try   HackMD

國立屏東大學電腦五子棋 AI 程式競賽──參賽指引

五子棋 AI 程式競賽是讓參賽作品透過對奕的方式,比較並判定棋力的高低。在進行一場對奕時,競賽平台會安排雙方程式輪流擔任先攻的黑子以及是後攻的白子,依序呼叫雙方程式進行十個回合的競賽,若無法分出勝負責由程式執行時間較少的一方勝出。在每個回合,競賽平台會傳入一個代表棋盤的二維陣列,再由參賽程式依棋盤內容決定下棋的位置(請注意,每個回合都是獨立的,這代表你的程式不需要保留過去棋盤的內容)。

所以一個參賽的作品主要的工作就是在對奕的過程中:一、讀入棋盤內容;二、選擇最佳落子位置並輸出。在這份參賽指引文件中,我們將逐一說明如何完成上述兩個工作,並帶領你完成第一次的參賽作品上傳。

讀入棋盤

一個五子棋的棋盤,是由縱橫交錯的 19 行與 19 列所構成的。競賽平台會在對奕進行的過程中,建立並維護一個代表棋盤狀態的字元陣列,並在對奕進行時,依序將這個字元陣列,使用標準輸入流的方式傳遞給對戰的雙方。這個檔案總共有 19 列,每列顯示 19 個位置;每個位置使用字元「0」代表白子,字元「1」代表黑子,至於還沒有下棋子的位置則以「.」表示。為幫助同學理解,我們每一行中間安插一個空白字元,並在每一列後面換行。以下是一個範例陣列:

【/home/stu/public/gomoku/chessboard.txt】

. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . 0 . . . . . . . . . . 
. . . . . . . . . 1 . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . .

為了便利後續測試,建議同學可以依據上述內容建立一個文字檔案 chessboard.txt。或者也可以從ws上的/home/stu/public/gomoku/裡複製取得chessboard.txt檔案用以測試。

由於參賽作品要能夠正確地接收到競賽平台所傳遞的棋盤內容,必須要能夠正確的讀取標準輸入流;至於所接收到的內容,則可以將其儲存到一個 19x19 的二維陣列裡,供後續計算落子位置之用。以下的程式 main.c 簡單示範了如何讀取棋盤內容,並為了測試是否正確讀入,再使用迴圈來印出棋盤內容:

【/home/stu/public/gomoku/main.c】

#include <stdio.h>

int main (){

    char chessboard[19][19];
    int i = 0, j = 0;
    
    for(i=0; i<19; i++){
        for(j=0; j<19; j++){
            scanf(" %c", &chessboard[i][j]);
        }
    }

    printf("   A B C D E F G H I J K L M N O P Q R S\n");
    for (i=0; i<19; i++){
        printf("%2d ", i + 1);
        for(j=0; j<19; j++){
            printf("%c ", chessboard[i][j]);
        }
        printf("\n");
    }
}

以上檔案已存放於ws上的/home/stu/public/gomoku/main.c,同學亦可自行複製取得。

請使用 gcc main.c 進行編譯並且執行 ./a.out < chessboard.txt 以測試是否正確讀入棋盤內容,其輸出結果應如下:

   A B C D E F G H I J K L M N O P Q R S
 1 . . . . . . . . . . . . . . . . . . . 
 2 . . . . . . . . . . . . . . . . . . . 
 3 . . . . . . . . . . . . . . . . . . . 
 4 . . . . . . . . . . . . . . . . . . . 
 5 . . . . . . . . . . . . . . . . . . . 
 6 . . . . . . . . . . . . . . . . . . . 
 7 . . . . . . . . . . . . . . . . . . . 
 8 . . . . . . . . . . . . . . . . . . . 
 9 . . . . . . . . 0 . . . . . . . . . . 
10 . . . . . . . . . 1 . . . . . . . . . 
11 . . . . . . . . . . . . . . . . . . . 
12 . . . . . . . . . . . . . . . . . . . 
13 . . . . . . . . . . . . . . . . . . . 
14 . . . . . . . . . . . . . . . . . . . 
15 . . . . . . . . . . . . . . . . . . . 
16 . . . . . . . . . . . . . . . . . . . 
17 . . . . . . . . . . . . . . . . . . . 
18 . . . . . . . . . . . . . . . . . . . 
19 . . . . . . . . . . . . . . . . . . . 

太棒了!現在您的程式已經可以順利將棋盤讀入!

使用亂數下子

在這一個章節,我們會告訴您如何讓您的程式符合比賽的資格,但您的程式仍不具備基本的棋力。一開始,最簡單的一種做法就是透過亂數的方式,隨機地將棋子下在棋盤上的任一個位置。以下的範例採用時間函數的方式建立亂數種子(當然,你也可以利用某個變數的記憶體位作為亂數種子):

【/home/stu/public/gomoku/rand.c】

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main(){

    char chessboard[19][19];
    char letters[19] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S'}; 
    int row = 0, col = 0;

    srand(time(NULL));
    // 由於 time() 函數的輸出單位為秒,
    // 您也可以使用某個變數的記憶體位址作為亂數種子
    // srand((unsigned long int)&row);
    row = rand()%19;
    col = rand()%19;
    printf("%c, %d\n", letters[col], row+1);
}

編譯完成後輸入 ./a.out 執行程式,您可以發現每一次輸出的內容都是不一樣的。以下為範例輸出:


[user@ws gomoku]$ ./a.out
R,▴6⏎ 

如何判斷黑子白子?

您的程式每次啟動時都會收到兩個命令列引數,其中argv[1] 的內容為字串「Black」或「White」,分別代表程式目前應該要下黑子或是白子。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main(int argc, char *argv[]){

    char chessboard[19][19];
    char letter_arr[19] = "ABCDEFGHIJKLMNOPQRS";
    int i = 0, j = 0;
    int row = 0, col = 0;
    int quit = 0;

    for(i=0; i<19; i++){
        for(j=0; j<19; j++){
            scanf(" %c", &chessboard[i][j]);
        }
    }
    if( strcmp(argv[1],"Black")==0){
        // 要下黑子
    }
    else 
    {
        // 要下白子        
    }
    ...
}

turnin 參賽作品

賽場測試中,發現問題請洽 TA 或是 SA

你可以在屏東大學資工系計算機網路中心的主機(ssh <username>@ws.csie2.nptu.edu.tw)上,完成前述程式的開發。

接下來只要將你的程式碼透過 turnin 指令上傳,就可以完成參賽。建議你可以將所有要上傳的檔案依下列架構加以準備:

   .
   └── Gomoku_Project_Floder/
       ├── main.c
       ├── function.c
       ├── function.h
       ├── Makefile
       └── cbb113000.webp
檔案名稱 說明
main.c 主程式
function.c 各項函式,當然您也可以建立多個檔案以管理不同函式
function.h 函示標頭檔案,用於定義函式
Makefile Makefile,產生以你學號為檔名的可執行檔
<系計中帳號>.webp 大頭貼(正方形,解析度 >= 300*300,WebP 格式,可以使用 scp指令或filezilla 上傳到工作站後再 turnin 到競賽伺服器上

Makefile 範例檔:

all: main.c function.o
	cc -o cbb113000 main.c function.o

function.o: function.c
	cc -c function.c

clean:
	rm *.o a.out *.*~

欲參賽的同學必須先使用 ssh 連線到 ws.csie2.nptu.edu.tw,並且使用 turnin上傳(turnin code:gomoku2025)例如參賽同學cbb113000想上傳上述的所有檔案,使用以下指令:

[user@ws gomoku]$ turnin gomoku2025 main.c function.c function.h Makefile photo.webp

再次提醒同學,你可以自行決定所需要的檔案及其名稱,但「必須」提供 Makefile 以及確保編譯出的可執行檔檔名為你的系計中帳號(例如上例中的 cbb113000)

寫給2023(含)前的競賽選手

目前2024年起,新版的競賽平台啟用後,有一些與過往不同的地方,請同學們注意:

  • 移除放置使用者名稱的檔案,請直接登入系統設定。
  • 更改大頭貼檔名與格式為 <系計中帳號>.webp。你可以在網路上找到轉換器或是使用 FFmpeg 手動轉換。過去的參賽作品已自動完成此步驟。
  • 直接使用 ws 而非 ws2;使用 turnin 而非 gturnin。

前往對戰平台網站

完成 turnin 後,就可以到五子棋競賽網站登入,並檢查是否能夠由平台順利完成編譯。