【SwiftUI】Conway's Game of Life

2次元配列を扱っていると作りたくなります。

👉 Conway's Game of Life - Wikipedia hatena-bookmark

GitHub に上手できれいなコードが公開されているので流用してみます。

👉 lifegame/swift/lifegame.swift at master · tex2e/lifegame hatena-bookmark

SwiftUI周りのみ書いてみました。

タップでスタート、ストップ。

ダブルタップでリセットです。

少し思うのが、

GeometryReader

てのどうなのか。

なんとなく気持ち悪くないですかね。

👉 Is SwiftUI GeometryReader bad? : iOSProgramming hatena-bookmark


【Swift】配列をコンソールにテーブル形式で表示する

見づらい配列を Xcode コンソールにテーブル表示します。

というのをやってみました。

等幅フォント 英語:日本語 = 1:2 が必要です。


 

🤔 文字数のカウント

String.isASCII を使ってアスキー文字なら 半角 で 1、

その他は 全角 で 2、

としてカウントします。


print(
  Array("1234ご")
    .map { $0.isASCII ? 1 : 2 }
    .reduce(0, +)
)

// 6

Array.reduce() で一文字ごとのカウント数を合計しています。

👉 reduce(_:_:) | Apple Developer Documentation hatena-bookmark

こんなことができるんですね !

 

🤔 文字列のパディング

String.padding(toLength:withPad:startingAt:)

を使おうとしたけれど、

うまくいかなかったので以下のようにやります。


print(
  String(
    repeating: "*",
    count: 10 - 6
  ) + "みかん"
)

// ****みかん

半角10文字分まで文字列「みかん」をパディングしています。

👉 padding(toLength:withPad:startingAt:) | Apple Developer Documentation hatena-bookmark

 

🤔 まとめ

そんな処理を使って、

以下の順番で処理していきます。


1. 縦横を入れ替え(転置)したあと、

2. 1行ごとに最大半角文字数をカウントして、

3. それぞれをパディングして、

4. 再度転置して出力する

転置を使うことで、

テーブル縦方向の最大文字数を取得しやすくしています。

👉 【Swift】2次元配列 で 転置行列 ( transpose matrix ) hatena-bookmark



しかし、

絵文字の文字幅って微妙です。


【 Swift 】2次元配列と1次元配列の相互変換 🤔

こんな12年前の so の書き込み。


p.x = index / 3;
p.y = index % 3;

👉 c# - Convert 1D array index to 2D array index - Stack Overflow hatena-bookmark

さすが、先人先生。

やってみました。

長ったらしいですが考え方を思い出したいときのために。

 

🤔 配列をn個ずつに分割する

副産物としてこんなかんじに書けました。


extension Array {
  func chunked(by: Int) -> [[Element]] {
    return  (0 ..< (self.count / by)).map {
      Array(self[$0 * by ..< ($0 + 1) * by])
    }
  }
}

print(
  [1, 2, 3, 4, 5, 6].chunked(by: 3)
)
// [[1, 2, 3], [4, 5, 6]]

 

🤔 まとめ

配列の変換


let d2: [[String]] = [
  ["あ", "い", "う", "え", "お"],
  ["か", "き", "く", "け", "こ"],
  ["さ", "し", "す", "せ", "そ"]
]

// 2次元から1次元 *
let d1 = d2.flatMap { $0 }
print(d1)
// ["あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ"]

print(
  // 1次元から2次元 *
  (0 ..< d1.count / numCols).map {
    Array(d1[($0 * numCols) ..< ($0 + 1) * numCols])
  }
)
// [["あ", "い", "う", "え", "お"], ["か", "き", "く", "け", "こ"], ["さ", "し", "す", "せ", "そ"]]

extension 化する。


extension Array where Element: Collection { 
  func toD1() -> [Element.Element] { // nested
    return self.flatMap { $0 } 
  }
}

extension Array {
  func toD2(numCols: Int) -> [[Element]] {
    return (0 ..< self.count / numCols).map {
      Array(self[($0 * numCols) ..< ($0 + 1) * numCols])
    }
  }
}

1次元配列インデックスと2次元配列座標の関係


index = y * numCols + x

x = index % numCols
y = index / numCols

SwiftData の inMemory で実装してみたが遅かったので、@Observable クラスへ。

👉 【 SwiftUI 】 Pong Wars を SwiftUI に移植してみた hatena-bookmark

しかし、正直、勉強すればするほど謎が増えます !


👉 【Swift】2次元配列 で 転置行列 ( transpose matrix ) hatena-bookmark