# Sprout Project 1: Sprout Crush
## 公告
- 4/29 20:59 ai 分數獲取方式說明
- 4/28 21:14 新增時限規定
- 4/26 13:11 add some hints about task 1
- 4/23 11:00 新增 FAQ
- 4/23 19:51 新增 `moved_tags` 使用說明
- 4/23 18:13 新增 `gen_rand_type()` 使用範例
- 4/22 13:31 新增 `gameboard` 使用範例
- 4/16 00:02 繳交壓縮檔檔名修正
- 4/15 23:28 新增 `check_eliminate()` 函式更多的說明。
- 4/15 23:07 修正 release 的一些錯誤,不會影響到同學們的程式碼正確性,可以安心服用。
## 遊戲規則
### 基礎介紹
- 有一個 $10 \times 10$ 的棋盤,棋盤上每個方格中都有不同顏色的寶石。玩家的目標是盡可能的使三個以上相同的寶石連成一條直線,只要滿足條件的寶石就會被消除,並且由上方的寶石來替補他們的位置,而最上方的寶石則會由隨機產生的合法寶石替補。若是替補過程中產生了新的直線,一樣會進行消除、週而復始。
- 玩家在每一輪操作中,可以選擇兩個相鄰的方格並交換裡面的寶石,如果交換寶石後可以消除,則會開始進行消除,否則為一次無效的操作。
- 當盤面上無法進行任何有效操作時,盤面會刷新。
### 特殊寶石
- 十字寶石 **+**:
- **生成**:四個相同的寶石連線並消除後,會立刻在「最後被移動的寶石」的位置產生一個同色的 **+**,並接著進行後續的掉落等程序,若是「最後被移動的寶石」有複數個,生成位置則在那些「最後被移動的寶石」中隨機產生。
- **功能**:在消去 **+** 時,會一併消除同行、列的所有字母。
- 閃電寶石 **z**:
- **生成**:五個以上相同的寶石連成直線並消除後,會立刻在「最後被移動的寶石」的位置產生一個寶石 **z**,並接著進行後續的掉落等程序,若是「最後被移動的寶石」有複數個,生成位置則在那些「最後被移動的寶石」中隨機產生。
- **功能**:寶石 **z** 與相鄰的寶石替換時,不論是否產生連線皆為有效操作,替換後會消去自己以及所有與替換對象同種類的寶石。如果該寶石是被十字寶石觸發,消去的種類與該十字寶石相同;若是被不穩定寶石觸發,隨機選擇消去的種類。該寶石不能與寶石 **Q** 替換。如果與 **z** 交換則不會發動能力,只會讓兩個 **z** 消失而已。
- 不穩定寶石 **Q**:
- **生成**:五個以上相同的寶石在消除時若是相鄰,且不滿足 **+** 或是 **z** 的生成條件,會立刻在「最後被移動的寶石」的位置產生一個寶石 **Q**,並接著進行後續的掉落等程序,若是「最後被移動的寶石」有複數個,生成位置則在那些「最後被移動的寶石」中隨機產生。
- **功能**:字母 **Q** 與相鄰的寶石替換時,不論是否產生連線皆為有效操作,替換時會消去自己以及周圍 5*5 範圍的所有寶石。但該寶石不能與寶石 **z** 替換。
### 遊戲得分
- 原則上每消除一個寶石或生成一個特殊寶石+100,而combo數會讓加的分數變多,公式如下:
$final\_score=basic\_score\times(1+{combo\over10})$
### 遊戲模式
- 模式一:在有限步數中獲得最多分數。
- 模式二:用最少步數達到目標分數。
## 編譯選項1:使用Dev-C++
- 打開Dev-C++,按左上角「檔案」→「開新檔案」→「專案」→「Empty Project」→「確定」
- 將專案存在一個你會記得在哪裡的地方。
- 在專案資料夾上按右鍵,選擇「New File」新增檔案。

- 照著上一個步驟新增四個檔案,分別是`main.cpp`, `ai.cpp`, `splendor.hpp`和`splendor.cpp`。
- 接著就可以把[需要的檔案](https://drive.google.com/file/d/1OvJJCY5tymgTeRfKK3W73wx6JWJ5QjBJ/view?usp=sharing)下載下來後解壓縮,把程式碼複製到專案中同名的檔案內。
**注意:不能直接把解壓縮後的檔案搬移到專案資料夾內,那樣Dev-C++不會認為是屬於這份專案的檔案!**
- 由於編譯這份專案需要使用C++ 2011版本,因此我們要修改編譯指令。點一下「工具」→「編譯器選項」。

- 接著把「呼叫編譯器時加入以下的命令」**左側格子打勾**,下方輸入`-std=c++11`,再按下確認,就可以讓dev-c++編譯時使用正確版本。

- 寫好程式要編譯與執行時,按一下「編譯並執行」。如果跑出來的終端機一直是黑色的,沒有反應,就到專案所在位置把所有執行檔(副檔名`.exe`的檔案)都刪掉,再重新編譯與執行。

## 編譯選項2:手動安裝環境
### Step 1. 安裝 `g++`, `make`
如果你已經可以在終端機中使用 `g++`, `make` 指令,請跳過這個步驟。
依照下列步驟安裝完畢後,請利用以下兩個指令測試:
```bash
g++ -v
make -v
```
如果沒有報錯就沒有問題。
#### Windows user
在[這裡](https://github.com/skeeto/w64devkit/releases)下載 `w64devlit-x.xx.x.zip`,解壓縮後將裡面的 `bin` 這個目錄加進環境變數。然後重新開機。
#### Linux user
依照各個 package manager 相應的作法安裝。
#### Mac user
先參考[這裡](https://brew.sh/)安裝 homebrew (如果已經安裝請跳過)
然後
```bash
brew install gcc
brew install make
```
### Step 2. 下載作業檔案
把[需要的檔案](https://drive.google.com/drive/folders/1KjjUfWM2x-vIxUVlTWJGGneoIPCW0BHJ?usp=share_link)下載下來後解壓縮。
### Step 3. 編譯程式碼
在解壓縮後的目錄中開啟終端機,並執行 `make` 即可。
如果要以 AI 模式進行編譯,請先改成執行 `make MODE=ai`。
每次要換模式編譯之前記得先執行 `make clean` 哦。
### Step 4. 執行遊戲程式
編譯完成後,執行 `bin/main.exe` 就可以開始遊戲了!
## 程式碼導覽
### 程式碼架構
```bash
.
├── Makefile # Help compiling the source code
├── README.md # Introduction to this game
└── src
├── ai.cpp # Your AI implementation
├── main.cpp # Main executor of the game
├── splendor.cpp # Implementation of required functions
└── splendor.hpp # Prototype of required functions
```
### 變數與函式介紹
#### 全域變數
- MODE_\*:遊戲模式
- EXIT:結束遊戲
- STEP:模式1,有限步數
- SCORE:模式2,目標分數
- BOARD_\*:棋盤屬性
- WIDTH:棋盤寬度
- HEIGHT:棋盤高度
- GEM_\*:寶石種類
- NULL:沒有寶石
- RUBY
- LAPIZ
- EMERALD
- AMBER
- AMETHYST
- COLOR_\*:寶石顏色
- 表示同名寶石的顏色
- ABI_\*:寶石特殊效果
- NORMAL:一般寶石
- CROSS:十字寶石 **+**
- BOMB:不穩定寶石 **Q**
- KILLSAME:閃電寶石 **z**
- DRAW_PAUSE_TIME:顯示盤面前等待的時間,單位是毫秒(ms)。
- STEP_LIMIT:模式1的步數限制。
- SCORE_TARGET:模式2的目標分數。
- RND_SEED:亂數種子碼,如果想要產生其他盤面請修改這裡的值。
#### Struct
- Pos
```cpp
struct Pos {
int x, y; // The (x, y) position of certain gem.
};
```
- Gem
```cpp
struct Gem {
int type; // The type of gem, one of GEM_* constants.
int ability; // The ability of gem, one of ABI_* constants.
};
```
- ElimiData
```cpp
struct ElimiData {
int total_elimi; // The number of gem eliminated.
int mid_elimi; // The number of the line consist of three gem
int rnd_cnt; // Moved gem count
};
```
- GemData
```cpp
struct GemData {
Pos pos; // Gem position
Gem gem; // Gem attributes
};
```
#### <font color="#f00">工具函式介紹</font>
- `gen_rand_type()`: 隨機回傳一個 $1$ 到 $5$ 的數字(剛好對應五種寶石的編號)
- `dist_sq(Pos a, Pos b)`: 回傳兩個位置的距離平方
- `get_score()`: 回傳目前遊戲分數,由於原本沒有、但還滿必要的,所以需要的同學可以自己實做:
1. 在 `splender.hpp` 裡面加入 `int get_score()`
2. 在 `splender.cpp` 最下面加入 `int get_score() {return player_score;}`
#### <font color="#f00">很重要的變數</font>
- `gameboard[x][y]`

x 是由上到下、 y 是由左到右
ex. `gameboard[1][2]` 是藍色的寶石
- `moved_tags[x][y]`: 紀錄各個位置的寶石是否剛被移動過(用於決定特殊寶石生成的位置),如果位置為 $(x, y)$ 的寶石剛被移動過,`moved_tags[x][y]` 就會是 `true`, 反之則為 `false`
## TODO
### Task 0 (0pt)
- 可以將你在各個 Task 中的想法、遇到的問題標示好題號後寫在 `report.pdf` 中,儘管寫錯了還是有機會獲得部份分哦。
### Task 1-1 (6pt)
- 實作`check_inboard`函式,檢查輸入的參數`t`的位置是否在盤面內,是的話回傳`true`,否則回傳`false`。
<font color="#f00">Hint: </font>
```=
check_inboard ((x, y)):
if x, y is between 1 and BOARD_HEIGHT, BOARD_WEIGHT respectively:
return true
else:
return false
```
### Task 1-2 (7pt)
- 實作`check_eliminate`函式,檢查盤面上是否有可以消除的三點直線。如果有的話,把任意一個直線的中心點存在函式的參數`pos`中。(請參考`struct Pos`的定義)
- <font color="#f00">這裡傳入的 `pos` 是一個 `Pos` 的指標,而且他有可能會是空指標 `nullptr`,如果是空指標的話,不用對中心位置進行任何處理,只需要回傳是否存在可消除的連線即可,修改 `pos` 的方式請參考下列片段:</font>
```c++
Pos pos = {1, 2};
Pos *pos_ptr = &pos;
(*pos_ptr).x = 2;
(*pos_ptr).y = 3;
cout << pos.x << ' ' << pos.y << '\n';
// output: 2 3
```
<font color="#f00">Hint: </font>
```=
check_eliminate ((ret_x, ret_y)):
for all (x, y) in board:
if is_line(x, y):
if we should return the pos:
ret_x = x
ret_y = y
return true
return false
```
### Task 1-3 (7pt)
- 實作`check_swap`函式,檢查輸入的兩個位置上的寶石交換後是否可以消除,是的話回傳`true`,否則回傳`false`。
- 請注意特殊寶石不一定要連線才能消除!
<font color="#f00">Hint: </font>
```=
check_swap ((x1, y1), (x2, y2)):
if at least one of the two position is not in-board:
return false
if they are not adjacent:
return false
if their ability are ABI_KILLSAME and ABI_BOMB:
return false
if only one of their type are ABI_KILLSAME or ABI_BOMB:
return true
ret = false
swap gems at (x1, y1) and (x2, y2)
if there are something can be eliminated:
ret = true
swap gems at (x1, y1) and (x2, y2) back
return ret
```
### Task 2-1 (5pt)
- 實作`apply_bomb`函式,消除參數`pos`周圍 $5\times 5$ 範圍的寶石,如果有因此消除特殊寶石,需要遞迴發動該寶石的能力
### Task 2-2 (5pt)
- 實作`apply_killsame`函式,消除和參數`tar`對應寶石一樣顏色的寶石,如果有因此消除特殊寶石,需要遞迴發動該寶石的能力,請特別注意不同消除方式時的對應作法。
### Task 2-3 (5pt)
- 實作`apply_cross`函式,消除和參數`pos`同行及同列的寶石,如果有因此消除特殊寶石,需要遞迴發動該寶石的能力。
### Task 2-4 (5pt)
- 實作`apply_special`函式,根據參數`pos`對應的特殊寶石決定要呼叫`apply_bomb`、`apply_killsame`或是`apply_cross`。
### Task 3 (10pt)
- `dropping`函式中有bug,請你找出它藏在哪裡並修好它。
- 修復正確 (5pt)
- edit distance $<= 15$ (3pt)
- edit distance $<= 10$ (2pt)
- edit distance $< 10$ (bonus: 2pt)
> 什麼是 edit distance? 兩個字串 $A, B$ 的 edit distance 指的就是要把 $A$ 變成 $B$ 至少需要經過幾次(加入/刪除/更改)字元的操作。可以使用[這個網站](https://planetcalc.com/1721/)協助計算。
> 在計算 edit distance 時,我們會將空格與換行通通移除後比較。
### Task 4 (50pt)
**會在原始規則下進行評分,與 bonus 的創意題無關**
- 請你設計一套演算法,根據當前盤面情況決定要移動哪兩個寶石以獲得最佳結果,例如用最少步數達到指定分數。
- 決定要移動的寶石後,請將兩個寶石的位置存在參數`pos1`和`pos2`中再結束函式。
- 在模式一中獲得 $1$ 分(10pt)
- 在模式一中獲得 $6000$ 分(10pt)
- 在模式一中獲得 $12000$ 分(5pt)
- 在模式一中獲得 $15000$ 分 (5pt)
- 在模式二中以 $30$ 步達成目標 (10pt)
- 在模式二中獲得前 $10n\%, 1\le n\le 10, n\in\mathbb{N}$ 的排名 (n pt)
- 在模式二中獲得 30000 分 (bonus: 5pt)
- 在模式一中戰勝講師 ai (bonus: 4pt)
- 在模式二中戰勝講師 ai (bonus: 4pt)
<font color="#f00">另外,對於所有模式一的測試,時限皆為 10 秒,如果進行超過 10 秒,將會以 10 秒當下的分數作為最終分數。
而對於所有模式二的測試,時限皆為 30 秒,如果運行超過 30 秒,將會視為花費 31 步達成目標。
時限的計算不包含那些渲染時的等待時間,基本上不要暴力遞迴模擬應該都不會超過。</font>
> 我們將會使用另外的固定種子碼來測試你們的 ai ,也就是說自己在測試的結果**不代表**你們會得到的成績。
> 為了降低運氣因素,兩種模式各會進行 3 次測試,取最高分作為成績。
### Task Bonus-1 (10pt)
- 發揮創意,新增一些酷酷的新功能吧!
- 請為新增的功能寫一份說明,放在 `report.pdf` 檔案中。
## 繳交相關
### 繳交格式
請繳交 `splendor.cpp`, `ai.cpp`, `report.pdf`, `main.cpp`, `splendor.hpp` 以及創意題新增的檔案,並壓縮成 zip 檔,壓縮後的檔名為 `[你的 judge id].zip` (不用中括號),解壓縮後的檔案架構應該長這樣:
```bash
9876
├── ai.cpp
├── main.cpp
├── splendor.cpp
├── splendor.hpp
├── make_bomb_stronger_or_something_else.cpp
└── report.pdf
```
不可以包含 `*.exe`, `*.o` 等等的垃圾檔案
### 繳交表單
[表單](https://docs.google.com/forms/d/e/1FAIpQLSdwRtDKVp1Ee1QcPDxhdxnGF5N5XcY3ZL6cd566Q20sianaXQ/viewform?usp=sf_link)。
交完後悔的話可以再重新編輯哦!
### 死線
5/5 23:59 沒有遲交這回事。
### 違規事項
- 繳交格式錯誤(-10pt)
- 使用非正規方式操作 ai (-$\min(50, \text{your score in task 4})$pt)
- 課程作業規範中所禁止的事項 (-?pt)
## FAQ
### Q: kill_same的random要怎麼用啊?
A: 更新在 Spec 上囉
### Q: 如果沒做完會怎樣嗎
A: 拿不到全部的分數、而分數會跟結業等等有些關係,詳情請參考課程介紹。但只要有寫就會有一些部份分,有時候最關鍵的就是這幾分,所以很鼓勵各位同學盡自己所能的推進大作業進度ㄛ
### Q: 一定要相鄰才能交換嗎?
A: Yes
### Q: 希望可以有遊戲的執行檔可以看執行起來是什麼樣子,當作對照組
A: 無法,講師這裡編出來的東西不一定能在同學們的作業系統上跑。有對照需求的話可以參考講師上傳的影片
### Q: 還沒想好ai的部分怎辦
A: 可以多加利用 Task 1 寫出來的工具函式哦!順帶一提,由於評分時我們會使用我們自己的 `splendor.cpp`, `splendor.hpp`,所以就算 Task 1, 2, 3 沒有寫出來,也可以使用那些工具函式來完成哦(只是沒辦法測試)
### Q: 大作業跟段考、備審 blablabla 的都好近
A: 據說是考量到我們早點改完、證書早點發可能來得及讓你們放備審,~~而且我也一樣燒,大作業 release 那周是我段考周,根本森林大火~~。
### Q: 之前有幾堂課聽不懂...
A: 有什麼問題都可以在 dc 上發問哦,不管是對課程還是對作業的問題都可以,講師們都很樂意提供協助~(不要害羞~ DC 應該滿匿名的
### Q: 大作業如果用vscode編譯,要從哪裡測試遊戲🤔️
~~可以先教我怎麼用 vscode 編譯嗎? vscode 是編輯器餒。~~
理論上在 vscode 的終端機也是可以使用 `make` 來編譯、然後下 `bin/main.exe` 來執行,但畫面的部份不保證安好。
建議還是另外在作業的根目錄下開終端機來跑會比較好一點
#### Mode 2 累積分數
14 竹區 葉南俊+7
14 竹區 陳明揚+7
9 竹區 劉亮渝+8
6 北區 廖培君+10
6 北區 賴柏岑+10
11 北區 張以緹+8
6 北區 張九勻+10
8 北區 陳慶鴻+9
11 北區 張以靖+8
8 北區 蕭立安+9
12 北區 彭博礫+8
7 北區 李瑞茵+9
6 北區 楊晉宇+10
14 北區 劉起維+7
13 北區 陳詣安+8
6: 4(100%)
7: 1(93%)
8: 2(91%)
9: 1(87%)
11: 2(85%)
12: 1(82%)
13: 1(80%)
14: 3(78%)
31: 40(0%)