# 大作業二 SPEC
作者:iceylemon
[toc]
# 環境建置
請見 [Github Readme 連結](https://github.com/iceylemon157/sprout-c-game-2024/tree/main)
# 大作業二目的
實作一個 C++ 的 Server 來控制遊戲,並且在不同模式下,遊戲結束前拿到的分數越高越好!
如果你覺得你已經懂遊戲規則的話,可以直接看要怎麼用程式碼去控制角色操作,如果有不太懂得地方再回去翻 SPEC 就好!
快速 Get Started:
* [點我跳到程式碼](#程式碼)
* [點我跳到實作策略](#實作策略)
* [點我看大作業二網站](https://tw-csie-sprout.github.io/c2024/#!project2.md)
# 遊戲畫面
## 主畫面
在主畫面點擊 PLAY 按鈕會開啟遊戲設定畫面。
## 遊戲設定頁面
遊戲設定主要有三個
* Play w/ Server:
* 切換手動模式跟 server 模式 (由 C++ 程式碼控制)
* Custom Seed:
* 預設是 Random Seed,也就是每次等候的餐點的出現順序都不一樣
* 只有在評分的時候會用到,不開放給學員們使用
* Recipe Mode:
* 選擇餐點生成的模式,詳細請見 [遊戲模式](#遊戲模式)
遊戲設定完成之後,點擊 START! 會跳轉到主遊戲畫面。

## 主遊戲畫面
遊戲的玩法在下面。如果在主遊戲畫面按 ESC,可以暫停遊戲

## 暫停畫面
按 Resume 按鈕可以繼續遊戲,按 Main Menu 按鈕會回到主畫面。

# 遊戲模式
遊戲模式有分成三種:salad only、salad and cheese burger、all recipe
這三種遊戲模式代表著可能會出現的餐點。salad only 代表所有的餐點都只會是 salad。salad, cheeseburger 代表兩者皆可能出現。all recipe 則是四種餐點都可能會出現。
# 遊戲操作方式
遊戲有分「手動版」、「server 版」。手動版就是可以自己動手玩的版本。server 版則是要用 C++ 程式控制。
強力推薦大家先去玩手動版的遊戲,自己先玩過、知道遊戲怎麼玩之後讀 SPEC 會輕鬆很多喔!
另外,手動版的遊戲只是要讓各位自己玩玩看操作而已,讓各位熟悉一下遊戲規則,並不會算分,請同學們不要花太多時間在手動版上面喔 ~
可以自己動手玩的版本。server 版則是要用 C++ 程式控制。
## 手動版
### 遊戲方式
* 讓角色上/左/下/右移動:wasd
* 拿起/放下物品:e
* 切菜:f
### 遊戲架構
手動版為了讓大家熟悉遊戲規則,所以沒有設置時間上限,想玩多久就玩多久!但因為手動版遊戲沒辦法讓大家一回合一回合跑 (太久了),所以是用時間作計算 (像是煎肉餅的時間)。
## server 版
### 遊戲方式
* 讓角色上/左/下/右移動:`GameController` 的 `MoveUp/Left/Down/Right()` 函式
* 拿起/放下物品:`GameController` 的 `Interact()` 函式
* 切菜:`GameController` 的 `InteractSpecial()` 函式
### 遊戲架構
server 版的遊戲採取回合制,總共有 1800 個回合。每一回合玩家可以執行一次操作。操作包含上、下、左、右移動,拿起/放下物品,切菜,總共六種,每種操作作一次皆需要一回合。
當遊戲要開始的時候 (設定完遊戲設定之後),遊戲會呼叫 `UserAction.cc` 裡面的 `UserAction::InitGame` 來通知玩家遊戲即將開始。`UserAction::InitGame` 裡面會呼叫 `UserAction::Initialize()`,你可以在這個函式裡面進行初始化。
如果你在遊戲結束之後使用 Play Again 按鈕,那麼不會重新進入遊戲設定,而是沿用上一次遊戲的遊戲設定,並且直接呼叫 `UserAction::InitGame()` 來重新開始遊戲。
在遊戲的每一回合開始的時候,遊戲會呼叫 `GameController.cc` 裡面的 `GameController::ReceiveEvents()` 來更新遊戲數據。你可以透過 `GameController` 裡面的一些函式來查看這些數據。
當得到數據之後,遊戲會呼叫 `UserAction.cc` 裡面的 `UserAction::SendOperation()`。此時,遊戲在詢問你要讓 player 進行什麼樣的操作。你需要呼叫 `GameController` 裡面的 `MoveUp()`, `MoveDown()`, `MoveLeft()`, `MoveRight()`, `Interact()`, `InteractSpecial()` 這六個函式的其中一個來進行玩家的操作 (剛好對應到玩家能進行的六種操作,其中 `Interact()` 代表拿起/放下物品,而 `InteractSpecial()` 代表切菜)。
**請注意,因為每回合只能執行一次操作,所以遊戲只會紀錄你最後一次呼叫的函式。**
當執行完 `UserAction::SendOperation()` 之後,角色會在遊戲視窗中進行對應的行動。行動完之後更新的遊戲數據可以在下一個回合的時候得到。
## 手動版/server 版差異
| 項目 | 手動版 | server 版 |
| -------- | -------- | -------- |
| 遊戲架構 | 時間制 (無上限) | 見 [回合制](#回合制),共 1800 回合 |
| 操作方式 | wasdef | C++ 函式 |
| 訂單分數 | 每個訂單都是 1000 分 | 會隨時間改變 |
| 平底鍋煎肉餅 | 用時間算,可以看畫面判斷 | 用回合算,見 [平底鍋](#平底鍋) |
# 遊戲架構
## 食材
食材分成三種:生的、處理過的、烤焦的
* 生的:
* 生菜 (Cabbage)
* 起司**塊** (CheeseBlock)
* 生肉餅 (RawPatty)
* 番茄 (Tomato)
* 處理過的:
* **麵包** (Bread)
* 生菜片 (CabbageSlices)
* 起司**片** (CheeseSlices)
* 熟肉餅 (CookedPatty)
* 蕃茄片 (TomatoSlices)
* 烤焦的:
* 碳粉肉餅 (BurntPatty)
只有 **處理過的** 或 **烤焦的** 食材可以放到盤子上。
## Counter
各種不同功能的桌子、櫃子,這邊統一稱為 counter。
總共有 7 種 counter:普通桌子、儲藏櫃、砧板、平底鍋、神奇盤子桌、垃圾桶、送餐櫃台
### 普通桌子
沒有特別功用、可以把物品放在上面
### 儲藏櫃
如果玩家**空手**對著儲藏櫃 `Interact()`,就可以拿到對應的食材。有五種不同的儲藏櫃:麵包、番茄、生菜、起司塊、生肉餅
### 砧板
砧板上可以放三種食材:「**番茄、生菜、起司塊**」。
對著放有食材的的「**砧板**」使用 `InteractSpecial()` 時可以進行切菜的動作。每一種食材要切好需要切的 Round 都不一樣。切完之後,砧板上的食材會直接變成切完的食材。以下是每一種食材所需要的回合數以及切完後會變成的食材:
| 原料 | 切的回合數 | 完成的食材 |
| -------- | -------- | -------- |
| 起司塊 | 3 Round | 起司片 |
| 番茄 | 3 Round | 番茄片 |
| 生菜 | 5 Round | 生菜片 |
### 平底鍋
平底鍋上可以放兩種食材:「**生肉餅、熟肉餅**」。
當手上有「生肉餅」或「熟肉餅」時,對著沒有放食材的平底鍋使用 `Interact()` 可以把他們放到平底鍋上。放上去之後會開始煎肉餅。以下是每一種肉餅所需要的回合數以及煎完後會變成的食材:
| 原料 | 煎的回合數 | 完成的食材 |
| -------- | -------- | -------- |
| 生肉餅 | 60 Round | 熟肉餅 |
| 熟肉餅 | 90 Round | 碳粉肉餅 |
注意,當生肉餅經過 60 Round 變成熟肉餅之後,會接著繼續煎,再經過 90 Round 之後就會變成碳粉肉餅。換句話說,如果你把生肉餅放在平底鍋上面 150 Round 他就會直接變成碳粉肉餅。所以要記得在他還是熟肉餅的時候拿起來,不然他就會烤焦喔!
### 神奇盤子桌
神奇盤子桌每 20 Round 會「試著」生成一次盤子。神奇盤子桌最多可以累積 4 個盤子放在上面。如果已經神奇盤子桌上面放滿 4 個盤子的話,就不會生成新的盤子。
請注意,神奇盤子桌會固定每 20 Round 試著生成一次盤子,就算因為已經放滿了而沒有生成新的盤子,也會再等 20 Round 才會再試著生成一次。
### 垃圾桶
如果玩家手上有物品,對垃圾桶使用 `Interact()`,就會把手上的所有物品都丟掉。
### 送餐櫃台
當你完成某個等候中的訂單的要求的時候 (請參考 [等候中的訂單](#等候中的訂單)),可以帶著你的餐點對送餐櫃台使用 `Interact()` 就可以提交餐點。提交餐點之後遊戲會判斷你的餐點是否符合任何一個正在等待中的訂單。
遊戲會最先從最早出現的等候中的訂單去找 (也就是 `OrderId` 最小的),如果餐點符合,分數就會增加該 Order 對應的分數。如果所有等候中的訂單都跟你提交的餐點不同,那麼就會提交失敗,你的餐點也拿不回來。(但是不會額外扣分)
## 盤子
盤子是一個特別的物品,盤子的上面可以放**不是生的**的食材。一個盤子上面可以放不只一個食材,但是不能放重複的食材到盤子上面。
玩家如果對裝有食材的盤子使用 `Interact()`,會直接把整個盤子連著上面的食材拿起來。
玩家如果拿著 **不是生的**,並且 **盤子上面沒有的** 的食材對一個放在 **普通桌子** 上面的盤子使用 `Interact()`,那麼該食材會被放到盤子裡面。
玩家如果拿著盤子對**不是生的**,並且 **盤子上面沒有的**食材使用 `Interact()`,會把食材直接拿起來放到手上的盤子裡。
## 玩家 Player
玩家的操作除了上下左右的移動之外,還可以拿起/放下物品,切菜。
### 上下左右移動 MoveXXX()
可以透過 `Move[Up,Down,Left,Right]()` 來進行移動。移動的時候不可以超過地圖範圍 (撞到 counter 就不會移動,會直接浪費一回合)。每次移動會往其移動的方向移動地圖上的一格。
### 拿起/放下物品 Interact()
**面對著**任何台子使用 `Interact()` 可以進行拿起/放下物品的操作。特別注意,要「**面對著**」互動才可以,走到台子旁邊如果沒有面對他的話也不會進行操作。(同樣會浪費一回合)
可以拿起物品的情境有以下幾種:
1. 玩家的手是空的,並且對著有放著物品的 counter Interact (拿起物品)
2. 玩家的手是空的,並且對著儲藏箱 Interact (從儲藏箱裡面拿物品)
3. 玩家的手上拿著盤子,並且對 **不是生的** 而且 **盤子裡面沒有的** 食材 Interact (把食材放到盤子裡)
可以放下物品的情境有以下幾種:
1. 玩家的手上拿著物品,並且對著沒有放著物品的「**普通桌子**」Interact (放下物品)
2. 玩家的手上拿著物品,並且對著「**垃圾桶**」Interact (丟掉物品)
3. 玩家的手上拿著盤子 (上面可以放食材),並且對著「**送餐櫃台**」Interact (提交餐點)
4. 玩家的手上拿著「**番茄、生菜、起司塊**」,並且對著沒有放著物品的「**砧板**」Interact (把食材放到砧板上)
5. 玩家的手上拿著「**生肉餅、熟肉餅**」,並且對著沒有放著物品的「**平底鍋**」Interact (把肉餅放到平底鍋上)
6. :warning: 如果 **普通桌子** 上放了一個盤子,並且玩家的手上拿著 **不是生的** 而且 **盤子裡面沒有的** 食材。此時對著盤子 `Interact()` 會把食材放到盤子裡面,而不會把盤子拿起來。
### 切菜 InteractSpecial()
當「砧板」上面有「**番茄、生菜、起司塊**」的時候,對著「砧板」使用 `InteractSpecial()` 可以進行切菜的動作。
## 餐點 Recipe
總共有四種餐點:Salad、Burger、CheeseBurger、MegaBurger。他們的成分如下表:
| 餐點 | 成分 |
| -------- | -------- |
| Salad | 生菜片、番茄片 |
| Burger | 麵包、熟肉餅 |
| CheeseBurger | 麵包、起司片、熟肉餅 |
| MegaBurger | 麵包、生菜片、起司片、熟肉餅、蕃茄片 |
## 訂單 Order
每筆訂單包含了恰好一份餐點。
遊戲畫面的左上角可以看到等候中的訂單。等候中的訂單每 100 Round 會「試著」生成一次。總共同時最多會有四個等候中的訂單。如果等候中的訂單已經滿四個了,那這樣下次「試著」生成的時候就不會生成新的等候中的訂單。
請注意,遊戲固定每 100 Round 會試著生成一次等候中的訂單,就算因為已經滿了而沒有生成新的,也會再等 100 Round 才會再試著生成一次。
每當你提交一個等候中的訂單,會得到一個分數。提交餐點的分數會隨著經過的 round 數改變 (從該等候中的訂單第一次出現的 round 開始計算),其中計算規則如下:
| 100 Round 以內 | 101 ~ 200 Round | 201 ~ 300 Round 以內 | 超過 300 Round |
| -------- | -------- | -------- | - |
| 1000 | 700 | 350 | 200 |
## 地圖
地圖的大小 $9\times 21$。左上角的座標為 $(0,0)$,右下角的座標為 $(8,20)$。其中,遊戲最一開始的時候你會在 $(4,10)$ 的位置。如果想要跟一個 counter 進行互動,你需要面對他並且站在他的前面那幾格。舉例來說,如果想要跟起司塊儲藏櫃互動,就必須要站在 $(0,8),(0,9),(0,10)$ 這三格其中一格,並且面對起司塊儲藏櫃 (上方)。
當玩家在移動的時候,如果接下來會往地圖外面移動,那麼角色會直接停在原地不動,但他會面對原本要前進的方向。
特別注意,並不是所有 counter 的可互動範圍都有三格 ,有些只有一格或兩格,詳細請看下圖 (遊戲畫面可能會看起來有很多格,但實際情況請以下圖為主):

<center>
感謝 葉昱揚 (YYYeh) 做了超漂亮彩色地圖!
</center>
<!-- 
-->
# 程式碼
需要看的程式碼都在 `cserver` 這個資料夾裡面,需要看的程式碼總共有五個檔案:
* `kitchen.h`
* `GameController.h` / `GameController.cc`
* `UserAction.h` / `UserAction.cc`
你 **一定** 要實作的函式只有一個:`UserAction.cc` 中的 `UserAction::SendOperation(GameController& controller)`
這份大作業二你可以自由的增加,修改函式,但是有以下幾個限制:
## 限制
1. <b style="color:blue">不可以修改原本 `UserAction.h`, `GameController.h`, `kitchen.h` 裡面的程式碼</b>
* 可能會破壞 server,請不要修改他們
2. `GameController.cc` 裡面的檔案只可以修改 `Print` 開頭的函式
* 可以根據自己的喜好改變輸出文字,非必要
* **不可以修改 `GameController::ReceiveEvents` ,可能會破壞 server**
3. `UserAction.cc` 只有三個地方不可以修改:
* `// -- DON'T MODIFY ANY CODE ABOVE THIS LINE -- //` 以上的程式碼
* `UserAction::InitGame()` 整個函式
* `UserAction.h` 中有的函式的定義 (但還是可以修改函式內容)
## UserAction
### 必要函式
遊戲 server 會呼叫以下函式,所以不可以修改其 prototype (其實就是 `UserAction.h` 裡面定義的函式)。
* `UserAction::InitGame()`
* `UserAction::SendOperation()`
這兩個函式的功能在 [server 版遊戲架構](#遊戲架構),請根據裡面的說明來實作這些函式的功能。
理論上你只需要修改 `SendOperation` 這個函式即可。但也歡迎各位自己發揮想像力修改其他函式 ~
### UserAction::SendOperation()
每回合這個函式都會被呼叫一次 (總共會有 1800 次)。每次要作的事情是選一個操作去呼叫他對應的函式。所以要想想看要怎麼決定下一個要呼叫的函式是哪一個歐。
一個簡單的例子是像是 `DefaultInitialize()`,他使用了 `operations` 這個 `vector<string>`,每次取他的最後一個元素來當作下一步的指令。
### GameController Instance
`UserAction.cc` 裡面有一個 global 的 GameController 這個 class 可以用:
`GameController& controller = GameController::getInstance("default");`
不用自己建立一個 `GameController` 的 class,用這個就好了
### 輔助資料結構/函式
請看:[實作策略](#實作策略)
在 `UserAction.cc` 裡面可能還會有其他一些包含 `// TODO:` 的函式 (例如 `UserAction::Initialize`)。這些函式不一定要實作,遊戲也會可以正常進行。這些函式是給你們參考的一些方向,在設計策略的時候可以考慮完成那些函式。
當然,**你也可以自己隨意增加函式或程式碼,也不一定要實作那些函式,也不一定需要用到他們。各位可以依照自己的想法/策略去實作!**
#### Default 系列
有一些函式是 `Default` 開頭的,會是一些預設的函式。你們可以從這個函式去作修改,或是可能可以得到一些啟發。
* `DefaultSendOperation()`
* `DefaultInitialize()`
## GameController
`GameController` 這個 class 是用來控制遊戲角色的操作,以及得到遊戲數據的程式碼。
(除了修改輸出之外,請不要修改這裡面的程式碼)
接下來是一些你可能會使用到的函式:
### 角色操作
* `MoveUp()` / `MoveDown()` / `MoveLeft()` / `MoveRight()` : 讓角色上下左右移動一格
* `Interact()`: 拿起/放下物品
* `InteractSpecial()`: 切菜
### 遊戲數據
這些函式可以讓你獲得遊戲數據,不一定要全部用到,可以按照自己的策略使用這些函式。
| 函式 | 回傳值型態 | 功能說明 |
| -------- | -------- | -------- |
| `GetRound()` | `int` | 回傳遊戲進行到第幾回合 |
| `GetRecipeMode()` | `string` | 總共有三種回傳值:<br> 1. `"Salad"` <br>2. `"SaladAndCheeseBurger"`<br>3. `"AllRecipe"`<br>分別對應到三個不同的 `RecipeMode`<br> :warning: 不要在 `Initialize()` 的時候呼叫他!講師實作爛掉了,所以 `RecipeMode` 會在每回合 `ReceiveEvents` 的時候一起傳給遊戲。所以**在遊戲開始之後**再呼叫他,不然 server 有可能會 crash 掉
| `GetTotalScore()` | `int` | 回傳當下的總分 |
| `GetNewOrder()` | `Order` | 如果該回合有新訂單,會回傳該訂單的 `Order`<br>如果沒有,會回傳 `OrderId` 是 -1 的 `Order` |
| `GetOrderList()` | `vector<Order>` | 目前所有等候中的 `Order` 的 `vector` |
| `GetOrderDelivered()` | `pair<int,int>` | 大部分的時候都會回傳 `(0, 0)`。如果你在某一回合提交一個訂單,下一回合的回傳值會有兩種可能:<br>1. Order 配對成功:`(OrderID, 得到的 Score)`<br>2. Order 配對失敗:`(-1, 0)`|
| `GetPlayerPosition()` | `pair<int,int>` | 回傳玩家該回合所在的位置 |
| `GetPlayerHoldItems()` | `vector<Items>` | 回傳玩家手上的 **所有** 東西 |
| `GetFryingState()` | `FryingState` | 回傳該回合平底鍋上的肉餅的狀態 |
| `GetFryingTimer()` | `int` | 回傳肉餅從變成現在的狀態之後過了幾回合 |
## kitchen.h
`kitchen.h` 檔案裡面定義了許多實用的 `enum`、`struct`、跟`map` 資料結構
**enum 的順序是固定的,不可以改變 enum 內元素的順序,**
### Items (enum)
包含了 [盤子](#盤子 (Plate)) 跟 [食材](#食材 (Ingredients)),詳細內容請點超連結跳過去看
### Recipe (enum)
四種不同餐點的一個 `enum`。可以透過 [RecipeIngredients](#RecipeIngredients-mapltRecipe-vectorltItemsgtgt) 這個 `map` 拿到餐點對應的食材們。
### FryingState (enum)
在這個遊戲當中,只有肉餅可以被放到平底鍋上面煎。所以 `FryingState` 代表著平底鍋煎肉餅的四種可能的狀態:
| `FryingState` | 狀態 |
| -------- | -------- |
| `Idle` | 平底鍋閒置,沒有在煎肉餅 |
| `Frying` | 平底鍋正在煎 **生肉餅** |
| `Fried` | 平底鍋正在煎 **熟肉餅** (已經熟了) |
| `Burnt` | 肉餅已經變成 **碳粉肉餅** 了 (平底鍋沒有繼續煎肉餅) |
### Counter (enum)
定義了所有不同的 counter,counter 的種類可以在 [Counter](#Counter) 找到詳細的資訊。
這個 enum 可以幫助你決定你接下來要怎麼移動。(參考 `UserAction.cc` 裡面的 `MoveCounterToCounter` 函式)
### Order 訂單 (struct)
`Order` 這個 `struct` 存的是訂單的資訊。裡面包含了四個資料:
| 屬性 | 資料型態 | 意義 |
| -------- | -------- | - |
| `orderId` | `int` | 該訂單的編號 |
| `recipe` | `Recipe` |該訂單所要求的餐點 (每筆訂單只會要求一個餐點) |
| `score` | `int` | 成功將訂單送出的分數 (會隨時間變化,請見 [訂單](#訂單-Order)) |
| `arrivalRound` | `int` | 該訂單第一次出現的回合 |
### ItemsMap (map<Items, string>)
一個 `map`,用來把 `Items` 這個 `enum` 對應到相對應的字串
### RecipeMap (map<Recipe, string>)
一個 `map`,用來把 `Recipe` 這個 `enum` 對應到相對應的字串
### FryingStateMap (map<FryingState, string>)
一個 `map`,用來把 `FryingState` 這個 `enum` 對應到相對應的字串
### RecipeIngredients (map<Recipe, vector\<Items\>\>)
一個 `map`,可以從這個 `map` 拿到某個 `Recipe` 所需要的所有食材。
### 貼心範例
這裡的 `map` 為了防止各位不小心自己新增元素,都有使用 `const` 屬性。而帶有 `const` 屬性的 `map` 不能使用 `[]` 來存取,要改成使用 `.at()` 來存取。
舉例 (列印出 `Salad` 的食材):
```cpp=
vector<Items> saladRecipe = RecipeIngredients.at(Salad);
for (Items ingredient : saladRecipe) {
cout << ItemsMap.at(ingredient) << " ";
}
```
# 實作策略
以下的策略都只是一個方向,歡迎同學們多多發揮想像力!
## 回合制(策略)
回合制相對比較麻煩,因為一次只能作一個操作,但是製作一份餐點的操作會是連續的。
為了解決這個問題,可以透過先把所有做好一個餐點所需要的步驟放到一個 global 的「資料結構」裡面。然後每次要執行操作的時候就從這個資料結構裡面拿。常用的選擇有 陣列、`vector`、`queue`、`stack`。
在 `UserAction.cc` 裡面我設計了一個叫做 `operations` 的 `vector`。他的內容會由 `DefaultInitialize()` 去作初始化。
## 做好一道餐點
作一道餐點會需要很多的步驟。所以我們需要建立一個「流水線」的函式。這個流水線的函式裡面可能會包含很多其他的函式,每個函式都是其中的一個步驟。
在 `UserAction.cc` 裡面有四個流水線的函式可以去實作:
`MakeSalad`, `MakeBurger`, `MakeCheeseBurger`, `MakeMegaBurger`
其中,`MakeSalad` 裡面有放了很多註解,這些註解是做好一份沙拉的步驟拆解。同學們可以參考這些註解並且實作相對應的函式或功能。
另外,有一個函式叫做 `SimpleExample`,他裡面的內容是 `MakeSalad` 的前四個步驟的一種實作方式,同學們可以參考裡面的函式去實作剩下的內容。(或是也可以自己魔改!)
## Counter 座標
地圖上面有很多不同 counter,我們需要去維護這些 counter 的座標跟他們上面放了什麼東西。在 `UserAction.cc` 裡面有提供一個 `const map` 叫做 `counterPosition`。裡面提供了每一個 counter 對應到的「其中一個可能的座標」。因為只有其中一個可能的座標,所以不一定會可以解決所有的情況,同學們可以依據自己的策略去對這個 `map` 去作修改,或是另外定義資料結構。
另外,為了跟 Counter 互動,我們需要「面對他」,所以除了 `counterPosition` 這個 `const map` 之外,還有 `counterDirection` 這個 `const map`,代表要和 `counterPosition` 裡面的 Counter 互動要面對的方向。
## 移動方式
在處理移動的時候,有幾種可能的策略:
1. 先決定好接下來去的地方,然後把路徑先存到一個地方,之後就按照那個路徑去走
2. 每次動態決定下一步要怎麼走 (e.g. 每回合會有要前往的一個目標,每次往目標前進一步)
以上兩種策略都是可行的,可以根據自己的想法決定要使用哪一種移動的策略。這邊給出第一種策略的說明:
### 移動方式策略一
當我們決定好接下來要去的地方/座標了,就可以把整個路徑放到一個資料結構裡面 (例如上面的 `operations`)。這個時候我們可能就需要實作一些生成路徑的函式。以下是一些你們可以實作的函式:
- `MovePointToPoint(from, to)`: 從某個座標移動到某個座標的路徑
- `MovePointToCounter(from, to)`: 從某個座標移動到某個 counter 的路徑
- `MoveCounterToCounter(from, to)`: 從 `from` 這個 counter,接下來要移動到 `to` 這個 counter 的路徑
另外,有時候你可能已經移動到 counter 了,但是你還是需要轉個方向面對 counter 才能夠跟他互動。所以可以在這些函式裡面額外加一些判斷之類的決定要怎麼面對 counter。(或是也可以修改原本的 map 增加方位等等)
## 遊戲模式策略
不同的 Recipe (餐點)模式 (salad only, salad and cheeseburger, all recipe) 的策略可能會不一樣。所以會需要實作三種不同的遊戲模式的策略。
不同的 Recipe Mode 只差在訂單的餐點種類而已。所以我們可以實作一些函式,他們會根據當下的遊戲狀態決定接下來要作哪一種餐點 (或者是哪一個 Order 的 Recipe)。`UserAction.cc` 提供了三個函式的範例可以實作:
`SaladModeStrategy`, `SaladCheeseburgerModeStrategy`, `AllRecipeModeStrategy`
另外,`UserAction.cc` 裡面有一個 `GetNextOrder` 的函式。他會根據你當下選擇的 `RecipeMode` 回傳不同的 Strategy 結果,這樣就不用每次都去切 Strategy。
## 雜項
### 切菜
不同的食材要切起來的時間不一定,所以可以寫一個函式來處理要切幾次:
`void CutIngredient(int times)`
### 煎肉
(這個沒有輔助函式)
煎肉餅的步驟很簡單,只要把生肉餅放到平底鍋就可以了。但是如果放太久會烤焦,所以要記得去把肉餅拿起來!煎肉的策略可以在處理「流水線」的時候一起作!
# 成績計算
-----
* Report 40%
* Implementation: 10%
* Presentation: 15%
* Strategy and Others 20%
* Code & Result 55%
* salad only 15%
* salad and cheese 20%
* all recipe 20%
* Bonus 5% (Hand in bonus hw before 5/31 ends)
-----
* Report:寫一份 report 說明自己組別運用了什麼策略、在哪些部分用怎樣的方式去處理、一些細節的部分、團隊分工
* Implementation: 為了避免 Hard-Code (勞工智慧),以效率、可延展性、創意性等標準對 Code 進行評分
* Code and result:依照所有組別的相對成績來評分
* 會有 baseline,但會參考所有組別的相對成績