あおぶろ

スマホアプリ開発のこととか

RecyclerViewでオセロをつくる

なんか楽しいことしてえ... ってなったのでオセロをつくりました

完成品

f:id:aoshima214:20211129233714g:plain:w300

つくり方

オセロの条件判定とかの部分は、ネットにもたくさん記事がありますし、そこまで複雑ではないので根性で書けます。

それよりも、UIをどうするかが問題になります。

  • 盤面をどう描画するか
  • タップされた位置をどう取得するか
  • 置かれた石・ひっくり返った石がどれか、わかるようにしたい

これらはRecyclerViewのGridLayoutManagerと、ListAdapterの差分更新の仕組みを使うことで簡単に解決できます。


盤面とセルのレイアウトを作る

背景を黒くしたRecyclerView
f:id:aoshima214:20211129224643p:plain:w250

1マス分のセルのレイアウト
f:id:aoshima214:20211129225659p:plain:w50 f:id:aoshima214:20211129225104p:plain:w50

RecyclerViewにGridLayoutManagerをセット

 // BOARD_SIZE = 8
recyclerView.layoutManager = GridLayoutManager(this, BOARD_SIZE)

これらを組み合わせると↓のようになります

f:id:aoshima214:20211129230521p:plain:w300


盤面の状態管理とタップされた位置の取得

オセロの盤面の状態は二次元リストとして保持したいです。
RecyclerViewには、これを単一のリストに変換して渡します。

このとき、リストに詰めるのは以下のようなデータクラスにします。

data class Cell(
    val vertical :Int,
    val horizontal : Int,
    var stone: Stone
)

enum class Stone {
    BLACK,
    WHITE,
    NONE
}

Cell自体に自分の位置情報を持たせると、Adapterからタップされた座標を得るのが簡単になる上、 今後色々な条件判定を書くときに便利です。


置かれた石、ひっくり返った石だけアニメーションさせる

ListAdapter & DiffUtil を使います

developer.android.com

class CellAdapter() : 
    ListAdapter<Cell, CellAdapter.ViewHolder>(CellAdapterDiffCallback()) {
        ........
}


class CellAdapterDiffCallback : DiffUtil.ItemCallback<Cell>() {
    // アイテム自体が同じか
    override fun areItemsTheSame(oldItem: Cell, newItem: Cell): Boolean {
        return oldItem.vertical == newItem.vertical && 
                oldItem.horizontal == newItem.horizontal
    }
    // アイテムの内容が同じか
    override fun areContentsTheSame(oldItem: Cell, newItem: Cell): Boolean {
        return oldItem == newItem
    }
}

adapterにsubmitList() すると、すでに表示されているリストとの差分だけ更新 & アニメーションされます。

加えて、postDelayed() を処理の合間に挟んでゲームスピードを遅くするといい感じになります。

参考にしたもの

Kotlinの練習にオセロ作って遊んだ - みんからきりまで
8方向の判定方法について参考にさせていただきました。

オセロ(リバーシ)の作り方(アルゴリズム) ~石の位置による評価~
オセロのAIについて参考にさせていただきました

感想

それなりの時間で作れてプログラミングっぽさも味わえて楽しいのでおすすめです

今回のコードは以下にあります

github.com