/
Board.ts
144 lines (128 loc) · 3.58 KB
/
Board.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import * as _ from "lodash"
import * as UiTypes from "ui/types"
// black: 00
// white: 01
// empty: 10
export type Row = number
export interface Board {
rows: Row[]
cols: Row[]
// left-bottom to right-top
diagsR: Row[]
// right-bottom to left-top
diagsL: Row[]
stones: number
}
export function reverse(desc: Board): Board {
const ma = 0b1010101010101010
const mb = 0b0101010101010101
const rev = (r: Row) => r ^ mb ^ ((r & ma) >>> 1)
return {
rows: desc.rows.map(rev),
cols: desc.cols.map(rev),
diagsL: desc.diagsL.map(rev),
diagsR: desc.diagsR.map(rev),
stones: desc.stones
}
}
export function flip(desc: Board, x: number, y: number) {
desc.rows[y] &= ~(0b11 << (2 * (7 - x)))
desc.cols[x] &= ~(0b11 << (2 * (7 - y)))
if (x + y < 8) {
desc.diagsR[x + y] &= ~(0b11 << (2 * (7 - x)))
} else {
desc.diagsR[x + y] &= ~(0b11 << (2 * y))
}
const rx = 7 - x
if (rx + y < 8) {
desc.diagsL[rx + y] &= ~(0b11 << (2 * (7 - rx)))
} else {
desc.diagsL[rx + y] &= ~(0b11 << (2 * y))
}
}
export function stones(board: Board): number[] {
return _.flatten(
board.rows.map(row =>
_.range(8)
.map(i => (row >> ((7 - i) * 2)) & 0b11)
.map(c => {
if (c == 0b00) return [1, 0]
if (c == 0b01) return [0, 1]
return [0, 0]
})
)
)
.reduce((acc, crr) => [acc[0] + crr[0], acc[1] + crr[1]], [0, 0])
}
export function fromUiState(cells: UiTypes.CellState[]): Board {
const rows = _.chunk(cells, 8)
.map(row => genRow(row))
const cols = (_.zip.apply(null, _.chunk(cells, 8)) as Cell[][])
.map(col => genRow(col))
const diagsR = genDiagsR(cells)
.map(diag => genRow(diag))
const diagsL = genDiagsR(
_.flatten(_.chunk(cells, 8).map(r => _.reverse(r)))
)
.map(diag => genRow(diag))
const stones = cells.filter(c => c != ".").length
return { rows, cols, diagsR, diagsL, stones }
}
export function toUiState(board: Board): UiTypes.CellState[] {
return _.flatten(
board.rows.map(row =>
rowToCells(row) as UiTypes.CellState[]
)
)
}
type Cell = "." | "b" | "w"
function genRow(row: Cell[]): Row {
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 rowToCells(octetCells: Row) {
return _.range(8).map(idx => {
const byte = (octetCells >> (2 * (7 - idx))) & 3
if (byte === 0) return "b"
if (byte === 1) return "w"
return "."
})
}