猫史档案馆


试试发到其他聊天软件(围棋大赛的代码)

用户:周某a周某a查看:0 回复:0 评论:0 创建时间:2020-06-03T17:07:50


console.clear();

const KOMI = 7.5;

const RESIGN_RE = /认输/i;
const PASS_RE = /过/i;

const TICKS_PER_SECOND = (1024 / 喵);
const MAIN_TICKS = 10 * 60 * TICKS_PER_SECOND;
const LIGHTNING_TICKS = 10 * TICKS_PER_SECOND;
const ALLOW_SELF_PLAY = false;

const DIGIT = [
    'zero',
    'one',
    'two',
    'three',
    'four',
    'five',
    'six',
    'seven',
    'eight',
    'nine',
];

const B = {
    '+': voxels.id('board1'),
    'T': voxels.id('board4'),
    'L': voxels.id('board3'),
    '.': voxels.id('board2'),
}

const R = {
    N: 0x8000,
    S: 0,
    E: 0xc000,
    W: 0x4000,
};

const COLORS = [
    '黑',
    '白',
];

const STONE_MESH = [
    world.querySelector('.black_stone').mesh,
    world.querySelector('.white_stone').mesh,
];

const CURRENT_STONE_MESH = [
    world.querySelector('.cur_black_stone').mesh,
    world.querySelector('.cur_white_stone').mesh,
];

const BOWL_MESH = [
    world.querySelector('.black_bowl').mesh,
    world.querySelector('.white_bowl').mesh,
];
const CHOOSE_BUTTON_MESH = world.querySelector('.choose_button').mesh;

const teleport = (function(/* 传送相关 */) {
    const locations = {};
    world.querySelectorAll('.地点').forEach((e) => {
        locations[e.id] = e.position;
        e.destroy();
    });
    world.onEntityContact(({entity, other}) => {
        if (other.isPlayer && entity.hasTag('绝对传送')) {
            const attr = getEntityAttr(entity);
            const pos = locations[attr['目的地']];
            if (pos) other.position.copy(pos);
        }
    });
    world.onEntityContact(({entity, other}) => {
        if (other.isPlayer && entity.hasTag('相对传送')) {
            const attr = getEntityAttr(entity);
            other.position.addEq({
                x: Number(attr.x) || 0,
                y: Number(attr.y) || 0,
                z: Number(attr.z) || 0,
            });
        }
    });
    return function(e, name) {
        const pos = locations[name];
        if (e && e.isPlayer && pos) e.position.copy(pos);
    }
})();

async function playGo (name, size, originX, originY, originZ) {
    const DOT_MOD = {
        19: [6, 3],
        13: [3, 0],
        9: [2, 0],
        7: [2, 0],
    };
    const SCORE_LENGTH = size <= 9 ? 3 : 5;
    if (!(size in DOT_MOD)) {
        size = 19;
    }

    const captures = [];
    const pieces = new Array(size * size);
    const board = new Array(size * size);
    const visited = new Array(size * size);
    const prevBoards = [];
    const playerEntities = [ null, null ];
    const score = [ 0, 0 ];
    const clock = [ 0, 0 ];
    let currentPlayer = 0;
    let gameState = 'wait';
    let passCount = 0;
    let lastPieceIdx = -1;
    const visitors = new Set();

    function broadcast(s) {
        world.say(`[${name}]: ${s}`);
    }
    function leaveVisotor(e) {
        teleport(e, `${name}观战室`);
        visitors.delete(e);
    }
    world.querySelector(`.${name}离开房间`).onEntityContact(({other}) => {
        if (gameState === 'play') {
            resign(other);
        } else {
            leaveVisotor(other);
        }
    });
    world.querySelector(`.${name}入口`).onEntityContact(({other}) => {
        if (gameState !== 'play') {
            visitors.add(other);
            other.position.y -= 5;
        }
    });
    function createBoard () {
        const [MOD, VAL] =  DOT_MOD[size];
        const border = voxels.id('palace_lamp');
        const borderLight = voxels.id('lantern_02');
        voxels.setVoxelId(originX - 1, originY + 1, originZ - 1, borderLight);
        voxels.setVoxelId(originX + size, originY + 1, originZ - 1, borderLight);
        voxels.setVoxelId(originX - 1, originY + 1, originZ + size, borderLight);
        voxels.setVoxelId(originX + size, originY + 1, originZ + size, borderLight);
        for (let i = 0; i <= size; ++i) {
            voxels.setVoxelId(originX + i, originY, originZ + size, border);
            voxels.setVoxelId(originX + i - 1, originY, originZ - 1, border);
            voxels.setVoxelId(originX + size, originY, originZ + i - 1, border);
            voxels.setVoxelId(originX - 1, originY, originZ + i , border);
        }
        for (let x = 0; x < size; ++x) {
            for (let z = 0; z < size; ++z) {
                let p = B['+'];
                if (x === 0 && z === 0) {
                    p = B['L'] | R['S'];
                } else if (x === 0 && z === size - 1) {
                    p = B['L'] | R['E'];
                } else if (x === size -1 && z === 0) {
                    p = B['L'] | R['W'];
                } else if (x === size -1 && z === size - 1) {
                    p = B['L'] | R['N'];
                } else if (x === 0) {
                    p = B['T'] | R['E'];
                } else if (x === size -1) {
                    p = B['T'] | R['W'];
                } else if (z === 0) {
                    p = B['T'] | R['S'];
                } else if (z === size -1) {
                    p = B['T'] | R['N'];
                } else if (x > 0 && x < size - 1 && z > 0 && z < size - 1 && (x % MOD) === VAL && (z % MOD) === VAL) {
                    p = B['.'];
                }
                voxels.setVoxelId(x + originX, originY, z + originZ, p);
            }
        }
    }

    function drawNumber (x, y, z, n, dx, dz, rotation) {
        for (let i = 0; i < SCORE_LENGTH; ++i) {
            voxels.setVoxel(x, y, z, DIGIT[n % 10], rotation);
            x += dx;
            z += dz;
            n = (n / 10) | 0;
        }
    }
    function drawTime(time) {
        drawNumber(originX + size + 2, originY - 1, originZ + size - SCORE_LENGTH + 1, time !== undefined ? time : (clock[0] / TICKS_PER_SECOND) | 0, 0, 1, 3);
        drawNumber(originX - 3, originY - 1, originZ + SCORE_LENGTH - 2, time !== undefined ? time : (clock[1] / TICKS_PER_SECOND) | 0, 0, -1, 5);
    }

    function index (x, y) {
        return x + y * size;
    }

    function setup () {
        captures.forEach((c) => c.destroy());
        captures.length = 0;
        for (let i = 0; i < size * size; ++i) {
            board[i] = -1;
            if (pieces[i]) {
                pieces[i].destroy();
            }
            pieces[i] = null;
        }
        score[0] = 0;
        score[1] = KOMI;
        currentPlayer = 0;
        prevBoards.length = 0;
        playerEntities[0] = playerEntities[1] = null;
        gameState = 'wait';
        passCount = 0;
        clock[0] = clock[1] = MAIN_TICKS;
    }

    function hasLiberties (pX, pY) {
        visited.fill(false);

        const toVisit = [ pX, pY ];
        const c = board[index(pX, pY)];
        visited[index(pX, pY)] = true;

        let libs = false;

        function push (x, y) {
            const idx = index(x, y);
            if (visited[idx]) {
                return;
            }
            const d = board[idx];
            if (d === c) {
                toVisit.push(x, y);
                visited[idx] = true;
            } else if (d < 0) {
                libs = true;
            }
        }

        for (let i = 0; i < toVisit.length; i += 2) {
            const x = toVisit[i];
            const y = toVisit[i + 1];
            if (x > 0) { push(x - 1, y); }
            if (x < size - 1) { push(x + 1, y); }
            if (y > 0) { push(x, y - 1); }
            if (y < size - 1) { push(x, y + 1); }
        }

        return libs;
    }

    function capture (x, y, koTest) {
        if (board[index(x, y)] < 0) {
            return;
        }
        const libs = hasLiberties(x, y);
        if (gameState === 'play' && libs) {
            return;
        }
        let count = 0;
        for (let x = 0; x < size; ++x) {
            for (let y = 0; y < size; ++y) {
                const idx = index(x, y);
                if (visited[idx]) {
                    if (!koTest) {
                        const c = board[idx] ^ 1;
                        const p = pieces[idx];

                        score[c] += 1;
                        p.destroy();
                        pieces[idx] = null;
                        captures.push(p);
                    }

                    board[idx] = -1;
                }
            }
        }
    }

    function placePiece (x, y) {
        const c = currentPlayer;
        const idx = index(x, y);

        // 检查放置棋子的位置是否为空
        if (board[idx] >= 0) {
            return;
        }

        // 尝试放置棋子
        board[idx] = c;
        
        // 保存棋盘状态
        const koTest = board.slice();

        // 对劫规则的处理
        if (x > 0) { capture(x - 1, y, true); }
        if (x < size - 1) { capture(x + 1, y, true); }
        if (y > 0) { capture(x, y - 1, true); }
        if (y < size - 1) { capture(x, y + 1, true); }
        capture(x, y, true);

        // 计算当前状态
        const ko = board.join();

        // 恢复棋盘状态
        for (let j = 0; j < koTest.length; ++j) {
            board[j] = koTest[j];
        }

        // 检查当前棋盘状态是否已经出现过
        for (let i = 0; i < prevBoards.length; ++i) {
            if (prevBoards[i] === ko) {
                board[idx] = -1;
                return;
            }
        }

        // 创建棋子对象
        pieces[idx] = world.createEntity({
            position: [
                originX + x + 0.5,
                originY + 1.125,
                originZ + y + 0.5,
            ],
            meshScale: [1 / 24, 1 / 24, 1 / 24],
            mesh: CURRENT_STONE_MESH[c],
            gravity: false,
            collides: true,
            fixed: true,
        });

        // 计算提子
        if (x > 0) { capture(x - 1, y); }
        if (x < size - 1) { capture(x + 1, y); }
        if (y > 0) { capture(x, y - 1); }
        if (y < size - 1) { capture(x, y + 1); }
        capture(x, y);
        
        // 保存当前棋盘状态
        prevBoards.push(board.join());

        // 切换到下个玩家
        currentPlayer ^= 1;

        // 清空pass计数
        passCount = 0;

        // 更新clock
        clock[currentPlayer] = Math.max(clock[currentPlayer], LIGHTNING_TICKS);
        if (lastPieceIdx >= 0 && pieces[lastPieceIdx]) {
            pieces[lastPieceIdx].mesh = STONE_MESH[board[lastPieceIdx]];
        }
        lastPieceIdx = idx;
        return true;
    }

    // 统计占地
    function countScore () {
        visited.fill(false);

        const adj = [ false, false ];
        function count (x, y) {
            if (x < 0 || y < 0 || x >= size || y >= size) {
                return 0;
            }

            const idx = index(x, y);
            if (visited[idx]) {
                return 0;
            }
            visited[idx] = true;

            const b = board[idx];
            if (b === -1) {
                return (1 +
                    count(x - 1, y) +
                    count(x + 1, y) + 
                    count(x, y - 1) +
                    count(x, y + 1)
                );
            }
            adj[b] = true;
            return 0;
        }

        for (let x = 0; x < size; ++x) {
            for (let y = 0; y < size; ++y) {
                adj[0] = adj[1] = false;
                const s = count(x, y);
                if (adj[0] && !adj[1]) {
                    score[0] += s;
                } else if (adj[1] && !adj[0]) {
                    score[1] += s;
                }
            }
        }
    }

    function pass (entity) {
        if (gameState === 'play') {
            if (entity === playerEntities[currentPlayer]) {
                broadcast(`${entity.player.name} 弃权一手`);

                passCount += 1;
                currentPlayer ^= 1;

                // 更新clock
                clock[currentPlayer] = Math.max(clock[currentPlayer], LIGHTNING_TICKS);

                if (passCount >= 2) {
                    // broadcast('count territory, remove dead stones');
                    gameState = 'done';
                    passCount = 0;
                }
            }
        } else if (gameState === 'count') {
            for (let i = 0; i < playerEntities.length; ++i) {
                if (entity === playerEntities[i]) {
                    if (passCount & (1 << i) === 0) {
                        broadcast(`${entity.player.name} finshed counting`);
                        passCount |= (1 << i);
                    }
                }
            }
            if (passCount === 3) {
                broadcast('done counting');
                gameState = 'done';
            }
        }
    }
    
    function resign (entity) {
        for (let i = 0; i < 2; ++i) {
            if (entity === playerEntities[i]) {
                if (gameState === 'play' ||
                    gameState === 'count') {
                    broadcast(`${entity.player.name} 认输`);
                    score[i] = -Infinity;
                    gameState = 'done';
                }
                playerEntities[i] = null;
            }
        }
    }

    createBoard();
    drawTime(0);
    [
        [originX + size + 3, originZ],
        [originX - 3, originZ + size],
    ].map(([x, z], i) => world.createEntity({
        position: [
            x,
            originY + 0.5,
            z,
        ],
        mesh: BOWL_MESH[i],
        meshScale: [1 / 16, 1 / 16, 1 / 16],
        fixed: true,
        collides: true,
        gravity: false,
    }));
    const chooseButtons = [originX + size + 2.5, originX - 2.5].map((x) => world.createEntity({
        position: [
            x,
            originY + 0.3,
            originZ + size / 2,
        ],
        mesh: CHOOSE_BUTTON_MESH,
        meshScale: [1 / 24, 1 / 24, 1 / 24],
        fixed: true,
        collides: true,
        gravity: false,
    }))

    world.onPress(({ entity, button, raycast:{ hitPosition:{x, y, z}, hitEntity } }) => {
        if (gameState === 'play') {
            if (button !== 'action0' ||
                entity !== playerEntities[currentPlayer] ||
                Math.abs(y - originY) > 2) {
                return;
            }
            const dx = Math.floor(x) - originX;
            const dz = Math.floor(z) - originZ;
            if (dx < 0 || dx >= size || dz < 0 || dz >= size) {
                return;
            }
            placePiece(dx, dz);
        } else if (gameState === 'count' && hitEntity) {
            for (let i = 0; i < playerEntities.length; ++i) {
                if (entity === playerEntities[i] && pieces.indexOf(hitEntity) >= 0) {
                    capture(
                        Math.floor(hitEntity.position.x - originX),
                        Math.floor(hitEntity.position.z - originZ));
                }
            }
        }
    });

    chooseButtons.forEach((button, c) => {
        button.onEntityContact(({ other }) => {
            if (gameState === 'wait' && other.isPlayer && playerEntities[c] !== other) {
                broadcast(`${other.player.name}执${COLORS[c]}棋`);
                playerEntities[c] = other;
                if (!ALLOW_SELF_PLAY && playerEntities[c^1] === other) {
                    playerEntities[c^1] = null;
                }
            }
        });
    });

    // pas喵utton.onEntityContact(({ other }) => pass(other));
    // resignButton.onEntityContact(({ other }) => resign(other));
    world.onPlayerLeave(({ entity }) => resign(entity));

    world.onChat(({ entity, message }) => {
        if (message.match(PASS_RE)) {
            pass(entity);
        } else if (message.match(RESIGN_RE)) {
            resign(entity);
        }
    });
    
    while (true) {
        // 初始化
        setup();

        // 等待双方玩家选择颜色
        while (!playerEntities[0] || !playerEntities[1]) {
            await world.nextTick();
        }

        broadcast('对局开始');
        gameState = 'play';

        for (const v of visitors) {
            if (playerEntities.includes(v)) continue;
            teleport(v, `${name}观战室`);
        }
        visitors.clear();
        // 处理每回合
        while (gameState === 'play') {
            await world.nextTick();
            if (--clock[currentPlayer] <= 0) {
                pass(playerEntities[currentPlayer]);
            }
            drawTime();
        }

        broadcast('对局结束');

        // 计算占地
        while (gameState === 'count') {
            await world.nextTick();
        }
        
        countScore();
        
        broadcast(`黑棋${score[0]}目 vs 白棋${score[1]}目`);
        
        if (score[0] > score[1]) {
            broadcast('黑棋胜');
        } else {
            broadcast('白棋胜');
        }
        drawTime(0);
        playerEntities.forEach((e) => teleport(e, `${name}观战室`));
        // 让棋盘上的棋子动起来, 表示本局结束
        pieces.forEach((p) => {
            if (p) {
                p.collides = true;
                p.fixed = false;
                p.gravity = true;
                p.velocity.x += 0.1 * (Math.random() - 0.5);
                p.velocity.y += 0.1 * Math.random();
                p.velocity.z += 0.1 * (Math.random() - 0.5)
            }
        });

        // 十秒后开始下一轮
        await sleep(10 * 1000);
    }
}

function getEntityAttr(e) {
    const attr = {};
    e.tags().forEach((tag) => {
        const g = tag.match(/(\S+)\s*:\s*(\S+)/);
        if (g) {
            attr[g[1]] = g[2];
        }
    });
    return attr;
}

// 生成棋盘
world.querySelectorAll('.go_spawner').forEach((e) => {
    const attr = getEntityAttr(e);
    if ('name' in attr) {
        playGo(attr.name, parseInt(attr.size) || 9,
            Math.floor(e.position.x),
            Math.floor(e.position.y),
            Math.floor(e.position.z));
    }  
});
    

// 删除临时实体
world.querySelectorAll('.temporary').forEach((e) => e.destroy());
console.log(world.onPlayerJoin);
world.onPlayerJoin(({ entity }) => {
    entity.player.enableActionA = true;
});


回复

上一页1 页 / 共 0下一页