Skip to content

Commit

Permalink
BitBoard (#2)
Browse files Browse the repository at this point in the history
* redux

* wip

* action

* reducer

* bitboard

* genBitBoard

* canmove

* move

* works

* build
  • Loading branch information
na-o-ys authored Dec 1, 2017
1 parent 6cc435a commit e271ef4
Show file tree
Hide file tree
Showing 17 changed files with 8,423 additions and 5,557 deletions.
13,049 changes: 7,904 additions & 5,145 deletions assets/bundle.js

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
"@types/node": "^8.0.53",
"@types/react": "^16.0.25",
"@types/react-dom": "^16.0.3",
"@types/react-redux": "^5.0.14",
"@types/redux": "^3.6.0",
"@types/redux-logger": "^3.0.5",
"lodash": "^4.17.4",
"react": "^16.1.1",
"react-dom": "^16.1.1",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"ts-loader": "^3.1.1",
"typescript": "^2.6.2",
"webpack": "^3.8.1",
Expand Down
45 changes: 45 additions & 0 deletions src/BitBoard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as _ from "lodash"
import { octetCellsToCells, genOctetCells, OctetCells } from "GameDescription"

interface BitBoardEntry {
[key: number]: OctetCells | undefined
}

export const BitBoard: BitBoardEntry[] = _.range(1<<16).map(i => {
const currCells = octetCellsToCells(i)
let entry: BitBoardEntry = {}

_.range(8).forEach(x => {
if (currCells[x] !== ".") return
const cells = _.clone(currCells)
// 左方向
let lEnd = x
if (x - 1 >= 0 && currCells[x - 1] === "w") {
for (let j = x - 2; j >= 0; j--) {
if (currCells[j] === "b") {
lEnd = j
break
}
if (currCells[j] === "." || currCells[j] === "-") break
}
}
let rEnd = x
if (x + 1 < 8 && currCells[x + 1] === "w") {
for (let j = x + 2; j < 8; j++) {
if (currCells[j] === "b") {
rEnd = j
break
}
if (currCells[j] === "." || currCells[j] === "-") break
}
}
if (lEnd != rEnd) {
for (let j = lEnd; j <= rEnd; j++) {
cells[j] = "b"
}
entry[x] = genOctetCells(cells)
}
})

return entry
})
172 changes: 172 additions & 0 deletions src/GameDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import * as _ from "lodash"
import { GameState } from "ui/containers/Game"
import * as UiTypes from "ui/types"
import { reverse } from "dns";

// 自石: 00
// 相手石: 01
// 空: 10
// 壁: 11
export type OctetCells = number

export interface GameDescription {
rows: OctetCells[]
cols: OctetCells[]
// 左下から右上
diagsR: OctetCells[]
// 右下から左上
diagsL: OctetCells[]
}

export function fromUiState({ turn, cells }: GameState): GameDescription {
const fpCells = turn == "b" ? cells : reverseColor(cells)

const rows = _.chunk(fpCells, 8)
.map(row => genOctetCells(row))
const cols = (_.zip.apply(null, _.chunk(fpCells, 8)) as Cell[][])
.map(col => genOctetCells(col))
const diagsR = genDiagsR(fpCells)
.map(diag => genOctetCells(diag))
const diagsL = genDiagsL(fpCells)
.map(diag => genOctetCells(diag))

return { rows, cols, diagsR, diagsL }
}

export function toUiState(desc: GameDescription, turn: UiTypes.Color): UiTypes.CellState[] {
const cells = octetCellRowsToUiCells(desc.rows)
return turn == "b" ? cells : reverseColor(cells)
}

type Cell = "." | "b" | "w" | "-"

export function genOctetCells(row: Cell[]): OctetCells {
return _.reduce(
row,
(octet, cell) => (octet << 2) + cellToByte(cell),
0
)
}

function cellToByte(cell: Cell): number {
if (cell === "b") return 0
if (cell === "w") return 1
if (cell === ".") return 2
return 3
}

function genDiagsR(cells: Cell[]): Cell[][] {
const rows = _.chunk(cells, 8)
// (0, 0), (1, -1), (2, -2), ...
// (0, 1), (1, 0), (2, -1), ...
// (0, 2), (1, 1), (2, 0), ...
// ...
// (0, 7), (1, 6), ...
const seg1 = _.range(8).map(idx =>
_.range(8).map(x => {
const y = idx - x
if (y < 0) return "-"
return rows[y][x]
})
)

// (1, 7), (2, 6), ...
// (2, 7), (3, 6), ...
// (3, 7), (4, 6), ...
// ...
// (7, 7), *(8, 6), ...
const seg2 = _.range(8).map(idxY =>
_.range(8).map(idxX => {
const x = idxX + 1 + idxY
const y = 7 - idxX
if (x > 7) return "-"
return rows[y][x]
})
)

return _.concat(seg1, seg2)
}

function genDiagsL(cells: Cell[]): Cell[][] {
const rows = _.chunk(cells, 8)
const seg1 = _.range(8).map(idx =>
_.range(8).map(x => {
const y = idx - x
if (y < 0) return "-"
return rows[y][7 - x]
})
)

const seg2 = _.range(8).map(idxY =>
_.range(8).map(idxX => {
const x = idxX + 1 + idxY
const y = 7 - idxX
if (x > 7) return "-"
return rows[y][7 - x]
})
)

return _.concat(seg1, seg2)
}

function octetCellRowsToUiCells(rows: OctetCells[]): UiTypes.CellState[] {
return _.flatten(
rows.map(row =>
octetCellsToCells(row) as UiTypes.CellState[]
)
)
}

function reverseColor(cells: UiTypes.CellState[]): UiTypes.CellState[] {
return cells.map(c => {
if (c == "b") return "w"
if (c == "w") return "b"
return c
})
}

export function octetCellsToCells(octetCells: OctetCells): Cell[] {
return _.range(8).map(idx => {
const byte = (octetCells >> (2 * (7 - idx))) & 3
if (byte === 0) return "b"
if (byte === 1) return "w"
if (byte === 2) return "."
return "-"
})
}

// for debug

export function showOctetCols(cols: OctetCells[]) {
const colCells = cols.map(octetCellsToCells)
const str = _.range(8).map(y =>
_.range(8).map(x =>
colCells[x][y]
).join("")
).join("\n")
console.log("--- debug show octet cols")
console.log(str)
}

export function showOctetDiags(diags: OctetCells[]) {
console.log("--- debug show octet diags")
let rows: any = _.range(8).map(() => _.range(8))
diags.map((diag, idxY) => {
const cells = octetCellsToCells(diag)
if (idxY < 8) {
_.range(8).forEach(x => {
const y = idxY - x
if (y < 0) return
rows[y][x] = cells[x]
})
} else {
_.range(8).forEach(idxX => {
const x = (idxY - 7) + idxX
const y = 7 - idxX
if (x > 7) return
rows[y][x] = cells[idxX]
})
}
})
console.log(rows.map((row: any) => row.join("")).join("\n"))
}
106 changes: 106 additions & 0 deletions src/rule/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { GameDescription, fromUiState, toUiState, octetCellsToCells } from "GameDescription"
import { BitBoard } from "BitBoard"
import * as UiTypes from "ui/types"

export function canMove(desc: GameDescription, x: number, y: number): boolean {
// row
if (BitBoard[desc.rows[y]][x]) return true
// col
if (BitBoard[desc.cols[x]][y]) return true
// diagR
const diagR = desc.diagsR[x + y]
if (x + y < 8) {
// seg1
if (BitBoard[diagR][x]) return true
} else {
// seg2
if (BitBoard[diagR][7 - y]) return true
}
// diagL
const rx = 7 - x
const diagL = desc.diagsL[rx + y]
if (rx + y < 8) {
// seg1
if (BitBoard[diagL][rx]) return true
} else {
// seg2
if (BitBoard[diagL][7 - y]) return true
}
return false
}

export function move(desc: GameDescription, x: number, y: number): GameDescription {
const cells = toUiState(desc, "b")
// row
const nextRow = BitBoard[desc.rows[y]][x]
if (nextRow) {
octetCellsToCells(nextRow)
.forEach((cell, ix) => {
cells[8 * y + ix] = cell as UiTypes.CellState
})
}
// col
const nextCol = BitBoard[desc.cols[x]][y]
if (nextCol) {
octetCellsToCells(nextCol)
.forEach((cell, iy) => {
cells[8 * iy + x] = cell as UiTypes.CellState
})
}
// diagR
const diagR = desc.diagsR[x + y]
if (x + y < 8) {
// seg1
const nextDiag = BitBoard[diagR][x]
if (nextDiag) {
octetCellsToCells(nextDiag)
.forEach((cell, ix) => {
const iy = (x + y) - ix
if (iy < 0) return
cells[8 * iy + ix] = cell as UiTypes.CellState
})
}
} else {
// seg2
const nextDiag = BitBoard[diagR][7 - y]
if (nextDiag) {
octetCellsToCells(nextDiag)
.forEach((cell, i) => {
const ix = (x + y - 7) + i
const iy = 7 - i
if (ix > 7) return
cells[8 * iy + ix] = cell as UiTypes.CellState
})
}
}
// diagL
const rx = 7 - x
const diagL = desc.diagsL[rx + y]
if (rx + y < 8) {
// seg1
const nextDiag = BitBoard[diagL][rx]
if (nextDiag) {
octetCellsToCells(nextDiag)
.forEach((cell, ix) => {
const iy = (rx + y) - ix
if (iy < 0) return
cells[8 * iy + 7 - ix] = cell as UiTypes.CellState
})
}
} else {
// seg2
const nextDiag = BitBoard[diagL][7 - y]
if (nextDiag) {
octetCellsToCells(nextDiag)
.forEach((cell, i) => {
const ix = (rx + y - 7) + i
const iy = 7 - i
if (ix > 7) return
cells[8 * iy + 7 - ix] = cell as UiTypes.CellState
})
}
}

// TODO: たぶんdescに変換する処理を飛ばせる
return fromUiState({ turn: "b", cells })
}
35 changes: 29 additions & 6 deletions src/ui/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
import * as React from "react"
import * as ReactDOM from "react-dom"
import { Board } from "ui/components/Board"
import { InitialBoard } from "ui/constants"
import { Store, Middleware } from "redux"
import { applyMiddleware, createStore } from "redux"
import { createLogger } from "redux-logger"
import { Provider } from "react-redux"
import { Game, GameState } from "ui/containers/Game"
import { reducers } from "ui/reducers"
import { initialBoard } from "ui/constants"

export const App = () => (
<Board cells={InitialBoard} />
)
const middleWares: Middleware[] = [
process.env.NODE_ENV !== "production" && createLogger() as any
].filter(Boolean)

export function start(dom: HTMLElement | null) {
let store = createStore<GameState>(
reducers,
{
cells: initialBoard,
turn: "b"
},
applyMiddleware(...middleWares)
)

ReactDOM.render(
<App />,
<App store={store} />,
dom
)
}

export interface AppProps {
store: Store<GameState>
}
const App = ({ store }: AppProps) => (
<Provider store={store}>
<Game />
</Provider>
)
Loading

0 comments on commit e271ef4

Please sign in to comment.