Episode 47 — 21 點

Shien
彼得潘的 Swift iOS / Flutter App 開發教室
17 min readFeb 24, 2022

完整操作

下注

lkj玩家在叫牌前要先下賭金,賭金會從本金拿取

var total = 5000
var bet = 0
@IBOutlet weak var betLabel: UILabel!
@IBOutlet weak var totalLabel: UILabel!

先宣告兩個變數跟標籤改變本金跟賭金

@IBAction func changeBet(_ sender: UIStepper) {
betLabel.text = "\(Int(sender.value))"
bet = Int(sender.value)
total = Int(sender.maximumValue)-bet
totalLabel.text = "\(total)"
}

利用 stepper 的值來改變賭金與本金

發牌

左:發玩家跟莊家的牌、中:發莊家的牌、右:發玩家的牌

發牌分發給玩家跟發給莊家,把這兩根拆開會比較好寫,但發牌前要先洗牌

洗牌

var nums = [Int]()
var suits = [String]()
// d = spade, c = heart, b = diamond, a = club
let codes = ["d", "c", "b", "a"]

我除了宣告數字跟花色外,還另外給花色一個代碼

func shuffleCards(){
for suit in 0...3 {
for num in 1...13 {
nums.append(num)
suits.append(codes[suit])
}
}

nums.shuffle()
suits.shuffle()
}

分別把數字跟花色變成 52 個,然後在洗牌

發牌給莊家

var banker = [String]()

宣告一個花色加數字的矩陣給莊家、假如 “4a” 就是梅花四

//發牌給莊家
func serveBanker(image: Int, time: Int){
banker.append("\(suits[0]+String(nums[0]))")
bankerCardImageViews[image].image = UIImage(named: "\(banker[image])")

//蓋牌
let animator = UIViewPropertyAnimator(duration: TimeInterval(time), curve: .linear) {
self.bankerCardImageViews[image].alpha = 1
}
animator.startAnimation()


//人像為十點
if nums[0] == 13 || nums[0] == 12 || nums[0] == 11 {
nums[0] = 10
}

//一點為十一點
if nums[0] == 1 {
if bankerValue < 11 {
nums[0] = 11
}
}

bankerValue += nums[0]

nums.removeFirst()
suits.removeFirst()

}

每發一次牌,數字矩陣跟花色矩陣的第一個會給出去,假設數字為[1,2,3],花色為[“a”, “b”, “c”],發的牌就是 “a1” 梅花 A。

因為發出去了所以最後數字跟花色記得除去第一個值。

發牌給玩家

var player = [String]()

宣告一個花色加數字的矩陣給玩家、假如 “4a” 就是梅花四

func servePlayer(image: Int, time: Int) {

//補足卡牌
if nums.count < 10 {
shuffleCards()
}


player.append("\(suits[0]+String(nums[0]))")
playerCardImageViews[image].image = UIImage(named: "\(player[image])")


let animator = UIViewPropertyAnimator(duration: TimeInterval(time), curve: .linear) {
self.playerCardImageViews[image].alpha = 1
}
animator.startAnimation()

//人像為十點
if nums[0] == 13 || nums[0] == 12 || nums[0] == 11 {
nums[0] = 10
}

//一點為十一點


if nums[0] == 1 {
if bankerValue < 11 {
nums[0] = 11
}
}

playerValue += nums[0]

nums.removeFirst()
suits.removeFirst()


playerValueLabel.text = "\(playerValue)"
}

跟莊家做法大同小異,只是這邊我多判斷如果牌數不夠要再補牌。

叫牌

下完注較第一次牌會各給莊家跟玩家兩張牌,且莊家的第一張牌會蓋住。玩家如果一直加牌可能會爆牌、過五關或21點。

//叫牌
@IBAction func call(_ sender: Any) {
betStepper.isHidden = true
callButton.isHidden = false
giveUPButton.isHidden = false
stopButton.isHidden = false
//蓋牌
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
self.backCardImageView.alpha = 1
}
animator.startAnimation()
//首次發牌
if image == 0 {
firstServe()
image += 2
checkPlayer()
} else if image < 5 {
servePlayer(image: image, time: 1)
image += 1
checkPlayer()
//過五關
if image == 5 && playerValue <= 21 {
playerResult(win: true,times: 3)
resultLabel.text = "玩家過五關!"
hideButtons()
readyToAgain()
}
}

當image 為零代表發的是第一張牌的話,就連發兩次。再如果小於五張則發一張給玩家,如果玩家拿到五張就過五關。

//首輪發牌
func firstServe(){
var cardImage = 0
var time = 0
for _ in 0...1 {
time += 1
serveBanker(image: cardImage,time: cardImage+1)
time += 1
servePlayer(image: cardImage, time: time)
cardImage += 1
}

}

第一次發牌要連續發兩張

爆牌

//檢查玩家手牌狀況
func checkPlayer(){
if playerValue > 21 {
resultLabel.text = "玩家爆牌"
callButton.isHidden = true
readyToAgain()
playerResult(win: false, times: 0)
hideButtons()
}
else if playerValue == 21 {
resultLabel.text = "玩家 Black Jack!"
callButton.isHidden = true
readyToAgain()
playerResult(win: true, times: 2.5)
hideButtons()
}
}

玩家的手牌超過 21 點即爆牌

//玩家輸贏賭金結果
func playerResult(win: Bool, times: Float){
//贏的話根據倍數獲得獎金
if win == true {
total += Int(times*Float(bet))
totalLabel.text = "\(total)"
resultLabel.text! += "\n玩家獲得 $\(Int(times*Float(bet)))"
} else {
if resultLabel.text != "平手" {
resultLabel.text! += "\n玩家損失 $\(bet)"
}
}

bet = 0
betLabel.text = "\(bet)"
betStepper.maximumValue = Double(total)
betStepper.value = 0


}

玩家爆牌賭金會直接歸零,本金自然也就少了。

停牌

玩家如果沒爆牌,可以選擇停牌來看莊家的牌況是怎樣以決定輸贏。但基本上小於 17 點就會輸,因為莊家的點數一定超過 17 點。

//停牌
@IBAction func stop(_ sender: Any) {
image = 2
hideButtons()
//開莊家首張牌
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
self.backCardImageView.alpha = 0
}
animator.startAnimation()

//莊家點數不足17點時需補發牌
if bankerValue < 17 {
while bankerValue < 17 && image != 5 {
serveBanker(image: image, time: 1)
image += 1
}
checkBanker()

} else {
checkBanker()
}
checkIsBankrupt()
}

當莊家牌不足17點會用 while 加牌直到超過 17點五張牌為止。

//檢查莊家手牌狀況
func checkBanker(){
//爆牌
if bankerValue > 21 {
resultLabel.text = "莊家爆牌"
playerResult(win: true, times: 2)
//黑傑克
} else if bankerValue == 21 {

resultLabel.text = "莊家 Black Jack!"
playerResult(win: false, times: 0)
} else {
//莊家手牌大
if bankerValue > playerValue {
resultLabel.text = "莊家獲勝"
playerResult(win: false, times: 0)
//玩家手牌大
} else if bankerValue < playerValue {
resultLabel.text = "玩家獲勝"
playerResult(win: true, times: 2)
//平手
} else {
resultLabel.text = "平手"
total += bet
totalLabel.text = "\(total)"
bet = 0
betStepper.maximumValue = Double(total)
betStepper.value = 0
betLabel.text = "\(bet)"
}

}
hideButtons()
readyToAgain()
bankerValueLabel.text = "\(bankerValue)"
}

最後階段就是用莊家的點數跟玩家點數比較。或看莊家點數是 21 點或爆牌。

保險

當莊家的第二張牌為 Ace 時,玩家可以選擇買保險。如果莊家是 21 點,玩家可以獲得兩倍理賠,當莊家不是 21 點,保金就會被收走。

//叫牌
@IBAction func call(_ sender: Any) {
betStepper.isHidden = true
callButton.isHidden = false
giveUPButton.isHidden = false
stopButton.isHidden = false
//蓋牌
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
self.backCardImageView.alpha = 1
}
animator.startAnimation()
//首次發牌
if image == 0 {
firstServe()
image += 2
checkPlayer()
} else if image < 5 {
servePlayer(image: image, time: 1)
image += 1
checkPlayer()
//過五關
if image == 5 && playerValue <= 21 {
playerResult(win: true,times: 3)
resultLabel.text = "玩家過五關!"
hideButtons()
readyToAgain()
}
}

//加注
if playerValue == 11 {
doubleButton.isHidden = false
}
//保險
if resultLabel.text?.contains("點") == true {
insuranceButton.isHidden = true
} else if banker[1].contains("1") && banker[1].count == 2 && total > 500 && playerValue < 21 {
insuranceButton.isHidden = false
}


checkIsBankrupt()
}

在玩家第一次叫牌時,就能判斷使否需要投保。

//使用保險
@IBAction func insure(_ sender: Any) {
insuranceView.isHidden = false
okButton.isHidden = true
insuranceStepper.maximumValue = Double(total)
}

//調整保險金
@IBAction func makeInsurance(_ sender: UIStepper) {
total = Int(insuranceStepper.maximumValue) - Int(insuranceStepper.value)
totalLabel.text = "\(total)"
insuranceLabel.text = "\(Int(insuranceStepper.value))"

if sender.value == 0 {
okButton.isHidden = true
} else {
okButton.isHidden = false
}
}

//使用保險金檢查是否莊家為黑傑克
@IBAction func checkIsBlackJack(_ sender: Any) {
if bankerValue == 21 {
insuranceStepper.value *= 2
total += Int(insuranceStepper.value)

resultLabel.text = "莊家為 21 點\n玩家獲得 $\(Int(insuranceStepper.value))"


} else {
resultLabel.text = "莊家非 21 點\n玩家損失 $\(Int(insuranceStepper.value))"
insuranceStepper.value = 0
}


insuranceLabel.text = "0"
insuranceStepper.maximumValue = 0
insuranceView.isHidden = true
insuranceButton.isHidden = true
}

按下保險會跳出保金視窗,選定保金後可以查看莊家是否為 21 點。

加注

當玩家目前點數總和為 11 點,可以選擇加注賭金。但加注後只會發一張牌且立即開結果。

//叫牌
@IBAction func call(_ sender: Any) {
betStepper.isHidden = true
callButton.isHidden = false
giveUPButton.isHidden = false
stopButton.isHidden = false
//蓋牌
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
self.backCardImageView.alpha = 1
}
animator.startAnimation()
//首次發牌
if image == 0 {
firstServe()
image += 2
checkPlayer()
} else if image < 5 {
servePlayer(image: image, time: 1)
image += 1
checkPlayer()
//過五關
if image == 5 && playerValue <= 21 {
playerResult(win: true,times: 3)
resultLabel.text = "玩家過五關!"
hideButtons()
readyToAgain()
}
}

//加注
if playerValue == 11 {
doubleButton.isHidden = false
}

//保險
if resultLabel.text?.contains("點") == true {
insuranceButton.isHidden = true
} else if banker[1].contains("1") && banker[1].count == 2 && total > 500 && playerValue < 21 {
insuranceButton.isHidden = false
}

checkIsBankrupt()
}

每次叫牌時可以知道玩家是否為 11 點且跳出加注按鈕。

//加注
@IBAction func double(_ sender: Any) {
hideButtons()
total -= bet
bet *= 2
totalLabel.text = "\(total)"
betStepper.maximumValue = Double(total)
betStepper.value = 0
betLabel.text = "\(bet)"
servePlayer(image: 2, time: 1)
checkPlayer()
if playerValue < 21 {
stop((Any).self)
}
}

加注後賭金多一倍,本金會減少。玩家如果不是 21 點或爆牌,就開莊家的牌比較。

--

--