首页 文章

这个游戏背后有什么数学/计算原理?

提问于
浏览
186

我的孩子有这个有趣的游戏叫Spot It!游戏限制(我能说的最好)是:

  • 这是一张55张牌

  • 每张卡上有8张独特的图片(即一张卡不能有2张相同的图片)

  • Given any 2 cards chosen from the deck, there is 1 and only 1 matching picture .

  • 匹配的图片在不同的卡片上可能会有不同的缩放比例,但这只会让游戏变得更难(即小树仍与较大的树匹配)

游戏的原理是:翻转2张牌,无论谁先挑选匹配的图片都能获得一分 .

这是一张澄清的图片:

spot it

(例如:你可以从上面的2张牌看到匹配的图片是绿色的恐龙 . 在右下角和右中角的图片之间,它是一个小丑的头 . )

我想了解以下内容:

  • 满足这些标准所需的最小不同图片数量是多少?您如何确定?

  • 使用伪代码(或Ruby),如何从N个图片阵列中生成55张游戏卡(其中N是问题1中的最小数字)?

Update:

每张照片确实发生了两次以上的照片(与一些人猜测的情况相反) . 看到这张3张牌的照片,每张牌都带有闪电:
3 cards

9 回答

  • 18

    Finite Projective Geometries

    projective (plane) geometryprojective (plane) geometry与欧氏几何略有不同:

    • 每两个点只有一条线穿过它们(这是相同的) .

    • 每两行恰好在一个点上相遇(这与Euclid略有不同) .

    现在,将"finite"添加到汤中,您就有了问题:

    我们可以只有2个点的几何?有3分? 4?有7?

    关于这个问题仍然存在悬而未决的问题,但我们知道这一点:

    • 如果存在具有 Q 点的几何,则 Q = n^2 + n + 1n 称为几何的 order .

    • 每行都有 n+1 个点 .

    • 从每一点开始,准确传递 n+1 行 .

    • 总行数也是 Q .

    • 最后,如果 n 是素数,那么确实存在一个阶数 n 的几何 .


    有人可能会问,这与谜题有什么关系 .

    card 而不是 pointpicture 而不是 line ,公理成为:

    • 每两张卡片只有一张共同的图片 .

    • 对于每两张图片,只有一张卡片同时具有这两张图片 .

    现在,让我们拿 n=7 ,我们有 order-7 有限几何与 Q = 7^2 + 7 + 1 . 这使 Q=57 行(图片)和 Q=57 点(卡片) . 我猜拼图制造商认为55是圆形数字而不是57,剩下2张牌 .

    我们也得到 n+1 = 8 ,所以从每个点(卡),8行通过(出现8张图片),每行(图片)有8个点(出现在8张卡片中) .


    这里是最着名的有限投影(order-2)平面(几何)的表示,有7个点,称为Fano Plane,复制自Noelle Evans - Finite Geometry Problem Page

    enter image description here

    我正在考虑创建一个图像,解释上面的2阶飞机如何制作一个类似的拼图,有7张卡片和7张图片,但是来自math.exchange双胞胎问题的链接就是这样一张图: Dobble-et-la-geometrie-finie

    Fano Plane

  • 3

    因此,存在k = 55张卡,其中m = 8张图片,每张图片来自n张图片的总数 . 我们可以重申一个问题'我们需要多少张图片,以便我们可以构建一组k卡,在任何一张卡片之间只有一张共享图片?'等价地问:

    给定一个n维向量空间和所有向量的集合,其中包含正好m个元素等于1和所有其他零,n有多大,这样我们就可以找到一组k向量,其成对点积都等于1?

    确切地(n选择m)可能的向量来构建对 . 所以我们至少需要一个足够大的n,以便(n选择m)> = k . 这只是一个下限,因此为了实现成对兼容性约束,我们可能需要更高的n .

    只是为了试验一点,我写了一个小的Haskell程序来计算有效的卡集:

    Edit: 我刚看到Neil 's and Gajet'解决方案后才意识到,我使用的算法并不一定有效 . 我会尽快更新我的代码 .

    module Main where
    
    cardCandidates n m = cardCandidates' [] (n-m) m
    cardCandidates' buildup  0  0 = [buildup]
    cardCandidates' buildup zc oc
        | zc>0 && oc>0 = zerorec ++ onerec
        | zc>0         = zerorec
        | otherwise    = onerec
        where zerorec = cardCandidates' (0:buildup) (zc-1) oc
              onerec  = cardCandidates' (1:buildup) zc (oc-1)
    
    dot x y = sum $ zipWith (*) x y
    compatible x y = dot x y == 1
    
    compatibleCards = compatibleCards' []
    compatibleCards' valid     [] = valid
    compatibleCards' valid (c:cs)
      | all (compatible c) valid = compatibleCards' (c:valid) cs
      |                otherwise = compatibleCards'    valid  cs
    
    legalCardSet n m = compatibleCards $ cardCandidates n m
    
    main = mapM_ print [(n, length $ legalCardSet n m) | n<-[m..]]
      where m = 8
    

    每张卡m = 8张图片的兼容卡的最大数量,对于前几个n,从n中选择不同数量的图片,如下所示:

    由于组合爆炸,这种蛮力方法并没有走得太远 . 但我认为它可能仍然很有趣 .

    有趣的是,似乎对于给定的m,k随着n的增加而增加,直到某个n,之后它保持不变 .

    这意味着,对于每张卡的每个数量的图片,有一定数量的图片可供选择,这导致最大可能数量的合法卡 . 添加更多图片以从过去的最佳数字中进行选择不会进一步增加合法卡的数量 .

    前几个最佳k是:

    optimal k table

  • 3

    对于那些无法用57个点描绘投影平面几何体的人来说,有一个非常好的,直观的方法来构建57张牌和57个符号的游戏(根据Yuval Filmusthis question的答案):

    • 对于具有8个符号的卡片,请创建7x7独特符号网格 .

    • 为"slopes"添加额外的8个符号,从0到6,加上一个用于无限斜率 .

    • 每张卡是网格上的一条线(7个符号)加上一条符号,该符号来自为该线的斜率设置的斜率 . 线具有偏移(即左边的起点)和斜率(即,每个步骤右边有多少个符号) . 当线条离开顶部的网格时,重新进入底部 . 对于两张这样的卡,请参见此示例图(来自boardgamegeek的图片):

    Two example cards (red and green) taken as lines from the grid

    在这个例子中,我采用斜率为零(红色)的一条线和一条斜率为1的线(绿色) . 它们恰好在一个共同点(猫头鹰)相交 .

    此方法可确保任意两张卡只有一个共同的符号,因为

    • 如果斜率不同,则线条将始终在一个点上相交 .

    • 如果斜率相同,则线不会相交,并且网格中没有公共符号 . 在这种情况下,斜率符号将是相同的 .

    通过这种方式,我们可以构建7x7卡(7个偏移和7个斜率) .

    我们还可以通过网格从垂直线构建七个附加卡(即,取每列) . 对于那些,使用无限斜率图标 .

    因为每张卡由网格中的七个符号和正好一个“斜率”符号组成,我们可以创建一个附加卡,它只包含所有8个斜率符号 .

    这给我们留下了7x8 1 = 57张可能的牌,7 x 7 8 = 57张所需的牌 .

    (当然,这仅适用于素数大小的网格(例如,n = 7) . 否则,如果斜率是网格大小的除数,则不同斜率的线可以具有零或多于一个交点 . )

  • 140

    我刚刚找到了用57或58张照片做的方法,但现在我头疼得厉害,我会在睡觉后8-10小时内发布红宝石代码!只是暗示我的解决方案每7张卡共享相同的标记,并且可以使用我的解决方案构建总共56张卡 .

    这是生成ypercube正在讨论的所有57张卡片的代码 . 它只使用了57张图片,抱歉的是's I'已经编写了实际的C代码,但是知道 vector <something> 是一个包含 something 类型值的数组,很容易理解这段代码的作用 . 此代码使用 P^2+P+1 图片生成 P^2+P+1 张卡片,每张图片包含 P+1 图片,并且每个素数P值共享1张图片 . 这意味着我们可以拥有7张卡片,每张7张图片,每张图片有3张图片(p = 2),13张卡片使用13张图片(p = 3),31张卡片使用31张图片(p = 5),57张卡片用于57张图片(对于p = 7)等等......

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    vector <vector<int> > cards;
    
    void createcards(int p)
    {
        cards.resize(0);
        for (int i=0;i<p;i++)
        {
            cards.resize(cards.size()+1);
            for(int j=0;j<p;j++)
            {
                cards.back().push_back(i*p+j);
            }
            cards.back().push_back(p*p+1);
        }
    
        for (int i=0;i<p;i++)
        {
            for(int j=0;j<p;j++)
            {
                cards.resize(cards.size()+1);
                for(int k=0;k<p;k++)
                {
                    cards.back().push_back(k*p+(j+i*k)%p);
                }
                cards.back().push_back(p*p+2+i);
            }
        }
    
        cards.resize(cards.size()+1);
    
        for (int i=0;i<p+1;i++)
            cards.back().push_back(p*p+1+i);
    }
    
    void checkCards()
    {
        cout << "---------------------\n";
        for(unsigned i=0;i<cards.size();i++)
        {
            for(unsigned j=0;j<cards[i].size();j++)
            {
                printf("%3d",cards[i][j]);
            }
            cout << "\n";
        }
        cout << "---------------------\n";
        for(unsigned i=0;i<cards.size();i++)
        {
            for(unsigned j=i+1;j<cards.size();j++)
            {
                int sim = 0;
                for(unsigned k=0;k<cards[i].size();k++)
                    for(unsigned l=0;l<cards[j].size();l++)
                        if (cards[i][k] == cards[j][l])
                            sim ++;
                if (sim != 1)
                    cout << "there is a problem between cards : " << i << " " << j << "\n";
    
            }
        }
    }
    
    int main()
    {
        int p;
        for(cin >> p; p!=0;cin>> p)
        {
            createcards(p);
            checkCards();
        }
    }
    

    再次抱歉延迟代码 .

  • 1

    其他人已经描述了设计的一般框架(有限投影平面),并展示了如何生成素数阶的有限投影平面 . 我想填补一些空白 .

    可以为许多不同的阶数生成有限投影平面,但在素数阶 p 的情况下它们是最直接的 . 然后,整数模 p 形成一个有限域,可用于描述平面中点和线的坐标 . 点有3种不同的坐标: (1,x,y)(0,1,x)(0,0,1) ,其中 xy 可以取值 0p-1 的值 . 3种不同的点解释了系统中点数的公式 p^2+p+1 . 我们还可以描述具有相同3种不同坐标的线: [1,x,y][0,1,x][0,0,1] .

    我们通过坐标的点积是否等于0 mod p 来计算点和线是否发生事故 . 因此,例如 (1,2,5)[0,1,1] 行是 p=71*0+2*1+5*1 = 7 == 0 mod 7 以来的事件,但是点 (1,3,3) 和行 [1,2,6]1*1+3*2+3*6 = 25 != 0 mod 7 以来不是事件 .

    转换为卡片和图片的语言,这意味着坐标为 (1,2,5) 的卡片包含坐标为 [0,1,1] 的图片,但坐标为 (1,3,3) 的卡片不包含坐标为 [1,2,6] 的图片 . 我们可以使用此程序开发完整的卡片列表及其中包含的图片 .

    顺便说一句,我认为将图片看作点和卡就像线条一样容易,但点和线之间的投影几何图形存在二元性,所以它确实无关紧要 . 但是,接下来我将使用点卡片和卡片线 .

    相同的结构适用于任何有限的场 . 我们知道有一个有限的订单字段 q 当且仅当 q=p^k ,一个主要力量 . 该字段名为 GF(p^k) ,代表"Galois field" . 在主要情况下,这些字段并不像在素数情况下那样容易构建 .

    幸运的是,辛勤工作已经在自由软件中完成并实施,即Sage . 例如,要获得4阶投影平面设计,只需键入

    print designs.ProjectiveGeometryDesign(2,1,GF(4,'z'))
    

    你将获得看起来像的输出

    ProjectiveGeometryDesign<points=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20], blocks=[[0, 1, 2, 3, 20], [0,
    4, 8, 12, 16], [0, 5, 10, 15, 19], [0, 6, 11, 13, 17], [0, 7, 9, 14,
    18], [1, 4, 11, 14, 19], [1, 5, 9, 13, 16], [1, 6, 8, 15, 18], [1, 7,
    10, 12, 17], [2, 4, 9, 15, 17], [2, 5, 11, 12, 18], [2, 6, 10, 14, 16],
    [2, 7, 8, 13, 19], [3, 4, 10, 13, 18], [3, 5, 8, 14, 17], [3, 6, 9, 12,
    19], [3, 7, 11, 15, 16], [4, 5, 6, 7, 20], [8, 9, 10, 11, 20], [12, 13,
    14, 15, 20], [16, 17, 18, 19, 20]]>
    

    我解释如下:有21张图片标记为0到20.每个块(投影几何线)告诉我哪些图片出现在卡片上 . 例如,第一张卡片将包含图片0,1,2,3和20;第二张卡片上有0,4,8,12和16张图片;等等 .

    可以通过生成订单7的系统

    print designs.ProjectiveGeometryDesign(2,1,GF(7))
    

    它产生输出

    ProjectiveGeometryDesign<points=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
    29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
    47, 48, 49, 50, 51, 52, 53, 54, 55, 56], blocks=[[0, 1, 2, 3, 4, 5, 6,
    56], [0, 7, 14, 21, 28, 35, 42, 49], [0, 8, 16, 24, 32, 40, 48, 50], [0,
    9, 18, 27, 29, 38, 47, 51], [0, 10, 20, 23, 33, 36, 46, 52], [0, 11, 15,
    26, 30, 41, 45, 53], [0, 12, 17, 22, 34, 39, 44, 54], [0, 13, 19, 25,
    31, 37, 43, 55], [1, 7, 20, 26, 32, 38, 44, 55], [1, 8, 15, 22, 29, 36,
    43, 49], [1, 9, 17, 25, 33, 41, 42, 50], [1, 10, 19, 21, 30, 39, 48,
    51], [1, 11, 14, 24, 34, 37, 47, 52], [1, 12, 16, 27, 31, 35, 46, 53],
    [1, 13, 18, 23, 28, 40, 45, 54], [2, 7, 19, 24, 29, 41, 46, 54], [2, 8,
    14, 27, 33, 39, 45, 55], [2, 9, 16, 23, 30, 37, 44, 49], [2, 10, 18, 26,
    34, 35, 43, 50], [2, 11, 20, 22, 31, 40, 42, 51], [2, 12, 15, 25, 28,
    38, 48, 52], [2, 13, 17, 21, 32, 36, 47, 53], [3, 7, 18, 22, 33, 37, 48,
    53], [3, 8, 20, 25, 30, 35, 47, 54], [3, 9, 15, 21, 34, 40, 46, 55], [3,
    10, 17, 24, 31, 38, 45, 49], [3, 11, 19, 27, 28, 36, 44, 50], [3, 12,
    14, 23, 32, 41, 43, 51], [3, 13, 16, 26, 29, 39, 42, 52], [4, 7, 17, 27,
    30, 40, 43, 52], [4, 8, 19, 23, 34, 38, 42, 53], [4, 9, 14, 26, 31, 36,
    48, 54], [4, 10, 16, 22, 28, 41, 47, 55], [4, 11, 18, 25, 32, 39, 46,
    49], [4, 12, 20, 21, 29, 37, 45, 50], [4, 13, 15, 24, 33, 35, 44, 51],
    [5, 7, 16, 25, 34, 36, 45, 51], [5, 8, 18, 21, 31, 41, 44, 52], [5, 9,
    20, 24, 28, 39, 43, 53], [5, 10, 15, 27, 32, 37, 42, 54], [5, 11, 17,
    23, 29, 35, 48, 55], [5, 12, 19, 26, 33, 40, 47, 49], [5, 13, 14, 22,
    30, 38, 46, 50], [6, 7, 15, 23, 31, 39, 47, 50], [6, 8, 17, 26, 28, 37,
    46, 51], [6, 9, 19, 22, 32, 35, 45, 52], [6, 10, 14, 25, 29, 40, 44,
    53], [6, 11, 16, 21, 33, 38, 43, 54], [6, 12, 18, 24, 30, 36, 42, 55],
    [6, 13, 20, 27, 34, 41, 48, 49], [7, 8, 9, 10, 11, 12, 13, 56], [14, 15,
    16, 17, 18, 19, 20, 56], [21, 22, 23, 24, 25, 26, 27, 56], [28, 29, 30,
    31, 32, 33, 34, 56], [35, 36, 37, 38, 39, 40, 41, 56], [42, 43, 44, 45,
    46, 47, 48, 56], [49, 50, 51, 52, 53, 54, 55, 56]]>
    
  • 8

    这是Gajet在Python中的解决方案,因为我发现Python更具可读性 . 我修改了它,以便它也适用于非素数 . 我使用Thies insight来生成一些更容易理解的显示代码 .

    from __future__ import print_function
    from itertools import *
    
    def create_cards(p):
        for min_factor in range(2, 1 + int(p ** 0.5)):
            if p % min_factor == 0:
                break
        else:
            min_factor = p
        cards = []
        for i in range(p):
            cards.append(set([i * p + j for j in range(p)] + [p * p]))
        for i in range(min_factor):
            for j in range(p):
                cards.append(set([k * p + (j + i * k) % p
                                  for k in range(p)] + [p * p + 1 + i]))
    
        cards.append(set([p * p + i for i in range(min_factor + 1)]))
        return cards, p * p + p + 1
    
    def display_using_stars(cards, num_pictures):
        for pictures_for_card in cards:
            print("".join('*' if picture in pictures_for_card else ' '
                          for picture in range(num_pictures)))
    
    def check_cards(cards):
        for card, other_card in combinations(cards, 2):
            if len(card & other_card) != 1:
                print("Cards", sorted(card), "and", sorted(other_card),
                      "have intersection", sorted(card & other_card))
    
    cards, num_pictures = create_cards(7)
    display_using_stars(cards, num_pictures)
    check_cards(cards)
    

    带输出:

    ***      *   
       ***   *   
          ****   
    *  *  *   *  
     *  *  *  *  
      *  *  * *  
    *   *   *  * 
     *   **    * 
      **   *   * 
    *    * *    *
     * *    *   *
      * * *     *
             ****
    
  • 8

    Using the z3 theorem prover

    P 是每张卡的符号数 . 根据this articleypercubeᵀᴹ 的答案,分别有 N = P**2 - P + 1 卡片和符号 . 一副牌可以用其入射矩阵表示,每个卡有一行,每个可能符号有一列 . 如果卡 i 上有符号 j ,则 (i,j) 元素为 1 . 我们只需要考虑这些约束来填充这个矩阵:

    • 每个元素都是零或一个

    • 每行的总和正是 P

    • 每列的总和正是 P

    • 任何两行必须只有一个共同的符号

    这意味着 N**2 变量和 N**2 + 2*N + (N choose 2) 约束 . 对于小输入,在不太长的时间内似乎可以管理 z3 .

    edit :不幸的是,对于这种方法,P = 8似乎太大了 . 我在14小时的计算时间后杀了这个过程 .

    from z3 import *
    from itertools import combinations
    
    def is_prime_exponent(K):
        return K > 1 and K not in 6  # next non-prime exponent is 10, 
                                     # but that is too big anyway
    
    def transposed(rows):
        return zip(*rows)
    
    def spotit_z3(symbols_per_card):
        K = symbols_per_card - 1
        N = symbols_per_card ** 2 - symbols_per_card + 1
        if not is_prime_exponent(K):
            raise TypeError("Symbols per card must be a prime exponent plus one.")
    
        constraints = []
    
        # the rows of the incidence matrix
        s = N.bit_length()
        rows = [[BitVec("r%dc%d" % (r, c), s) for c in range(N)] for r in range(N)]
    
        # every element must be either 1 or 0
        constraints += [Or([elem == 1, elem == 0]) for row in rows for elem in row]
    
        # sum of rows and cols must be exactly symbols_per_card
        constraints += [Sum(row) == symbols_per_card for row in rows]
        constraints += [Sum(col) == symbols_per_card for col in transposed(rows)]
    
        # Any two rows must have exactly one symbol in common, in other words they
        # differ in (symbols_per_card - 1) symbols, so their element-wise XOR will
        # have 2 * (symbols_per_card - 1) ones.
        D = 2 * (symbols_per_card - 1)
        for row_a, row_b in combinations(rows, 2):
            constraints += [Sum([a ^ b for a, b in zip(row_a, row_b)]) == D]
    
        solver = Solver()
        solver.add(constraints)
    
        if solver.check() == unsat:
            raise RuntimeError("Could not solve it :(")
    
        # create the incidence matrix
        model = solver.model()
        return [[model[elem].as_long() for elem in row] for row in rows]
    
    
    if __name__ == "__main__":
        import sys
        symbols_per_card = int(sys.argv[1])
        incidence_matrix = spotit_z3(symbols_per_card)
        for row in incidence_matrix:
            print(row)
    

    Results

    $python spotit_z3.py 3
    [0, 0, 1, 1, 0, 1, 0]
    [0, 0, 0, 0, 1, 1, 1]
    [0, 1, 0, 1, 0, 0, 1]
    [1, 1, 0, 0, 0, 1, 0]
    [0, 1, 1, 0, 1, 0, 0]
    [1, 0, 0, 1, 1, 0, 0]
    [1, 0, 1, 0, 0, 0, 1]
    python spotit_z3.py 3  1.12s user 0.06s system 96% cpu 1.225 total
    
    $ time python3 spotit_z3.py 4
    [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0]
    [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]
    [0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1]
    [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0]        
    [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1]
    [0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0]
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1]
    [0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0]
    [0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]
    [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0]
    [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
    [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]
    python spotit_z3.py 4  664.62s user 0.15s system 99% cpu 11:04.88 total
    
    $ time python3 spotit_z3.py 5
    [1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]
    [0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
    [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0]
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0]
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1]
    [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
    [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1]
    [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0]
    [0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]
    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1]
    [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0]
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0]
    [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1]
    [1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0]
    python spotit_z3.py 5  1162.72s user 20.34s system 99% cpu 19:43.39 total
    
    $ time python3 spotit_z3.py 8
    <I killed it after 14 hours of run time.>
    
  • 13

    我非常喜欢这个帖子 . 我使用此代码的一部分构建此github python项目,以将自定义卡片绘制为png(因此可以在互联网上订购自定义卡片游戏) .

    https://github.com/plagtag/ProjectiveGeometry-Game

  • 6

    我写了一个article关于如何使用Perl中的代码生成这种类型的套牌 . 代码没有优化,但它至少能够生成"reasonable"订单的甲板......还有更多 .

    这是一个8阶的例子,它必须考虑稍微复杂的基础数学,因为8虽然是生成这种甲板的有效顺序,但不是素数 . 请参阅上文或文章以获得更详细的解释,如果您只想生成稍微困难的Spot-It :-)

    $ time pg2 8
    elements in field: 8
      0. (1, 9, 17, 25, 33, 41, 49, 57, 65)
      1. (0, 9, 10, 11, 12, 13, 14, 15, 16)
      2. (2, 9, 18, 27, 36, 45, 54, 63, 72)
      3. (6, 9, 22, 26, 37, 43, 56, 60, 71)
      4. (7, 9, 23, 32, 34, 46, 52, 59, 69)
      5. (8, 9, 24, 30, 35, 42, 55, 61, 68)
      6. (3, 9, 19, 29, 39, 44, 50, 64, 70)
      7. (4, 9, 20, 31, 38, 48, 53, 58, 67)
      8. (5, 9, 21, 28, 40, 47, 51, 62, 66)
      9. (0, 1, 2, 3, 4, 5, 6, 7, 8)
     10. (1, 10, 18, 26, 34, 42, 50, 58, 66)
     11. (1, 14, 22, 30, 38, 46, 54, 62, 70)
     12. (1, 15, 23, 31, 39, 47, 55, 63, 71)
     13. (1, 16, 24, 32, 40, 48, 56, 64, 72)
     14. (1, 11, 19, 27, 35, 43, 51, 59, 67)
     15. (1, 12, 20, 28, 36, 44, 52, 60, 68)
     16. (1, 13, 21, 29, 37, 45, 53, 61, 69)
     17. (0, 17, 18, 19, 20, 21, 22, 23, 24)
     18. (2, 10, 17, 28, 35, 46, 53, 64, 71)
     19. (6, 14, 17, 29, 34, 48, 51, 63, 68)
     20. (7, 15, 17, 26, 40, 44, 54, 61, 67)
     21. (8, 16, 17, 27, 38, 47, 50, 60, 69)
     22. (3, 11, 17, 31, 37, 42, 52, 62, 72)
     23. (4, 12, 17, 30, 39, 45, 56, 59, 66)
     24. (5, 13, 17, 32, 36, 43, 55, 58, 70)
     25. (0, 49, 50, 51, 52, 53, 54, 55, 56)
     26. (3, 10, 20, 30, 40, 43, 49, 63, 69)
     27. (2, 14, 21, 32, 39, 42, 49, 60, 67)
     28. (8, 15, 18, 28, 37, 48, 49, 59, 70)
     29. (6, 16, 19, 31, 36, 46, 49, 61, 66)
     30. (5, 11, 23, 26, 38, 45, 49, 64, 68)
     31. (7, 12, 22, 29, 35, 47, 49, 58, 72)
     32. (4, 13, 24, 27, 34, 44, 49, 62, 71)
     33. (0, 57, 58, 59, 60, 61, 62, 63, 64)
     34. (4, 10, 19, 32, 37, 47, 54, 57, 68)
     35. (5, 14, 18, 31, 35, 44, 56, 57, 69)
     36. (2, 15, 24, 29, 38, 43, 52, 57, 66)
     37. (3, 16, 22, 28, 34, 45, 55, 57, 67)
     38. (7, 11, 21, 30, 36, 48, 50, 57, 71)
     39. (6, 12, 23, 27, 40, 42, 53, 57, 70)
     40. (8, 13, 20, 26, 39, 46, 51, 57, 72)
     41. (0, 65, 66, 67, 68, 69, 70, 71, 72)
     42. (5, 10, 22, 27, 39, 48, 52, 61, 65)
     43. (3, 14, 24, 26, 36, 47, 53, 59, 65)
     44. (6, 15, 20, 32, 35, 45, 50, 62, 65)
     45. (2, 16, 23, 30, 37, 44, 51, 58, 65)
     46. (4, 11, 18, 29, 40, 46, 55, 60, 65)
     47. (8, 12, 21, 31, 34, 43, 54, 64, 65)
     48. (7, 13, 19, 28, 38, 42, 56, 63, 65)
     49. (0, 25, 26, 27, 28, 29, 30, 31, 32)
     50. (6, 10, 21, 25, 38, 44, 55, 59, 72)
     51. (8, 14, 19, 25, 40, 45, 52, 58, 71)
     52. (4, 15, 22, 25, 36, 42, 51, 64, 69)
     53. (7, 16, 18, 25, 39, 43, 53, 62, 68)
     54. (2, 11, 20, 25, 34, 47, 56, 61, 70)
     55. (5, 12, 24, 25, 37, 46, 50, 63, 67)
     56. (3, 13, 23, 25, 35, 48, 54, 60, 66)
     57. (0, 33, 34, 35, 36, 37, 38, 39, 40)
     58. (7, 10, 24, 31, 33, 45, 51, 60, 70)
     59. (4, 14, 23, 28, 33, 43, 50, 61, 72)
     60. (3, 15, 21, 27, 33, 46, 56, 58, 68)
     61. (5, 16, 20, 29, 33, 42, 54, 59, 71)
     62. (8, 11, 22, 32, 33, 44, 53, 63, 66)
     63. (2, 12, 19, 26, 33, 48, 55, 62, 69)
     64. (6, 13, 18, 30, 33, 47, 52, 64, 67)
     65. (0, 41, 42, 43, 44, 45, 46, 47, 48)
     66. (8, 10, 23, 29, 36, 41, 56, 62, 67)
     67. (7, 14, 20, 27, 37, 41, 55, 64, 66)
     68. (5, 15, 19, 30, 34, 41, 53, 60, 72)
     69. (4, 16, 21, 26, 35, 41, 52, 63, 70)
     70. (6, 11, 24, 28, 39, 41, 54, 58, 69)
     71. (3, 12, 18, 32, 38, 41, 51, 61, 71)
     72. (2, 13, 22, 31, 40, 41, 50, 59, 68)
    errors in check: 0
    
    real    0m0.303s
    user    0m0.200s
    sys 0m0.016s
    

    072 中的每个标识符都可以作为卡标识符和图片标识符读取 . 例如,最后一行表示:

    • 72 包含图片 21322 ,..., 5968 ,AND

    • 图片 72 出现在卡 21322 ,..., 5968 中 .

相关问题