LinuxDo2048

LinuxDo2048 25w分

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         LinuxDo2048
// @namespace    http://tampermonkey.net/
// @version      2025-07-02
// @author       TigerWang
// @description  LinuxDo2048 25w分
// @license      MIT
// @match        https://2048.linux.do/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=linux.do
// @grant        none
// ==/UserScript==

(function () {
  // 标记 AI 是否正在运行
  let aiRunning = false;

  // Worker 内部代码(字符串形式)- 增强版本
  const workerCode = `var Module = typeof Module !== "undefined" ? Module : {};
var moduleOverrides = {};
var key;
for (key in Module) {
    if (Module.hasOwnProperty(key)) {
        moduleOverrides[key] = Module[key]
    }
}

// 新增: 最大化棋面价值策略 - 实现"清理与巩固"
function evaluateMaximizeBoardValue(board) {
  let score = 0;
  const tiles = [];
  const positions = [];
  
  // 提取所有非零数值和位置
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      const tile = (board[i] >> (12 - 4 * j)) & 0xf;
      if (tile > 0) {
        const value = 1 << tile;
        tiles.push(value);
        positions.push({i, j, value});
      }
    }
  }
  
  // 计算当前棋面总价值
  const totalValue = tiles.reduce((sum, tile) => sum + tile, 0);
  score += totalValue * 10; // 直接奖励高总分
  
  // 检测游戏阶段
  const count8192 = tiles.filter(x => x === 8192).length;
  const smallTiles = tiles.filter(x => x <= 32);
  const mediumTiles = tiles.filter(x => x >= 128 && x <= 512);
  
  // 动态策略选择
  const emptySpaces = 16 - tiles.length;
  
  if (count8192 >= 2) {
    // 双8192模式:最大化终盘价值,但避免合并
    score += evaluateEndgameValue(tiles, positions);
  } else if (count8192 === 1) {
    // 单8192策略:根据棋盘状况决定
    if (smallTiles.length > 8 && emptySpaces >= 3) {
      // 清理模式:小瓦片过多且有空间时清理
      score += evaluateCleanupPhase(tiles, positions);
    } else if (emptySpaces <= 2) {
      // 危险模式:空间不足时优先保持可玩性
      score += evaluateSurvivalMode(tiles, positions);
    } else {
      // 发展模式:继续向第二个8192努力
      score += evaluateSecond8192Growth(tiles, positions);
    }
  } else {
    // 标准发展模式
    score += evaluateStandardGrowth(tiles, positions);
  }
  
  // 中等瓦片保护奖励 - 平衡保护与发展
  if (count8192 >= 1) {
    // 已有8192时,适度保护中等瓦片
    score += mediumTiles.length * 5000;
    score += tiles.filter(x => x === 256).length * 8000;
    score += tiles.filter(x => x === 512).length * 12000;
  } else {
    // 没有8192时,优先发展大瓦片
    score += mediumTiles.length * 3000;
  }
  
  // 大瓦片发展奖励
  score += tiles.filter(x => x === 1024).length * 25000;
  score += tiles.filter(x => x === 2048).length * 50000;
  
  // 4096动态保护策略
  const tiles4096 = tiles.filter(x => x === 4096);
  if (count8192 === 0) {
    // 没有8192时,4096是发展目标
    score += tiles4096.length * 75000;
  } else if (count8192 === 1 && emptySpaces > 2) {
    // 有1个8192且空间充足时,可以发展第二个
    score += tiles4096.length * 40000;
  } else {
    // 其他情况适度保护
    score += tiles4096.length * 20000;
  }
  
  // 单调性评估
  score += getMonotonicity(board) * 1000;
  
  // 智能平滑性评估
  score += getSmartSmoothness(board, positions) * 500;
  
  // 空格数量与质量
  const emptyCount = 16 - tiles.length;
  score += emptyCount * 4000; // 增加空格权重
  
  // 容错空间评估
  if (emptyCount < 3) {
    score -= 50000; // 惩罚过满的棋盘
  }
  
  return score;
}

// 新增: 清理阶段评估
function evaluateCleanupPhase(tiles, positions) {
  let cleanupScore = 0;
  
  // 奖励小瓦片合并潜力
  const smallGroups = {};
  tiles.forEach(tile => {
    if (tile <= 32) {
      smallGroups[tile] = (smallGroups[tile] || 0) + 1;
    }
  });
  
  // 成对的小瓦片加分
  Object.entries(smallGroups).forEach(([value, count]) => {
    if (count >= 2) {
      cleanupScore += parseInt(value) * count * 100;
    }
  });
  
  // 惩罚过多的2和4
  const count2 = tiles.filter(x => x === 2).length;
  const count4 = tiles.filter(x => x === 4).length;
  if (count2 + count4 > 8) {
    cleanupScore -= (count2 + count4 - 8) * 10000;
  }
  
  return cleanupScore;
}

// 新增: 终盘价值评估
function evaluateEndgameValue(tiles, positions) {
  let endgameScore = 0;
  
  // 奖励保留高价值瓦片
  tiles.forEach(tile => {
    if (tile >= 256) {
      endgameScore += tile * 5; // 中高价值瓦片额外加分
    }
  });
  
  // 8192防合并保护
  const tiles8192Count = tiles.filter(x => x === 8192).length;
  if (tiles8192Count >= 2) {
    const pos8192 = positions.filter(p => p.value === 8192);
    let minSeparation = Infinity;
    
    for (let i = 0; i < pos8192.length - 1; i++) {
      for (let j = i + 1; j < pos8192.length; j++) {
        const distance = Math.abs(pos8192[i].i - pos8192[j].i) + 
                        Math.abs(pos8192[i].j - pos8192[j].j);
        minSeparation = Math.min(minSeparation, distance);
      }
    }
    
    if (minSeparation === 1) {
      endgameScore -= 300000; // 相邻8192惩罚
    } else if (minSeparation >= 3) {
      endgameScore += 100000; // 分离良好奖励
    }
  }
  
  return endgameScore;
}

// 新增: 标准发展评估
function evaluateStandardGrowth(tiles, positions) {
  let growthScore = 0;
  
  // 奖励瓦片升级链
  const upgradePotential = calculateUpgradePotential(tiles);
  growthScore += upgradePotential * 1000;
  
  return growthScore;
}

// 新增: 生存模式评估 - 棋盘满时保持可玩性
function evaluateSurvivalMode(tiles, positions) {
  let survivalScore = 0;
  
  // 优先保持移动可能性
  survivalScore += calculateMovePossibilities(tiles, positions) * 50000;
  
  // 降低对中等瓦片的过度保护
  const mediumTiles = tiles.filter(x => x >= 128 && x <= 512);
  if (mediumTiles.length > 4) {
    survivalScore -= 20000; // 中等瓦片过多时减分
  }
  
  return survivalScore;
}

// 新增: 第二个8192发展模式
function evaluateSecond8192Growth(tiles, positions) {
  let growthScore = 0;
  
  // 奖励4096的存在和发展
  const count4096 = tiles.filter(x => x === 4096).length;
  const count2048 = tiles.filter(x => x === 2048).length;
  const count1024 = tiles.filter(x => x === 1024).length;
  
  // 4096是第二个8192的关键
  growthScore += count4096 * 80000;
  
  // 2048链条发展
  growthScore += count2048 * 30000;
  growthScore += count1024 * 15000;
  
  // 如果有多个4096,优先合并其中一个
  if (count4096 >= 2) {
    growthScore += 100000;
  }
  
  return growthScore;
}

// 新增: 计算移动可能性
function calculateMovePossibilities(tiles, positions) {
  let possibilities = 0;
  
  // 检查相邻相同瓦片(可合并)
  for (let i = 0; i < positions.length; i++) {
    for (let j = i + 1; j < positions.length; j++) {
      const pos1 = positions[i];
      const pos2 = positions[j];
      
      // 相邻且相同值
      if (pos1.value === pos2.value &&
          ((Math.abs(pos1.i - pos2.i) === 1 && pos1.j === pos2.j) ||
           (Math.abs(pos1.j - pos2.j) === 1 && pos1.i === pos2.i))) {
        possibilities += pos1.value; // 大瓦片合并更有价值
      }
    }
  }
  
  return possibilities;
}

// 新增: 计算升级潜力
function calculateUpgradePotential(tiles) {
  const tileCounts = {};
  tiles.forEach(tile => {
    tileCounts[tile] = (tileCounts[tile] || 0) + 1;
  });
  
  let potential = 0;
  Object.entries(tileCounts).forEach(([value, count]) => {
    if (count >= 2) {
      potential += parseInt(value) * (count - 1);
    }
  });
  
  return potential;
}

// 新增: 智能平滑性评估
function getSmartSmoothness(board, positions) {
  let smooth = 0;
  
  // 根据瓦片大小调整平滑性权重
  for (let i = 0; i < positions.length - 1; i++) {
    for (let j = i + 1; j < positions.length; j++) {
      const pos1 = positions[i];
      const pos2 = positions[j];
      
      // 相邻瓦片
      if ((Math.abs(pos1.i - pos2.i) === 1 && pos1.j === pos2.j) ||
          (Math.abs(pos1.j - pos2.j) === 1 && pos1.i === pos2.i)) {
        
        const ratio = Math.max(pos1.value, pos2.value) / Math.min(pos1.value, pos2.value);
        
        // 2倍关系最理想
        if (ratio === 2) {
          smooth += 100;
        } else if (ratio === 4) {
          smooth += 50;
        } else if (ratio > 4) {
          smooth -= (ratio - 4) * 20;
        }
      }
    }
  }
  
  return smooth;
}

// 新增: 排除8192的平滑性计算
function getSmoothnessExclude8192(board, positions) {
  let smooth = 0;
  const pos8192 = positions.filter(p => p.value === 8192);
  
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 3; j++) {
      const curr = (board[i] >> (12 - 4 * j)) & 0xf;
      const next = (board[i] >> (12 - 4 * (j + 1))) & 0xf;
      
      // 跳过涉及8192的平滑性计算
      const currVal = curr > 0 ? (1 << curr) : 0;
      const nextVal = next > 0 ? (1 << next) : 0;
      
      if (currVal === 8192 || nextVal === 8192) continue;
      
      if (curr > 0 && next > 0) {
        smooth -= Math.abs(curr - next);
      }
    }
  }
  return smooth;
}

// 新增: 检测8192合并风险
function detect8192MergeRisk(board) {
  let riskLevel = 0;
  const positions8192 = [];
  
  // 找到所有8192的位置
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      const tile = (board[i] >> (12 - 4 * j)) & 0xf;
      if ((1 << tile) === 8192) {
        positions8192.push({i, j});
      }
    }
  }
  
  if (positions8192.length < 2) return 0;
  
  // 检查8192之间的关系
  for (let a = 0; a < positions8192.length; a++) {
    for (let b = a + 1; b < positions8192.length; b++) {
      const pos1 = positions8192[a];
      const pos2 = positions8192[b];
      
      // 同行或同列的8192
      if (pos1.i === pos2.i || pos1.j === pos2.j) {
        const distance = Math.abs(pos1.i - pos2.i) + Math.abs(pos1.j - pos2.j);
        
        if (distance === 1) {
          // 直接相邻:极高风险
          riskLevel += 5;
        } else {
          // 同行/列但不相邻:检查中间是否有空格
          let hasEmptyBetween = false;
          
          if (pos1.i === pos2.i) {
            // 同行
            const minJ = Math.min(pos1.j, pos2.j);
            const maxJ = Math.max(pos1.j, pos2.j);
            for (let j = minJ + 1; j < maxJ; j++) {
              const tile = (board[pos1.i] >> (12 - 4 * j)) & 0xf;
              if (tile === 0) {
                hasEmptyBetween = true;
                break;
              }
            }
          } else {
            // 同列
            const minI = Math.min(pos1.i, pos2.i);
            const maxI = Math.max(pos1.i, pos2.i);
            for (let i = minI + 1; i < maxI; i++) {
              const tile = (board[i] >> (12 - 4 * pos1.j)) & 0xf;
              if (tile === 0) {
                hasEmptyBetween = true;
                break;
              }
            }
          }
          
          if (!hasEmptyBetween) {
            // 同行/列且中间无空格:高风险
            riskLevel += 3;
          } else {
            // 同行/列但中间有空格:中风险
            riskLevel += 1;
          }
        }
      }
    }
  }
  
  return riskLevel;
}

// 新增: 计算8192分离度
function calculate8192Separation(board) {
  const positions8192 = [];
  
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      const tile = (board[i] >> (12 - 4 * j)) & 0xf;
      if ((1 << tile) === 8192) {
        positions8192.push({i, j});
      }
    }
  }
  
  if (positions8192.length < 2) return 0;
  
  // 计算8192之间的最小距离
  let minDistance = Infinity;
  for (let a = 0; a < positions8192.length; a++) {
    for (let b = a + 1; b < positions8192.length; b++) {
      const pos1 = positions8192[a];
      const pos2 = positions8192[b];
      const distance = Math.abs(pos1.i - pos2.i) + Math.abs(pos1.j - pos2.j);
      minDistance = Math.min(minDistance, distance);
    }
  }
  
  return minDistance; // 距离越远越好
}

// 新增: 单调性计算
function getMonotonicity(board) {
  let mono = 0;
  for (let i = 0; i < 4; i++) {
    let inc = 0, dec = 0;
    for (let j = 0; j < 3; j++) {
      const curr = (board[i] >> (12 - 4 * j)) & 0xf;
      const next = (board[i] >> (12 - 4 * (j + 1))) & 0xf;
      if (curr > next) inc++;
      if (curr < next) dec++;
    }
    mono += Math.max(inc, dec);
  }
  return mono;
}

// 新增: 平滑性计算
function getSmoothness(board) {
  let smooth = 0;
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 3; j++) {
      const curr = (board[i] >> (12 - 4 * j)) & 0xf;
      const next = (board[i] >> (12 - 4 * (j + 1))) & 0xf;
      if (curr > 0 && next > 0) {
        smooth -= Math.abs(curr - next);
      }
    }
  }
  return smooth;
}
var arguments_ = [];
var thisProgram = "./this.program";
var quit_ = function (status, toThrow) {
    throw toThrow
};

var scriptDirectory = "";
function locateFile(path) {
    if (Module["locateFile"]) {
        return Module["locateFile"](path, scriptDirectory)
    }
    return scriptDirectory + path
}
var read_, readAsync, readBinary, setWindowTitle;
var nodeFS;
var nodePath;

scriptDirectory = self.location.href
if (scriptDirectory.indexOf("blob:") !== 0) {
    scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf("/") + 1)
} else {
    scriptDirectory = ""
}
{
    read_ = function shell_read(url) {
        var xhr = new XMLHttpRequest;
        xhr.open("GET", url, false);
        xhr.send(null);
        return xhr.responseText
    }
    readBinary = function readBinary(url) {
        var xhr = new XMLHttpRequest;
        xhr.open("GET", url, false);
        xhr.responseType = "arraybuffer";
        xhr.send(null);
        return new Uint8Array(xhr.response)
    }
    readAsync = function readAsync(url, onload, onerror) {
        var xhr = new XMLHttpRequest;
        xhr.open("GET", url, true);
        xhr.responseType = "arraybuffer";
        xhr.onload = function xhr_onload() {
            if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
                onload(xhr.response);
                return
            }
            onerror()
        }
            ;
        xhr.onerror = onerror;
        xhr.send(null)
    }
}
setWindowTitle = function (title) {
    document.title = title
}

var out = Module["print"] || console.log.bind(console);
var err = Module["printErr"] || console.warn.bind(console);
for (key in moduleOverrides) {
    if (moduleOverrides.hasOwnProperty(key)) {
        Module[key] = moduleOverrides[key]
    }
}
moduleOverrides = null;
if (Module["arguments"])
    arguments_ = Module["arguments"];
if (Module["thisProgram"])
    thisProgram = Module["thisProgram"];
if (Module["quit"])
    quit_ = Module["quit"];
var wasmBinary;
if (Module["wasmBinary"])
    wasmBinary = Module["wasmBinary"];
var noExitRuntime;
if (Module["noExitRuntime"])
    noExitRuntime = Module["noExitRuntime"];
if (typeof WebAssembly !== "object") {
    abort("no native wasm support detected")
}
var wasmMemory;
var wasmTable;
var ABORT = false;
var EXITSTATUS = 0;
function assert(condition, text) {
    if (!condition) {
        abort("Assertion failed: " + text)
    }
}
var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined;
function UTF8ArrayToString(heap, idx, maxBytesToRead) {
    var endIdx = idx + maxBytesToRead;
    var endPtr = idx;
    while (heap[endPtr] && !(endPtr >= endIdx))
        ++endPtr;
    if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) {
        return UTF8Decoder.decode(heap.subarray(idx, endPtr))
    } else {
        var str = "";
        while (idx < endPtr) {
            var u0 = heap[idx++];
            if (!(u0 & 128)) {
                str += String.fromCharCode(u0);
                continue
            }
            var u1 = heap[idx++] & 63;
            if ((u0 & 224) == 192) {
                str += String.fromCharCode((u0 & 31) << 6 | u1);
                continue
            }
            var u2 = heap[idx++] & 63;
            if ((u0 & 240) == 224) {
                u0 = (u0 & 15) << 12 | u1 << 6 | u2
            } else {
                u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heap[idx++] & 63
            }
            if (u0 < 65536) {
                str += String.fromCharCode(u0)
            } else {
                var ch = u0 - 65536;
                str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023)
            }
        }
    }
    return str
}
function UTF8ToString(ptr, maxBytesToRead) {
    return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""
}
var WASM_PAGE_SIZE = 65536;
var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
function updateGlobalBufferAndViews(buf) {
    buffer = buf;
    Module["HEAP8"] = HEAP8 = new Int8Array(buf);
    Module["HEAP16"] = HEAP16 = new Int16Array(buf);
    Module["HEAP32"] = HEAP32 = new Int32Array(buf);
    Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf);
    Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf);
    Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf);
    Module["HEAPF32"] = HEAPF32 = new Float32Array(buf);
    Module["HEAPF64"] = HEAPF64 = new Float64Array(buf)
}
var INITIAL_INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 134217728;
if (Module["wasmMemory"]) {
    wasmMemory = Module["wasmMemory"]
} else {
    wasmMemory = new WebAssembly.Memory({
        "initial": INITIAL_INITIAL_MEMORY / WASM_PAGE_SIZE,
        "maximum": INITIAL_INITIAL_MEMORY / WASM_PAGE_SIZE
    })
}
if (wasmMemory) {
    buffer = wasmMemory.buffer
}
INITIAL_INITIAL_MEMORY = buffer.byteLength;
updateGlobalBufferAndViews(buffer);
var __ATPRERUN__ = [];
var __ATINIT__ = [];
var __ATMAIN__ = [];
var __ATPOSTRUN__ = [];
var runtimeInitialized = false;
var runtimeExited = false;
function preRun() {
    if (Module["preRun"]) {
        if (typeof Module["preRun"] == "function")
            Module["preRun"] = [Module["preRun"]];
        while (Module["preRun"].length) {
            addOnPreRun(Module["preRun"].shift())
        }
    }
    callRuntimeCallbacks(__ATPRERUN__)
}
function initRuntime() {
    runtimeInitialized = true;
    callRuntimeCallbacks(__ATINIT__)
}
function preMain() {
    callRuntimeCallbacks(__ATMAIN__)
}
function exitRuntime() {
    runtimeExited = true
}
function postRun() {
    if (Module["postRun"]) {
        if (typeof Module["postRun"] == "function")
            Module["postRun"] = [Module["postRun"]];
        while (Module["postRun"].length) {
            addOnPostRun(Module["postRun"].shift())
        }
    }
    callRuntimeCallbacks(__ATPOSTRUN__)
}
function addOnPreRun(cb) {
    __ATPRERUN__.unshift(cb)
}
function addOnPostRun(cb) {
    __ATPOSTRUN__.unshift(cb)
}
var runDependencies = 0;
var runDependencyWatcher = null;
var dependenciesFulfilled = null;
function addRunDependency(id) {
    runDependencies++;
    if (Module["monitorRunDependencies"]) {
        Module["monitorRunDependencies"](runDependencies)
    }
}
function removeRunDependency(id) {
    runDependencies--;
    if (Module["monitorRunDependencies"]) {
        Module["monitorRunDependencies"](runDependencies)
    }
    if (runDependencies == 0) {
        if (runDependencyWatcher !== null) {
            clearInterval(runDependencyWatcher);
            runDependencyWatcher = null
        }
        if (dependenciesFulfilled) {
            var callback = dependenciesFulfilled;
            dependenciesFulfilled = null;
            callback()
        }
    }
}
Module["preloadedImages"] = {};
Module["preloadedAudios"] = {};
function abort(what) {
    if (Module["onAbort"]) {
        Module["onAbort"](what)
    }
    what += "";
    err(what);
    ABORT = true;
    EXITSTATUS = 1;
    what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info.";
    var e = new WebAssembly.RuntimeError(what);
    throw e
}
function hasPrefix(str, prefix) {
    return String.prototype.startsWith ? str.startsWith(prefix) : str.indexOf(prefix) === 0
}
var dataURIPrefix = "data:application/octet-stream;base64,";
function isDataURI(filename) {
    return hasPrefix(filename, dataURIPrefix)
}
var fileURIPrefix = "file://";
function isFileURI(filename) {
    return hasPrefix(filename, fileURIPrefix)
}
var wasmBinaryFile = "data:application/octet-stream;base64,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";
if (!isDataURI(wasmBinaryFile)) {
    wasmBinaryFile = locateFile(wasmBinaryFile)
}
function getBinary() {
    try {
        if (wasmBinary) {
            return new Uint8Array(wasmBinary)
        }
        if (readBinary) {
            return readBinary(wasmBinaryFile)
        } else {
            throw "both async and sync fetching of the wasm failed"
        }
    } catch (err) {
        abort(err)
    }
}
function getBinaryPromise() {
    if (!wasmBinary && typeof fetch === "function" && !isFileURI(wasmBinaryFile)) {
        return fetch(wasmBinaryFile, {
            credentials: "same-origin"
        }).then(function (response) {
            if (!response["ok"]) {
                throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"
            }
            return response["arrayBuffer"]()
        }).catch(function () {
            return getBinary()
        })
    }
    return Promise.resolve().then(getBinary)
}
function createWasm() {
    var info = {
        "a": asmLibraryArg
    };
    function receiveInstance(instance, module) {
        var exports = instance.exports;
        Module["asm"] = exports;
        wasmTable = Module["asm"]["e"];
        removeRunDependency("wasm-instantiate")
    }
    addRunDependency("wasm-instantiate");
    function receiveInstantiatedSource(output) {
        receiveInstance(output["instance"])
    }
    function instantiateArrayBuffer(receiver) {
        return getBinaryPromise().then(function (binary) {
            return WebAssembly.instantiate(binary, info)
        }).then(receiver, function (reason) {
            err("failed to asynchronously prepare wasm: " + reason);
            abort(reason)
        })
    }
    function instantiateAsync() {
        if (!wasmBinary && typeof WebAssembly.instantiateStreaming === "function" && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && typeof fetch === "function") {
            fetch(wasmBinaryFile, {
                credentials: "same-origin"
            }).then(function (response) {
                var result = WebAssembly.instantiateStreaming(response, info);
                return result.then(receiveInstantiatedSource, function (reason) {
                    err("wasm streaming compile failed: " + reason);
                    err("falling back to ArrayBuffer instantiation");
                    return instantiateArrayBuffer(receiveInstantiatedSource)
                })
            })
        } else {
            return instantiateArrayBuffer(receiveInstantiatedSource)
        }
    }
    if (Module["instantiateWasm"]) {
        try {
            var exports = Module["instantiateWasm"](info, receiveInstance);
            return exports
        } catch (e) {
            err("Module.instantiateWasm callback failed with error: " + e);
            return false
        }
    }
    instantiateAsync();
    return {}
}
function callRuntimeCallbacks(callbacks) {
    while (callbacks.length > 0) {
        var callback = callbacks.shift();
        if (typeof callback == "function") {
            callback(Module);
            continue
        }
        var func = callback.func;
        if (typeof func === "number") {
            if (callback.arg === undefined) {
                wasmTable.get(func)()
            } else {
                wasmTable.get(func)(callback.arg)
            }
        } else {
            func(callback.arg === undefined ? null : callback.arg)
        }
    }
}
function _abort() {
    abort()
}
var _emscripten_get_now = function () {
    return performance.now()
}
var _emscripten_get_now_is_monotonic = true;
function setErrNo(value) {
    HEAP32[___errno_location() >> 2] = value;
    return value
}
function _clock_gettime(clk_id, tp) {
    var now;
    if (clk_id === 0) {
        now = Date.now()
    } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) {
        now = _emscripten_get_now()
    } else {
        setErrNo(28);
        return -1
    }
    HEAP32[tp >> 2] = now / 1e3 | 0;
    HEAP32[tp + 4 >> 2] = now % 1e3 * 1e3 * 1e3 | 0;
    return 0
}
function _emscripten_run_script(ptr) {
    eval(UTF8ToString(ptr))
}
__ATINIT__.push({
    func: function () {
        ___wasm_call_ctors()
    }
});
var asmLibraryArg = {
    "b": _abort,
    "c": _clock_gettime,
    "d": _emscripten_run_script,
    "a": wasmMemory
};
var asm = createWasm();
var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function () {
    return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["f"]).apply(null, arguments)
}
    ;
var _jsWork = Module["_jsWork"] = function () {
    return (_jsWork = Module["_jsWork"] = Module["asm"]["g"]).apply(null, arguments)
}
    ;
var _main = Module["_main"] = function () {
    return (_main = Module["_main"] = Module["asm"]["h"]).apply(null, arguments)
}
    ;
var ___errno_location = Module["___errno_location"] = function () {
    return (___errno_location = Module["___errno_location"] = Module["asm"]["i"]).apply(null, arguments)
}
    ;
var calledRun;
function ExitStatus(status) {
    this.name = "ExitStatus";
    this.message = "Program terminated with exit(" + status + ")";
    this.status = status
}
var calledMain = false;
dependenciesFulfilled = function runCaller() {
    if (!calledRun)
        run();
    if (!calledRun)
        dependenciesFulfilled = runCaller
}
    ;
function callMain(args) {
    var entryFunction = Module["_main"];
    var argc = 0;
    var argv = 0;
    try {
        var ret = entryFunction(argc, argv);
        exit(ret, true)
    } catch (e) {
        if (e instanceof ExitStatus) {
            return
        } else if (e == "unwind") {
            noExitRuntime = true;
            return
        } else {
            var toLog = e;
            if (e && typeof e === "object" && e.stack) {
                toLog = [e, e.stack]
            }
            err("exception thrown: " + toLog);
            quit_(1, e)
        }
    } finally {
        calledMain = true
    }
}
function run(args) {
    args = args || arguments_;
    if (runDependencies > 0) {
        return
    }
    preRun();
    if (runDependencies > 0)
        return;
    function doRun() {
        if (calledRun)
            return;
        calledRun = true;
        Module["calledRun"] = true;
        if (ABORT)
            return;
        initRuntime();
        preMain();
        if (Module["onRuntimeInitialized"])
            Module["onRuntimeInitialized"]();
        if (shouldRunNow)
            callMain(args);
        postRun()
    }
    if (Module["setStatus"]) {
        Module["setStatus"]("Running...");
        setTimeout(function () {
            setTimeout(function () {
                Module["setStatus"]("")
            }, 1);
            doRun()
        }, 1)
    } else {
        doRun()
    }
}
Module["run"] = run;
function exit(status, implicit) {
    if (implicit && noExitRuntime && status === 0) {
        return
    }
    if (noExitRuntime) { } else {
        EXITSTATUS = status;
        exitRuntime();
        if (Module["onExit"])
            Module["onExit"](status);
        ABORT = true
    }
    quit_(status, new ExitStatus(status))
}
if (Module["preInit"]) {
    if (typeof Module["preInit"] == "function")
        Module["preInit"] = [Module["preInit"]];
    while (Module["preInit"].length > 0) {
        Module["preInit"].pop()()
    }
}
var shouldRunNow = true;
if (Module["noInitialRun"])
    shouldRunNow = false;
noExitRuntime = true;
run();

// 增强的Worker消息处理 - 最大化棋面价值策略
self.onmessage = function(e) {
  const { board, dir, depth = 3, stage = 'middle' } = e.data;
  
  // 根据游戏阶段选择评估策略
  let result;
  
  if (stage === 'cleanup') {
    // 清理阶段:专注清理小瓦片
    result = evaluateMaximizeBoardValue(board);
  } else if (stage === 'survival') {
    // 生存模式:优先保持可玩性
    const baseResult = Module._jsWork(board[0], board[1], board[2], board[3], dir);
    const valueScore = evaluateMaximizeBoardValue(board);
    result = baseResult * 0.7 + valueScore * 0.3; // 优先基础算法
  } else if (stage === 'second8192') {
    // 第二个8192发展模式:平衡发展
    const baseResult = Module._jsWork(board[0], board[1], board[2], board[3], dir);
    const valueScore = evaluateMaximizeBoardValue(board);
    result = baseResult * 0.6 + valueScore * 0.4; // 略偏向基础算法
  } else if (stage === 'double8192' || stage === 'continue') {
    // 续局/终盘模式:最大化棋面价值
    const baseResult = Module._jsWork(board[0], board[1], board[2], board[3], dir);
    const valueScore = evaluateMaximizeBoardValue(board);
    result = baseResult * 0.3 + valueScore * 0.7; // 价值策略占70%权重
  } else if (stage === 'late') {
    // 后期模式:平衡发展与价值
    const baseResult = Module._jsWork(board[0], board[1], board[2], board[3], dir);
    const valueScore = evaluateMaximizeBoardValue(board);
    result = baseResult * 0.5 + valueScore * 0.5; // 各占50%
  } else {
    // 早中期使用原始WASM算法
    result = Module._jsWork(board[0], board[1], board[2], board[3], dir);
  }
  
  self.postMessage(result);
};
`;

  // 创建 Blob 和 URL
  const workerBlob = new Blob([workerCode], { type: 'application/javascript' });
  const workerUrl = URL.createObjectURL(workerBlob);

  // 创建工作线程
  const workers = [
    new Worker(workerUrl),
    new Worker(workerUrl),
    new Worker(workerUrl),
    new Worker(workerUrl)
  ];

  let working = 0;  // 当前正在执行的任务数量
  let startTime, totalMove;  // 记录 AI 启动时间 记录 AI 执行的总步数
  let oldBoard = "";
  let resultArray = [null, null, null, null];

  for (let i = 0; i < 4; ++i) {
    // 每个工作线程在完成计算后会发送一个消息回来,包含当前方向的评估值。
    workers[i].onmessage = ({ data }) => {
      working--;
      resultArray[i] = {
        move: i,
        result: data
      };

      // 当所有方向计算完成后
      if (working == 0) {
        // 执行最佳移动
        resultArray.sort((a, b) => b.result - a.result);
        let bestMove = resultArray[0].move;
        canvasGame.handleMove(["up", "right", "down", "left"][bestMove]);
        // 更新步数
        totalMove++;
        
        // 修改胜利检测:只有游戏结束才停止,忽略胜利状态(继续刷分)
        if (canvasGame.gameOver) {
          stopAI();
        } else if (canvasGame.victory) {
          // 检测到胜利但不停止,继续刷分
          console.log(`🎯 达成8192x2!继续刷分模式,当前分数: ${canvasGame.score || 0}`);
          if (aiRunning) step();
        } else {
          if (aiRunning) step();
        }
      }
    }
  }

  function currentState() {
    const result = new Uint16Array(4);
    for (let i = 0; i < 4; ++i) {
      for (let j = 0; j < 4; ++j) {
        const tile = canvasGame.board[i][j];
        if (tile) result[i] = result[i] | ((Math.log2(tile) & 0xf) << (12 - 4 * j));
      }
    }
    return result;
  }

  async function awaitBoardUpdate() {
    // 等待移动完成
    let count = 0, flag = false;
    while (count < 300) {
      await new Promise(resolve => setTimeout(resolve, 10));
      if (JSON.stringify(canvasGame.board) !== oldBoard) {
        oldBoard = JSON.stringify(canvasGame.board);
        flag = true;
        break;
      }
      count++;
    }
    return flag;
  }

  // 游戏阶段检测 - 支持"清理与巩固"模式
  function getGameStage() {
    const board = canvasGame.board.flat().filter(x => x > 0);
    const maxTile = Math.max(...board);
    const emptySpaces = 16 - board.length;
    const totalScore = board.reduce((sum, tile) => sum + tile, 0);
    
    // 关键瓦片统计
    const count8192 = board.filter(x => x === 8192).length;
    const count4096 = board.filter(x => x === 4096).length;
    const smallTiles = board.filter(x => x <= 32);
    const mediumTiles = board.filter(x => x >= 128 && x <= 512);
    const highValueTiles = board.filter(x => x >= 2048).length;
    
    // 动态策略选择
    if (count8192 === 1) {
      if (smallTiles.length > 8 && emptySpaces >= 3) {
        // 清理模式:小瓦片过多且有空间
        return { stage: 'cleanup', depth: 4, priority: 'cleanup_small_tiles' };
      } else if (emptySpaces <= 2) {
        // 生存模式:空间不足时保持可玩性
        return { stage: 'survival', depth: 5, priority: 'maintain_playability' };
      } else {
        // 发展模式:继续向第二个8192努力
        return { stage: 'second8192', depth: 4, priority: 'reach_second_8192' };
      }
    }
    
    if (count8192 >= 2) {
      // 双8192模式:最大化终盘价值
      return { stage: 'double8192', depth: 6, priority: 'maximize_board_value' };
    } else if (count8192 === 1 && highValueTiles >= 6) {
      // 单8192+大量高价值瓦片:准备终盘
      return { stage: 'continue', depth: 5, priority: 'prepare_endgame' };
    }
    
    // 根据盘面密度和发展程度判断阶段
    const boardDensity = board.length / 16; // 盘面密度 (0-1)
    const avgTileValue = totalScore / board.length; // 平均瓦片值
    
    if (maxTile >= 4096 || avgTileValue > 200) {
      // 后期:冲击8192阶段
      return { 
        stage: 'late', 
        depth: emptySpaces <= 4 ? 5 : 4,
        priority: 'reach_first_8192'
      };
    } else if (maxTile >= 1024 || boardDensity > 0.7) {
      // 中期:稳健发展
      return { 
        stage: 'middle', 
        depth: emptySpaces <= 6 ? 4 : 3,
        priority: 'stable_growth'
      };
    } else {
      // 早期:快速发展
      return { stage: 'early', depth: 2, priority: 'rapid_growth' };
    }
  }

  async function step() {
    if (!await awaitBoardUpdate()) return stopAI();

    const board = currentState();
    const gameInfo = getGameStage();
    
    // 危险局面检测 - 空格过少时需要更谨慎
    const emptySpaces = canvasGame.board.flat().filter(x => x === 0).length;
    const searchDepth = emptySpaces <= 3 ? gameInfo.depth + 1 : gameInfo.depth;
    
    bestResult = 0;
    working = 4;
    bestMove = 0 | 4 * Math.random();
    
    for (let i = 0; i < 4; ++i) {
      workers[i].postMessage({ 
        board, 
        dir: i, 
        depth: searchDepth,
        stage: gameInfo.stage 
      });
    }
  }

  function toggleAI() { }

  function startAI() {
    totalMove = 0;
    startTime = Date.now();
    document.getElementById("ai-start").textContent = "停止AI";
    aiRunning = true;
    step();
    toggleAI = stopAI;
  }

  function stopAI() {
    const endTime = Date.now();
    const board = canvasGame.board.flat().filter(x => x > 0);
    const maxTile = Math.max(...canvasGame.board.flat());
    const score = canvasGame.score || 0;
    const tiles8192 = board.filter(x => x === 8192).length;
    const mediumTiles = board.filter(x => x >= 128 && x <= 512);
    const boardValue = board.reduce((sum, tile) => sum + tile, 0);
    
    console.log(`=== AI性能统计 ===`);
    console.log(`已用时间: ${(endTime - startTime) / 1000} 秒`);
    console.log(`动作数: ${totalMove} 个`);
    console.log(`速度: ${totalMove * 1000 / (endTime - startTime)} 步/秒`);
    console.log(`最大数字: ${maxTile}`);
    console.log(`当前分数: ${score}`);
    console.log(`棋面价值: ${boardValue}`);
    console.log(`总合价值: ${score + boardValue}`);
    console.log(`8192数量: ${tiles8192}`);
    console.log(`中等瓦片: ${mediumTiles.length}个 (价值${mediumTiles.reduce((sum, tile) => sum + tile, 0)})`);
    
    // 25万分成就评估
    const totalValue = score + boardValue;
    if (totalValue >= 250000) {
      console.log(`🏆 恭喜突破25万分!总价值: ${totalValue}`);
    } else if (totalValue >= 200000) {
      console.log(`🎯 接近25万分目标!当前: ${totalValue} (${(totalValue/250000*100).toFixed(1)}%)`);
    }
    
    if (tiles8192 >= 2) {
      console.log(`🎉 已达成8192x2!${mediumTiles.length > 3 ? '成功保留中等瓦片' : '可优化中等瓦片保护'}!`);
    } else if (tiles8192 === 1) {
      console.log(`🎯 已获得一个8192,继续努力!`);
    }
    
    document.getElementById("ai-start").textContent = "启动AI";
    aiRunning = false;
    toggleAI = startAI;
  }

  toggleAI = startAI;

  function initButton() {
    const headerLeft = document.querySelector(".header-left");

    const startBtn = document.createElement("button");
    startBtn.textContent = "启动AI";
    startBtn.id = "ai-start";
    startBtn.addEventListener('click', () => toggleAI());
    headerLeft.appendChild(startBtn);

    const stepBtn = document.createElement("button");
    stepBtn.textContent = "下一步";
    stepBtn.id = "ai-step";
    stepBtn.addEventListener('click', () => step());
    headerLeft.appendChild(stepBtn);

    const infoBtn = document.createElement("button");
    infoBtn.textContent = "游戏信息";
    infoBtn.id = "ai-info";
    infoBtn.addEventListener('click', () => {
      const gameInfo = getGameStage();
      const board = canvasGame.board.flat().filter(x => x > 0);
      const maxTile = Math.max(...board);
      const emptySpaces = canvasGame.board.flat().filter(x => x === 0).length;
      const tiles8192 = board.filter(x => x === 8192).length;
      const smallTiles = board.filter(x => x <= 32);
      const mediumTiles = board.filter(x => x >= 128 && x <= 512);
      const highValueTiles = board.filter(x => x >= 2048).length;
      const totalScore = board.reduce((sum, tile) => sum + tile, 0);
      const avgTileValue = totalScore / board.length;
      const boardDensity = board.length / 16;
      
      console.log(`=== 游戏状态分析 ===`);
      console.log(`游戏阶段: ${gameInfo.stage} (${gameInfo.priority})`);
      console.log(`搜索深度: ${gameInfo.depth}`);
      console.log(`最大数字: ${maxTile}`);
      console.log(`空格数量: ${emptySpaces}`);
      console.log(`8192数量: ${tiles8192}`);
      console.log(`小瓦片(≤32): ${smallTiles.length}`);
      console.log(`中等瓦片(128-512): ${mediumTiles.length}`);
      console.log(`高价值瓦片(≥2048): ${highValueTiles}`);
      console.log(`盘面密度: ${(boardDensity * 100).toFixed(1)}%`);
      console.log(`平均瓦片值: ${avgTileValue.toFixed(1)}`);
      console.log(`棋面总价值: ${totalScore}`);
      console.log(`当前分数: ${canvasGame.score || 0}`);
      
      // 阶段特定信息
      if (gameInfo.stage === 'cleanup') {
        console.log(`🧹 清理模式:专注清理${smallTiles.length}个小瓦片,为终盘腾出空间`);
        console.log(`📊 清理效率: ${((16 - smallTiles.length) / 16 * 100).toFixed(1)}%`);
      } else if (gameInfo.stage === 'survival') {
        console.log(`⚠️ 生存模式:空间不足(${emptySpaces}格),优先保持可玩性`);
        console.log(`🔄 合并可能性评估中...`);
      } else if (gameInfo.stage === 'second8192') {
        const count4096 = board.filter(x => x === 4096).length;
        console.log(`🎯 第二8192模式:当前4096数量=${count4096},空间=${emptySpaces}格`);
        console.log(`📈 继续发展大瓦片链条`);
      } else if (gameInfo.stage === 'double8192') {
        console.log(`🎯 终盘模式:保持8192x2状态,最大化棋面价值`);
        console.log(`💎 中等瓦片价值: ${mediumTiles.reduce((sum, tile) => sum + tile, 0)}`);
      } else if (tiles8192 === 1) {
        console.log(`📈 单8192状态,策略将根据棋盘情况动态调整`);
      }
      
      // 25万分潜力评估
      const potentialScore = totalScore + (canvasGame.score || 0);
      if (potentialScore > 200000) {
        console.log(`🏆 25万分潜力:${(potentialScore / 250000 * 100).toFixed(1)}%`);
      }
    });
    headerLeft.appendChild(infoBtn);
  }

  initButton();

})();