# APCS實作題2016年10月第4題:棒球遊戲
> 第1版:2023年2月14日
> 第2版:2023年6月14日,加上 C++ 程式碼
> 第3版:2023年10月19日,將 Python 程式碼改成較為精簡的版本
> 作者:王一哲
> 題目來源:[105年10月29日實作題第4題](https://apcs.csie.ntnu.edu.tw/wp-content/uploads/2020/11/1051029APCSImplementation.pdf)
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=c297)
<br />
## 題目
### 問題描述
謙謙最近迷上棒球,他想自己寫一個簡化的棒球遊戲計分程式。這個程式會讀入球隊
中每位球員的打擊結果,然後計算出球隊的得分。
這是個簡化版的模擬,假設擊球員的打擊結果只有以下情況:
1. 安打:以 1B, 2B, 3B 和 HR 分別代表一壘打、二壘打、三壘打和全(四)壘打。
2. 出局:以 FO, GO, 和 SO 表示。
這個簡化版的規則如下:
1. 球場上有四個壘包,稱為本壘、一壘、二壘和三壘。
2. 站在本壘握著球棒打球的稱為「擊球員」,站在另外三個壘包的稱為「跑壘員」。
3. 當擊球員的打擊結果為「安打」時,場上球員(擊球員與跑壘員)可以移動;結果為「出局」時,跑壘員不動,擊球員離場,換下一位擊球員。
4. 球隊總共有九位球員,依序排列。比賽開始由第 1 位開始打擊,當第 $i$ 位球員打擊完畢後,由第 $(i+1)$ 位球員擔任擊球員。當第九位球員完畢後,則輪回第一位球員。
5. 當打出 K 壘打時,場上球員(擊球員和跑壘員)會前進 K 個壘包。從本壘前進一個壘包會移動到一壘,接著是二壘、三壘,最後回到本壘。
6. 每位球員回到本壘時可得 1 分。
7. 每達到三個出局數時,一、二和三壘就會清空(跑壘員都得離開),重新開始。
現在請你也寫出具備這樣功能的程式,計算球隊的總得分。
<br />
<img height="30%" width="30%" src="https://i.imgur.com/LJSgADK.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<br />
### 輸入格式
1. 每組測試資料固定有十行。
2. 第一到九行,依照球員順序,每一行代表一位球員的打擊資訊。每一行開始有一個正整數 $a$ ($1 \leq a \leq 5$),代表球員總共打了 $a$ 次。接下來有 $a$ 個字串(均為兩個字元),依序代表每次打擊的結果。資料之間均以一個空白字元隔開。球員的打擊資訊不會有錯誤也不會缺漏。
3. 第十行有一個正整數 $b$ ($1 \leq b \leq 27$),表示我們想要計算當總出局數累計到 $b$ 時,該球隊的得分。輸入的打擊資訊中至少包含 $b$ 個出局。
<br />
### 輸出格式
計算在總計第 $b$ 個出局數發生時的總得分,並將此得分輸出於一行。
<br />
### 範例一:輸入
```
5 1B 1B FO GO 1B
5 1B 2B FO FO SO
4 SO HR SO 1B
4 FO FO FO HR
4 1B 1B 1B 1B
4 GO GO 3B GO
4 1B GO GO SO
4 SO GO 2B 2B
4 3B GO GO FO
3
```
### 範例一:正確輸出
```
0
```
(說明)
1B:一壘有跑壘員。
1B:一、二壘有跑壘員。
SO:一、二壘有跑壘員,一出局。
FO:一、二壘有跑壘員,兩出局。
1B:一、二、三壘有跑壘員,兩出局。
GO:一、二、三壘有跑壘員,三出局。
達到第三個出局數時,一、二、三壘均有跑壘員,但無法得分。因為 $b = 3$,代表三個出局就結束比賽,因此得到 0 分。
<br />
### 範例二:輸入
```
5 1B 1B FO GO 1B
5 1B 2B FO FO SO
4 SO HR SO 1B
4 FO FO FO HR
4 1B 1B 1B 1B
4 GO GO 3B GO
4 1B GO GO SO
4 SO GO 2B 2B
4 3B GO GO FO
6
```
### 範例二:正確輸出
```
5
```
(說明)接續範例一,達到第三個出局數時未得分,壘上清空。
1B:一壘有跑壘員。
SO:一壘有跑壘員,一出局。
3B:三壘有跑壘員,一出局,得一分。
1B:一壘有跑壘員,一出局,得兩分。
2B:二、三壘有跑壘員,一出局,得兩分。
HR:一出局,得五分。
FO:兩出局,得五分。
1B:一壘有跑壘員,兩出局,得五分。
GO:一壘有跑壘員,三出局,得五分。
因為 $b = 6$,代表我們要計算的是累積六個出局時的得分,因此在前 3 個出局數時得 0 分,第 4~6 個出局數得到 5 分,因此總得分是 0 + 5 = 5 分。
<br />
### 評分說明
輸入包含若干筆測試資料,每一筆測試資料的執行時間限制(time limit)均為 1 秒,依正確通過測資筆數給分。其中:
- 第 1 子題組 20 分,打擊表現只有 HR 和 SO 兩種。
- 第 2 子題組 20 分,安打表現只有 1B,而且 b 固定為 3。
- 第 3 子題組 20 分, b 固定為 3。
- 第 4 子題組 40 分,無特別限制。
<br />
## Python 程式碼
```python=
players = {} # 字典格式,儲存球員各打席的資料
for i in range(1, 10): # 棒次 i,由標準輸入讀取打擊表現
line = list(input().split())
players[i] = line[1:]
b = int(input())
# 印出各打席資料及輸出分數時的出局數,提交程式碼測試時要註解或刪除
#print(players)
#print(b)
outs = 0 # 出局數
count = 0 # 總出局數
runs = 0 # 得分
first, second, third = False, False, False # 各壘包是否有人
idx = 1 # 目前的打者棒次
pa = 0 # 第幾打席,索引值由0開始
# 主要的程式碼,總出局數小於題目要求的b時繼續執行
while count < b:
play = players[idx][pa] # 讀取打者該打席的表現
if play == "1B": # 一壘安打
if third == True: runs += 1; third = False
if second == True: third = True; second = False
if first == True: second = True; first = False
first = True
elif play == "2B": # 二壘安打
if third == True: runs += 1; third = False
if second == True: runs += 1; second = False
if first == True: third = True; first = False
second = True
elif play == "3B": # 三壘安打
if third == True: runs += 1; third = False
if second == True: runs += 1; second = False
if first == True: runs += 1; first = False
third = True
elif play == "HR": # 全壘打
if third == True: runs += 1; third = False
if second == True: runs += 1; second = False
if first == True: runs += 1; first = False
runs += 1
elif play == "FO" or play == "GO" or play == "SO": # 出局
outs += 1; count += 1
if outs == 3: # 三出局,壘包清空,出局數 outs 歸零
first = second = third = False
outs = 0
# 印出此打者打擊後壘上狀況、分數、出局數,提交程式碼測試時要註解或刪除
#print("idx: {:d}, First: {:}, Second: {:}, Third: {:}, Runs: {:d}, Outs:{:d}".format(idx, first, second, third, runs, outs))
idx += 1 # 棒次 idx 加1,若棒次大於9,輪回第1棒,打席 pa 加1
if idx > 9: idx -= 9; pa += 1
print(runs) # 印出第b個出局數時的分數,提交程式碼測試時維一印出的值
```
<br />
1. 程式碼中的變數名稱及用途已經寫在後方的註釋中,為了便於理解變數的用途,盡量採用棒球術語作為變數名稱,例如得分 (runs)、出局數 (outs)、打席 (plate appearance, PA),有興趣的同學可以上網搜尋一下。
2. 我選擇用字典格式儲存球員打席資料,key 值為棒次,格式為整數;元素值為各打席表現,格式為字串;不儲存打席數;資料內容為
```python
{1: ['1B', '1B', 'FO', 'GO', '1B'], 2: ['1B', '2B', 'FO', 'FO', 'SO'],
3: ['SO', 'HR', 'SO', '1B'], 4: ['FO', 'FO', 'FO', 'HR'],
5: ['1B', '1B', '1B', '1B'], 6: ['GO', 'GO', '3B', 'GO'],
7: ['1B', 'GO', 'GO', 'SO'], 8: ['SO', 'GO', '2B', '2B'],
9: ['3B', 'GO', 'GO', 'FO']}
```
3. 依據打席表現可以分為以下5種狀況
1. 第21 ~ 25行:一壘安打 (1B),若三壘有人得1分、三壘清空;若二壘有人推進到三壘、二壘清空;若一壘有人推進到二壘、一壘清空;打者上一壘。
2. 第26 ~ 30行:二壘安打 (2B),若三壘有人得1分、三壘清空;若二壘有人得1分、二壘清空;若一壘有人推進到三壘、一壘清空;打者上二壘。
3. 第31 ~ 35行:三壘安打 (3B),若三壘有人得1分、三壘清空;若二壘有人得1分、二壘清空;若一壘有人得1分、一壘清空;打者上三壘。
4. 第36 ~ 40行:全壘打 (HR),若三壘有人得1分、三壘清空;若二壘有人得1分、二壘清空;若一壘有人得1分、一壘清空;打者回本壘得1分。
5. 第41、42行:飛球出局 (FO)、滾地球出局 (GO)、三振出局 (SO),出局數 outs 加1,總出局數 count 加1。
4. 第43 ~ 45行:三出局,壘包清空,出局數 outs 歸零。
5. 第48、49行:棒次 idx 加1,若 idx 等於 9,輪回第1棒,打席數 pa 加1。
6. 第51行:印出第b個出局數時的總得分,也是提交程式碼測試時維一印出的值。
6. 於 ZeroJudge 測試結果,每筆測資花費時間約 21 ms,使用記憶體 3.4 MB。
<br /><br />
## C++ 程式碼
```cpp=
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
// 由標準輸入讀取各棒次打席數存入陣列 PAs、打擊表現存入陣列 players
int PAs[9];
string** players = new string* [9];
for(int i=0; i<9; i++) {
cin >> PAs[i];
players[i] = new string[PAs[i]];
for(int j=0; j<PAs[i]; j++) {
cin >> players[i][j];
}
}
// 由標準輸入讀取題目要求的出局數存入 b
int b; cin >> b;
// 除錯用,印出打擊表現
/* for(int i=0; i<9; i++) {
for(int j=0; j<pa[i]; j++) {
cout << players[i][j] << " ";
}
cout << endl;
}*/
// 依序為出局數、總出局數、得分、目前打者棒次索引值由0開始、第幾打席索引值由0開始
int outs = 0, count = 0, runs = 0, idx = 0, pa = 0;
// 各壘包是否有人
bool first = false, second = false, third = false;
// 比賽過程
while(count < b) {
string play = players[idx][pa]; // 讀取打者該打席的表現
if (play == "1B") { // 一壘安打
if (third) { runs++; third = false; }
if (second) { third = true; second = false; }
if (first) { second = true; first = false; }
first = true;
} else if (play == "2B") { // 二壘安打
if (third) { runs++; third = false; }
if (second) { runs++; second = false; }
if (first) { third = true; first = false; }
second = true;
} else if (play == "3B") { // 三壘安打
if (third) { runs++; third = false; }
if (second) { runs++; second = false; }
if (first) { runs++; first = false; }
third = true;
} else if (play == "HR") { // 全壘打
if (third) { runs++; third = false; }
if (second) { runs++; second = false; }
if (first) { runs++; first = false; }
runs++;
} else if (play == "FO" || play == "GO" || play == "SO") { // 出局
outs++; count++;
}
// 三出局,壘包清空,出局數 outs 歸零
if (outs == 3) {
first = false; second = false; third = false; outs = 0;
}
// 棒次 idx 加1,若棒等於9,輪回第1棒,idx = 0,打席 pa 加1
idx++;
if (idx == 9) { idx = 0; pa++; }
}
// 印出第b個出局數時的分數,提交程式碼測試時維一印出的值
cout << runs << endl;
return 0;
}
```
<br />
1. 程式運作邏輯與 Python 程式碼相同,以下只說明不同之處。
2. 第7 ~ 15行:由標準輸入讀取各棒次打席數存入整數陣列 PAs、打擊表現存入二維字串陣列 players。
3. 第27行:棒次索引值 idx 由0開始。
4. 第62、63行:棒次 idx 加1,若棒等於9,輪回第1棒,idx = 0,打席數 pa 加1。
5. 第67行:印出第b個出局數時的總得分,也是提交程式碼測試時維一印出的值。
6. 於 ZeroJudge 測試結果,每筆測資花費時間約 2 ms,使用記憶體 332 kB。
<br /><br />
---
###### tags:`APCS`、`C++`、`Python`