必应自动拼图

2024/8/9 17:27:30

// ==UserScript==
// @name 必应自动拼图
// @namespace 自动
// @match *://*.bing.*/spotlight/*
// @grant none
// @version 1.1
// @author auuuu
// @license MIT
// @description 2024/8/9 17:27:30
// python 版本 已经发布在 52pj上面了
// ==/UserScript==
(function() {
    'use strict';
    let tiles;
    const loadElements = () => {
        tiles = document.getElementById("tiles").children; // 加载拼图元素
        console.log(tiles);
    };

    const inputArrowUp = () => {
        for (let i = 0; i < tiles.length; i++) {
            const next = i - 3; // 上移
            if (checkTileByIndex(next)) {
                tiles[i].click(); // 点击移动
                break;
            }
        }
    };

    const inputArrowDown = () => {
        for (let i = 0; i < tiles.length; i++) {
            const next = i + 3; // 下移
            if (checkTileByIndex(next)) {
                tiles[i].click(); // 点击移动
                break;
            }
        }
    };

    const inputArrowLeft = () => {
        for (let i = 0; i < tiles.length; i++) {
            const next = i - 1; // 左移
            if (checkTileByIndex(next)) {
                tiles[i].click(); // 点击移动
                break;
            }
        }
    };

    const inputArrowRight = () => {
        for (let i = 0; i < tiles.length; i++) {
            const next = i + 1; // 右移
            if (checkTileByIndex(next)) {
                tiles[i].click(); // 点击移动
                break;
            }
        }
    };

    const checkTileByIndex = (index) => {
        if (index < 0 || index >= tiles.length) {
            return false; // 超出边界
        }
        const targetChildren = tiles[index].children;
        return targetChildren.length === 0; // 返回是否为空白
    };

    // 添加按钮到 Tampermonkey 菜单
    const addButtonToMenu = () => {
        const button = document.createElement('button');
        button.innerText = '开始自动拼图';
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.zIndex = 1000;
        button.style.padding = '10px';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';

        button.onclick = () => {
            main(); // 点击按钮时调用主函数
        };

        document.body.appendChild(button);
    };

    // 拼图状态类
    class PuzzleState {
        constructor(board, zeroPos, moves) {
            this.board = board; // 当前拼图状态
            this.zeroPos = zeroPos; // 空白位置
            this.moves = moves; // 移动记录
            this.cost = this.calculateCost(); // 计算总代价 (g + h)
        }

        // 计算总代价 (g + h)
        calculateCost() {
            return this.moves.length + this.heuristic(); // g + h
        }

        // 启发式:曼哈顿距离
        heuristic() {
            let distance = 0;
            const targetPositions = {
                1: [0, 0], 2: [0, 1], 3: [0, 2],
                4: [1, 0], 5: [1, 1], 6: [1, 2],
                7: [2, 0], 8: [2, 1], 0: [2, 2]
            };
            for (let i = 0; i < 3; i++) {
                for (let j = 0; j < 3; j++) {
                    const value = this.board[i][j];
                    if (value !== 0) {
                        const [targetX, targetY] = targetPositions[value];
                        distance += Math.abs(targetX - i) + Math.abs(targetY - j);
                    }
                }
            }
            return distance; // 返回总距离
        }

        // 优先队列比较函数
        compareTo(other) {
            return this.cost - other.cost; // 比较总代价
        }
    }

    // 打印矩阵
    function printMatrix(matrix) {
        matrix.forEach(row => {
            console.log(row.join(', ')); // 打印每一行
        });
    }

    // 查找空白位置
    function findBlankPosition(matrix) {
        for (let i = 0; i < 3; i++) {
            for (let j = 0; j < 3; j++) {
                if (matrix[i][j] === 0) {
                    return [i, j]; // 返回空白位置
                }
            }
        }
        return null;
    }

    // 检查拼图是否可解
    function isSolvable(puzzle) {
        let inversions = 0;
        const flatPuzzle = puzzle.flat().filter(num => num !== 0);
        for (let i = 0; i < flatPuzzle.length; i++) {
            for (let j = i + 1; j < flatPuzzle.length; j++) {
                if (flatPuzzle[i] > flatPuzzle[j]) {
                    inversions++; // 计算逆序对
                }
            }
        }
        return inversions % 2 === 0; // 可解条件
    }

    // 获取邻居状态
    function getNeighbors(state) {
        const neighbors = [];
        const [x, y] = state.zeroPos; // 当前空白位置
        const directions = [[-1, 0, '下'], [1, 0, '上'], [0, -1, '右'], [0, 1, '左']];

        for (const [dx, dy, move] of directions) {
            const newX = x + dx;
            const newY = y + dy;
            if (newX >= 0 && newX < 3 && newY >= 0 && newY < 3) {
                const newBoard = state.board.map(row => row.slice());
                [newBoard[x][y], newBoard[newX][newY]] = [newBoard[newX][newY], newBoard[x][y]];
                neighbors.push(new PuzzleState(newBoard, [newX, newY], [...state.moves, move])); // 添加邻居状态
            }
        }
        return neighbors;
    }

    // A* 算法
    function aStar(start) {
        const target = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]; // 目标状态
        const startState = new PuzzleState(start, findBlankPosition(start), []);
        const priorityQueue = [startState];
        const visited = new Set();

        while (priorityQueue.length > 0) {
            // 根据总代价排序优先队列
            priorityQueue.sort((a, b) => a.compareTo(b));

            const currentState = priorityQueue.shift(); // 取出当前状态
            const currentTuple = JSON.stringify(currentState.board);

            if (JSON.stringify(currentState.board) === JSON.stringify(target)) {
                return currentState.moves; // 返回移动步骤
            }

            if (visited.has(currentTuple)) {
                continue; // 如果已访问,跳过
            }
            visited.add(currentTuple);

            for (const neighbor of getNeighbors(currentState)) {
                priorityQueue.push(neighbor); // 添加邻居状态到优先队列
            }
        }

        return []; // 如果没有找到解决方案,返回空数组
    }

    // 主函数
    function main() {
        // 获取 id 为 tiles 的 div
        const tilesContainer = document.getElementById('tiles');
        const results = [];

        // 获取 tilesContainer 下的所有子 div
        const childDivs = tilesContainer.children;

        Array.from(childDivs).forEach(child => {
            // 查找 .parentTile
            const parentTiles = child.querySelectorAll('.parentTile');

            if (parentTiles.length === 0) {
                results.push('0'); // 如果没有 .parentTile,返回 0
            } else {
                let tileValues = [];

                parentTiles.forEach(parent => {
                    // 查找 .tileNumber
                    const tileNumbers = parent.querySelectorAll('.tileNumber');
                    if (tileNumbers.length === 0) {
                        tileValues.push('0'); // 如果没有 .tileNumber,返回 0
                    } else {
                        // 获取 .tileNumber 的值
                        tileNumbers.forEach(tile => {
                            const value = tile.textContent.trim();
                            tileValues.push(value); // 收集每个 tileNumber 的值
                            console.log(`.tileNumber: ${value}`); // 打印每个 .tileNumber 的结果
                        });
                    }
                });

                // 拼接 tileNumber 的值
                const tileResult = tileValues.join(',');
                results.push(tileResult); // 将结果添加到结果数组中
                console.log(`.parentTile: ${tileResult}`); // 打印对应的 .parentTile 的结果
            }
        });
        const inputList = results.map(Number);
        const puzzle = [];
        for (let i = 0; i < 3; i++) {
            puzzle.push(inputList.slice(i * 3, i * 3 + 3)); // 生成拼图矩阵
        }

        console.log("原始矩阵:");
        printMatrix(puzzle);

        if (!isSolvable(puzzle)) {
            console.log("这个拼图无法解决。");
            return; // 如果拼图不可解,结束
        }

        const moves = aStar(puzzle); // 获取还原步骤
        console.log("还原步骤:");
        moves.forEach((move, index) => {
            setTimeout(() => {
                console.log(move); // 打印每一步的移动
                // 这里可以调用处理方法来执行每一步的移动
                // 例如:executeMove(move);
                if (move == "上") {
                    console.log("向上移动");
                    inputArrowUp();
                }
                if (move == "下") {
                    console.log("向下移动");
                    inputArrowDown();
                }
                if (move == "左") {
                    console.log("向左移动");
                    inputArrowLeft();
                }
                if (move == "右") {
                    console.log("向右移动");
                    inputArrowRight();
                }
            }, index * 10); // 每个移动之间停顿500ms
        });
    }
    loadElements();
    addButtonToMenu(); // 添加按钮
})();