|
38 | 38 | right: 0; |
39 | 39 | bottom: 0; |
40 | 40 | top: 0; |
41 | | - border: 2px solid #f0f; |
| 41 | + z-index: 1; |
| 42 | + border: 3px dashed #fdad19; |
42 | 43 | } |
43 | 44 |
|
44 | 45 | .reversi-board .piece { |
|
55 | 56 | background: #000; |
56 | 57 | } |
57 | 58 |
|
| 59 | + .reversi-board .piece.B.flip { |
| 60 | + animation: PieceWTB .5s linear; |
| 61 | + } |
| 62 | + |
| 63 | + .reversi-board .piece.W.flip { |
| 64 | + animation: PieceBTW .5s linear; |
| 65 | + } |
| 66 | + |
| 67 | + @keyframes PieceBTW { |
| 68 | + 0% { |
| 69 | + background: #000; |
| 70 | + transform: rotateY(0); |
| 71 | + } |
| 72 | + |
| 73 | + 49% { |
| 74 | + background: #000; |
| 75 | + transform: rotateY(90deg); |
| 76 | + } |
| 77 | + |
| 78 | + 50% { |
| 79 | + background: #fff; |
| 80 | + transform: rotateY(90deg); |
| 81 | + } |
| 82 | + |
| 83 | + 100% { |
| 84 | + background: #fff; |
| 85 | + transform: rotateY(180deg); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + @keyframes PieceWTB { |
| 90 | + 0% { |
| 91 | + background: #fff; |
| 92 | + transform: rotateY(0); |
| 93 | + } |
| 94 | + |
| 95 | + 49% { |
| 96 | + background: #fff; |
| 97 | + transform: rotateY(90deg); |
| 98 | + } |
| 99 | + |
| 100 | + 50% { |
| 101 | + background: #000; |
| 102 | + transform: rotateY(90deg); |
| 103 | + } |
| 104 | + |
| 105 | + 100% { |
| 106 | + background: #000; |
| 107 | + transform: rotateY(180deg); |
| 108 | + } |
| 109 | + } |
| 110 | + |
58 | 111 | .reversi-board .game-over { |
59 | 112 | position: absolute; |
60 | 113 | left: 0; |
|
102 | 155 |
|
103 | 156 | let reversiBoardDOM = document.querySelector(".reversi-board") |
104 | 157 | let sideLength = (reversiBoardDOM.clientWidth - 40) / 8 // 边长 |
105 | | - let operatePlayer = 'B'; // 操作的颜色 |
| 158 | + let currPlayer = 'B'; // 操作的颜色 |
106 | 159 | let lastPos = [] // 操作点位 |
| 160 | + let lastPosDict = {} // 操作字典 |
107 | 161 | let reversiBoard = new Proxy({}, { |
108 | 162 | get(obj, prop) { |
109 | 163 | return obj[prop]; |
|
128 | 182 | pieceDOM.classList.remove('W') |
129 | 183 | pieceDOM.classList.remove('B') |
130 | 184 | pieceDOM.classList.add(color) |
| 185 | + pieceDOM.classList.add('flip') |
| 186 | + setTimeout(() => { |
| 187 | + pieceDOM.classList.remove('flip') |
| 188 | + }, 800); |
131 | 189 | } |
132 | | - |
133 | 190 | return true |
134 | 191 | } |
135 | 192 | }) |
|
148 | 205 | if (e.target.classList.contains("allowSelect")) { // 允许选择 |
149 | 206 | let x = e.target.getAttribute('data-x') |
150 | 207 | let y = e.target.getAttribute('data-y') |
151 | | - let allowSelectPos = lastPos.filter(op => op[op.length - 1] == v2ToStr(x, y)) |
152 | | - allowSelectPos.forEach((arr) => { |
153 | | - arr.forEach(key => { |
154 | | - reversiBoard[key] = operatePlayer; |
155 | | - }) |
156 | | - }) |
| 208 | + let key = v2ToStr(x, y); |
| 209 | + lastPosDict[key].forEach(key => reversiBoard[key] = currPlayer) |
157 | 210 | reversiBoardDOM.querySelectorAll('.allowSelect').forEach(dom => dom.classList.remove('allowSelect')) |
158 | | - operatePlayer = 'W'; |
159 | | - nextPlayerOperate() |
| 211 | + currPlayer = 'W'; |
| 212 | + setTimeout(() => nextPlayerOperate(), 1000) |
| 213 | + |
160 | 214 | } |
161 | 215 | }) |
162 | 216 | reversiBoardDOM.appendChild(gridDOM) |
|
176 | 230 | showReversiBoard(); |
177 | 231 | let pos = [] |
178 | 232 | for (const key in reversiBoard) { |
179 | | - if (reversiBoard[key] === operatePlayer) { |
| 233 | + if (reversiBoard[key] === currPlayer) { |
180 | 234 | let { x, y } = strToV2(key) |
181 | 235 | pos.push(...find8Direction(x, y)) |
182 | 236 | } |
|
189 | 243 | return |
190 | 244 | } |
191 | 245 | } |
192 | | - else if (operatePlayer === "B") { // 玩家提供可操作的点位 |
193 | | - // 渲染到页面 |
194 | | - let allowSelect = pos.map(a => a[a.length - 1]) |
195 | | - allowSelect.forEach((key) => { |
| 246 | + else if (currPlayer === "B") { // 玩家提供可操作的点位 |
| 247 | + lastPosDict = doubleArrayToDictArray(pos) |
| 248 | + Object.keys(lastPosDict).forEach((key) => { |
196 | 249 | let { x, y } = strToV2(key) |
197 | 250 | reversiBoardDOM.querySelector(`.grid[data-x="${x}"][data-y="${y}"]`).classList.add('allowSelect') |
198 | 251 | }) |
199 | 252 | lastPos = pos; |
200 | 253 | return; |
201 | 254 | } |
202 | | - else if (operatePlayer === 'W') { // 机器人 |
| 255 | + else if (currPlayer === 'W') { // 机器人 |
203 | 256 | let allowSelect = pos.sort((a, b) => a.length - b.length); |
204 | 257 | let selected = allowSelect[allowSelect.length - 1] |
205 | 258 | let key = selected[selected.length - 1] |
206 | | - allowSelect.filter(arr => arr[arr.length - 1] === key).forEach(arr => { |
207 | | - arr.forEach(key => { |
208 | | - reversiBoard[key] = operatePlayer; |
209 | | - }) |
210 | | - }) |
| 259 | + lastPosDict = doubleArrayToDictArray(pos); |
| 260 | + lastPosDict[key].forEach(key => reversiBoard[key] = currPlayer) |
211 | 261 | } |
212 | | - operatePlayer = operatePlayer == "B" ? "W" : "B" |
| 262 | + currPlayer = currPlayer == "B" ? "W" : "B" |
213 | 263 | lastPos = pos; |
214 | 264 | nextPlayerOperate(); |
215 | 265 | } |
|
222 | 272 | while (-1 < cx && cx < 8 && -1 < cy && cy < 8) { |
223 | 273 | let key = v2ToStr(cx, cy) |
224 | 274 | if (!reversiBoard[key]) return pos.length ? pos.concat(key) : [] |
225 | | - else if (reversiBoard[key] === operatePlayer) return [] |
| 275 | + else if (reversiBoard[key] === currPlayer) return [] |
226 | 276 | else pos.push(key) |
227 | 277 | cx += dx |
228 | 278 | cy += dy |
|
247 | 297 | ].filter(a => a.length) |
248 | 298 | } |
249 | 299 |
|
| 300 | + // 将多个点位打组 |
| 301 | + function doubleArrayToDictArray(da) { |
| 302 | + let dict = {} |
| 303 | + da.forEach(arr => { |
| 304 | + let last = arr[arr.length - 1]; |
| 305 | + let booty = arr.slice(0, arr.length - 1); |
| 306 | + if (dict[last]) dict[last] = dict[last].concat(booty) |
| 307 | + else dict[last] = [...booty, last] |
| 308 | + }) |
| 309 | + return dict |
| 310 | + } |
| 311 | + |
250 | 312 | // 展示游戏结束 |
251 | 313 | function showGameOver() { |
252 | 314 | let gameOverDOM = document.createElement("div") |
|
0 commit comments