59
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オセロをビットボードで実装する

Last updated at Posted at 2017-12-18

対象読者

ビットボード初心者がオセロをさくっと実装してみること」に重点を置いてます

"ビットボード"を知らない人は、まず以下あたりを読むと良いかもしれません
ビットボードの凄さをざっくりと解説してみる

本記事は上記リンクからもう少し踏み込んで、初歩的な部分に関する実装例をまとめました
解説というよりも、"サンプルコード紹介"の意味合いが強い記事です
(ひっくり返す処理のサンプルであり、戻るや進むなどの処理は省略しています)

ちなみに

更なる高みを目指す場合は「edax (世界最高峰のオセロソフト)」などを眺めると幸せになれます。
ビットボードのロジックもはるかに綺麗な実装になっています。
Null Window Search等の探索アルゴリズム、空きマス別メソッド、book構築などなど色々参考になります

2進数で表す

0と1しかないので、黒・白・空の3種類は表現できない
そこで、黒番と白番とで手番ごとに盤面を表現するのが上手い

例えばこんな盤面(黒番)だとする
Simulator Screen Shot - iPhone 5 - 2017-12-18 at 13.55.28.png

画像の出典:iOSアプリ「詰めオセロOrca

石:1,空:0 で以下のように表現できる (A1から順に)
-黒番から見たボード-
00000000 00100000 00000010 00000100 00001100 01011100 00001100 0000000
-白番から見たボード-
00000000 00010000 01111000 01111000 00100000 00110000 00010000 0000000

16進数で表す

上記の2進数は長くて見にくいので、16進数で表現しなおす
数字4つごとに0〜F(15)に直すと以下のようになる
-黒番から見たボード-
00 20 02 04 9C 0C 00 00
すなわち  「0x002002049C0C0000
-白番から見たボード-
00 10 70 78 20 30 10 00
すなわち  「0x0010707820301000

*頭の「0x」は、「この数字は16進数だよ」という意味

基本的には、16進数で盤面を表現していく

盤面の空きマス

ひとまず
手番側の盤面: P  (PlayerBoardに由来)
相手側の盤面: O  (OpponentBoardに由来)
とすると、

この2つの盤面から、盤面の空きマスを生成するには、
~(P|O)
これで、空:1, 石:0とした盤面を取得できる

P|Oで論理和をとり、石:1,空:0の盤面を生成
そして、~(否定)によって、その0と1を反転させている

こんな感じで全てを細かく説明しているとキリがないので、もうコード紹介に入ります

石をひっくり返す

今回のメイン
ビットボードであれば、再帰処理を書かずに石をひっくり返せる

盤面(黒番)
Simulator Screen Shot - iPhone 5 - 2017-12-18 at 13.55.28.png

例えばA4に置きたい場合、以下の流れをたどる

  1. "A4"をビットに変換 →「0x0000008000000000」
  2. 「0x0000008000000000」が着手可能な場所かを調べる
  3. 可能なら着手し、石をひっくり返す
  4. パスの判定
  5. 終局の判定
  6. 手番を交代

Swift4で実装していく
ファイルは以下の2つを作る

  • Board.swift : Boardクラス定義ファイル
  • bit.swift : ビット関連の関数定義ファイル

Boardクラスを作る

以下のようなBoardクラスを前提とする

Board.swift
import Foundation

class Board {

    //MARK: Constant
    let BLACK_TURN = 100
    let WHITE_TURN = -100

    //MARK: Properties
    var nowTurn  : Int          // 現在の手番
    var nowIndex : Int          // 現在何手目か
    var playerBoard   : UInt64  // player側ビットボード
    var opponentBoard : UInt64  // opponent側ビットボード
    
    //MARK: Initialization
    init() {
        self.nowTurn       = BLACK_TURN
        self.nowIndex      = 1

        // 一般的な初期配置を指定
        self.playerBoard   = 0x0000000810000000
        self.opponentBoard = 0x0000001008000000
    }
}

座標をビットに変換

「C8」や「D1」という座標文字列をビットに変換

Board.swift
/**
 * @brief 座標をbitに変換する
 * @param x 横座標(A~H)
 * @param y 縦座標(1~8)
 * @return 着手箇所のみにフラグが立っている64ビット
 */
func coordinateToBit(x: String, y: String) -> UInt64 {
    var mask: UInt64 = 0x8000000000000000
    // X方向へのシフト
    switch x {
    case "A":
        break
    case "B":
        mask = mask >> 1
    case "C":
        mask = mask >> 2
    case "D":
        mask = mask >> 3
    case "E":
        mask = mask >> 4
    case "F":
        mask = mask >> 5
    case "G":
        mask = mask >> 6
    case "H":
        mask = mask >> 7
    default:
        break
    }
    // Y方向へのシフト
    let intY = Int(y)
    mask = mask >> ( (intY! - 1) * 8)
    
    return mask
}

着手できるかを判定

Board.swift
/**
 * @brief 着手可否の判定
 * @param put 置いた位置のみにフラグが立っている64ビット
 * @return 着手可能ならtrue
 */
func canPut (put: UInt64) -> Bool {
    // 着手可能なマスにフラグが立っている合法手ボードを生成
    let legalBoard : UInt64 = makeLegalBoard(board: self)
    // 今回の着手が、その合法手ボードに含まれれば着手可能
    return (put & legalBoard) == put
}

合法手ボードの生成はビットボードの醍醐味の1つ。
手番側のボードを8方向それぞれにずらし、置ける場所にフラグを立てる。
番人として、最初に相手側ボードと特定のビットで積を取っている。これは、盤外に置ける可能性を排除するため。

Board.swift
/**
 * @brief 手番側の合法手ボードを生成
 * @param board Boardインスタンス
 * @return Uint64  playerから見て、置けるマスのみにフラグが立っている64ビット
 */
private func makeLegalBoard(board: Board) -> UInt64 {
    //左右端の番人
    let horizontalWatchBoard : UInt64 = board.opponentBoard & 0x7e7e7e7e7e7e7e7e
    //上下端の番人
    let verticalWatchBoard : UInt64   = board.opponentBoard & 0x00FFFFFFFFFFFF00
    //全辺の番人
    let allSideWatchBoard : UInt64    = board.opponentBoard & 0x007e7e7e7e7e7e00
    //空きマスのみにビットが立っているボード
    let blankBoard : UInt64 = ~(board.playerBoard | board.opponentBoard)
    //隣に相手の色があるかを一時保存する
    var tmp : UInt64
    //返り値
    var legalBoard : UInt64
    
    //8方向チェック
    // ・一度に返せる石は最大6つ ・高速化のためにforを展開(ほぼ意味ないけどw)
    //左
    tmp = horizontalWatchBoard & (board.playerBoard << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    legalBoard = blankBoard & (tmp << 1)

    //右
    tmp = horizontalWatchBoard & (board.playerBoard >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    legalBoard |= blankBoard & (tmp >> 1)

    //上
    tmp = verticalWatchBoard & (board.playerBoard << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    legalBoard |= blankBoard & (tmp << 8)

    //下
    tmp = verticalWatchBoard & (board.playerBoard >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    legalBoard |= blankBoard & (tmp >> 8)

    //右斜め上
    tmp = allSideWatchBoard & (board.playerBoard << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    legalBoard |= blankBoard & (tmp << 7)

    //左斜め上
    tmp = allSideWatchBoard & (board.playerBoard << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    legalBoard |= blankBoard & (tmp << 9)

    //右斜め下
    tmp = allSideWatchBoard & (board.playerBoard >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    legalBoard |= blankBoard & (tmp >> 9)

    //左斜め下
    tmp = allSideWatchBoard & (board.playerBoard >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    legalBoard |= blankBoard & (tmp >> 7)

    return legalBoard
}

着手し、石を反転する

Board.swift
/**
 * @brief 着手し,反転処理を行う
 * @param put: 着手した場所のみにフラグが立つ64ビット
 */
func reverse(put: UInt64) {
    //着手した場合のボードを生成
    var rev : UInt64 = 0
    for k in 0..<8 {
        var rev_ : UInt64 = 0
        var mask : UInt64 = transfer(put: put, k: k)
        while (mask != 0) && ((mask & opponentBoard) != 0) {
            rev_ |= mask
            mask = transfer(put: mask, k: k)
        }
        if (mask & playerBoard) != 0 {
            rev |= rev_
        }
    }
    //反転する
    playerBoard   ^= put | rev
    opponentBoard ^= rev
    //現在何手目かを更新
    nowIndex = self.nowIndex + 1
}
Board.swift
/**
 * @brief 反転箇所を求める
 * @param put 着手した場所のビット値
 * @param k   反転方向(8つ)
 * @return 反転箇所にフラグが立っている64ビット
 */
private func transfer(put: UInt64, k: Int) -> UInt64 {
    switch k {
    case 0: //上
        return (put << 8) & 0xffffffffffffff00
    case 1: //右上
        return (put << 7) & 0x7f7f7f7f7f7f7f00
    case 2: //右
        return (put >> 1) & 0x7f7f7f7f7f7f7f7f
    case 3: //右下
        return (put >> 9) & 0x007f7f7f7f7f7f7f
    case 4: //下
        return (put >> 8) & 0x00ffffffffffffff
    case 5: //左下
        return (put >> 7) & 0x00fefefefefefefe
    case 6: //左
        return (put << 1) & 0xfefefefefefefefe
    case 7: //左上
        return (put << 9) & 0xfefefefefefefe00
    default:
        return 0
    }
}

パスの判定

パスとは、「手番側の合法手ボードのみにフラグが1つも立っていない状態」である
相手側もパスの場合は、終局になってしまうため

Board.swift
/**
 * @brief パス判定  (= プレイヤーのみが置けない時)
 * @return パスならtrue
 */
func isPass() -> Bool {
    // 手番側の合法手ボードを生成
    let playerLegalBoard = makeLegalBoard(board: self)

    // 相手側の合法手ボードを生成    
    let tmpBoard = Board()
    tmpBoard.nowTurn  = nowTurn
    tmpBoard.nowIndex = nowIndex
    tmpBoard.playerBoard = opponentBoard
    tmpBoard.opponentBoard = playerBoard
    let opponentLegalBoard = makeLegalBoard(board: tmpBoard)
    
    // 手番側だけがパスの場合    
    return playerLegalBoard == 0x0000000000000000 && opponentLegalBoard != 0x0000000000000000
}

終局の判定

終局とは、「両手番の合法手ボードに1つもフラグが立っていない状態」である

Board.swift
/**
 * @brief 終局判定  (= 二人ともが置けない時)
 * @return 終局ならtrue
 */
func isGameFinished() -> Bool {
    let playerLegalBoard = makeLegalBoard(board: self)
        
    let tmpBoard = Board()
    tmpBoard.nowTurn  = nowTurn
    tmpBoard.nowIndex = nowIndex
    tmpBoard.playerBoard = playerBoard
    tmpBoard.opponentBoard = opponentBoard
    let opponentLegalBoard = makeLegalBoard(board: tmpBoard)
    
    // 両手番とも置く場所がない場合    
    return playerLegalBoard == 0x0000000000000000 && opponentLegalBoard == 0x0000000000000000
}

手番交代

Board.swift
/**
 * @brief  手番の入れ替え
 */
func swapBoard() {
    //ボードの入れ替え
    let tmp = playerBoard
    playerBoard   = opponentBoard
    opponentBoard = tmp

    //色の入れ替え
    nowTurn = nowTurn * -1
}

終局結果の取得

勝者手番(色)と石数の取得メソッドを実装しておく

Board.swift
/**
 * @brief 結果を取得する
 * @return blackScore 黒石の数
 * @return whiteScore 白石の数
 * @return winner     勝ち手番
 */
func getResult() -> (blackScore: Int, whiteScore: Int, winner: String) {
    //石数を取得
    var blackScore = bitCount(playerBoard)
    var whiteScore = bitCount(opponentBoard)
    if nowTurn == WHITE_TURN {
        let tmp = blackScore
        blackScore = whiteScore
        whiteScore = tmp
    }
    // 勝敗情報を取得
    var winner = "黒番"
    let isWhiteWin = whiteScore >= blackScore
    if isWhiteWin {
        winner = "白番"
    } 
    return (blackScore, whiteScore, winner)
}
bit.swift
/**
 * @brief ビットカウント
 * @param num 64ビット
 * @return 立ってるフラグの数[Int]
 */
public func bitCount(_ num: UInt64) -> Int {
    let BOARDSIZE = 64
    var mask: UInt64 = 0x8000000000000000
    var count: Int = 0

    for _ in 0..<BOARDSIZE {
        if mask & num != 0 {
            count += 1
        }
        mask = mask >> 1
    }
    return count
}

まとめ

Board.swift
import Foundation

class Board {

    //MARK: Constant
    let BLACK_TURN = 100
    let WHITE_TURN = -100

    //MARK: Properties
    var nowTurn  : Int          // 現在の手番
    var nowIndex : Int          // 現在何手目か
    var playerBoard   : UInt64  // player側ビットボード
    var opponentBoard : UInt64  // opponent側ビットボード
    
    //MARK: Initialization
    init() {
        self.nowTurn       = BLACK_TURN
        self.nowIndex      = 1

        // 一般的な初期配置を指定
        self.playerBoard   = 0x0000000810000000
        self.opponentBoard = 0x0000001008000000
    }

    //MARK: Instance Function

    /**
     * @brief 着手可否の判定
     * @param put 置いた位置のみにフラグが立っている64ビット
     * @return 着手可能ならtrue
     */
    func canPut (put: UInt64) -> Bool {
        // 着手可能なマスにフラグが立っている合法手ボードを生成
        let legalBoard : UInt64 = makeLegalBoard(board: self)
        // 今回の着手が、その合法手ボードに含まれれば着手可能
        return (put & legalBoard) == put
    }

    /**
     * @brief 着手し,反転処理を行う
     * @param put 着手した場所のみにフラグが立つ64ビット
     */
    func reverse(put: UInt64) {
        //着手した場合のボードを生成
        var rev : UInt64 = 0
        for k in 0..<8 {
            var rev_ : UInt64 = 0
            var mask : UInt64 = transfer(put: put, k: k)
            while (mask != 0) && ((mask & opponentBoard) != 0) {
                rev_ |= mask
                mask = transfer(put: mask, k: k)
            }
            if (mask & playerBoard) != 0 {
                rev |= rev_
            }
        }
        //反転する
        self.playerBoard   ^= put | rev
        self.opponentBoard ^= rev
        //現在何手目かを更新
        self.nowIndex = self.nowIndex + 1
    }

    /**
     * @brief パス判定  (= プレイヤーのみが置けない時)
     * @return パスならtrue
     */
    func isPass() -> Bool {
        // 手番側の合法手ボードを生成
        let playerLegalBoard = makeLegalBoard(board: self)

        // 相手側の合法手ボードを生成    
        let tmpBoard = Board()
        tmpBoard.nowTurn  = nowTurn
        tmpBoard.nowIndex = nowIndex
        tmpBoard.playerBoard = playerBoard
        tmpBoard.opponentBoard = opponentBoard
        let opponentLegalBoard = makeLegalBoard(board: tmpBoard)
        
        // 手番側だけがパスの場合    
        return playerLegalBoard == 0x0000000000000000 && opponentLegalBoard != 0x0000000000000000
    }

    /**
     * @brief 終局判定  (= 二人ともが置けない時)
     * @return 終局ならtrue
     */
    func isGameFinished() -> Bool {
        let playerLegalBoard = makeLegalBoard(board: self)
        
        let tmpBoard = Board()
        tmpBoard.nowTurn  = nowTurn
        tmpBoard.nowIndex = nowIndex
        tmpBoard.playerBoard = playerBoard
        tmpBoard.opponentBoard = opponentBoard
        let opponentLegalBoard = makeLegalBoard(board: tmpBoard)
    
        // 両手番とも置く場所がない場合    
        return playerLegalBoard == 0x0000000000000000 && opponentLegalBoard == 0x0000000000000000
    }

    /**
     * @brief  手番の入れ替え
     */
    func swapBoard() {
        //ボードの入れ替え
        let tmp = playerBoard
        playerBoard   = opponentBoard
        opponentBoard = tmp

        //色の入れ替え
        nowTurn = nowTurn * -1
    }

    /**
     * @brief 結果を取得する
     * @return blackScore 黒石の数
     * @return whiteScore 白石の数
     * @return winner     勝ち手番
     */
    func getResult() -> (blackScore: Int, whiteScore: Int, winner: String) {
        //石数を取得
        var blackScore = bitCount(playerBoard)
        var whiteScore = bitCount(opponentBoard)
        if self.nowTurn == WHITE_TURN {
            let tmp = blackScore
            blackScore = whiteScore
            whiteScore = tmp
        }
        // 勝敗情報を取得
        var winner = "黒番"
        let isWhiteWin = whiteScore >= blackScore
        if isWhiteWin {
            winner = "白番"
        } 
        return (blackScore, whiteScore, winner)
    }
}


//MARK: Private Function

/**
 * @brief 反転箇所を求める
 * @param put 着手した場所のみにフラグが立つ64ビット
 * @param k   反転方向(8つ)
 * @return 反転箇所にフラグが立っている64ビット
 */
private func transfer(put: UInt64, k: Int) -> UInt64 {
    switch k {
    case 0: //上
        return (put << 8) & 0xffffffffffffff00
    case 1: //右上
        return (put << 7) & 0x7f7f7f7f7f7f7f00
    case 2: //右
        return (put >> 1) & 0x7f7f7f7f7f7f7f7f
    case 3: //右下
        return (put >> 9) & 0x007f7f7f7f7f7f7f
    case 4: //下
        return (put >> 8) & 0x00ffffffffffffff
    case 5: //左下
        return (put >> 7) & 0x00fefefefefefefe
    case 6: //左
        return (put << 1) & 0xfefefefefefefefe
    case 7: //左上
        return (put << 9) & 0xfefefefefefefe00
    default:
        return 0
    }
}

/**
 * @brief 手番側の合法手ボードを生成
 * @param  board Boardインスタンス
 * @return playerから見て、置ける場所のみにフラグが立っている64ビット
 */
private func makeLegalBoard(board: Board) -> UInt64 {
    //左右端の番人
    let horizontalWatchBoard : UInt64 = board.opponentBoard & 0x7e7e7e7e7e7e7e7e
    //上下端の番人
    let verticalWatchBoard : UInt64   = board.opponentBoard & 0x00FFFFFFFFFFFF00
    //全辺の番人
    let allSideWatchBoard : UInt64    = board.opponentBoard & 0x007e7e7e7e7e7e00
    //空きマスのみにビットが立っているボード
    let blankBoard : UInt64 = ~(board.playerBoard | board.opponentBoard)
    //隣に相手の色があるかを一時保存する
    var tmp : UInt64
    //返り値
    var legalBoard : UInt64
    
    //8方向チェック (・一度に返せる石は最大6つ ・高速化のためにforを展開)
    //左
    tmp = horizontalWatchBoard & (board.playerBoard << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    tmp |= horizontalWatchBoard & (tmp << 1)
    legalBoard = blankBoard & (tmp << 1)

    //右
    tmp = horizontalWatchBoard & (board.playerBoard >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    tmp |= horizontalWatchBoard & (tmp >> 1)
    legalBoard |= blankBoard & (tmp >> 1)

    //上
    tmp = verticalWatchBoard & (board.playerBoard << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    tmp |= verticalWatchBoard & (tmp << 8)
    legalBoard |= blankBoard & (tmp << 8)

    //下
    tmp = verticalWatchBoard & (board.playerBoard >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    tmp |= verticalWatchBoard & (tmp >> 8)
    legalBoard |= blankBoard & (tmp >> 8)

    //右斜め上
    tmp = allSideWatchBoard & (board.playerBoard << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    tmp |= allSideWatchBoard & (tmp << 7)
    legalBoard |= blankBoard & (tmp << 7)

    //左斜め上
    tmp = allSideWatchBoard & (board.playerBoard << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    tmp |= allSideWatchBoard & (tmp << 9)
    legalBoard |= blankBoard & (tmp << 9)

    //右斜め下
    tmp = allSideWatchBoard & (board.playerBoard >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    tmp |= allSideWatchBoard & (tmp >> 9)
    legalBoard |= blankBoard & (tmp >> 9)

    //左斜め下
    tmp = allSideWatchBoard & (board.playerBoard >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    tmp |= allSideWatchBoard & (tmp >> 7)
    legalBoard |= blankBoard & (tmp >> 7)

    return legalBoard
}
bit.swift
/**
 * @brief 座標をbitに変換する
 * @param x 横座標(A~H)
 * @param y 縦座標(1~8)
 * @return 着手箇所のみにフラグが立っている64ビット
 */
public func coordinateToBit(x: String, y: String) -> UInt64 {
    var mask: UInt64 = 0x8000000000000000
    // X方向へのシフト
    switch x {
    case "A":
        break
    case "B":
        mask = mask >> 1
    case "C":
        mask = mask >> 2
    case "D":
        mask = mask >> 3
    case "E":
        mask = mask >> 4
    case "F":
        mask = mask >> 5
    case "G":
        mask = mask >> 6
    case "H":
        mask = mask >> 7
    default:
        break
    }
    // Y方向へのシフト
    let intY = Int(y)
    mask = mask >> ( (intY! - 1) * 8)
    
    return mask
}

/**
 * @brief ビットカウント
 * @param (num: UINT64)
 * @return 立ってるフラグの数[Int]
 */
public func bitCount(_ num: UInt64) -> Int {
    let BOARDSIZE = 64
    var mask: UInt64 = 0x8000000000000000
    var count: Int = 0

    for _ in 0..<BOARDSIZE {
        if mask & num != 0 {
            count += 1
        }
        mask = mask >> 1
    }
    return count
}

実例

かなり雑な例だが、こんな感じで使う
もちろん実際にはこんな風には書かないです。

main.swift
var board = Board()

// F5に着手
var put = coordinateToBit(x: "F", y: "5")
if board.canPut(put: put) {
    board.reverse(put: put)
    board.swapBoard()
}
if board.isPass(){
    board.swapBoard()
}
if board.isGameFinished() {
    let result = board.getResult()
    print(result)
}

// D6に着手
put = coordinateToBit(x: "D", y: "6")
if board.canPut(put: put) {
    board.reverse(put: put)
    board.swapBoard()
}
if board.isPass(){
    board.swapBoard()
}
if board.isGameFinished() {
    let result = board.getResult()
    print(result)
}

// C3に...
59
33
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?