利用DeepSeek提取Javascript代码实现命令行求解方块完全覆盖问题

AI赋能编程语言挑战赛 10w+人浏览 254人参与

https://github.com/JozefJarosciak/TangramPuzzleSolver 是一个图形界面的俄罗斯方块覆盖问题求解器,为了能够批量求解,让DeepSeek提取其中的计算代码,和字符串输入组成一个命令行工具。

将附件javascript代码提取出与显示无关的后台计算代码,读入一行 矩形长度,矩形宽度,[形状编号(A、B、C、D、I、L等),数量 ]列表,有解,则输出填充了形状编号的矩阵,无解提示无解

他完全理解了需求,输出代码如下。

// block_solver_core.js
// 俄罗斯方块覆盖问题求解器 - 核心计算模块

// ============================
// 配置常量
// ============================

// 方块形状定义
const BLOCKS = {
    'A': [ // Monomino (1x1)
        [[1]]
    ],
    'B': [ // Domino (2x1)
        [[1,1]],       // Horizontal
        [[1],[1]]      // Vertical
    ],
    'C': [ // Triomino I (3x1)
        [[1,1,1]],     // Horizontal
        [[1],[1],[1]]  // Vertical
    ],
    'D': [ // Triomino L
        [[1,0],
         [1,1]],
        [[0,1],
         [1,1]],
        [[1,1],
         [1,0]],
        [[1,1],
         [0,1]]
    ],
    'I': [ // Tetromino I (4x1)
        [[1,1,1,1]],
        [[1],
         [1],
         [1],
         [1]]
    ],
    'L': [ // Tetromino L
        [[1,1,1],
         [1,0,0]],
        [[1,0],
         [1,0],
         [1,1]],
        [[0,0,1],
         [1,1,1]],
        [[1,1],
         [0,1],
         [0,1]]
    ],
    'O': [ // Tetromino O (2x2)
        [[1,1],
         [1,1]]
    ],
    'S': [ // Tetromino S
        [[0,1,1],
         [1,1,0]],
        [[1,0],
         [1,1],
         [0,1]]
    ],
    'T': [ // Tetromino T
        [[1,1,1],
         [0,1,0]],
        [[0,1,0],
         [1,1,1]],
        [[1,0],
         [1,1],
         [1,0]],
        [[0,1],
         [1,1],
         [0,1]]
    ],
    'U': [ // Pentomino U
        [[1,0,1],
         [1,1,1]],
        [[1,1,1],
         [1,0,1]],
        [[1,1],
         [1,0],
         [1,1]],
        [[1,1],
         [0,1],
         [1,1]]
    ],
    'X': [ // Pentomino X
        [[0,1,0],
         [1,1,1],
         [0,1,0]]
    ]
};

// 方块尺寸映射
const BLOCK_SIZES = {
    'A': 1, 'B': 2, 'C': 3, 'D': 3,
    'I': 4, 'L': 4, 'O': 4, 'S': 4,
    'T': 4, 'U': 5, 'X': 5
};

// ============================
// 工具函数
// ============================

/**
 * 打乱数组顺序
 * @param {Array} array - 要打乱的数组
 */
function shuffle(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

/**
 * 水平翻转形状
 * @param {Array<Array<number>>} shape - 原始形状
 * @returns {Array<Array<number>>} - 翻转后的形状
 */
function reflectShape(shape) {
    return shape.map(row => [...row].reverse());
}

/**
 * 获取方块在网格上的所有可能放置位置
 * @param {Array<Array<number>>} shape - 方块形状
 * @param {number} gridWidth - 网格宽度
 * @param {number} gridHeight - 网格高度
 * @returns {Array<Array<string>>} - 所有可能的放置位置(每个位置是一个单元格坐标数组)
 */
function getAllPlacements(shape, gridWidth, gridHeight) {
    if (!shape || shape.length === 0) {
        return [];
    }

    const placements = [];
    const blockHeight = shape.length;
    const blockWidth = shape[0].length;

    for (let y = 0; y <= gridHeight - blockHeight; y++) {
        for (let x = 0; x <= gridWidth - blockWidth; x++) {
            const cells = [];
            for (let i = 0; i < blockHeight; i++) {
                for (let j = 0; j < blockWidth; j++) {
                    if (shape[i][j] === 1) {
                        cells.push(`${y + i},${x + j}`);
                    }
                }
            }
            placements.push(cells);
        }
    }
    return placements;
}

// ============================
// 舞蹈链算法实现
// ============================

class DancingLinksNode {
    constructor() {
        this.left = this;
        this.right = this;
        this.up = this;
        this.down = this;
        this.column = null;
    }
}

class ColumnNode extends DancingLinksNode {
    constructor(name) {
        super();
        this.name = name;
        this.size = 0;
        this.column = this;
    }

    /**
     * 覆盖这一列
     */
    cover() {
        this.right.left = this.left;
        this.left.right = this.right;

        let i = this.down;
        while (i !== this) {
            let j = i.right;
            while (j !== i) {
                j.down.up = j.up;
                j.up.down = j.down;
                j.column.size--;
                j = j.right;
            }
            i = i.down;
        }
    }

    /**
     * 恢复这一列
     */
    uncover() {
        let i = this.up;
        while (i !== this) {
            let j = i.left;
            while (j !== i) {
                j.column.size++;
                j.down.up = j;
                j.up.down = j;
                j = j.left;
            }
            i = i.up;
        }

        this.right.left = this;
        this.left.right = this;
    }
}

class DataNode extends DancingLinksNode {
    constructor(column) {
        super();
        this.column = column;
    }
}

class DancingLinksSolver {
    constructor(columns, maxTimeMs = 10000) {
        this.header = new ColumnNode('header');
        this.columns = {};
        this.startTime = performance.now();
        this.maxTimeMs = maxTimeMs;
        this.found = false;
        
        // 初始化列节点
        let last = this.header;
        columns.forEach(colName => {
            const column = new ColumnNode(colName);
            this.columns[colName] = column;
            last.right = column;
            column.left = last;
            last = column;
        });
        
        last.right = this.header;
        this.header.left = last;
    }

    /**
     * 搜索解
     * @param {number} k - 当前搜索深度
     * @param {Array} solution - 当前解决方案
     * @param {Array} solutions - 所有找到的解决方案
     */
    search(k, solution, solutions) {
        // 检查超时
        if (performance.now() - this.startTime > this.maxTimeMs) {
            return;
        }

        // 如果所有列都被覆盖,找到解
        if (this.header.right === this.header) {
            solutions.push([...solution]);
            this.found = true;
            return;
        }

        // 选择最小约束的列
        let min = Infinity;
        let chosenColumn = null;
        let tmp = this.header.right;
        while (tmp !== this.header) {
            if (tmp.size < min) {
                min = tmp.size;
                chosenColumn = tmp;
            }
            tmp = tmp.right;
        }

        if (chosenColumn === null || chosenColumn.size === 0) {
            return;
        }

        chosenColumn.cover();

        // 收集该列的所有行
        let row = chosenColumn.down;
        const rows = [];
        while (row !== chosenColumn) {
            rows.push(row);
            row = row.down;
        }

        // 打乱行顺序以获得随机解
        shuffle(rows);

        for (let i = 0; i < rows.length && !this.found; i++) {
            row = rows[i];
            solution.push(row);

            // 覆盖该行涉及的所有列
            let j = row.right;
            while (j !== row) {
                j.column.cover();
                j = j.right;
            }

            // 递归搜索
            this.search(k + 1, solution, solutions);

            // 回溯
            solution.pop();
            j = row.left;
            while (j !== row) {
                j.column.uncover();
                j = j.left;
            }
        }

        chosenColumn.uncover();
    }
}

// ============================
// 主要求解函数
// ============================

/**
 * 解析输入字符串
 * @param {string} inputStr - 输入字符串,格式:width,height,[shape,count...]
 * @returns {Object} - 解析后的参数对象
 */
function parseInput(inputStr) {
    // 移除所有空格
    const cleaned = inputStr.replace(/\s+/g, '');
    
    // 匹配矩形尺寸
    const sizeMatch = cleaned.match(/^(\d+),(\d+),\[/);
    if (!sizeMatch) {
        throw new Error('输入格式错误: 应以 "width,height,[" 开头');
    }
    
    const width = parseInt(sizeMatch[1]);
    const height = parseInt(sizeMatch[2]);
    
    // 提取方块列表部分
    const listMatch = cleaned.match(/\[(.*)\]/);
    if (!listMatch) {
        throw new Error('输入格式错误: 缺少方块列表');
    }
    
    const blockListStr = listMatch[1];
    const blockCounts = {};
    
    // 解析方块列表
    const blockPattern = /([A-Z]),(\d+)/g;
    let match;
    let totalArea = 0;
    
    while ((match = blockPattern.exec(blockListStr)) !== null) {
        const shape = match[1];
        const count = parseInt(match[2]);
        
        if (!BLOCKS[shape]) {
            throw new Error(`未知方块类型: ${shape}`);
        }
        
        if (blockCounts[shape]) {
            blockCounts[shape] += count;
        } else {
            blockCounts[shape] = count;
        }
        
        totalArea += count * (BLOCK_SIZES[shape] || 0);
    }
    
    // 验证面积
    const gridArea = width * height;
    if (totalArea !== gridArea) {
        throw new Error(`面积不匹配: 网格面积=${gridArea}, 方块总面积=${totalArea}`);
    }
    
    return {
        width,
        height,
        blockCounts,
        totalBlocks: Object.values(blockCounts).reduce((a, b) => a + b, 0)
    };
}

/**
 * 求解方块覆盖问题
 * @param {number} gridWidth - 网格宽度
 * @param {number} gridHeight - 网格高度
 * @param {Object} blockCounts - 方块数量映射
 * @returns {Array<Array<string>>|null} - 解决方案矩阵,无解返回null
 */
function solveBlockPuzzle(gridWidth, gridHeight, blockCounts) {
    // 生成列定义
    const columns = [];
    
    // 单元格约束列
    for (let y = 0; y < gridHeight; y++) {
        for (let x = 0; x < gridWidth; x++) {
            columns.push(`${y},${x}`);
        }
    }
    
    // 方块实例约束列
    for (let block in blockCounts) {
        for (let i = 0; i < blockCounts[block]; i++) {
            columns.push(`${block}_${i}`);
        }
    }
    
    // 创建舞蹈链求解器
    const dlx = new DancingLinksSolver(columns);
    
    // 收集所有可能的放置方式
    const allPlacements = [];
    
    for (let block in blockCounts) {
        for (let count = 0; count < blockCounts[block]; count++) {
            // 原始方向
            BLOCKS[block].forEach(shape => {
                const placements = getAllPlacements(shape, gridWidth, gridHeight);
                placements.forEach(cells => {
                    allPlacements.push({
                        block,
                        cells,
                        count,
                        shape
                    });
                });
            });
            
            // 如果需要翻转,添加翻转后的方向
            if (['D', 'L', 'S', 'T', 'U'].includes(block)) {
                BLOCKS[block].forEach(shape => {
                    const mirrored = reflectShape(shape);
                    const placements = getAllPlacements(mirrored, gridWidth, gridHeight);
                    placements.forEach(cells => {
                        allPlacements.push({
                            block,
                            cells,
                            count,
                            shape: mirrored
                        });
                    });
                });
            }
        }
    }
    
    // 打乱放置顺序以获得随机解
    shuffle(allPlacements);
    
    // 构建舞蹈链数据结构
    allPlacements.forEach(placement => {
        const nodesInRow = [];
        
        // 添加单元格约束节点
        placement.cells.forEach(cell => {
            const colNode = dlx.columns[cell];
            if (!colNode) return;
            
            const newNode = new DataNode(colNode);
            nodesInRow.push(newNode);
            
            newNode.up = colNode.up;
            newNode.down = colNode;
            colNode.up.down = newNode;
            colNode.up = newNode;
            colNode.size++;
        });
        
        // 添加方块实例约束节点
        const blockInstance = `${placement.block}_${placement.count}`;
        const blockColNode = dlx.columns[blockInstance];
        if (blockColNode) {
            const newNode = new DataNode(blockColNode);
            nodesInRow.push(newNode);
            
            newNode.up = blockColNode.up;
            newNode.down = blockColNode;
            blockColNode.up.down = newNode;
            blockColNode.up = newNode;
            blockColNode.size++;
        }
        
        // 连接行内的节点
        nodesInRow.forEach((node, i) => {
            node.left = nodesInRow[i - 1] || nodesInRow[nodesInRow.length - 1];
            node.right = nodesInRow[i + 1] || nodesInRow[0];
        });
    });
    
    // 搜索解
    const solutions = [];
    dlx.search(0, [], solutions);
    
    if (solutions.length === 0) {
        return null;
    }
    
    // 将第一个解转换为矩阵形式
    const solutionMatrix = Array(gridHeight).fill().map(() => Array(gridWidth).fill('.'));
    const solution = solutions[0];
    
    solution.forEach(rowNode => {
        // 找到这个方块对应的单元格
        const blockCells = [];
        let node = rowNode;
        
        do {
            if (node.column && /\d+,\d+/.test(node.column.name)) {
                blockCells.push(node.column.name);
            }
            node = node.right;
        } while (node !== rowNode);
        
        // 找到方块类型
        let blockType = null;
        node = rowNode;
        do {
            if (node.column && /^[A-Z]_\d+$/.test(node.column.name)) {
                blockType = node.column.name.split('_')[0];
                break;
            }
            node = node.right;
        } while (node !== rowNode);
        
        // 在矩阵中标记方块
        if (blockType && blockCells.length > 0) {
            blockCells.forEach(cell => {
                const [y, x] = cell.split(',').map(Number);
                if (y >= 0 && y < gridHeight && x >= 0 && x < gridWidth) {
                    solutionMatrix[y][x] = blockType;
                }
            });
        }
    });
    
    return solutionMatrix;
}

/**
 * 格式化输出矩阵
 * @param {Array<Array<string>>} matrix - 解决方案矩阵
 * @returns {string} - 格式化后的字符串
 */
function formatMatrix(matrix) {
    return matrix.map(row => row.join(' ')).join('\n');
}

// ============================
// 主函数(命令行接口)
// ============================

/**
 * 命令行主函数
 */
function main() {
    // 判断是否在Node.js环境中
    if (typeof process !== 'undefined' && process.argv) {
        // 从命令行参数读取输入
        if (process.argv.length < 3) {
            console.log('用法: node block_solver_core.js "width,height,[shape,count...]"');
            console.log('示例: node block_solver_core.js "4,4,[A,4,B,6]"');
            return;
        }
        
        const inputStr = process.argv[2];
        
        try {
            // 解析输入
            const { width, height, blockCounts } = parseInput(inputStr);
            
            console.log(`求解 ${width}x${height} 网格的方块覆盖问题...`);
            console.log(`方块配置: ${JSON.stringify(blockCounts)}`);
            
            // 求解
            const startTime = performance.now();
            const solution = solveBlockPuzzle(width, height, blockCounts);
            const endTime = performance.now();
            
            console.log(`求解时间: ${(endTime - startTime).toFixed(2)}ms`);
            
            // 输出结果
            if (solution) {
                console.log('\n找到解决方案:');
                console.log(formatMatrix(solution));
            } else {
                console.log('\n无解');
            }
            
        } catch (error) {
            console.error('错误:', error.message);
        }
        
    } else {
        // 浏览器环境 - 提供测试接口
        console.log('方块覆盖求解器核心模块已加载');
        console.log('在Node.js环境中使用: node block_solver_core.js "width,height,[shape,count...]"');
        
        // 测试用例
        const testCases = [
            "4,4,[A,4,B,6]",           // 4x4网格,4个A方块,6个B方块
            "3,3,[A,9]",               // 3x3网格,9个A方块
            "2,4,[B,4]",               // 2x4网格,4个B方块
            "4,2,[I,2]",               // 4x2网格,2个I方块
            "4,5,[A,5,B,5,C,5]"        // 4x5网格,各5个A,B,C方块
        ];
        
        console.log('\n测试用例:');
        testCases.forEach(testCase => {
            try {
                const { width, height, blockCounts } = parseInput(testCase);
                const solution = solveBlockPuzzle(width, height, blockCounts);
                console.log(`\n输入: ${testCase}`);
                if (solution) {
                    console.log('有解');
                } else {
                    console.log('无解');
                }
            } catch (error) {
                console.log(`\n输入: ${testCase}`);
                console.log(`错误: ${error.message}`);
            }
        });
    }
}

// 如果直接运行此文件,执行主函数
if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        parseInput,
        solveBlockPuzzle,
        formatMatrix,
        BLOCKS,
        BLOCK_SIZES
    };
    
    // 如果通过命令行调用,执行主函数
    if (require.main === module) {
        main();
    }
}

// 浏览器环境直接执行
if (typeof window !== 'undefined') {
    main();
}

用node.js执行结果如下:

node-v24.4.1-linux-x64/bin/node block_solver_core.js "4,4,[A,4,B,6]"
求解 4x4 网格的方块覆盖问题...
方块配置: {"A":4,"B":6}
求解时间: 22.74ms

找到解决方案:
B B B B
B B B B
B B B A
A A B A

node-v24.4.1-linux-x64/bin/node block_solver_core.js "3,3,[A,9]"
求解 3x3 网格的方块覆盖问题...
方块配置: {"A":9}
求解时间: 2.56ms

找到解决方案:
A A A
A A A
A A A

node-v24.4.1-linux-x64/bin/node block_solver_core.js "5,5,[O,1,X,1,U,2,L,2]"
错误: 面积不匹配: 网格面积=25, 方块总面积=27

也可以自定义形状,实现任意形状的覆盖。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值