参考画像1〜32014-09-27参考画像4〜52014-09-27第28回目:Swiftでオセロを作る(Part5)2014-09-07筆者:村田こんにちは。Xcode6のbeta7が出ていたので試してみました。前回と同じく暗黙Optional型の使用箇所について修正されているようです。OthelloViewの以下のメソッドでエラーがでました。}NSSet!型からNSSet型へ、UIEvent!型からUIEvent型へ変更したようです。リリースノートを確認すると大きな変更はありませんでした。それではオセロの実装を進めていきます。前回は一方向分の石をひっくり返すアルゴリズムを再帰ループを使って書きました。実はこのflipLine関数が当オセロプログラムのハイライトでして、残りの部分は簡単な紹介程度でサクサク進めていきたいと思います。縦横斜めの8方向分のひっくり返しを行うflip関数です。nil}}戻り値の型はOptionalにしました。ひっくり返すxy座標の配列かnilを返します。4行目からはflipLine関数を使って8方向分のひっくり返しの位置をresultに加えています。方向はflipLine関数のdx,dy引数で指定します。最後の部分ですが、一つ以上ひっくり返せる石がある場合にのみ、最初に置こうとした石の位置もresultに加えています。次は盤面上に黒石または白石を置くことができるか調べる関数です。64マスのどこに石を置くことができるかを配列にして返します。result}}64マスに対しflip関数を実行してチェックしています。flip関数の中ではflipLine関数を8回呼び出しているので一回のチェックで512回flipLine関数が呼び出されます。前回少し触れましたがflipLine関数のパフォーマンスが悪いとゲームとして遊べなくなります。次はCPUの指し手を返す関数です。canPlaced関数が返した配列からランダムに一箇所選び、flip関数を呼び出してひっくり返す位置を返します。arc4random関数は0〜UIntMaxをランダムに返す関数です。nil}次は盤面上の石の数を数える関数です。ゲームの勝敗を判断する際に使用します。white)}Swiftではタプルを戻り値に使うことができるので、関数から複数の値を返すことが簡単にできます。戻り値の型宣言を見ると分かるように、タプルを返す場合はタプルのメンバーに名前を付けることができます。以下のように戻り値を変数一つで束縛した場合、名前でタプルの中身にアクセスできます。calcStones(board)println(stones.black)タプルの戻り値を分配束縛することもできます。以下の例のように不要な部分は_(アンダースコア)でマスクできます。calcStones(board)println(foo)ここまで副作用を持たない参照透過な関数としてflipLine,flip,canPlaced,cpuFlip,calcStonesを作ってきました。以降はOthelloViewクラス内に戻り、これら関数を組み合わせて副作用を持つメソッドを作っていきたいと思います。ひっくり返す石の配列をもらって実際にboard上の石をひっくり返すメソッドです。stone}}次はゲーム進行の実装です。今回のゲーム進行はユーザーが盤面をタッチしたタイミングで行われます。この瞬間にひっくり返す処理、CPUの次の指し手、ゲームの終了判定など全て行うことにします。一回のタッチでプレイヤー及びCPUのターンも処理します。別のゲーム進行方法も考えられます。一回のタッチではプレイヤーのターン処理のみ行い、次のタッチでCPUのターン処理を行うこともできます。その場合はプレイヤー/CPUのターン管理が必要になります。CPUの思考アルゴリズムを賢く複雑にする場合にはどちらのゲーム進行手法も不適切になります。思考アルゴリズムが結論を出すまでに時間を要する場合、タッチ後しばらく画面が固まってしまいますので、別途CPUの思考を処理するタイミングを設ける必要があります。今回はランダム選択というほぼ何も考えないAIですのでタッチのタイミングで処理可能です。ゲームの勝敗判定(次に説明します)setNeedsDisplay()}最後にゲームの勝敗判定メソッドを作りましょう。White:\(white))")}}以上でほぼ完成です。完成したオセロゲームをiPhoneエミュレータで動かして対戦してみました。角を簡単に取らせてくれるのでプレイヤー(黒)の圧倒的勝利となりました。ゲームオーバーがちゃんとユーザーに分かるように画面にラベルを置いたり、ゲームオーバーから再度ゲームができるようにリセットするなど仕上げ残っていますが、これについては以下のGitHubのコードを参照して下さい。見直してみるとxy座標を走査する2重for文があちこちにあるのが気になります。高階メソッドを作るなど何らかの抽象化を行うべきだったかもしれません。https://github.com/muratamuu/SwiftOthello/blob/master/SwiftOthello/OthelloView.swift[おわりに]Swiftはいかがでしたか?特徴が無いのが特徴というか、最新言語なだけに他の言語のいい部分をうまく持ってきているというか、モダンというのでしょうね。読み書きし易いバランスの良い言語だと思いました。言語機能の一部しか紹介できませんでしたが、今後正式リリースされたら是非Xcode6をダウンロードして遊んでみて下さい。それにしても、OSX/iOSの開発言語として活躍していたObjCは今後どうなるのでしょう。Swiftに置き換わっていくにつれてObjCはあまり見かけなくなってしまうのでしょうか。個人的にはObjCのシンタックスも好きなのでSwiftもObjCも楽しんでいきたいなと思います。:勝利の瞬間2014-09-07iPhoneシミュレータスクリーンショット第27回目:Swiftでオセロを作る(Part4)2014-08-31筆者:村田こんにちは。今回も最初はXcodeのアップデートからです。beta6が出ました。アップデートしてコンパイルしたところ大きなエラーはでませんでした。一応リリースノートを眺めてみるとOptional型を使ったいくつかのAPIに変更があるようです。その内容について触れたいと思います。「第25回の続き」という記事でOptional型を紹介しましたが、もう少し続きがあるのです。Int?型の復習から。Int?型とはIntの数値かnilを値として持てる型です。Int?型からInt型を取り出すためには、nilじゃないことを確認してから「!」で取り出すのでした。「!」で安全な取り出し...}もう少しスッキリしたif文の使い方もありました。optApplesがnilではない場合にifの中に入る}上記はInt?型から「明示的」にInt型へ変換する手順です。プログラムの構造によってはOptional型変数に確実に値が入っていると見なせる場合もあります。こんな場合でも参照する度に「!」で型変換を行うのは少々面倒です。そこで登場するのがInt!型です。型名の後に「!」がついています。Int!型はOptional型ですが「暗黙的」にInt型へ変換されます。nilからIntへは変換できません普通のOptional型と同じようにnilチェックしてから明示的に「!」で取り出すこともできます。optApples!...}スッキリ版も同様に書けます。{...}というわけでOptional型には2通りあり、中身を取り出す際に「!」を付けなければならない明示的なInt?型と、「!」を付けなくても取り出せる暗黙的なInt!型があります。この暗黙のOptional型は実際にどんなところで使われているのでしょう。「一応nilが入ることがあるかもしれないけど、実際はただのInt」というようなニュアンスで使われているのだと思います。画面にタッチした際のコールバックメソッドを見てみましょう。UIEvent!)引数の型を見るとNSSet!、UIEvent!と暗黙のOptionalが使われています。実際にnilが入ることはあり得ないでしょう。明示的なNSSet?型だったらアクセスに一手間かかってしまいますが、暗黙のNSSet!型なので通常のNSSet型とみなしてアクセスできるわけです。(まあ本当にnilが入っていたらアウトですけど)では話をbeta6のリリースノートに戻します。暗黙のOptional型が使われている一部の箇所について見直されました。その結果、「ほんとにもうっ、全然nilなんて入らないんだからねっ!」という場所は通常型に修正され、「いや〜結構nilが入るのでちゃんとnilチェックして欲しいな・・」という場所は明示的なOptional型に修正されました。前回までのオセロプログラムでもこのエラーが1箇所でました。}上のメソッドは前回話題にしたUIViewのイニシャライザですが、以下のように通常型の引数に修正されました。}aDecoderにnilなんて入らないというわけです。NSCoderはクラスですのでaDecoderは参照型変数です。JavaやC#であればnullを設定できる変数ですね。SwiftではaDecoder変数が書き換え可能だったとしてもnilを設定できません。SwiftのnilはJavaやC#のnull、CやObjCのNULLとは異なります。nilというリテラル値はOptional型変数にしか設定できません。Swiftの参照型変数はnilにならないので値として扱いやすく、またOptional型のおかげでnilのあるところが分かりやすくていいですね。えっと、今回もVerUpネタに時間をとってしましましたが、気を取り直してオセロの続きをやりたいと思います。オセロは自分の石で相手の石を挟むとひっくり返すことができます。この部分を作りたいと思います。石を置いた場所から上下左右+斜めの8方向に対してひっくり返せるかチェックする必要があります。左方向にひっくり返せるかを例にして考えましょう。●○○○という状態で白石の隣に黒石を置く場合のアルゴリズムを考えます。1すぐ左隣が黒石ではなく白石があることを確認したら、2白石が途切れるまで、つまり黒石が見つかるまで繰り返し左隣のチェックを行います。黒石が見つからなければひっくり返せません。3黒石が見つかったら今度は間にある白石を繰り返しひっくり返します。2と3は繰り返し処理を含んでいますね。2のループで順番にチェックし、3のループで今度はひっくり返すという2回のループで書くことができそうです。動きを観察すると、2で左隣を順にチェックし、3はその逆を辿るようにひっくり返しながら戻ってくるという方法も考えられます。後者は再帰関数で書くとうまくいきそうです。関数名はひっくり返すループですのでflipLoop関数にします。書き始める前にもう少し細かい点を説明します。メソッドにはせず関数で書いてみようと考えているので、グローバルスコープ関数として書きます。盤面上の石をひっくり返す方法ですが、OthelloViewのメンバにいるboardをflipLoop関数から書き換えてしまうことは副作用になりますので避けようと思います。一つの方法として、関数内で新しいboardのコピーを用意して、その上でひっくり返した結果を返すという方法が考えられます。flipLoopは頻繁に呼ばれそうです。boardをリストではなく配列として定義しているため、Immutableに扱うためには部分変更でも全体をコピーすることになりパフォーマンスが悪そうです。そこで別の方法として、ひっくり返す相手の石の位置(x,y)を配列にして返すようにして、実際の副作用(ひっくり返し)は後でやることにします。nil}現在のboardと石を置く位置xyと石の種類(黒/白)をもらって、ひっくり返す場所を配列にして返すという関数です。戻り値型はOptionalにしておいて、ひっくり返す場所が無い場合はnilを返すようにします。中身を書いていきましょう。再帰的にチェックしていった場合に、黒でも白でもなく空のマスに辿り着いてしまったら、ひっくり返せないのでnilを返して再帰ループを停止します。nil}次は再帰チェックで自分の石を見つけた場合です。ここまでひっくり返せるので再帰を停止します。再帰の巻き戻し時に相手の石の場所を格納するための空配列[]を返しましょう。nil}最後は相手の石を見つけた場合です。左隣をチェックするため再帰呼び出しをしましょう。nil}再帰呼び出しの戻り値resultはどうしましょう。nilが入っていたら再帰の果てに自分の石が無かったのですから、そのままnilを返しましょう。配列が返ってきた場合、再帰の果てに自分の石があったということですので、今調べた相手の石をひっくり返すために配列に加えましょう。いうわけで以下のようになりました。nil}再帰呼び出しの停止条件を確認しておきましょう。自分の石が見つかるか空っぽのマスが見つかれば停止します。boardの定義を思い出して下さい。8x8マスの周囲にぐるりと余分な領域を持たせていました。ここには石を置けませんので、必ず最後には空っぽのマスが見つかり無事停止します。今のところ左隣だけしか調べられませんので、走査する方向を引数でもらいましょう。x方向とy方向の増減分ということでdxとdyを引数に追加しました。nil}このままでもいいのですがちょっと突っ込みます。再帰関数のルールとして、呼び出し時にパラメータが変化しなければなりません。flipLoop関数も第2、3パラメータが変化します。ところがそれ以外の4つのパラメータは変わっていません。これらは再帰に本質的には関わっておらず、再帰パラメータである必要がないと言えます。いった変数はパラメータの外に出すことが可能です。いうわけでクロージャを使って外側の関数スコープに移してみましょう。外側の関数の名前は、ええと、ある方向に対してひっくり返し処理を行うという意味でflipLine関数とします。flipLine関数のスコープに再帰に不要な変数を保持するとして、クロージャはどうやって作りましょう。SwiftはJavaScriptやC++のような関数内関数を定義することもできますが、今回は関数リテラル(無名関数)で書いてみます。(そう言えばC++11からC++もクロージャが使えると聞いたことがあるのですが試したことはないです。どんな風に書くのかな・・)Swiftの関数リテラルは以下のように書きます。}中括弧で全体を囲み、inの前に関数の型、inの後に本体を書きます。上記は基本形ですが、Swiftの関数リテラルは条件が揃えば色々記述を省略することができ、{本体だけ}のように短く書くこともできます(Scalaっぽい書き方です)。Rubyのコードブロックのように高階メソッドの直後にくっつける記法もあります。興味のある方は言語マニュアルを参照してみて下さい。flipLine関数の中にflipLoopを書いてみます。nil}どうでしょうか。flipLoopをクロージャにしたので引数が再帰パラメータだけになりました。それではクロージャを呼び出すコードも追加しておきましょう。flipLineの戻りはnilにする必要はない。ひっくり返す場所が無いなら空配列にします。}flipLine関数の戻り値型を見て下さい。Optional型をやめました。flipLoopの戻り値ではnilと[]の区別が必要なのでOptionalにしていますが、flipLineはひっくり返す位置が無い場合は空配列を返せば十分です。上のコードの7行目でflipLoopを再帰呼び出ししていますが、flipLoopが見つからないって怒られます。関数リテラルは再帰呼び出しできないみたいです。beta6のリリースノートを見ると「関数内関数で再帰呼び出し不可」という既知の問題がありました。再帰呼び出しはコンパイルエラー}関数内関数と関数リテラルの問題が同根原因かどうか分かりませんが、現在はflipLoopのような再帰関数リテラルはコンパイルできません。そこでStackOverflowを検索してみると回避策が見つかりました。同型のダミー関数オブジェクトを生成し、関数リテラルの中から前方参照することで回避できるようです。こんな感じでようやく最終版です。[]}再帰関数リテラルを書く前に変数を同じ型で宣言し、初期値として適当な関数オブジェクトを入れておきます。inの前のアンダースコアですが、プレースホルダとして機能する点はいいとして、なぜ1個でいいのか。言語マニュアルには書かれていませんが、Swiftでは引数が複数あっても実はタプルとして一つにまとめられているとのこと。詳しくはQiitaのdankogaiさんの記事で分析されています。興味のある方は検索してみて下さい。これでようやく「一方向のひっくり返し関数」が完成しました。今回はここまでにして、そろそろですね!iPhone6の発表イベント。9月9日(米時間)だそうです。関連情報を随分見かけるようになりました。大きなiPadやiWatchなんてのも期待されています。本体ハードウエアにも興味がありますが、発売の少し前になればiOS8のリリースがあるはずですね!iOS8はiOS7のようなUIデザインの大きな変更は無いでしょうが、APIがたくさん増えるようです。HomeKit(家電)、HealthKit(健康)のような新しいフレームワークを使ってどんなアプリやサービスが登場するのでしょうね。他にもWebKitやCloudKitなるものや、フォトライブラリやカメラ、CoreAudioにも手が入るとか。PromiseKitという非同期処理フレームワークは本コラムでのネタになりそうです。個人的に興味があるのはSceneKitというApple純正3D描画+物理演算フレームワークです。OSXだけでなくiOSでも動くようになるとのことで、この機会にトライしてみたいです。描画と言えばMetalというA7チップ用の高速グラフィックスAPIも発表され、これからどんなアプリが登場するのかとても楽しみです。