// ==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(); // 添加按钮
})();