Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
export const standardMap = [
|
||||
"--------",
|
||||
"--------",
|
||||
"--------",
|
||||
"---wb---",
|
||||
"---bw---",
|
||||
"--------",
|
||||
"--------",
|
||||
"--------"
|
||||
];
|
||||
export class ReversiEngine {
|
||||
map;
|
||||
mapWidth;
|
||||
mapHeight;
|
||||
board;
|
||||
turn = true;
|
||||
prevColor = null;
|
||||
opts;
|
||||
constructor(map, opts){
|
||||
this.opts = {
|
||||
isLlotheo: opts.isLlotheo ?? false,
|
||||
canPutEverywhere: opts.canPutEverywhere ?? false,
|
||||
loopedBoard: opts.loopedBoard ?? false
|
||||
};
|
||||
this.mapWidth = map[0]?.length ?? 0;
|
||||
this.mapHeight = map.length;
|
||||
const data = map.join("");
|
||||
this.board = data.split("").map((d)=>d === "-" ? null : d === "b" ? true : d === "w" ? false : undefined);
|
||||
this.map = data.split("").map((d)=>d === "-" || d === "b" || d === "w" ? "empty" : "null");
|
||||
if (!this.canPutSomewhere(true)) this.turn = this.canPutSomewhere(false) ? false : null;
|
||||
}
|
||||
get blackCount() {
|
||||
return this.board.filter((x)=>x === true).length;
|
||||
}
|
||||
get whiteCount() {
|
||||
return this.board.filter((x)=>x === false).length;
|
||||
}
|
||||
posToXy(pos) {
|
||||
return [
|
||||
pos % this.mapWidth,
|
||||
Math.floor(pos / this.mapWidth)
|
||||
];
|
||||
}
|
||||
xyToPos(x, y) {
|
||||
return x + y * this.mapWidth;
|
||||
}
|
||||
mapDataGet(pos) {
|
||||
const [x, y] = this.posToXy(pos);
|
||||
return x < 0 || y < 0 || x >= this.mapWidth || y >= this.mapHeight ? "null" : this.map[pos];
|
||||
}
|
||||
getPuttablePlaces(color) {
|
||||
return Array.from(this.board.keys()).filter((i)=>this.canPut(color, i));
|
||||
}
|
||||
canPutSomewhere(color) {
|
||||
return this.getPuttablePlaces(color).length > 0;
|
||||
}
|
||||
canPut(color, pos) {
|
||||
if (this.board[pos] !== null) return false;
|
||||
if (this.opts.canPutEverywhere) return this.mapDataGet(pos) === "empty";
|
||||
return this.effects(color, pos).length !== 0;
|
||||
}
|
||||
effects(color, initPos) {
|
||||
const enemyColor = !color;
|
||||
const diffVectors = [
|
||||
[
|
||||
0,
|
||||
-1
|
||||
],
|
||||
[
|
||||
1,
|
||||
-1
|
||||
],
|
||||
[
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
1,
|
||||
1
|
||||
],
|
||||
[
|
||||
0,
|
||||
1
|
||||
],
|
||||
[
|
||||
-1,
|
||||
1
|
||||
],
|
||||
[
|
||||
-1,
|
||||
0
|
||||
],
|
||||
[
|
||||
-1,
|
||||
-1
|
||||
]
|
||||
];
|
||||
return diffVectors.flatMap(([dx, dy])=>{
|
||||
const found = [];
|
||||
let [x, y] = this.posToXy(initPos);
|
||||
while(true){
|
||||
x += dx;
|
||||
y += dy;
|
||||
if (this.opts.loopedBoard && this.xyToPos(x = (x % this.mapWidth + this.mapWidth) % this.mapWidth, y = (y % this.mapHeight + this.mapHeight) % this.mapHeight) === initPos) {
|
||||
return found;
|
||||
}
|
||||
if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight) return [];
|
||||
const pos = this.xyToPos(x, y);
|
||||
if (this.mapDataGet(pos) === "null") return [];
|
||||
const stone = this.board[pos];
|
||||
if (stone === null) return [];
|
||||
if (stone === enemyColor) found.push(pos);
|
||||
if (stone === color) return found;
|
||||
}
|
||||
});
|
||||
}
|
||||
putStone(pos) {
|
||||
const color = this.turn;
|
||||
if (color == null) return;
|
||||
this.board[pos] = color;
|
||||
for (const effect of this.effects(color, pos)){
|
||||
this.board[effect] = color;
|
||||
}
|
||||
this.prevColor = color;
|
||||
this.turn = this.canPutSomewhere(!color) ? !color : this.canPutSomewhere(color) ? color : null;
|
||||
}
|
||||
get isEnded() {
|
||||
return this.turn == null;
|
||||
}
|
||||
get winner() {
|
||||
if (!this.isEnded) return null;
|
||||
if (this.blackCount === this.whiteCount) return null;
|
||||
return this.opts.isLlotheo === this.blackCount > this.whiteCount ? false : true;
|
||||
}
|
||||
calcCrc32() {
|
||||
let hash = 0;
|
||||
const text = JSON.stringify({
|
||||
board: this.board,
|
||||
turn: this.turn
|
||||
});
|
||||
for(let i = 0; i < text.length; i++){
|
||||
hash = hash * 31 + text.charCodeAt(i) | 0;
|
||||
}
|
||||
return hash.toString();
|
||||
}
|
||||
}
|
||||
export function serializeLogs(logs) {
|
||||
const serialized = [];
|
||||
for(let i = 0; i < logs.length; i++){
|
||||
const log = logs[i];
|
||||
const timeDelta = i === 0 ? log.time : log.time - logs[i - 1].time;
|
||||
serialized.push([
|
||||
timeDelta,
|
||||
log.player ? 1 : 0,
|
||||
0,
|
||||
log.pos
|
||||
]);
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
export function deserializeLogs(logs) {
|
||||
const deserialized = [];
|
||||
let time = 0;
|
||||
for (const log of logs){
|
||||
time += log[0];
|
||||
if (log[2] === 0) {
|
||||
deserialized.push({
|
||||
time,
|
||||
player: log[1] === 1,
|
||||
operation: "put",
|
||||
pos: log[3]
|
||||
});
|
||||
}
|
||||
}
|
||||
return deserialized;
|
||||
}
|
||||
export function restoreGame(env) {
|
||||
const game = new ReversiEngine(env.map, env);
|
||||
for (const log of deserializeLogs(env.logs)){
|
||||
if (log.operation === "put") game.putStone(log.pos);
|
||||
}
|
||||
return game;
|
||||
}
|
||||
Reference in New Issue
Block a user