卡拼图的算法解决方案

时间:2014-03-09 21:59:30

标签: algorithm permutation puzzle

鉴于是一款带有九张方卡的益智游戏 在每张卡片上,顶部,右侧,底部和左侧有4张图片 卡片上的每张图片都描绘了动物的前部或后部(鳄鱼)。每张图片都有5种颜色中的一种。

目标:在3x3网格中布置九张卡片,使所有“内部”(完整)鳄鱼与相邻的卡片正确组合,即具有前端和后端以及匹配的颜色。

为了对这个问题进行直观的掌控,这里有一个难题的图片:

我手工找到了所描述的解决方案 即使谜团看起来很简单,但由于你可以用4种不同的方式旋转每件作品,所以组合数量非常大。

现在的问题是,我希望有一个算法生成所有可能的3x3布局,以便检查所有可能的解决方案(如果还有其他解决方案)。最好在Processing / Java中。

到目前为止的想法:
我的方法是用4个整数数组来表示9个部分中的每个,表示一个部分的4个旋转状态。然后生成这9个片段的所有可能的排列,从片段阵列中挑选4个旋转状态中的1个。然后,函数isValidSolution()可以检查违反约束的解决方案(颜色匹配和前后匹配)。

关于如何实现这一点的任何想法?

2 个答案:

答案 0 :(得分:1)

只有9件,因此每个潜在的解决方案都可以通过一个小结构来表示(比如一个3x3的阵列,每件都有它的旋转),因此对这些部件的确切描述并不太重要。

尝试所有可能的排列是浪费的(在这里滥用LaTeX,将9件放在网格上可以用$ 9!$订单完成,因为每个可以在4个不同的方向,这总共9美元! cdot 4 ^ 9 = 95126814720 \大约10 ^ {11} $,有点太多了,无法检查它们。你用手做的是放置一块,比如说在左上方,并尝试通过将匹配的零件装入其余部分来完成方形。所以你永远不会考虑第一和第二部分不匹配的任何组合,大大减少搜索。这种想法被称为回溯。对于它你需要一个部分解决方案的描述(3x3网格,填充件和空白处,以及尚未使用的部件;填充网格的特定顺序),一种向前移动的方式(放置下一件如果它适合,跳过那个,如果它不合适)和向后(找不到任何拟合,撤消最后一步并尝试下一个可能性)。

显然你必须设计一种方法来确定是否存在潜在的匹配(给定填充的邻居,尝试其中所有方向的位置)。对于这样一个小问题,这可能不是性能关键,但如果你试图解决,比如100x100,情况则不同......

答案 1 :(得分:1)

可以找到所有解决方案,尽量不要探索搜索树的所有不成功路径。下面的C ++代码,没有经过高度优化,发现总共有2个解决方案(结果是相同的唯一解决方案,因为有一个重复的磁贴,正确答案?)几乎是我的计算机瞬间完成的。 / p>

这里避免探索所有可能性的技巧是在我们仍然放置切片时调用函数isValidSolution()(函数处理空切片)。另外,为了加快这个过程,我按照给定的顺序放置瓷砖,从中间开始,然后在左,右,顶部和底部围绕它的交叉,然后是左上角,右上角,底部的角落 - 左右两边。可能其他组合可以更快地执行。

当然可以优化这一点,因为这个拼图中的特殊模式分布(带有字母的模式只接受一个可能的匹配),但这超出了我的答案范围。

#include<iostream>

// possible pattern pairs (head, body)
#define PINK    1
#define YELLOW  2
#define BLUE    3
#define GREEN   4
#define LACOSTE 5

typedef int8_t pattern_t; // a pattern is a possible color, positive for head, and negative for body
typedef struct {
    pattern_t p[4]; // four patterns per piece: top, right, bottom, left
} piece_t;

unsigned long long int solutionsCounter = 0;

piece_t emptyPiece = {.p = {0,  0,  0, 0} };

piece_t board[3][3] = {
    { emptyPiece, emptyPiece, emptyPiece},
    { emptyPiece, emptyPiece, emptyPiece},
    { emptyPiece, emptyPiece, emptyPiece},
    };

inline bool isEmpty(const piece_t& piece) {
    bool result = (piece.p[0] == 0);
    return result;
}

// check current solution
bool isValidSolution() {
    int i, j;
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 3; j++) {
            if (!isEmpty(board[i][j]) && !isEmpty(board[i+1][j]) && (board[i][j].p[1] != -board[i+1][j].p[3])) {
                return false;
            }
        }
    }
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 2; j++) {
            if (!isEmpty(board[i][j]) && !isEmpty(board[i][j+1]) && (board[i][j].p[2] != -board[i][j+1].p[0])) {
                return false;
            }
        }
    }
    return true;
}

// rotate piece
void rotatePiece(piece_t& piece) {
    pattern_t paux = piece.p[0];
    piece.p[0] = piece.p[1];
    piece.p[1] = piece.p[2];
    piece.p[2] = piece.p[3];
    piece.p[3] = paux;
}

void printSolution() {
    printf("Solution:\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("\t  %2i  ", (int) board[j][i].p[0]);
        }
        printf("\n");
        for (int j = 0; j < 3; j++) {
            printf("\t%2i  %2i", (int) board[j][i].p[3], (int) board[j][i].p[1]);
        }
        printf("\n");
        for (int j = 0; j < 3; j++) {
            printf("\t  %2i  ", (int) board[j][i].p[2]);
        }
        printf("\n");
    }
    printf("\n");
}

bool usedPiece[9] = { false, false, false, false, false, false, false, false, false };
int colocationOrder[9] = { 4, 3, 5, 1, 7, 0, 2, 6, 8 };

void putNextPiece(piece_t pieces[9], int pieceNumber) {

    if (pieceNumber == 9) {
        if (isValidSolution()) {
            solutionsCounter++;
            printSolution();
        }
    } else {
        int nextPosition = colocationOrder[pieceNumber];
        int maxRotations = (pieceNumber == 0) ? 1 : 4; // avoids rotation symmetries.
        for (int pieceIndex = 0; pieceIndex < 9; pieceIndex++) {
            if (!usedPiece[pieceIndex]) {
                usedPiece[pieceIndex] = true;
                for (int rotationIndex = 0; rotationIndex < maxRotations; rotationIndex++) {
                    ((piece_t*) board)[nextPosition] = pieces[pieceIndex];
                    if (isValidSolution()) {
                        putNextPiece(pieces, pieceNumber + 1);
                    }
                    rotatePiece(pieces[pieceIndex]);
                }
                usedPiece[pieceIndex] = false;
                ((piece_t*) board)[nextPosition] = emptyPiece;
            }
        }
    }
}

int main() {

    // register all the pieces (already solved, scramble!)
    piece_t pieces[9] = {
        {.p = { -YELLOW,    -BLUE,     +GREEN,  +PINK} },
        {.p = { -YELLOW,    -GREEN,    +PINK,   +BLUE} },
        {.p = { -BLUE,      -YELLOW,   +PINK,   +GREEN }},
        {.p = { -GREEN,     -BLUE,     +PINK,   +YELLOW }},
        {.p = { -PINK,      -LACOSTE,  +GREEN,  +BLUE }},
        {.p = { -PINK,      -BLUE,     +GREEN,  +LACOSTE }},
        {.p = { -PINK,      -BLUE,     +PINK,   +YELLOW }},
        {.p = { -GREEN,     -YELLOW,   +GREEN,  +BLUE }},
        {.p = { -GREEN,     -BLUE,     +PINK,   +YELLOW }}
    };

    putNextPiece(pieces, 0);

    printf("found %llu solutions\n", solutionsCounter);

    return 0;
}