连连看

close_llk();
// toast("开始运行");
// while (true) {
//     sleep(200);
//     let img = images.captureScreen();
//     let color = images.pixel(img, 410, 615);
//     // let color = images.pixel(img, 390, 615);
//     if (colors.isSimilar(color, color_target, 5, "rgb+")) break;
// }
// // sleep(4000)
// let img = images.captureScreen();
// let img = images.read("/sdcard/Samsung Flow/llk2.png");
// llk();

var w = floaty.rawWindow(
    <frame gravity="center" w="226" h="70" alpha="0.6">
        <horizontal>
            <button id="b_run" w="113" h="70" text="准备中"></button>
            <button id="b_auto" w="113" h="70" text="准备中"></button>
        </horizontal>
    </frame >
);
w.setPosition(330, 2590);
auto.waitFor();
images.requestScreenCapture();
events.on("exit", function () {
    img.recycle();
    icon_block.recycle();
    for (let k = 1; k <= 28; ++k) {
        imgIcons[k].recycle();
        imgIcons_ice[k].recycle();
    }
    log("recycle imgs done!")
});
let icon_block = images.read("/sdcard/Samsung Flow/icon_block.png");
let imgIcons = new Array(29);
let imgIcons_ice = new Array(29);
for (let k = 1; k <= 28; ++k) {
    imgIcons[k] = images.read("/sdcard/Samsung Flow/llk1/" + k + ".png");
    imgIcons_ice[k] = images.read("/sdcard/Samsung Flow/llkice1/" + k + ".png");
}

let color_target = colors.parseColor("#F6A85E");
let llk_running = false;
let manual_run = false;
let llk_auto_run = true;
// llk_auto_run = false;
let llk_no_dfs = true;

w.b_run.click(() => {
    if (!llk_running) {
        manual_run = true;
        w.b_run.setText("队列中");
    }
});

w.b_auto.click(() => {
    if (llk_auto_run) {
        llk_auto_run = false;
        w.b_auto.setText("已关闭自动运行");
    } else {
        llk_auto_run = true;
        w.b_auto.setText("已开启自动运行");
    }
});

w.b_run.setText("手动启动");
w.b_auto.setText("已开启自动运行");
log("init done!");

while (true) {
    if (llk_auto_run) {
        img = images.captureScreen();
        let color = images.pixel(img, 410, 615);
        // let color = images.pixel(img, 390, 615);
        if (colors.isSimilar(color, color_target, 5, "rgb+")) {
            sleep(20);
            img = images.captureScreen();
            llk_running = true;
            // llk_auto_run = false;
            manual_run = false;
            // w.b_auto.setText("已关闭自动运行");
            w.b_run.setText("自动运行中");
            llk();
            llk_running = false;
            w.b_run.setText("手动启动");
            // sleep(300);
        }
    } else if (manual_run) {
        llk_running = true;
        manual_run = false;
        w.b_run.setText("手动运行中");
        img = images.captureScreen();
        llk();
        llk_running = false;
        w.b_run.setText("手动启动");
    }
    sleep(100);
}

function llk() {
    const map = {
        r: 8,          //地图行数
        c: 7,          //地图列数
        start_x: 0,    //方块起始点x坐标
        start_y: 808   //方块起始点y坐标
    }
    map.start_x = map.c == 8 ? 48 : 132;
    const arrLen = { r: (map.r + 2), c: (map.c + 2) };//数组大小
    const _region = [map.start_x, map.start_y, map.c * 168, map.r * 168]; //找图区间
    const _threshold = 0.85; //找图精确度
    const log_icons = ["⬛", "⬜", "🟥", "🟪", "🟨", "🟧", "🟦", "🟩", "🟫", "⚪", "🔴", "🟣", "🟡", "🟠", "🔵", "🟢", "🟤", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", "🌑", "❤", "🧡", "💛", "💚", "💙", "💜", "🤎", "⛔", "🧊"]; //打印图形用图标

    const d = [[-1, 0], [1, 0], [0, -1], [0, 1]]; //遍历四个方向
    const path = [{ y1: -1, x1: -1, y2: -1, x2: -1 }]; //存储路径搜索结果:表示 (y1, x1) 与 (y2, x2) 可消除
    const mp_bw = initArr(arrLen.r, arrLen.c, 0); //地图预处理结果,取值0/1表示是否存在方块
    const mp = initArr(arrLen.r, arrLen.c, 0); //地图最终处理结果,0为空,相同的数字代表是同一种方块
    const v = new Array(); //二维数组,以方块类型编号为下标,存储该类型方块的坐标
    for (let k = 1; k <= 28; ++k) v[k] = new Array();

    getMap(); //获取地图,将会写入 mp_bw, mp, v 数组, 其中 mp 数组是为了可视化展示路径用
    if (check_map_clean()) return;
    // logMap(mp_bw);
    logMap(mp);
    check_edge(); //检查是否存在某种类型的方块个数是奇数个(多为提取地图出错导致)

    // mp_bw[1][1] = mp_bw[2][2] = mp_bw[2][4] = mp_bw[4][2] = 0;
    // logMap(mp_bw);
    // log(check_connection({ y: 1, x: 5 }, { y: 6, x: 3 }));
    // log(check_connection(v[2][0], v[2][1]));
    // exit();

    let flag_success = false; //标记搜索路径是否成功
    let date1 = new Date().getTime();
    if (llk_no_dfs)
        no_backtrace_search();
    else
        dfs();
    let date2 = new Date().getTime();
    log("搜索路径用时:" + (date2 - date1) + "ms");
    /*  ⛔⛔⛔⛔    no_backtrace_search()无回溯暴力遍历所有点组合,只要遇到能连的点就连,
        ⛔🟩🟩⛔                         适用于地图只有部分解(只能消除一部分)的情况,
        ⛔     ⛔                         可能无法解决如左图的情况。
        ⛔🟪🟩⛔    bfs()同样是遍历所有点的组合,但遇到无解情况会回溯,直到成功为止,
        ⛔🟩🟪⛔         适用于当前地图有完全解(能清空)的情况,否则会返回空解集
        ⛔⛔⛔⛔         左图情况可以找到正确路径。                                    */

    // log(path);
    // exit();
    click_path(30);
    // draw_path();
    return;

    function click_path(speed) {
        // return;
        if (llk_no_dfs) {
            for (let k = 1; k < path.length; ++k) {
                press(getX(path[k].x1) + 50, getY(path[k].y1) + 50, 1);
                sleep(speed);
                // sleep(270);
                press(getX(path[k].x2) + 50, getY(path[k].y2) + 50, 1);
                sleep(speed);
                // sleep(470);
            }
        } else {
            for (let k = path.length - 1; k >= 1; --k) {
                press(getX(path[k].x1) + 50, getY(path[k].y1) + 50, 1);
                sleep(speed);
                // sleep(270);
                press(getX(path[k].x2) + 50, getY(path[k].y2) + 50, 1);
                sleep(speed);
                // sleep(470);
            }
        }
    }

    function draw_path() {
        logMap(mp);
        sleep(10);
        // for (let k = 1; k < path.length; ++k) {
        for (let k = path.length - 1; k >= 1; --k) {
            let logStr = "\n";
            for (let i = 0; i < arrLen.r; ++i) {
                for (let j = 0; j < arrLen.c - 1; ++j) {
                    if ((i == path[k].y1 && j == path[k].x1) || (i == path[k].y2 && j == path[k].x2))
                        logStr += "💎";//log_icons_c[mp[i][j]];
                    else
                        logStr += log_icons[mp[i][j]];
                }
                if ((i == path[k].y1 && (arrLen.c - 1) == path[k].x1) || (i == path[k].y2 && (arrLen.c - 1) == path[k].x2))
                    logStr += "💎"//log_icons_c[mp[i][arrLen.c - 1]];
                else
                    logStr += log_icons[mp[i][arrLen.c - 1]];
                if (i != arrLen.r - 1)
                    logStr += "\n";
            }
            log(logStr);
            mp[path[k].y1][path[k].x1] = mp[path[k].y2][path[k].x2] = 0;
            sleep(10);
        }
    }

    function no_backtrace_search() {
        while (true) {
            // log("--------");
            let flag_no_op = 1;
            for (let i = 1; i <= 28; ++i) {
                // log("dfs 第 " + i + " 种方块", difItem);
                for (let j = 0; j < v[i].length; ++j) {
                    for (let k = j + 1; k < v[i].length; ++k) {
                        if (v[i][j].vis || v[i][k].vis || check_ice(v[i][j]) || check_ice(v[i][k])) continue;
                        if (check_connection(v[i][j], v[i][k])) {
                            flag_no_op = 0;
                            // click_block(v[i][j].x, v[i][j].y, v[i][k].x, v[i][k].y, 30);
                            // log(" succ (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                            path.push({ y1: v[i][j].y, x1: v[i][j].x, y2: v[i][k].y, x2: v[i][k].x });
                            mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 0;
                            v[i][j].vis = v[i][k].vis = 1;
                            let tx, ty;
                            for (let m = 0; m < 4; ++m) {
                                tx = v[i][j].x + d[m][0]; ty = v[i][j].y + d[m][1];
                                if (mp_bw[ty][tx] == 33) mp_bw[ty][tx] = 1;
                                tx = v[i][k].x + d[m][0]; ty = v[i][k].y + d[m][1];
                                if (mp_bw[ty][tx] == 33) mp_bw[ty][tx] = 1;
                            }
                        } else {
                            // logMap(mp_bw);
                            // log(" **** [" + i + "][" + j + "] - [" + i + "][" + k + "] - " + check_connection(v[i][j], v[i][k]));
                            // log(" fail (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                        }
                    }
                }
            }
            // log("--------");
            if (flag_no_op) return;
        }
    }

    function check_ice(u) {
        return mp_bw[u.y][u.x] == 33;
    }

    function dfs() {
        if (check_map_clean()) {
            // logMap(mp_bw);
            log("dfs successed!");
            flag_success = true;
            return;
        }
        // log("dfs check fail");
        // 最坏情况 O() 
        for (let i = 1; i <= 28; ++i) {
            // log("dfs 第 " + i + " 种方块,共", v[i].length);
            for (let j = 0; j < v[i].length; ++j) {
                for (let k = j + 1; k < v[i].length; ++k) {
                    if (v[i][j].vis || v[i][k].vis || check_ice(v[i][j]) || check_ice(v[i][k])) continue;
                    if (check_connection(v[i][j], v[i][k])) {
                        // log(" ***suc (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ") ***");
                        mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 0;
                        v[i][j].vis = v[i][k].vis = 1;
                        let bt = new Array(); // backtrace数组记录解冻操作
                        let tx, ty;
                        for (let m = 0; m < 4; ++m) {
                            tx = v[i][j].x + d[m][0]; ty = v[i][j].y + d[m][1];
                            if (mp_bw[ty][tx] == 33) { mp_bw[ty][tx] = 1; bt.push({ y: ty, x: tx }); }
                            tx = v[i][k].x + d[m][0]; ty = v[i][k].y + d[m][1];
                            if (mp_bw[ty][tx] == 33) { mp_bw[ty][tx] = 1; bt.push({ y: ty, x: tx }); }
                        }
                        dfs();
                        if (flag_success) {
                            path.push({ y1: v[i][j].y, x1: v[i][j].x, y2: v[i][k].y, x2: v[i][k].x });
                            return;
                        }
                        // log(" **回溯 👆")
                        mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 1;
                        for (let m = 0; m < bt.length; ++m)
                            mp_bw[bt[m].y][bt[m].x] = 33;
                    } else {
                        // logMap(mp_bw);
                        // log(" **** [" + i + "][" + j + "] - [" + i + "][" + k + "] - " + check_connection(v[i][j], v[i][k]));
                        // log(" fail (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                    }
                }
            }
        }
        // flag_success = true;
        // throw new Error("No solution");
    }

    function check_connection(a, b) {
        // if (a.vis || b.vis) return false;
        let t = { x: 0, y: 0, dir: -1, turn: -1 };
        let x, y, dir, turn;
        const q = [{ x: a.x, y: a.y, dir: -1, turn: -1 }];

        const vis = new Array(arrLen.r);
        for (let k = 0; k < arrLen.r; k++) {
            vis[k] = new Array(arrLen.c);
            for (let j = 0; j < arrLen.c; j++) {
                vis[k][j] = new Array(4);
                vis[k][j][0] = vis[k][j][1] = vis[k][j][2] = vis[k][j][3] = 0x3f3f3f3f;
            }
        }
        // const vis = initArr(arrLen.r, arrLen.c, { 0: 0x3f3f3f3f, 1: 0x3f3f3f3f, 2: 0x3f3f3f3f, 3: 0x3f3f3f3f });
        vis[a.y][a.x] = 1;
        while (q.length) {
            t = q.shift();
            // log("****: " + t.y, t.x, t.dir, t.turn);
            for (let i = 0; i < 4; ++i) {
                x = t.x + d[i][0];
                y = t.y + d[i][1];
                dir = i;
                turn = t.turn + (t.dir != i);
                if (turn <= 2 && x >= 0 && y >= 0 && x < arrLen.c && y < arrLen.r) {
                    // log("  **: " + y, x, dir, turn, "mp:", mp_bw[y][x], "vis:", vis[y][x]);
                    if (y == b.y && x == b.x) return true;
                    if (mp_bw[y][x] == 0 && vis[y][x][dir] > turn) {
                        // log("push (" + y + ", " + x + ") d:" + dir + " turn:" + turn);
                        vis[y][x][dir] = turn;
                        q.push({ x: x, y: y, dir: dir, turn: turn });
                    }
                }
            }
        }
        return false;
    }

    function check_map_clean() {
        for (let i = 1; i <= map.r; ++i)
            for (let j = 1; j <= map.c; ++j)
                if (mp_bw[i][j] == 1 || mp_bw[i][j] == 33)
                    return false;
        return true;
    }

    function getMap() {
        let date1 = new Date().getTime();
        // let colorCorner = colors.parseColor("#f7f7f5");
        let colorCorner2 = colors.parseColor("#f7f7f7");
        let colorIce = colors.parseColor("#e5fcff");
        // log(colorCorner, colorCorner2);

        // var file_catalog = "/sdcard/Samsung Flow/llkice1/";
        // var recv = files.listDir(file_catalog, function (name) {
        //     return name.endsWith(".png") && files.isFile(files.join(file_catalog, name));
        // });
        // let cnt = recv.length;
        // log("cnt1: ", cnt);
        // log("mp_bw start");
        for (let i = 1; i <= map.r; ++i) {
            for (let j = 1; j <= map.c; ++j) {
                let color = images.pixel(img, getX(j) + 10, getY(i) + 10);
                // log("(" + i + ", " + j + ") :" + colors.toString(color).replace("#ff", "#"));
                if (colors.isSimilar(color, colorCorner2, 10, "rgb+")) {
                    mp_bw[i][j] = 1;
                }
            }
        }
        if (check_map_clean()) return;
        // log("mp_bw done");
        // log("cnt1: **", cnt);

        //线程1:寻找冰块 + 识别冰块类型
        let find_img_thread1 = threads.start(function () {
            // log("ice start");
            for (let i = 1; i <= map.r; ++i) {
                for (let j = 1; j <= map.c; ++j) {
                    let color = images.pixel(img, getX(j) + 10, getY(i) + 10);
                    if (colors.isSimilar(color, colorIce, 10, "rgb+")) {
                        mp_bw[i][j] = 33;
                        // let flag = true;
                        // let imgIcon = images.clip(img, getX(j) + 10, getY(i) + 10, 148, 148);
                        for (let k = 1; k <= 28; ++k) {
                            if (images.findImage(img, imgIcons_ice[k], {
                                region: [getX(j) + 10, getY(i) + 10, 148, 148],
                                threshold: 0.95,
                                level: 1
                            })) {
                                v[k].push({ y: i, x: j, vis: 0 });
                                mp[i][j] = k;
                                break;
                            }
                        }
                        // if (flag)
                        //     images.save(imgIcon, "/sdcard/Samsung Flow/llkice1/" + (++cnt) + ".png", "png", 100);
                        // imgIcon.recycle();
                    }
                }
            }
            log("ice done");
            logMap(mp_bw);
        });

        // let file_catalog2 = "/sdcard/Samsung Flow/llk1/";
        // let recv2 = files.listDir(file_catalog2, function (name) {
        //     return name.endsWith(".png") && files.isFile(files.join(file_catalog2, name));
        // });
        // let cnt2 = recv2.length;
        // log("cnt2: ", cnt2);

        // let imgMap = images.clip(img, map.start_x, map.start_y, map.c * 168, map.r * 168);
        // let idx = 1;

        function check_block_type(start, end) {
            for (let k = start; k <= end; ++k) {
                let points = images.matchTemplate(img, imgIcons[k], {
                    region: _region,
                    threshold: _threshold,
                    max: 6
                }).points;

                points.forEach(point => {
                    let ii, jj;
                    for (ii = map.c; ii > 0; --ii)
                        if (point.x >= getX(ii))
                            break;
                    for (jj = map.r; jj > 0; --jj)
                        if (point.y >= getY(jj))
                            break;
                    // log("(" + (ii + 1) + ", " + (jj + 1) + ") :" + k);
                    if (mp_bw[jj][ii] == 1) {
                        mp[jj][ii] = k;
                        v[k].push({ y: jj, x: ii, vis: 0 });
                    }
                });
            }
        }
        //线程2,3:和主线程分摊全图找图任务
        let find_img_thread2 = threads.start(function () {
            // log("thread 2 start");
            check_block_type(10, 19)
            log("thread 2 done");
            find_img_thread2.interrupt();
        });
        let find_img_thread3 = threads.start(function () {
            // log("thread 3 start");
            check_block_type(20, 28)
            log("thread 3 done");
            find_img_thread3.interrupt();
        });
        // log("thread main start");
        check_block_type(1, 9);
        log("thread main done");
        while (find_img_thread2 && find_img_thread2.isAlive()) { sleep(30); }
        while (find_img_thread3 && find_img_thread3.isAlive()) { sleep(30); }
        log("**** all done!")


        //********* 旧版方法:遇到未识别的方块剪切处理后进行全图识别,难以并行处理 *********
        // for (let i = 1; i <= map.r; ++i) {
        //     for (let j = 1; j <= map.c; ++j) {
        //         if (mp_bw[i][j] == 1 && 0 == mp[i][j]) {
        //             // log("(" + (i + 1) + ", " + (j + 1) + ") :", 310 + 168 * j, 1154 + 168 * i, 148, 148);
        //             let imgIcon = images.clip(img, getX(j) + 10, getY(i) + 10, 148, 148);
        //             // images.save(imgIcon, "/sdcard/Samsung Flow/llk1/" + idx + ".png", "png", 100);
        //             let points = images.matchTemplate(img, imgIcon, {
        //                 region: _region,
        //                 threshold: _threshold,
        //                 max: 6
        //             }).points;

        //             // let flag = true;
        //             // for (let k = 1; k <= cnt2; ++k) {
        //             //     imga = images.read("/sdcard/Samsung Flow/llk1/" + k + ".png");
        //             //     if (images.findImage(imga, imgIcon)) { flag = false; break; }
        //             //     imga.recycle();
        //             // }
        //             // if (flag)
        //             //     log("save ", cnt2);
        //             // if (flag)
        //             //     images.save(imgIcon, "/sdcard/Samsung Flow/llk1/" + (++cnt2) + ".png", "png", 100);

        //             imgIcon.recycle();
        //             // log(points);
        //             v[idx] = new Array();
        //             points.forEach(point => {
        //                 let ii, jj;
        //                 for (ii = map.c; ii > 0; --ii) if (point.x >= getX(ii)) break;
        //                 for (jj = map.r; jj > 0; --jj) if (point.y >= getY(jj)) break;
        //                 // log("(" + (ii + 1) + ", " + (jj + 1) + ") :" + idx);
        //                 mp[jj][ii] = idx;
        //                 v[idx].push({ y: jj, x: ii, vis: 0 });
        //             });
        //             ++idx;
        //         }
        //     }
        // }
        // difItem = idx;
        difItem = 28;
        // imgMap.recycle();

        // log("cnt2: **", cnt2);


        let points = images.matchTemplate(img, icon_block, {
            region: [map.start_x, map.start_y, map.c * 168, map.r * 168],
            threshold: 0.8
        }).points;
        points.forEach(point => {
            let ii, jj;
            for (ii = map.c; ii > 0; --ii) if (point.x >= getX(ii)) break;
            for (jj = map.r; jj > 0; --jj) if (point.y >= getY(jj)) break;
            mp_bw[jj][ii] = mp[jj][ii] = 32;
        });

        while (find_img_thread1 && find_img_thread1.isAlive()) { sleep(30); }
        let date2 = new Date().getTime();
        log("地图处理用时:" + (date2 - date1) + "ms");
    }

    function check_edge() {
        return;
        for (let i = 1; i <= 28; ++i) {
            log(v[i]);
            // if (v[i].length % 2)
            // throw new Error("number of points is odd at " + log_icons[i] + "(color " + i + " )");
        }
    }

    function logMap(arr) {
        let logStr = "\n";
        for (let i = 0; i < arrLen.r; ++i) {
            for (let j = 0; j < arrLen.c - 1; ++j) {
                // log(String(mp_bw[i][j]));
                logStr += log_icons[arr[i][j]];
            }
            logStr += log_icons[arr[i][arrLen.c - 1]];
            if (i != arrLen.r - 1) logStr += "\n";
        }
        log(logStr);
    }

    function initArr(r, c, init) {
        let tArr = new Array(r);
        for (let k = 0; k < r; k++) {
            tArr[k] = new Array(c);
            for (let j = 0; j < c; j++) {
                tArr[k][j] = init;
            }
        }
        return tArr;
    }

    function getX(c) {
        return map.start_x + 168 * (c - 1);
    }
    function getY(r) {
        return map.start_y + 168 * (r - 1);
    }
}


function llk_old() {
    const mapSize = { r: 8, c: 8 };  //地图边长
    // const mapSize = { r: 7, c: 5 }; //地图边长
    // const mapStart = { x: 132, y: 808 }; //地图开始坐标
    const mapStart = { x: mapSize.c == 8 ? 48 : 132, y: 808 }; //地图开始坐标
    const arrLen = { r: (mapSize.r + 2), c: (mapSize.c + 2) };//数组大小
    const _region = [mapStart.x, mapStart.y, mapSize.c * 168, mapSize.r * 168];
    let difItem; // 方块种类数
    const _threshold = 0.85; //找图精确度
    const log_icons = ["⬛", "⬜", "🟥", "🟪", "🟨", "🟧", "🟦", "🟩", "🟫", "⚪", "🔴", "🟣", "🟡", "🟠", "🔵", "🟢", "🟤", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", "🌑", "⛔", "🧊"]; //打印图形用图标
    const log_icons_c = ["⚫", "⚪", "🔴", "🟣", "🟡", "🟠", "🔵", "🟢", "🟤", "⬜", "🟥", "🟪", "🟨", "🟧", "🟦", "🟩", "🟫", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", "🌑", "⛔", "🧊"]; //打印图形用图标
    let flag_success = false; //标记搜索路径是否成功
    let icon_block = images.read("/sdcard/Samsung Flow/icon_block.png");

    const mp_bw = initArr(arrLen.r, arrLen.c, 0); //地图预处理结果,取值0/1表示是否存在方块
    const mp = initArr(arrLen.r, arrLen.c, 0); //地图最终处理结果,0为空,相同的数字代表是同一种方块
    const v = new Array(); //二维数组,以方块类型编号为下标,存储该类型方块的坐标
    const path = [{ y1: -1, x1: -1, y2: -1, x2: -1 }]; //存储运算结果:表示 (y1, x1) 与 (y2, x2) 可消除

    getMap(); //获取地图,将会写入 mp_bw, mp,  v 数组
    if (check_map_clean()) return;
    // logMap(mp_bw);
    logMap(mp);
    // check_edge(); //检查是否存在某种类型的方块个数是奇数个(多为提取地图出错导致)

    // mp_bw[1][1] = mp_bw[2][2] = mp_bw[2][4] = mp_bw[4][2] = 0;
    // logMap(mp_bw);
    // log(judge_connection({ y: 1, x: 5 }, { y: 6, x: 3 }));
    // log(judge_connection(v[2][0], v[2][1]));
    // exit();

    let date1 = new Date().getTime();
    if (llk_no_dfs)
        no_backtrace_search();
    else
        dfs();
    let date2 = new Date().getTime();
    toastLog("搜索路径用时:" + (date2 - date1) + "ms");
    // log(path);
    // exit();
    for (let k = 1; k < path.length; ++k)
        click_block(path[k].x1, path[k].y1, path[k].x2, path[k].y2, 30);
    // draw_path();
    icon_block.recycle();
    return;

    function click_block(x1, y1, x2, y2, speed) {
        // for (let k = 1; k < path.length; ++k) {
        // for (let k = path.length - 1; k >= 1; --k) {
        press(getX(x1) + 50, getY(y1) + 50, 1);
        sleep(speed);
        // sleep(270);
        press(getX(x2) + 50, getY(y2) + 50, 1);
        sleep(speed);
        // sleep(470);
        // }
    }

    function draw_path() {
        logMap(mp);
        sleep(10);
        for (let k = 1; k < path.length; ++k) {
            // for (let k = path.length - 1; k >= 1; --k) {
            let logStr = "\n";
            for (let i = 0; i < arrLen.r; ++i) {
                for (let j = 0; j < arrLen.c - 1; ++j) {
                    if ((i == path[k].y1 && j == path[k].x1) || (i == path[k].y2 && j == path[k].x2))
                        logStr += "💎";//log_icons_c[mp[i][j]];
                    else
                        logStr += log_icons[mp[i][j]];
                }
                if ((i == path[k].y1 && (arrLen.c - 1) == path[k].x1) || (i == path[k].y2 && (arrLen.c - 1) == path[k].x2))
                    logStr += "💎"//log_icons_c[mp[i][arrLen.c - 1]];
                else
                    logStr += log_icons[mp[i][arrLen.c - 1]];
                if (i != arrLen.r - 1)
                    logStr += "\n";
            }
            log(logStr);
            mp[path[k].y1][path[k].x1] = mp[path[k].y2][path[k].x2] = 0;
            sleep(10);
        }
    }

    function no_backtrace_search() {
        while (true) {
            let flag_no_op = 1;
            for (let i = 1; i < difItem; ++i) {
                // log("dfs 第 " + i + " 种方块", difItem);
                for (let j = 0; j < v[i].length; ++j) {
                    for (let k = j + 1; k < v[i].length; ++k) {
                        if (v[i][j].vis || v[i][k].vis) continue;
                        if (judge_connection(v[i][j], v[i][k])) {
                            flag_no_op = 0;
                            // click_block(v[i][j].x, v[i][j].y, v[i][k].x, v[i][k].y, 30);
                            // log(" succ (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                            path.push({ y1: v[i][j].y, x1: v[i][j].x, y2: v[i][k].y, x2: v[i][k].x });
                            mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 0;
                            v[i][j].vis = v[i][k].vis = 1;
                        } else {
                            // logMap(mp_bw);
                            // log(" **** [" + i + "][" + j + "] - [" + i + "][" + k + "] - " + judge_connection(v[i][j], v[i][k]));
                            // log(" fail (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                        }
                    }
                }
            }
            if (flag_no_op) return;
        }
    }

    function dfs() {
        if (check_map_clean()) {
            // logMap(mp_bw);
            log("dfs successed!");
            flag_success = true;
            return;
        }
        // log("dfs check fail");
        for (let i = 1; i < difItem; ++i) {
            // log("dfs 第 " + i + " 种方块", difItem);
            for (let j = 0; j < v[i].length; ++j) {
                for (let k = j + 1; k < v[i].length; ++k) {
                    if (v[i][j].vis || v[i][k].vis) continue;
                    if (judge_connection(v[i][j], v[i][k])) {
                        log(" succ (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                        mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 0;
                        v[i][j].vis = v[i][k].vis = 1;
                        // return;
                        dfs();
                        if (flag_success) {
                            path.push({ y1: v[i][j].y, x1: v[i][j].x, y2: v[i][k].y, x2: v[i][k].x });
                            return;
                        }
                        log(" **回溯 👆")
                        mp_bw[v[i][j].y][v[i][j].x] = mp_bw[v[i][k].y][v[i][k].x] = 1;
                    } else {
                        // logMap(mp_bw);
                        // log(" **** [" + i + "][" + j + "] - [" + i + "][" + k + "] - " + judge_connection(v[i][j], v[i][k]));
                        log(" fail (" + v[i][j].y + ", " + v[i][j].x + ") - (" + v[i][k].y + ", " + v[i][k].x + ")");
                    }
                }
            }
        }
        // throw new Error("No solution");
    }

    function judge_connection(a, b) {
        // if (a.vis || b.vis) return false;
        let t = { x: 0, y: 0, dir: -1, turn: -1 };
        let x, y, dir, turn;
        const q = [{ x: a.x, y: a.y, dir: -1, turn: -1 }];

        const vis = new Array(arrLen.r);
        for (let k = 0; k < arrLen.r; k++) {
            vis[k] = new Array(arrLen.c);
            for (let j = 0; j < arrLen.c; j++) {
                vis[k][j] = new Array(4);
                vis[k][j][0] = vis[k][j][1] = vis[k][j][2] = vis[k][j][3] = 0x3f3f3f3f;
            }
        }
        // const vis = initArr(arrLen.r, arrLen.c, { 0: 0x3f3f3f3f, 1: 0x3f3f3f3f, 2: 0x3f3f3f3f, 3: 0x3f3f3f3f });
        const d = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        vis[a.y][a.x] = 1;
        while (q.length) {
            t = q.shift();
            // log("****: " + t.y, t.x, t.dir, t.turn);
            for (let i = 0; i < 4; ++i) {
                x = t.x + d[i][0];
                y = t.y + d[i][1];
                dir = i;
                turn = t.turn + (t.dir != i);
                if (turn <= 2 && x >= 0 && y >= 0 && x < arrLen.c && y < arrLen.r) {
                    // log("  **: " + y, x, dir, turn, "mp:", mp_bw[y][x], "vis:", vis[y][x]);
                    if (y == b.y && x == b.x) return true;
                    if (mp_bw[y][x] == 0 && vis[y][x][dir] > turn) {
                        // log("push (" + y + ", " + x + ") d:" + dir + " turn:" + turn);
                        vis[y][x][dir] = turn;
                        q.push({ x: x, y: y, dir: dir, turn: turn });
                    }
                }
            }
        }
        return false;
    }

    function check_map_clean() {
        for (let i = 1; i <= mapSize.r; ++i)
            for (let j = 1; j <= mapSize.c; ++j)
                if (mp_bw[i][j] == 1)
                    return false;
        return true;
    }

    function getMap() {
        let date1 = new Date().getTime();
        // let colorCorner = colors.parseColor("#f7f7f5");
        let colorCorner2 = colors.parseColor("#f7f7f7");
        let colorIce = colors.parseColor("#e5fcff");
        // log(colorCorner, colorCorner2);
        for (let i = 1; i <= mapSize.r; ++i) {
            for (let j = 1; j <= mapSize.c; ++j) {
                let color = images.pixel(img, getX(j) + 10, getY(i) + 10);
                // log("(" + i + ", " + j + ") :" + colors.toString(color).replace("#ff", "#"));
                if (colors.isSimilar(color, colorCorner2, 10, "rgb+")) {
                    mp_bw[i][j] = 1;
                } else if (colors.isSimilar(color, colorIce, 10, "rgb+")) {
                    mp_bw[i][j] = mp[i][j] = 26;
                }
            }
        }


        // let imgMap = images.clip(img, mapStart.x, mapStart.y, mapSize.c * 168, mapSize.r * 168);
        let idx = 1;
        for (let i = 1; i <= mapSize.r; ++i) {
            for (let j = 1; j <= mapSize.c; ++j) {
                if (mp_bw[i][j] == 1 && 0 == mp[i][j]) {
                    // log("(" + (i + 1) + ", " + (j + 1) + ") :", 310 + 168 * j, 1154 + 168 * i, 148, 148);
                    let imgIcon = images.clip(img, getX(j) + 10, getY(i) + 10, 148, 148);
                    // images.save(imgIcon, "/sdcard/Samsung Flow/llk2.png", "png", 100);
                    let points = images.matchTemplate(img, imgIcon, {
                        region: _region,
                        threshold: _threshold,
                        max: 6
                    }).points;
                    imgIcon.recycle();
                    // log(points);
                    v[idx] = new Array();
                    points.forEach(point => {
                        let ii, jj;
                        for (ii = mapSize.c; ii > 0; --ii) if (point.x >= getX(ii)) break;
                        for (jj = mapSize.r; jj > 0; --jj) if (point.y >= getY(jj)) break;
                        // log("(" + (ii + 1) + ", " + (jj + 1) + ") :" + idx);
                        mp[jj][ii] = idx;
                        v[idx].push({ y: jj, x: ii, vis: 0 });
                    });
                    ++idx;
                }
            }
        }
        difItem = idx;
        // imgMap.recycle();

        let points = images.matchTemplate(img, icon_block, {
            region: [mapStart.x, mapStart.y, mapSize.c * 168, mapSize.r * 168],
            threshold: 0.8
        }).points;
        points.forEach(point => {
            let ii, jj;
            for (ii = mapSize.c; ii > 0; --ii) if (point.x >= getX(ii)) break;
            for (jj = mapSize.r; jj > 0; --jj) if (point.y >= getY(jj)) break;
            mp_bw[jj][ii] = mp[jj][ii] = 25;
        });

        let date2 = new Date().getTime();
        log("地图处理用时:" + (date2 - date1) + "ms");
    }

    function check_edge() {
        return;
        for (let i = 1; i < difItem; ++i) {
            // log(v[i]);
            if (v[i].length % 2)
                throw new Error("number of points is odd at " + log_icons[i] + "(color " + i + " )");
        }
    }

    function logMap(arr) {
        // 🟥🟧🟨🟩🟦🟪🟫⬛⬜ 🔴🟠🟡🟢🔵🟣🟤⚫⚪
        let logStr = "\n";
        for (let i = 0; i < arrLen.r; ++i) {
            for (let j = 0; j < arrLen.c - 1; ++j) {
                // log(String(mp_bw[i][j]));
                logStr += log_icons[arr[i][j]];
            }
            logStr += log_icons[arr[i][arrLen.c - 1]];
            if (i != arrLen.r - 1) logStr += "\n";
        }
        log(logStr);
    }

    function initArr(r, c, init) {
        let tArr = new Array(r);
        for (let k = 0; k < r; k++) {
            tArr[k] = new Array(c);
            for (let j = 0; j < c; j++) {
                tArr[k][j] = init;
            }
        }
        return tArr;
    }

    function getX(c) {
        return mapStart.x + 168 * (c - 1);
    }
    function getY(r) {
        return mapStart.y + 168 * (r - 1);
    }
}


function close_llk() {
    var ThisEngine = engines.myEngine();
    var RunningNow = engines.all();
    if (RunningNow.length > 1) {
        for (var i = 0; i < RunningNow.length; i++) {
            if (RunningNow[i].toString() != ThisEngine.toString() && RunningNow[i].toString().indexOf("连连看") != -1) {
                log("停止脚本", RunningNow[i].toString());
                RunningNow[i].forceStop();
            }
        }
    }
    // toast("已关闭所有脚本");
}

  

posted @ 2020-09-14 08:54  楼主好菜啊  阅读(185)  评论(0编辑  收藏  举报