# 國立屏東大學電腦五子棋 AI 程式競賽──參賽指引 [toc] 五子棋 AI 程式競賽是讓參賽作品透過對奕的方式,比較並判定棋力的高低。在進行一場對奕時,競賽平台會安排雙方程式輪流擔任先攻的黑子以及是後攻的白子,依序呼叫雙方程式進行十個回合的競賽,若無法分出勝負責由程式執行時間較少的一方勝出。在每個回合,競賽平台會傳入一個代表棋盤的二維陣列,再由參賽程式依棋盤內容決定下棋的位置(請注意,每個回合都是獨立的,這代表你的程式不需要保留過去棋盤的內容)。 所以一個參賽的作品主要的工作就是在對奕的過程中:一、讀入棋盤內容;二、選擇最佳落子位置並輸出。在這份參賽指引文件中,我們將逐一說明如何完成上述兩個工作,並帶領你完成第一次的參賽作品上傳。 ## 讀入棋盤 一個五子棋的棋盤,是由縱橫交錯的 19 行與 19 列所構成的。競賽平台會在對奕進行的過程中,建立並維護一個代表棋盤狀態的字元陣列,並在對奕進行時,依序將這個字元陣列,使用標準輸入流的方式傳遞給對戰的雙方。這個檔案總共有 19 列,每列顯示 19 個位置;每個位置使用<mark>字元「0」代表白子,字元「1」代表黑子,至於還沒有下棋子的位置則以「.」表示。</mark>為幫助同學理解,我們每一行中間安插一個空白字元,並在每一列後面換行。以下是一個範例陣列: 【/home/stu/public/gomoku/chessboard.txt】 ```text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0 . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ``` 為了便利後續測試,建議同學可以依據上述內容建立一個文字檔案 chessboard.txt。或者也可以從ws上的/home/stu/public/gomoku/裡複製取得chessboard.txt檔案用以測試。 由於參賽作品要能夠正確地接收到競賽平台所傳遞的棋盤內容,必須要能夠正確的讀取標準輸入流;至於所接收到的內容,則可以將其儲存到一個 19x19 的二維陣列裡,供後續計算落子位置之用。以下的程式 `main.c` 簡單示範了如何讀取棋盤內容,並為了測試是否正確讀入,再使用迴圈來印出棋盤內容: 【/home/stu/public/gomoku/main.c】 ```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` 以測試是否正確讀入棋盤內容,其輸出結果應如下: ```text 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】 ```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` 執行程式,您可以發現每一次輸出的內容都是不一樣的。以下為範例輸出: ```bash [user@ws gomoku]$ ./a.out R,▴6⏎ ``` ## 如何判斷黑子白子? 您的程式每次啟動時都會收到兩個命令列引數,其中argv[1] 的內容為字串「Black」或「White」,分別代表程式目前應該要下黑子或是白子。 ```c #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 參賽作品 :::warning 賽場測試中,發現問題請洽 TA 或是 SA ::: 你可以在屏東大學資工系計算機網路中心的主機(`ssh <username>@ws.csie2.nptu.edu.tw`)上,完成前述程式的開發。 <!-- 輸入語法 **gomoku | gomoku1 | gomoku2 yourProgram 棋子顏色**(e.g. `gomoku ./a.out Black`) 來測試您的程式是否可以正常運作,其中 gomoku、gomoku1、gomoku2 分別代表著不同棋力的AI對手,你可以先透過這些指令檢查你的程式是否符合參賽的規定。至於棋子顏色在此階段並不重要,因為目前的版本不論你擔任的是黑子或白子,都只是簡單地使用亂數來輸出所要下的位置而已。--> 接下來只要將你的程式碼透過 turnin 指令上傳,就可以完成參賽。建議你可以將所有要上傳的檔案依下列架構加以準備: ```text . └── Gomoku_Project_Floder/ ├── main.c ├── function.c ├── function.h ├── Makefile └── cbb113000.webp ``` | 檔案名稱 | 說明 | | -------------- | -----------------------------------------| | main.c | 主程式 | | function.c | 各項函式,當然您也可以建立多個檔案以管理不同函式 | | function.h | 函示標頭檔案,用於定義函式 | | Makefile | [Makefile](https://www.gnu.org/software/make/manual/make.html),產生以你學號為檔名的可執行檔 | | <系計中帳號>.webp | 大頭貼(正方形,解析度 >= 300*300,[WebP](https://developers.google.com/speed/webp/docs/riff_container) 格式,可以使用 [scp](https://linux.die.net/man/1/scp)指令或[filezilla](https://filezilla-project.org/) 上傳到工作站後再 turnin 到競賽伺服器上 | Makefile 範例檔: ```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想上傳上述的所有檔案,使用以下指令: ```bash [user@ws gomoku]$ turnin gomoku2025 main.c function.c function.h Makefile photo.webp ``` **再次提醒同學,你可以自行決定所需要的檔案及其名稱,但「必須」提供 Makefile 以及確保編譯出的可執行檔檔名為你的系計中帳號(例如上例中的 cbb113000)** ### 寫給2023(含)前的競賽選手 目前2024年起,新版的競賽平台啟用後,有一些與過往不同的地方,請同學們注意: - 移除放置使用者名稱的檔案,請直接登入系統設定。 - 更改大頭貼檔名與格式為 `<系計中帳號>.webp`。你可以在網路上找到[轉換器](https://www.google.com/search?q=webp+converter)或是使用 [FFmpeg](https://ffmpeg.org/) 手動轉換。過去的參賽作品已自動完成此步驟。 - 直接使用 ws 而非 ws2;使用 turnin 而非 gturnin。 ## 前往對戰平台網站 完成 turnin 後,就可以到[五子棋競賽網站](https://gomoku.csie2.nptu.edu.tw/)登入,並檢查是否能夠由平台順利完成編譯。