龍博士魔術金字塔(平面)DFS破解

用DFS直接試出答案,最底下有範例測資。

語言:C++

最多也只有60種可能性
如圖(粉紅色積木放在那是要代表左邊已填滿),在參考點的左下方皆已被填滿的前提下,各種拼圖的擺放方式總共只有60種可能性,加上各種條件,預估這個量對程式而言不會太複雜。
計算時從最左下角的點開始排,不斷換拼片或擺法直到成功放入後,將參考點向右移到空白處(若已達最右邊,則移至向上一排的最左邊),開始試下一片;沒有拼片能夠成功拼上,則將上一步改用不同拼片或擺法。重複以上過程,直到試出答案為止。

題外話:做這個的前一天晚上完全睡不著,滿腦子都是這個要怎麼寫,像是「這個寫法會炸,那這樣呢?」。隔天早上一起床(雖然沒睡)就開始動工,結果只花了一個上午就完成了。興頭的力量真可怕。

範例測資:
F
FF
A00
A000
0A000
00A000
all0
AF
all0:以下都是0(未填滿空位),因為後面的題目大多都只有上面兩塊,打一堆0很麻煩。
AF:已使用積木代號(注意A跟F之間沒有空格)。

以下是程式碼:
#include<set>
#include<string>
#include<iostream>

//global value
std::set<char> unused = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'};
char map[10][10] = {{ '0','0','0','0','0','0','0','0','0','0'},
                    { '0','0','0','0','0','0','0','0','0','1'},
                    { '0','0','0','0','0','0','0','0','1','1'},
                    { '0','0','0','0','0','0','0','1','1','1'},
                    { '0','0','0','0','0','0','1','1','1','1'},
                    { '0','0','0','0','0','1','1','1','1','1'},
                    { '0','0','0','0','1','1','1','1','1','1'},
                    { '0','0','0','1','1','1','1','1','1','1'},
                    { '0','0','1','1','1','1','1','1','1','1'},
                    { '0','1','1','1','1','1','1','1','1','1'}};
int fuck = 0;

void printPyramid()
{
    for (int i = 9; i >= 0; i--)
    {
        for (int j = 0; j < i; j++)
            printf(" ");
        for (int j = 0; j <= 9-i; j++)
            printf("%c ", map[i][j]);
        printf("\n");
    }
}

//check if it's ok to put certain piece at certain angle
bool canBePut(int y, int x, int ballY[], int ballX[], int ballNum)
{
    for (int i = 0; i < ballNum; i++)
    {
        //printf("y:%d x:%d y+:%d x+:%d \n", y, x, ballY[i], ballX[i]);
        if (y + ballY[i] >= 10 || y + ballY[i] < 0 || x + ballX[i] >= 10 || x + ballX[i] < 0|| map[y + ballY[i]][x + ballX[i]] != '0')
        {
            return false;
        }
    }
    //printf("it's my hollllleeeeeeee!!!\n");
    return true;
}

//find the next refer-point
void nextPoint(int &y, int &x)
{
    while (map[y][x] != '0')
    {
        x++;
        if (x > 9)
        {
            x = 0;
            y++;
        }
        if (x > 0 && y == 9)
        {
            x = 0;
            y = 0;
            printPyramid();
            printf("!!!%d:fuck!!!\n", fuck);
            break;
        }
    }
    return;
}

int yArray[60][4] = {
    /*A*/
    { 1, 2, 3, 0 },
    { 1, 2, 3, 0 },
    { 1, 1, 2, 0 },
    { 1, 1, 2, 0 },
    { 1, 2, 3, 0 },
    { 1, 1, 2, 0 },
    { 1, 2, 3, 0 },
    { 1, 1, 2, 0 },
    /*B*/
    { 1, 2, 2, 3 },
    { 0, 1, 1, 2 },
    { 1, 2, 2, 3 },
    { 1, 1, 2, 2 },
    { 1, 1, 2, 2 },
    { 1, 1, 2, 3 },
    { 1, 1, 2, 3 },
    { 0, 1, 1, 2 },
    /*C*/
    { 1, 2, 3, 4 },
    { 1, 2, 3, 4 },
    { 1, 1, 2, 3 },
    { 1, 1, 2, 3 },
    { 1, 2, 3, 4 },
    { 1, 2, 2, 3 },
    { 1, 2, 3, 4 },
    { 1, 2, 2, 3 },
    /*D*/
    { 0, 1, 2, 3 },
    { 0, 1, 2, 3 },
    { 1, 2, 2, 3 },
    { 1, 2, 2, 3 },
    { 1, 2, 3, 3 },
    { 1, 1, 2, 3 },
    { 1, 2, 3, 3 },
    { 1, 1, 2, 3 },
    /*E*/
    { 0, 1, 1, 2 },
    { 1, 2, 3, 4 },
    { 1, 2, 3, 4 },
    { 0, 1, 1, 2 },
    { 1, 2, 3, 4 },
    { 1, 1, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 1, 2, 2 },
    /*F*/
    { 0, 1, 0, 0 },
    { 1, 2, 0, 0 },
    { 1, 2, 0, 0 },
    { 1, 1, 0, 0 },
    /*G*/
    { 1, 2, 3, 4 },
    { 1, 2, 3, 4 },
    { 0, 1, 1, 2 },
    { 1, 1, 2, 2 },
    /*H*/
    { 0, 0, 1, 1 },
    { 1, 2, 3, 4 },
    { 1, 2, 3, 4 },
    { 0, 1, 1, 1 },
    /*I*/
    { 1, 2, 2, 3 },
    { 1, 2, 2, 3 },
    { 1, 1, 2, 3 },
    { 1, 1, 2, 3 },
    /*J*/
    { 1, 2, 3, 0 },
    { 1, 2, 3, 0 },
    /*K*/
    { 1, 1, 2, 0 },
    /*L*/
    { 0, 1, 2, 2 } };
int xArray[60][4] = {
    /*A*/
    { 0,-1,-2, 0 },
    {-1,-1,-1, 0 },
    {-1, 0, 0, 0 },
    { 0,-1,-2, 0 },
    { 0, 0,-1, 0 },
    { 0, 1, 0, 0 },
    {-1,-2,-2, 0 },
    {-1,-2,-2, 0 },
    /*B*/
    {-1,-1,-2,-2 },
    { 1, 0, 1, 0 },
    { 0, 0,-1,-1 },
    { 0,-1,-1,-2 },
    { 0,-1,-1, 0 },
    { 0,-1,-1,-2 },
    { 0,-1,-1,-1 },
    { 1, 0,-1,-1 },
    /*C*/
    {-1,-1,-1,-1 },
    { 0,-1,-2,-3 },
    {-1, 0, 0, 0 },
    { 0,-1,-2,-3 },
    { 0, 0, 0,-1 },
    { 0, 0, 1, 0 },
    {-1,-2,-3,-3 },
    {-1,-2,-3,-3 },
    /*D*/
    { 1, 0,-1,-2 },
    { 1, 0, 0, 0 },
    { 0,-1, 0, 0 },
    {-1,-1,-2,-3 },
    { 0, 0, 0,-1 },
    { 0, 1, 0, 0 },
    {-1,-2,-2,-3 },
    {-1,-2,-2,-3 },
    /*E*/
    { 1, 0, 1, 1 },
    { 0,-1,-1,-1 },
    {-1,-1,-2,-3 },
    {-1,-1,-2,-3 },
    { 0, 0,-1,-1 },
    { 0, 1, 0, 1 },
    {-1,-2,-2,-3 },
    {-1,-2,-2,-3 },
    /*F*/
    { 1, 0, 0, 0 },
    { 0,-1, 0, 0 },
    {-1,-1, 0, 0 },
    { 0,-1, 0, 0 },
    /*G*/
    { 0, 0,-1,-2 },
    {-1,-2,-2,-2 },
    { 2, 0, 1, 0 },
    { 0,-1, 0,-2 },
    /*H*/
    { 1, 2, 0, 1 },
    {-1,-1,-2,-2 },
    { 0,-1,-1,-2 },
    { 1, 0,-1, 1 },
    /*I*/
    {-1, 0,-1,-1 },
    { 0,-1,-2,-2 },
    { 0,-1,-2,-2 },
    { 0,-1, 0,-1 },
    /*J*/
    { 0, 0, 0, 0 },
    {-1,-2,-3, 0},
    /*K*/
    { 0,-1,-1, 0 },
    /*L*/
    { 1, 0, 0,-1 } };
char piece[60] = { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
                   'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B',
                   'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C',
                   'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
                   'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E',
                   'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G',
                   'H', 'H', 'H', 'H', 'I', 'I', 'I', 'I',
                   'J', 'J', 'K', 'L' };

//dfs
bool put(int y, int x)
{
    int ballSize;
    int newX, newY;
    for (int t = 0; t < 60; t++)
    {  
        if (unused.empty())
        {
            printf("how the fuck???!!!\n");
            return true;
        }
        if (unused.count(piece[t]))
        {
            switch (piece[t])
            {
                case 'F':
                    ballSize = 2;
                    break;
                case 'A':
                case 'J':
                case 'K':
                    ballSize = 3;
                    break;
                default:
                    ballSize = 4;
                    break;
            }
            if (canBePut(y, x, yArray[t], xArray[t], ballSize))
            {
                map[y][x] = piece[t];
                for (int i = 0; i < ballSize; i++)
                {
                    map[y + yArray[t][i]][x + xArray[t][i]] = piece[t];
                }
                unused.erase(piece[t]);
                newX = x;
                newY = y;
                nextPoint(newY, newX);
                if (newX == 0 && newY == 0)
                    //return false;
                    return true;
                if (!put(newY, newX))//undo
                {
                    map[y][x] = '0';
                    for (int i = 0; i < ballSize; i++)
                    {
                        map[y + yArray[t][i]][x + xArray[t][i]] = '0';
                    }
                    unused.insert(piece[t]);
                }
                else
                    //return false;
                    return true;
            }
        }
    }
    //printPyramid();
    //printf("!!!%d:fuck!!!\n", fuck);
    fuck++;
    return false;
}

int main()
{
    int x = 0;
    int y = 9;
    //input
    std::string sin;
    while (std::cin >> sin)
    {
        if (sin == "all0")
            break;
        for (int i = 0; i < sin.length(); i++)
            map[y][i] = sin[i];
        y--;
        if (y < 0)
            break;
    }
    std::cin >> sin;
    for (int i = 0; i < sin.length(); i++)
        unused.erase(sin[i]);
    //show the question
    printPyramid();
    printf("unused:");
    for (const auto& ch : unused)
    {
        std::cout << ch << " ";
    }
    printf("\n");
    y = 0;
    if (map[y][x] != '0')
        nextPoint(y, x);
    //dfs
    if(put(y,x))
        printf("hell yeah!");
    else
        printf("no way!");
    return 0;
}
/*test input
#1

F
FF
A00
A000
0A000
00A000
all0
AF


#2

B
BB
BB0
A000
AA000
00A000
all0
AB


#3

I
II
IB0
AIB0
AABB0
A00B00
D000000
D0000000
D00000000
DD00000000
IBAD
*/

留言