Swift4でライフゲームを楽しむ

#ちょっと見てくれ！！！
初めてSwiftでアプリを作りました。
まだ洗練されていないですが、皆さんのアドヴァイスが頂きたく投稿します。
Swift4
xcode10.1
iPad第6世代
iPhone6s
で動作確認済み


#今後の課題
Intervalの処理
より効率的に処理（今は3万cellでもう重い）
一時停止やcellの数を変化させるボタンをつける
長生きしているcellに色の変化をつけるのも面白そう

```swift:ViewController.swift
import UIKit
import SpriteKit
class ViewController: UIViewController {
    //方形作成関数
    func fillRect(recSize:CGSize,color:CGColor) -> UIImage {
        //↓引数:図形の大きさ、”不透明さ”、よくわからん
        UIGraphicsBeginImageContextWithOptions(recSize, false, 0.0)
        let context = UIGraphicsGetCurrentContext()
        let origin = CGPoint(x:0, y: 0)
        let rect = CGRect(origin: origin, size: recSize)
        
        //pathとは何か？
        let path = UIBezierPath(rect: rect)
        context?.setFillColor(color)
        
        path.fill()
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image!
    }

    override func viewDidLoad() {
        //方形のサイズ
        let widthOfARect    =   UIScreen.main.bounds.width/CGFloat(width)
        let heightOfARect   =   UIScreen.main.bounds.height/CGFloat(height)
        let size:CGSize = CGSize(width: widthOfARect, height: heightOfARect)
        var rectangle:UIImage!
        
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = UIColor.black
        //cellの配列を作成
        makeCellsStructure()
        //celsBackUpも同構造にする
        cellsBackUp=cells
     //方形のimageを作る
         rectangle = self.fillRect(recSize:size, color: UIColor.green.cgColor)
        
        //空の配列を作成
        var rectView = [UIImageView?]()
        for _ in 0...width*height-1{
            rectView.append(nil)
        }
        
        //初期設定用ランダム
        let startNum:Int = Int.random(in:0...height*width-1)
        //テスト用初期設定どんぐり
            cells[startNum].dOA=1
            cells[startNum+width+2].dOA=1
            cells[startNum+width*2-1].dOA=1
            cells[startNum+width*2].dOA=1
            cells[startNum+width*2+3].dOA=1
            cells[startNum+width*2+4].dOA=1
            cells[startNum+width*2+5].dOA=1
        //テスト用銀河
       for i in 0...5{
            cells[coorToCellNum(x: 0+width/2, y: i+height/2)].dOA=1
            cells[coorToCellNum(x: 1+width/2, y: i+height/2)].dOA=1
            
            cells[coorToCellNum(x: 7+width/2, y: i+3+height/2)].dOA=1
            cells[coorToCellNum(x: 8+width/2, y: i+3+height/2)].dOA=1
            
            cells[coorToCellNum(x: i+3+width/2, y: 0+height/2)].dOA=1
            cells[coorToCellNum(x: i+3+width/2, y: 1+height/2)].dOA=1
            
            cells[coorToCellNum(x: i+width/2, y: 7+height/2)].dOA=1
            cells[coorToCellNum(x: i+width/2, y: 8+height/2)].dOA=1
        }
        //テスト用パルサー
        for i in 0...2{
            //横並びの
            cells[coorToCellNum(x: 15+2+i, y: 20+6)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20+6)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20+1)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20+1)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20-1)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20-1)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20-6)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20-6)].dOA=1
            //縦並びの
            cells[coorToCellNum(x: 15-6, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15-6, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15-1, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15-1, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15+1, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15+1, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15+6, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15+6, y: 20-2-i)].dOA=1
        }
        
        //計算と描写をする。
        //この時間処理をもっと簡単にできないか？
        _=Timer.scheduledTimer(withTimeInterval: speed*2.0, repeats: true) { (timer) in
            rectView = self.drawCells(rectangle: rectangle, rectViewTan: rectView, widthOfARect: widthOfARect)
            DispatchQueue.main.asyncAfter(deadline: .now()+speed, execute: {
                rectView = self.drawCellsBackUp(rectangle: rectangle, rectViewTan: rectView, widthOfARect: widthOfARect)}
        )}
    }
    
    func drawCells(rectangle:UIImage,rectViewTan:[UIImageView?],widthOfARect:CGFloat) ->[UIImageView?] {
        var rectView = rectViewTan
        
        for i in 0...width*height-1{
            //前回の描写の削除
            if rectView[i] != nil {
                rectView[i]!.removeFromSuperview()
            }
            //描写開始
            if cells[i].dOA==1 {
                //そこにimageを追加。この時点でrectView[i]はnilじゃない
                rectView[i] = UIImageView(image: rectangle)
                //rectViewのアンカーポイントを左上にする
                rectView[i]!.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
                //ancherポイントのsuperview座標x,y
                let xOfRect = CGFloat(cellNumToCoorX(celNum: i))*widthOfARect
                let yOfRect = CGFloat(cellNumToCoorY(celNum: i))*widthOfARect
                rectView[i]!.center =  CGPoint(x: xOfRect, y: yOfRect)
                self.view.addSubview(rectView[i]!)
                
            }
            //cellsから次のcellsBackUpを決めておく
            gameOfLife1(i: i)
        }
        return rectView
    }
    func drawCellsBackUp(rectangle:UIImage,rectViewTan:[UIImageView?],widthOfARect:CGFloat)->[UIImageView?] {
        var rectView = rectViewTan
        //cellsbackupより描写
        for m in 0...width*height-1{
            //さっきの描写の削除
            if rectView[m] != nil {
                rectView[m]!.removeFromSuperview()
            }
            if cellsBackUp[m].dOA==1{
                rectView[m] = UIImageView(image:rectangle)
                rectView[m]!.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
                //ancherポイントのsuperview座標x,y
                let xOfRect = CGFloat(cellNumToCoorX(celNum: m))*widthOfARect
                let yOfRect = CGFloat(cellNumToCoorY(celNum: m))*widthOfARect
                rectView[m]!.center =  CGPoint(x: xOfRect, y: yOfRect)
                self.view.addSubview(rectView[m]!)
            }//cellsVackUpから次のcellsを用意
            gameOfLife2(i: m)
        }
        return rectView
    }
       }

```

```swift:Cell.swift
import Foundation

//下処理
//描写と描写のインターバル（秒)
let speed:Double   = 0.05

//並べるcellの数...height:width = 4:3にしよう(iPadのアスペクト比は4:3)
let height  = 200
let width   = 150
struct Cell{
    var x:Int
    var y:Int
    //DeadOrAlive 0:死亡,1:生存
    var dOA:Int
}
//cellsとcellsBackUpを交互に計算し、描写する
//他のもっとシンプルな方法はないか？
var cells = [Cell]()
var cellsBackUp = [Cell]()

/*
 配列の並べ方、地球の歩き方
 example
 0  1   2   3
 4  5   6   7
 8  9   10  11
 */
//cellの配列を作成
func makeCellsStructure() {
    for i in 0...height-1 {
        for m in 0...width-1{
            cells.append(Cell(x: m, y: i, dOA: 0))
        }
    }
}
//座標系と配列の接続
//ドラクエ式ループX
func modifyX(v:Int ) ->Int {
    var modifiedX = v
    if v<0 {
        modifiedX = v+width
    }else if v>width-1{
        modifiedX = v-width
    }
    return modifiedX
}
//ドラクエ式ループY
func modifyY(v:Int ) ->Int {
    var modifiedY = v
    if v<0 {
        modifiedY = v+height
    }else if v>height-1{
        modifiedY = v-height
    }
    return modifiedY
}
//座標からcells[]のindex番号へ
func coorToCellNum(x:Int,y:Int)-> Int {
    var celNum = 0
    let xx = modifyX(v: x)
    let yy = modifyY(v: y)
    celNum = width*yy+xx
    return celNum
}
//index番号からx座標を出す
func cellNumToCoorX(celNum:Int)->Int {
    return celNum%width
}
//index番号からy座標を出す
func cellNumToCoorY(celNum:Int)->Int {
    var retY = 0
    for i in 0...height{
        if celNum+1 <= i*width {
            retY = i-1
            break
        }}
    return retY
}

//cellsでの周囲のセルの生死を確認
func countAliveNeighbors(celNum:Int)->Int {
    
    let coorX = cellNumToCoorX(celNum: celNum)
    let coorY = cellNumToCoorY(celNum: celNum)
    
    
    let uL = coorToCellNum(x: coorX-1, y: coorY+1)
    let uC = coorToCellNum(x: coorX, y: coorY+1)
    let uR = coorToCellNum(x: coorX+1, y: coorY+1)
    
    let mL = coorToCellNum(x: coorX-1, y: coorY)
    let mR = coorToCellNum(x: coorX+1, y: coorY)
    
    let lL = coorToCellNum(x: coorX-1, y: coorY-1)
    let lC = coorToCellNum(x: coorX, y: coorY-1)
    let lR = coorToCellNum(x: coorX+1, y: coorY-1)
    
    let counter =   cells[uL].dOA+cells[uC].dOA+cells[uR].dOA +
        cells[mL].dOA+cells[mR].dOA +
        cells[lL].dOA+cells[lC].dOA+cells[lR].dOA
    return counter
}
//cellsBackUpでの周囲のセルの生死を確認
func countAliveNeighborsAmongCellBackUp(celNum:Int)->Int {
    
    let coorX = cellNumToCoorX(celNum: celNum)
    let coorY = cellNumToCoorY(celNum: celNum)
    
    
    let uL = coorToCellNum(x: coorX-1, y: coorY+1)
    let uC = coorToCellNum(x: coorX, y: coorY+1)
    let uR = coorToCellNum(x: coorX+1, y: coorY+1)
    
    let mL = coorToCellNum(x: coorX-1, y: coorY)
    let mR = coorToCellNum(x: coorX+1, y: coorY)
    
    let lL = coorToCellNum(x: coorX-1, y: coorY-1)
    let lC = coorToCellNum(x: coorX, y: coorY-1)
    let lR = coorToCellNum(x: coorX+1, y: coorY-1)
    
    let counter =   cellsBackUp[uL].dOA+cellsBackUp[uC].dOA+cellsBackUp[uR].dOA +
        cellsBackUp[mL].dOA+cellsBackUp[mR].dOA +
        cellsBackUp[lL].dOA+cellsBackUp[lC].dOA+cellsBackUp[lR].dOA
    return counter
}

func gameOfLife1(i:Int) {
    //cellsでの周囲を考えてcellsBackUpに反映する関数
        let lives = countAliveNeighbors(celNum: i)
        switch lives {
            //中央のcellに接するcellのうち3個が生存なら中央のcellは誕生（あるいは生存）
        case 3:
            cellsBackUp[i].dOA=1
            //中央のcellに接するcellのうち2個が生存なら中央のcellは現状維持（死亡状態なら死亡、生存状態なら生存）
        case 2:
            cellsBackUp[i].dOA=cells[i].dOA
            //過密、過疎で死亡
        case 0,1,4,5,6,7,8:
            cellsBackUp[i].dOA=0
            if cells[i].dOA==1{
                    }
            //とりあえず0代入
        default:
            cellsBackUp[i].dOA=0
                }
    }

func gameOfLife2(i:Int) {
    //cellsBackUpでの周囲を考えてcellに反映する関数
        let lives = countAliveNeighborsAmongCellBackUp(celNum: i)
        switch lives {
        case 3:
            cells[i].dOA=1
        case 2:
            cells[i].dOA=cellsBackUp[i].dOA
            
        case 0,1,4,5,6,7,8:
            cells[i].dOA=0
            if cellsBackUp[i].dOA==1{
            }
        default:
            cells[i].dOA=0
    }
}

```