---
# System prepended metadata

title: 9/24 Arcade也能學演算法Ouo!? 演算法遊戲(2)
tags: [2022 iThome 鐵人賽]

---

---
tags: 2022 iThome 鐵人賽 
title: 9/24 Arcade也能學演算法Ouo!? 演算法遊戲(2)
AUTHOR: 鍾佳龍
---
# Arcade再進化
## Arcade也能學演算法Ouo!? 演算法遊戲!(2)
### **第二篇：油漆桶遊戲-實作篇**

> ### 大綱
> * 遊戲內容
> * 製造地圖
> * 選擇數字
> * 遊戲運作
> * 在arcade運作遇到的限制

> ## 成果預覽
![](https://i.imgur.com/LkofOqF.gif )
[遊戲連結](https://makecode.com/_9yfiWkC6y5zv)
選擇數字後 可以切換到相對應的顏色
按下A鍵後從箭頭處開始變色 想辦法讓畫面變成相同顏色

> ## 製造地圖
我們用上回講過的地圖生成函數改寫一下。
這邊要提醒一下arcade的特性，
在宣告陣列的時候不能是空陣列
所以我先在裡面塞東西。
```javascript=
// 地圖初始化
let map = [[0],[0]];
map = []
for (let i = 0; i < map_width; i++) {
    map.push([]);
    for (let j = 0; j < map_height; j++) {
        map[i].push(0);
    }
}
```
然後執行地圖生成，這些都是上回講過的，這邊就不細講了
```javascript=
// 生成斑點
function make_area(x:number, y:number, area_color:number, count:number) {
    let now_x = x; // 生成斑點的x座標
    let now_y = y; // 生成斑點的y座標
    for (let i = 0; i < count; i++) { // 嘗試生成count次
        if (map[now_x][now_y] == area_color) { // 如果已經是目標顏色，則不再生成
            i + 1;
        }
        map[now_x][now_y] = area_color; // 生成斑點
        let direction = Math.floor(Math.random() * 4);  // 隨機製造下次生成的方向
        if (direction == 0 && now_x > 0) {
            now_x -= 1;
        } else if (direction == 1 && now_x < map.length - 1) {
            now_x += 1;
        } else if (direction == 2 && now_y > 0) {
            now_y -= 1;
        } else if (direction == 3 && now_y < map.length - 1) {
            now_y += 1;
        }
    }
}
// 地圖生成
function create_map() {
    for (let i = 0; i < map.length; i++) {
        for (let j = 0; j < map.length; j++) {
            if (Math.random() > dispersion * 0.95) { // 有機率生成斑點
                make_area(i, j, Math.floor(Math.random() * color_number-1)+1, Math.floor((Math.random() + 1) * 5) * dispersion);
            }
        }
    }
}
```
最後輸出地圖。
這個輸出的部份我們不像在console一樣是用文字輸出。
所以我們先建立地圖物件
```javascript=
let map_object = sprites.create(img`
    ...
`, SpriteKind.Player)
map_object.setScale(8, ScaleAnchor.Middle)
map_object.setPosition(115, 100)
```
用`map_object.setScale(8, ScaleAnchor.Middle)`設定物件像素比例
用`map_object.setPosition(115, 100)`設定地圖位置
然後是改變地圖圖片
```javascript=
function print_map() {
    let create_map = image.create(map_width, map_height)
    for (let i = 0; i < map.length; i++) { 
        for (let j = 0; j < map.length; j++) {
            if(map[i][j] == 0){
                create_map.setPixel(i, j, 10)
            }else{
                create_map.setPixel(i, j, map[i][j])
            }
        }
    }
    map_object.setImage(create_map)
}
```
創建一個圖片
`let creat_map = image.create(map_width, map_height)`
設定像素顏色
`create_map.setPixel(i, j, color)`
下面這段的部分是因為arcade的0顏色是代表透明色 所以我將它改為代碼10的顏色
```javascript=
if(map[i][j] == 0){
    create_map.setPixel(i, j, 10)
}else{
    create_map.setPixel(i, j, map[i][j])
}
```
最後再讓地圖物件的圖片改為剛剛的`create_map`就完成地圖的顯示了


> ## 改變數字
**構思**
**偵測按鍵按壓事件 > 改變數字 > 在畫面中視覺化**
有了構思就可以開始實作了
```javascript=
controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
   ...
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    ...
})
```
上面的函式是取得右鍵的按鍵事件，
如果左鍵被按下就會執行裡面的調用函式。
下面則是左鍵。
接下來要在裡面做一些事
![](https://i.imgur.com/9IpeuL5.png)
可以看到上圖，數字從零開始往右遞增。
所以我們宣告一個變數用來存取現在的數字。
```javascript=
let now_number = 0
```
然後往右+1往左-1。
再來，我們的數字只有0到9我們不能讓數字超過，
所以我們利用取餘的方式替代if判斷式。
![](https://i.imgur.com/cv2agWp.jpg)

*蝦你說用if會更簡單喔? ~~我單純想秀不行嗎~~XD*
```javascript=
controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (10+(now_number-1))%10
})
```
接下來就是顯示出來啦
創立一個陣列儲存圖片
```javascript=
let chose_number_img = [
    img`
        ...
    `,
    img`
        ...
    `,
    ...
    ]
```
然後在處理完數字加減後變更圖片
這邊用到`object.setImage(Img)`更換物件圖片
```javascript=
controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
    chose_number.setImage(chose_number_img[now_number])
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
    chose_number.setImage(chose_number_img[now_number])
})
```
這樣選擇的部分就告一段落了。

> ## 遊戲運作
```javascript=
//main
create_map(); // 生成地圖
print_map(); // 輸出地圖
```
開始時先呼叫函數
再來是實際玩的部分
**一樣，先做構想**
**按下A鍵後 > 傳入now_number > 執行flood fill演算法 > 改變圖片**
```javascript
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
    flood_fill(map[0][0], now_number, 0, 0)
    print_map()
    if (check_end()) { // 檢查是否結束
        console.log("you win");

        game.over(true)
    }
})
```
所我們每次按下A鍵的時候執行`flood fill`演算法改變map陣列
再來執行`print_map`將結果印出來
最後看看是否都是同一個顏色了，結束了就結束遊戲。
檢查函數就沒甚麼好說的了，各位看看應該就懂了。
```javascript=
// 檢查是否結束
function check_end() {
    let end = true;
    let color = map[0][0]; // 設定初始顏色
    for (let i = 0; i < map.length; i++) {
        for (let j = 0; j < map.length; j++) {
            if (map[i][j] != color) { // 如果有不同的顏色，則還沒結束
                end = false;
                return end;
            }
        }

    }
    return end;
}
```
恭喜各位看到這裡，基本上遊戲就做完啦！
不過有人想說這地圖有點小，我可以增加難度讓地圖變大嗎?

> ## 在arcade運作遇到的限制
各位還記的上回我說過`flood fill`是怎麼運作的嗎?
沒錯是用遞迴的方式。
所以有學過遞迴的現在應該想到一件事，**遞迴有層數限制**。
但在python或是javascript本身理論上可以跑到九百多層。
不過各位的arcade機是用單片機運作的，效能跟電腦差了好幾倍。
說到這邊大家應該理解了。沒錯!arcade的遞迴層數沒有很多，具體有多少我沒有實測。
不過你如果地圖太大會讓搜尋的時候遞迴太多層，導致程式直接被kill掉。
目前這個大小算是在可以邊緣，有可能會被kill不過大部分時間是可以完成遊戲的。



---
> ## 下期預告
> **各種棋篇**
> \>\_一篇文章教你製作連線相關的棋類
> *黑白棋 立體四子棋 圈圈叉叉*

###### 作者：鍾佳龍