Edited at

TDDでじゃんけんアプリを作ってみる

簡単な座学

https://qiita.com/MHTcode_micky/private/774f16c90191c83cff85

GitHub

https://github.com/mht-mikiya-okugawa/SampleJanken1.git


TDDに則った開発


  • まずはテストかく

  • コンパイルエラーを解消する

  • テストが通る処理をかく

  • 繰り返す

  • 最後にリファクタリング

完成品はこれ

スクリーンショット 2019-07-07 16.32.33.png


以上!!! Just Do It


じゃんけんの判定ロジック


Red


SampleJanken1Tests.swift

//パターンscissors

//あいこ
func testYouScissorsDrow() {
let janken = Janken()
let rps = janken.rockPeparScissors()
XCTAssertEqual(rps, "あいこ")
}

スクリーンショット 2019-07-06 10.52.25.png

コンパイルエラーが出ました

解消しましょう


Janken.swift

open class Janken {

public func rockPeparScissors() -> String {
return ""
}

解消されるはずなのでbuildしてテストしてみましょう

緑色のチェック入っているところを押してください

スクリーンショット 2019-07-06 10.54.56.png

するとエラーが出るはずです。

スクリーンショット 2019-07-06 10.55.54.png


Green

次はテストをGreenにしていきましょう

ここではテストを通すだけの最低限の記述のみにしましょう


SampleJanken1.swift

public func rockPeparScissors() -> String {

return "あいこ"
}

これでもういちどテストをすると

スクリーンショット 2019-07-06 10.58.31.png

グリーンになりましたね!それではテストを増やしていきましょう

下記の記載をしてテストを実施してください


SampleJanken1.swift

    //かち

func testYouScissorsWin() {
let janken = Janken()
let rps = janken.rockPeparScissors()
XCTAssertEqual(rps, "あなたの勝ち")
}

エラーになったので解消します。


Janken.swift

    public func rockPeparScissors() -> String {

return "あなたの勝ち"
}

スクリーンショット 2019-07-06 11.06.11.png

ここでTestクラスに付いているテストボタンを押してみましょう

スクリーンショット 2019-07-06 11.07.18.png

1回目のテストが失敗してしまいましたね。

それでは両方が通るように実装を変えましょう


Janken.swift

    public func rockPeparScissors(pc: Int) -> String {

if pc == 1 {
return "あいこ"
} else {
return "あなたの勝ち"
}

}


コンパイルエラーが起きたのでテストも修正しましょう


SampleJankenTests1.swift

    //パターンscissors

//あいこ
func testYouScissorsDrow() {
let pcHand = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcHand)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouScissorsWin() {
let pcHand = 2
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcHand)
XCTAssertEqual(rps, "あなたの勝ち")
}

テストをしましょう

スクリーンショット 2019-07-06 11.12.49.png

通りましたね、これをあとは自分の手がぐー、チョキ、パーのパターンと相手の手のパターン全部を網羅できるように

ひたすら繰り返します。

SampleJanken1Test.swiftのテストを一つずつ追加していって、

グーの勝ち負けあいこ、チョキの勝ち負けあいこ、パーの勝ち負けあいこの判定テストを一つずつRed→Greenを繰り返していくとじゃんけんの判定ロジックの部分は最終的に以下のようになると思われます。


SampleJanken1Test.swift

//パターンscissors

//あいこ
func testYouScissorsDrow() {
let yourHandScissors: Int = 1
let pcScissors: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandScissors)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouScissorsWin() {
let yourHandScissors: Int = 1
let pcPaper = 2
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandScissors)
XCTAssertEqual(rps, "あなたの勝ち")
}
//負け
func testYouScissorsLoose() {
let yourHandScissors: Int = 1
let pcRock = 0
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandScissors)
XCTAssertEqual(rps, "あなたの負け")
}

//パターンRock
func testYouRockDrow() {
let yourHandRock: Int = 0
let pcRock: Int = 0
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandRock)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouRockWin() {
let yourHandRock: Int = 0
let pcPaper: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandRock)
XCTAssertEqual(rps, "あなたの勝ち")
}

//負け
func testYouRockLoose() {
let yourHandRock: Int = 0
let pcScissors: Int = -1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandRock)
XCTAssertEqual(rps, "あなたの負け")
}

//パターンぱー
//あいこ
func testYouPaperDrow() {
let yourHandPaper: Int = 2
let pcPaper: Int = 2
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandPaper)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouPaperWin() {
let yourHandPaper: Int = 2
let pcRock: Int = 3
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandPaper)
XCTAssertEqual(rps, "あなたの勝ち")
}
//負け
func testYouPaperLoose() {
let yourHandPaper: Int = 2
let pcScissors: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandPaper)
XCTAssertEqual(rps, "あなたの負け")
}



Janken.swift

public func rockPeparScissors(pc: Int, you: Int) -> String {

let paper1: Int = -1
let rock1: Int = 0
let scissors1: Int = 1
let paper2: Int = 2
let rock2: Int = 3

if you == scissors1 {
if pc == scissors1 {
return "あいこ"
} else if pc == paper1 || pc == paper2 {
return "あなたの勝ち"
} else if pc == rock1 || pc == rock2 {
return "あなたの負け"
} else {
return ""
}
} else if you == rock1 {
if pc == rock1 || pc == rock2 {
return "あいこ"
} else if pc == scissors1 {
return "あなたの勝ち"
} else if pc == paper1 || pc == paper2 {
return "あなたの負け"
} else {
return ""
}
} else if you == paper2 {
if pc == paper2 || pc == paper1 {
return "あいこ"
} else if pc == rock1 || pc == rock2 {
return "あなたの勝ち"
} else if pc == scissors1 {
return "あなたの負け"
} else {
return ""
}
} else {
return ""
}
}



CPの手のテスト

コンピュータのだす手のテストをしましょう

ここで仕様に付いてですがプレイヤーは

グー:0

チョキ:1

パー:2

を出してコンピューターは

グー:0,3

チョキ:1

パー:-1,2

を出すようにしています。

なのでコンピュータには-1~3の数字を出力してもらいます。

プレイヤーの出す手-1,同じ,1,+1の差で勝敗を判定してもらいます。

関係性は以下の図です。

手の種類
パー
ぐー
チョキ
パー
ぐー

プレイヤー

0
1
2

コンピュータ
-1
0
1
2
3

最後にリファクタリングするときにこの関係性は無駄になるんですが、今はRedをグリーンにしていきましょう。


Red


SampleJanken1Test.swift

    //ランダムにCPに手を出してもらう-1~3の間が帰ってきてほしい

func testRandomCpHand() {
let janken = Janken()
let result: Int = janken.randomHand()
XCTAssertEqualWithAccuracy(1.0, Float(result), accuracy: 2.0,"成功")

}



Janken.swift

    public func randomHand() -> Int {

return 5
}

それではGreenにしましょう


Janken.swift

    public func randomHand() -> Int {

return Int.random(in: -1..<4)
}

ランダムな値なので何回かテストしてみましょう


SampleJanken1Test.swift

    //ランダムにCPに手を出してもらう-1~3の間が帰ってきてほしい

func testRandomCpHand() {
let janken = Janken()
for num in 0...100 {
let result: Int = janken.randomHand()
print ("Result" + String(num) + "回目" + String(result))
XCTAssertEqualWithAccuracy(1.0, Float(result), accuracy: 2.0,"成功")
}
}


Greenになったと思います!

この時点でCPの出してくる手のテストと、じゃんけんの判定ロジックのテストが完了したと思います。

そろそろif分の段とか多くてうっとおしくなってないでしょうか?それでは次に


Refactor

じゃんけんには法則性があって

((プレイヤー) - (CP) + 3) % 3 = 

の結果で勝ち負けあいこが判定できます。

プレイヤーがぐー(0)の時、
CPが0なら 
(0 - 0 + 3) % 3 = 0(あいこ)
CPが1
(0 - 1 + 3) % 3 = 2(かち)
CPが2
(0 - 2 + 3) % 3 = 1(負け)

これを使ってソースコードを綺麗にしていきましょう


Janken.swift

    public func rockPeparScissors(pc: Int, you: Int) -> String {

let jadge: Int = (you - pc + 3) % 3

switch jadge {
case 0:
return "あいこ"
case 1:
return "あなたの負け"
case 2:
return "あなたの勝ち"
default:
return ""
}
}


なんてことでしょう、100行ぐらいあったコードが30行になってしまいました。

最後にここまでテストをしたロジックを使ってじゃんけんアプリを作りましょう

内容は完成品のViewControllerのコードを参考にしてください。


完成品


SampleJanken1Test.swift

//

// SampleJanken1Tests.swift
// SampleJanken1Tests
//
// Created by がーたろ on 2019/07/02.
// Copyright © 2019 がーたろ. All rights reserved.
//

import XCTest
@testable import SampleJanken1

class SampleJanken1Tests: XCTestCase {

override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
//パターンscissors
//あいこ
func testYouScissorsDrow() {
let yourHandScissors: Int = 1
let pcScissors: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandScissors)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouScissorsWin() {
let yourHandScissors: Int = 1
let pcPaper = 2
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandScissors)
XCTAssertEqual(rps, "あなたの勝ち")
}
//負け
func testYouScissorsLoose() {
let yourHandScissors: Int = 1
let pcRock = 0
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandScissors)
XCTAssertEqual(rps, "あなたの負け")
}

//パターンRock
func testYouRockDrow() {
let yourHandRock: Int = 0
let pcRock: Int = 0
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandRock)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouRockWin() {
let yourHandRock: Int = 0
let pcPaper: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandRock)
XCTAssertEqual(rps, "あなたの勝ち")
}

//負け
func testYouRockLoose() {
let yourHandRock: Int = 0
let pcScissors: Int = -1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandRock)
XCTAssertEqual(rps, "あなたの負け")
}

//パターンぱー
//あいこ
func testYouPaperDrow() {
let yourHandPaper: Int = 2
let pcPaper: Int = 2
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandPaper)
XCTAssertEqual(rps, "あいこ")
}
//かち
func testYouPaperWin() {
let yourHandPaper: Int = 2
let pcRock: Int = 3
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandPaper)
XCTAssertEqual(rps, "あなたの勝ち")
}
//負け
func testYouPaperLoose() {
let yourHandPaper: Int = 2
let pcScissors: Int = 1
let janken = Janken()
let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandPaper)
XCTAssertEqual(rps, "あなたの負け")
}

//ランダムにCPに手を出してもらう0~2の間が帰ってきてほしい

func testRandomCpHand() {
let janken = Janken()
for num in 0...100 {
let result: Int = janken.randomHand()
print ("Result" + String(num) + "回目" + String(result))
XCTAssertEqualWithAccuracy(1.0, Float(result), accuracy: 2.0,"成功")
}
}

func testRock() {
let janken = Janken()
for num in 0...100 {
let pcHand: Int = janken.randomHand()
let youHandRock: Int = 0
let rps = janken.rockPeparScissors(pc: pcHand, you: youHandRock)
print(rps)
XCTAssertNotEqual(rps, "")
}
}

func testScissors() {
let janken = Janken()
for num in 0...100 {
let pcHand: Int = janken.randomHand()
let youHandScissors: Int = 1
let rps = janken.rockPeparScissors(pc: pcHand, you: youHandScissors)
print(rps)
XCTAssertNotEqual(rps, "")
}
}

func testPaper() {
let janken = Janken()
for num in 0...100 {
let pcHand: Int = janken.randomHand()
let youHandPaper: Int = 2
let rps = janken.rockPeparScissors(pc: pcHand, you: youHandPaper)
print(rps)
XCTAssertNotEqual(rps, "")
}
}

}



Janken.swift

//

// Util.swift
// SampleJanken1
//
// Created by がーたろ on 2019/07/02.
// Copyright © 2019 がーたろ. All rights reserved.
//

import Foundation

open class Janken {
public func rockPeparScissors(pc: Int, you: Int) -> String {
let jadge: Int = (you - pc + 3) % 3

switch jadge {
case 0:
return "あいこ"
case 1:
return "あなたの負け"
case 2:
return "あなたの勝ち"
default:
return ""
}
}

public func randomHand() -> Int {
return Int.random(in: -1..<4)
}
}



ViewController.swift

//

// ViewController.swift
// SampleJanken1
//
// Created by がーたろ on 2019/07/02.
// Copyright © 2019 がーたろ. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

@IBOutlet var pcHandImgView: UIImageView!
@IBOutlet var resultLabel: UILabel!
@IBOutlet var rockBtn: UIButton!

@IBOutlet var scissorsBtn: UIButton!
@IBOutlet var paperBtn: UIButton!
let rock: Int = 0
let scissors: Int = 1
let paper: Int = 2
let janken = Janken()

override func viewDidLoad() {
super.viewDidLoad()

rockBtn.addTarget(self, action: #selector(ViewController.youHandRock), for: .touchUpInside)
scissorsBtn.addTarget(self, action: #selector(ViewController.youHandScissors), for: .touchUpInside)
paperBtn.addTarget(self, action: #selector(ViewController.youHandPaper), for: .touchUpInside)

}

@objc func youHandRock() {
let pcHand: Int = janken.randomHand()
pcImageChange(pcHand: pcHand)
rockBtn.backgroundColor = UIColor.blue
scissorsBtn.backgroundColor = UIColor.white
paperBtn.backgroundColor = UIColor.white
resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: rock)

}
@objc func youHandScissors() {
let pcHand: Int = janken.randomHand()
pcImageChange(pcHand: pcHand)
rockBtn.backgroundColor = UIColor.white
scissorsBtn.backgroundColor = UIColor.blue
paperBtn.backgroundColor = UIColor.white
resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: scissors)
}
@objc func youHandPaper() {
let pcHand: Int = janken.randomHand()
pcImageChange(pcHand: pcHand)
rockBtn.backgroundColor = UIColor.white
scissorsBtn.backgroundColor = UIColor.white
paperBtn.backgroundColor = UIColor.blue
resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: paper)
}

func pcImageChange(pcHand: Int) {
if pcHand == -1 || pcHand == 2 {
pcHandImgView.image = UIImage(named: "paper")
} else if pcHand == 0 || pcHand == 3 {
pcHandImgView.image = UIImage(named: "rock")
} else {
pcHandImgView.image = UIImage(named: "scissors")
}
}

}