おそるおそるSwift 4-3 ハイローゲームを作る ~処理結果を表示する~

Swift5+Xcode11.6
今回のゴール
おそるおそる機能を追加していくことにして、前回作成した基盤部分の処理結果をデバッグウィンドウではなく、画面に表示させます。
配列の値が存在しないときの処理は?
とりあえず、ContentView.Swiftのコードです。結果を表示させるテキストフィールドを追加しています(67〜71行目)。また、後で説明しますが、抽選履歴の表示部分をprocessor.num(x)に書き換えています(29〜37行目)。
import SwiftUI
struct ContentView: View {
@ObservedObject var processor = Processor()
var body: some View {
VStack {
Text("High&Low Game!")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(Color.white)
.background(Color.orange)
Spacer()
HStack {
Spacer()
Text("3回前")
.frame(width: 50.0)
Text("2回前")
.frame(width: 50.0)
Text("前回")
.frame(width: 50.0)
Text("今回")
.font(.title)
.frame(width: 80.0)
Spacer()
}
HStack {
Spacer()
Text(processor.num(times:4))
.frame(width: 50.0)
Text(processor.num(times:3))
.frame(width: 50.0)
Text(processor.num(times:2))
.frame(width: 50.0)
Text(processor.num(times:1))
.font(.largeTitle)
.frame(width: 80.0)
Spacer()
}
Spacer()
HStack {
Spacer()
Button(action: {
self.processor.judge(choice: "high")
}) {
Text("High")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.white)
}
.frame(width: 100.0)
.background(Color.red)
Spacer()
Button(action: {
self.processor.judge(choice: "low")
}) {
Text("Low")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color.white)
}
.frame(width: 100.0)
.background(Color.blue)
Spacer()
}
Spacer()
Text(processor.result)
.font(.title)
.fontWeight(.heavy)
.foregroundColor(Color.orange)
.multilineTextAlignment(.center)
Text("$1")
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(Color.red)
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
} さて、これまでの抽選履歴を「3回前」「2回前」「前回」と表示させる設計にしましたが、ゲームスタートの段階では、配列には値が1つ(history[0] = 5)しかありません。このとき3回前の値をhistory[history.endIndex – 4]で取り出そうとしても、Out of Rangeエラーか何かが返ってくるのは容易に想像できます。
「オプショナル型とやらを使うのか?それともエラーが出たときの処理を書くのか?」等と悩んだのですが、history.endIndexの数を数えさせることにしました。ContentViewのTextフィールドにif文を置けないようなので、Processorクラスにnum関数を定義しました。
また、結果を表示させるためには戻り値では都合が悪いため、returnを止めてresult変数に代入させることにしました。
改造後のprocessorx.swiftです。
import Foundation
class Processor : ObservableObject {
@Published var history :[Int] = [5] //history:抽選数字履歴保持用配列
@Published var result: String = ""
var randNum: Int = 0 //randNum:乱数
// func judge(choice:String) -> String {
func judge(choice:String) {
randNum = Int.random(in:1...9)
history.append(self.randNum)
for (index, his) in history.enumerated() {
print("history[\(index)]: \(his)")
}
print("endIndex:\(history.endIndex)")
let prevNum:Int = history[history.endIndex - 2] //prevNum:1つ前の抽選数字
print ("prevNum:\(prevNum)")
let nowNum:Int = history[history.endIndex - 1] //nowNum:今回の抽選数字
print ("NowNum:\(nowNum)")
if (nowNum == prevNum) || ((nowNum >= prevNum)&&(choice == "high")) || ((nowNum <= prevNum)&&( choice == "low")) {
result = "Win"
} else {
result = "lose"
}
}
func num(times:Int) -> String {
if history.endIndex >= times {
return String(history[history.endIndex - times])
} else {
return "-"
}
}
} 何回前の抽選履歴を引いてくるのかを引数timesで指定させています。今回はそれほど面倒ではありませんでしたが、selfの使い方がまだよく飲み込めていません。そのうちピカーンと閃くことを期待しつつ、今のところはごまかしごまかし進めます。
それと、今までは何となく@State宣言した変数がないと動作しないような気がしていたのですが、このコードには1つもないことに気づきました(という私的発見のつぶやきです)。
次回は
ハイ&ローゲームの醍醐味は、成功すれば賞金が倍増する一方で、1回でも失敗したら全てを失うところなので、その辺りを作り込んでみようと思います(今のところ、ピクリとも動かない”$1″の辺りです)。