# 開啟與關閉傳送門

{% tabs %}
{% tab title="main" %}

```swift
// 紀錄數量
var gems = 0   // 收集的寶石數
var turns = 0  // 轉反方向的次數 (每次迴轉都會 +2)

let portal = purplePortal

// 關閉傳送門
portal.close()

// 如果收集的寶石不到 7 個，就繼續走。
while gems < 7 {
    walk(along: .left, onMove: {
        // 往前走時，如果遇到寶石就收集、遇到開關就打開
        if isOnGem { 
            collectGem()
            gems += 1
        } else if isOnClosedSwitch {
            toggleSwitch()
        }
    }, onTurnOtherSide: { // 往反方向轉時，紀錄轉向的次數
        turns += 1
        if turns == 2 { portal.open()  } // 第一次迴轉時，打開傳送門
        if turns == 4 { portal.close() } // 第二次迴轉時，關閉傳送門
    })
}
```

{% endtab %}

{% tab title="Portal" %}

```swift
extension Portal {
    func close() { isActive = false }
    func open () { isActive = true  }
}
```

{% endtab %}

{% tab title="Side" %}

```swift
// 貼著哪邊走的選項
enum Side {
    case left, right
}

// 將「左右邊被擋住」的狀況放到同一個函數中處理。
func isBlockedOn(_ side: Side) -> Bool {
    side == .left ? isBlockedLeft : isBlockedRight
}

// 將「向左右轉」放到同一個函數中處理。
func turn(_ side: Side) {
    side == .left ? turnLeft() : turnRight()
}

// 轉反方向
func turnOtherSide(from side: Side) {
    side == .left ? turnRight() : turnLeft()
}
```

{% endtab %}

{% tab title="navigation" %}

```swift
typealias Handler = () -> Void

// ⭐️ 沿著某一邊走的策略，
//    將來可以寫入 Actor 的 extension 中。
func walk(
    along side     : Side = .left, // 沿著哪邊走（預設左邊）
    onMove         : Handler = {}, // 往前走一步時要做的事
    onTurnOtherSide: Handler = {}  // 往反方向轉時要做的事
) {
    if !isBlockedOn(side) { // 如果指定邊沒擋住，就走這一邊
        turn(side)
        moveForward()
        onMove()
    } else if !isBlocked {  // 不然如果前面沒擋住，就走前面
        moveForward()
        onMove()
    } else {                // 否則就轉向另一邊
        turnOtherSide(from: side)
        onTurnOtherSide()
    }
}
```

{% endtab %}
{% endtabs %}
