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;
}
