# 國立屏東大學電腦五子棋 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/)登入,並檢查是否能夠由平台順利完成編譯。