題目如下。

Challenge

實作俄羅斯方塊的計分系統

背景

俄羅斯方塊是一款益智遊戲,
最初是由蘇聯俄羅斯軟件工程師 Alexey Pajitnov 設計的。

關於計分制度

計分公式是基於 當玩家一次清除越多條線,所獲得的分數就會越多 的想法下去設計的。
例如,消去一行得 40 分消去四行得 1200 分

且還搭配了一個分數會隨等級倍增的規則。
遊戲會從等級 0 開始
玩家每清除 10 行,等級就會上升
值得注意的是,等級上升之後,消除的總行數是不會重置的。

關於這個題目,您可以參考此表:

Level 消去 1 行 消去 2 行 消去 3 行 消去 4 行
0 40 100 300 1200
1 80 200 600 2400
2 120 300 900 3600
3 160 400 1200 4800
7 320 800 2400 9600

你必須透過這張表格得出的公式來以此類推到 level N

任務

用 Nintendo 的原始計分系統來計算俄羅斯方塊的分數

type Line = 0 | 1 | 2 | 3 | 4; type getScore = (list: Line[]) => number;

Input

範例:[4, 2, 2, 3, 3, 4, 2]
隨機長度的 array 且裡面只包含數字 04

Output

計算的最終分數。

Example

1
getScore([4, 2, 2, 3, 3, 4, 2]); // returns 4900
  1. 消去 4 行 +1200 (當前等級0)。分數:0 + 1200 = 1200
  2. 消去 2 行 +100。分數:1200 + 100 = 1300
  3. 消去 2 行 +100。分數:1300 + 100 = 1400
  4. 消去 3 行 +300(當前等級仍為0)。分數:1400 + 300 = 1700
    消除的總行數 11=(4 + 2 + 2 + 3),因此等級上升到1(每消除10行上升一次)
  5. 消去 3 行 +600 (當前等級 1)。分數:1700 + 600 = 2300
  6. 消去 4 行 +2400。分數:2300 + 2400 = 4700
  7. 消去 2 行 +200。分數:4700 + 200 = 4900

Answer

1
2
3
4
function getScore(list) {

return score
}



看完題目的時候想說感覺蠻簡單的,實作上感覺有些邏輯有點不熟悉,還是花了點時間想了一下。


## 馬上開始
  1. 觀察分數如何得到
    觀察一下行數&等級的表格,第 0 級消除 1~4 行後得到的分數是基礎值,接下來依照級數,每一級的分數會是 ( 消除 x 行的基礎分數 _ (級數+1)_ 2)
    所以這邊得到幾個小結論

    1. 消除第 0 級的 1~4 行分數 => [40, 100, 300, 1200]
    2. 計算分數的公式會是 ( 消除 x 行的基礎分數 _ (級數+1)_ 2)
  2. 再來先假設只有傳入一個數字吧,會比較好思考。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 只消掉一行
    let lines = 1;
    function getScore(lines) {
    //消除行數
    let line = 0;
    //基礎分數
    let basic = [40, 100, 300, 1200];
    //級數
    let level = 0;
    // 公式
    return basic[lines - 1] * (level + 1) * 2;
    }
    console.log(getScore(lines));

    應該不難。

  3. 傳入多個參數要怎麼進行?
    從結果來看,因為要計算總合,這邊就直接聯想到 reduce(),還有一些需要特別注意的細節。

    1. 消除分數會隨著等級提升而提高,但是以新數值傳入前的等級計算
    2. 每一輪等級計算是以還沒加上傳入行數的等級為基礎
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function getScore(list) {
    let score_basic = [40, 100, 300, 1200];
    let level_times = 2;
    let level = 0;
    let line = 0;
    return list.reduce((acc, value) => {
    //以累積的行數(line)計算目前等級
    level = Math.floor(line / 10);
    //計算等級後,讓累績行數加上新增的消除行數 (為下一圈做準備)
    line += value;
    return acc + score_basic[value - 1] * (level + 1);
    }, 0);
    }
    getScore([4, 2, 2, 3, 3, 4, 2]); // 4900
  4. 小小檢討一下
    後來看了一下其他前輩的算法之後發現,在計算等級的地方,我想得有點複雜
    level = Math.floor(line/10)
    這一段其實可以改成
    level = (line- line%10)/10
    就不需要用到 Math 了。




後記

後來看到丟出這段題目的前輩的解法,沒看過 Functional Programming,直接被嚇到 XDD,程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
// J3小 XDD
export function getScore(arr: number[]) {
return arr.reduce(
({ scores, totalLines }, lines) => ({
scores:
scores +
[0, 40, 100, 300, 1200][lines] * (Math.floor(totalLines / 10) + 1),
totalLines: totalLines + lines,
}),
{ scores: 0, totalLines: 0 }
).scores;
}

慌張的我馬上去查了一下 FP 在幹嘛,學習清單又多一項可以學了 (擦汗..

PS. 直接跳過傳入資料的驗證 XD


本文章若有任何資訊誤植或侵權,煩請告知,我會立刻處理。