最小的棋盘压缩


39

编写可以对棋盘进行编码和解码的算法或程序。目标是制作棋盘的最小表示,可用于确定(一旦解码)棋手在该回合中的所有移动可能性。

编码必须能够显示:

  • 轮到谁了。
  • 玩家是否可以在两侧守城堡。
  • 玩家是否可以执行陪练,如果可以,则执行哪一个棋子?
  • 所有作品的位置。

关于cast皮的重要说明:如果白色将其国王移动一圈,然后再将其向后移动一次,则必须明确的是,此后他们无法在任一侧守城堡。如果他们移动左车子或右车子,情况​​也会一样。尽管棋盘在视觉上处于与两个回合之前相同的状态,但游戏状态已更改。此处提供更多信息:http : //en.wikipedia.org/wiki/Chess#Castling

有关通行证的重要说明:这也是一个转弯敏感动作。阅读规则以获取更多信息。http://en.wikipedia.org/wiki/Chess#En_passant

根据需要确定输入和输出。压缩力最大的道具!

您的分数取决于最坏的情况-最大可能的大小(以位为单位)。确保显示如何计算该数字以及所占金额。拍摄最小的最坏情况!


您所说的“按位”是什么意思?
彼得·泰勒

是最小的代码还是压缩最多的代码?最压缩的更有趣。
贾斯汀2014年

抱歉,无法澄清。按位表示,只有在开始将其表示为位时才可以压缩它,这将需要一些按位操作。我使用不当。也是压缩最多而不是最小的代码。
Seltzer

2
@GeekWithALife是的,有可能同时有18位皇后加入董事会。单击此链接,然后单击“播放”按钮作为示例。
吱吱作响的ossifrage,2014年

1
@AJMansfield,这可能对大约28个人的董事会有用,但我计算得出,对于所有32个人的董事会来说,117位就足够了,这比目标少了50位。复杂的是,一旦您的年龄降到32位以下,晋升就会给玩家带来更多主教。
彼得·泰勒

Answers:


27

最低:12位
最高:
平均:

昨晚曾想过我可以把它缩小。

x   Colour to play next (0 -> Black, 1-> White)
1   Only King left?

00000 Position of White King (0 -> A1 ... 63 -> H8)
00000 Position of Black King

01 00000 11111  WK:A1, BK:H2 (Black to play)
11 00000 11111  WK:A1, BK:H2 (White to play)

结果是令人印象深刻的12位大小!

那么K +1个其他类型的棋子呢?

x
 0
   0
     000  +Pawn
     001  +Rook   
     010  +Knight
     011  +Bishop
     100  +Queen

子树有2种可能的排列方式。

   /\      /\
  +  K    K  +

两者都导致所有片段的位大小相同。因此,我们使用它没有区别,我将选择第一个。

x
 0
  0
   000
      1011001110000000000000000000000000000000000000000000000000000000000000
(+ 000) En-Passant (if >= 2 pawn & pawn in en-passant positions)
(+ 00 ) Castlings  (if >= 1 rook)
Min: 75 bit
Max: 109 bits

所以在King +2个其他棋子类型上

x
 0
  1
   PRBNQ
   00011  +N +Q
   00101  +B +Q
   00110  +B +N
   01001  +R +Q
   01010  +R +N
   01100  +R +B
   10001  +P +Q
   10010  +P +N
   10100  +P +B
   11000  +P +R

有5个可能的子树(我将使用1和2来指示其中的哪一部分。)

   /\          /\       /\         /\          /\
  /  \        /  \     /  \       /  \        /  \
 K   /\      /\   2   /\   \     1   /\      /\   \
    1  2    K  1     K  2   1       K  2    1  2   K

因此,我们将需要3位来编码要使用的子树。

x
 0
  1
   PRBNQ
         000  Sub Tree used

Min:= 11 = Header 
       6 = 2 * 3
       4 = 1 * 4
       4 = 1 * 4
      60 = 60    Empty
      --
      85 bits

Max:=  11 = Header
        4 =  2 * 4 Kings
       48 = 16 * 3 Pawns
       12 =  4 * 3 Rook
       42 = 42 * 1 Empty
        3 =  1 * 3 En-Passant
        2 =  1 * 2 Castlings
      ---
      122 bits

仍在分析更多片段

+3其他

x
 0
  1
   PRBNQ
         0000  Sub Tree used (of 14 possible)

+4其他

x
 0
  1
   PRBNQ
         000000  Sub Tree used (of 42 possible)

+5其他

x
 0
  1
   PRBNQ
         0000000  Sub Tree used (of 132 possible)
 (+000)
 (+00)

最多:208?


是否可以将所有这些子树编码为9位?

如果我们将所有可能的子树加起来,我们将得到392个可能的子树。

 1  0
 2  2
 3  5
 4  14
 5  42
 6  132
    ---
    392  <= 2^9

使用频率ID

由于存在164603个唯一的乐曲频率

Log2( 164603) = 17.3286110452
             ~ 18 bits

0
 0000 0000 0000 0000 00  Freq ID

(+000)(+00)铸

最多:= 204位


版本3

最少:82最多:199平均:160

最后绕过一些分析以找到最大的位大小。对于每个独特的片段频率使用最佳霍夫曼编码。

               0   Player
              00  Castling
               0  En-Passant Possible
            ?000  En-Passant column (include if En-Passant Possible = 1
  0000 0000 0000  Tree Encoding ID
[Board Encoding]  Between 66 .. 180 bits 

请注意,这是最坏的可能的大小,如果典当的数量大于1,则“通过”列将使用该大小。无论这些棋子的颜色和位置如何,都有可能使某些木板缩小3位。

而且,只有144种不同尺寸(最坏的情况)的板子尺寸。


75-216位(v2) v1最小大小为98位(12.25字节),只有板上的两个国王。

最大大小仅为216位(27个字节)。

  9 x Queens
  1 x King
  2 x Rooks
  2 x Knights
  2 x Bishops
on each side.

平均而言,大小约为157位(19.625字节)。

为了对电路板进行编码,我使用了二进制树编码方案。空方块是出现次数在32到62之间的任何位置上最常见的。接下来是典当,然后是Rooks,Knights,Bishops,最不常见的是Queen和King。

0 - left node
1 - righ node

     /\
    e  \    e:= Empty Square
      B/\W  B:= Black ; W:= White
      /  \
     /    \
    /      \
   /\      /\
  p  \    p  \  p:= Pawn
     /\      /\
    /  \    /  \
   /\  /\  /\  /\
  r  b n \ r b n \  r:= Rook; b:= Bishop; n:= Knight
         /\      /\ 
        q  k    q  k  q:= Queen ; k:= King

起始板仅可编码166位(20.75字节)

  A     B     C      D      E     F     G     H
-----+-----+-----+------+------+-----+-----+------+
10100 10101 10110 101110 101111 10110 10101 10100 | 8 
  100   100   100    100    100   100   100   100 | 7
    0     0     0      0      0     0     0     0 | 6
    0     0     0      0      0     0     0     0 | 5
    0     0     0      0      0     0     0     0 | 4
    0     0     0      0      0     0     0     0 | 3
  110   110   110    110    110   110   110   110 | 2
11100 11101 11110 111110 111111 11110 11101 11100 | 1

仅需一点点即可表明谁在移动

0-> Black , 1-> White

Castling可以4位编码。

 B  W
LR LR
00 00

所以我用了171位(21.375字节)

En-Passe只能编码为16位(2字节)

所以总共就是187位(23.375字节)。

布局

  bits    Encodes
 0 -  15  En-Passe
16 -  19  Castling
      20  Move 
21 -  23  Unused
24 -> ..  Board

尚未编写任何代码。

请注意,有3位未使用。因此,最大为213bits


可能的改进

1)将标头块形式减少为24到8位(使用@Peter Taylor建议)

0 - 2 En-Passant
    3 Move
4 - 7 Castling
8 ... Board Pieces 

2)可变长度的标题

一个小的4位固定接头

0 0 0 0
| | | |
| | | +-> En-Passant Block Present?
| | | 
| | +---> Pawns on board?
| |
| +-----> Castling still possible?
|                
+-------> Who's move? 0-Black 
                      1-White

下一个附加位块(如果仍然可以铸造)

00 00
|| ||
|| |+-> White Castle Right
|| +--> White Castle Left
||
|+----> Black Castle Right
+-----> Black Castle Left

下一块附加位(如果有典当的话)

000--> En-Passant column Position

所以现在我有一个可变长度的报头4-11位


3)根据板上剩下的部分使用不同的编码方案。

通过改变木板上的编码,具体取决于板上的零件和频率。

最终游戏状态的一种可能编码(无卒)

        /\            
       e /\           
  Black /  \ White
       /    \
      /      \
     /        \       
    /\        /\
   /  \      /  \     
  /   /\    /   /\
 /\  / /\  /\  / /\   
r b n q k  r b n q k

大约每片约4位。

板上有哪种类型的棋子?

RBNQK Permutation
11111 (11111)

排列是可变长度0-5位。如果只剩下一种类型,则不要包括。

这些片段用于树的哪个排列?在上面的示例中,这是片段数的阶乘,为5片段,因此可以编码120个可能的排列。

 #    !  bit 
 6  720  10  (If pawn included)
 5  120   6
 4   24   5
 3    6   3
 2    2   1  Don't include as of equal size.
 1    1   0  Don't include as its not needed.

请记住,还有一些用于添加空正方形和彩色的位。


例子

让我们举一个仅剩QK的例子

RBNKQ
00011

  /\
 s  \
    /\
  B/  \W
  /\  /\
q  k q  k

101 100  0 x 60 110 111 ==> 60 + (2 x 6) = 60 + 12 = 72 bits for the board

0000 00011 Header ==> 9 bits

总共81位


让我们举个例子,只剩下国王

 RBNQK
 00001 

  /\
 s  k
   / \
  B   W

 10 0 0 0 0 0 0 0   K... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 0   .... ....
  0 0 0 0 0 0 0 11  .... ...k

放在一起

 header  4   0 0 0 0
 pieces  5   0 0 0 0 1
 perm    0   - - - - - -
  board 66   10 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 0
              0 0 0 0 0 0 0 11

因此,我以75位(9位3位)计算板的最小编码

尚未计算此编码方案如何影响最大大小。


改进4

将转换的位数减少到仅2位。只是为轮到他的玩家而奋斗。

 0 Castling possible (from header block)
 LR 
 00

考虑一下,最好将2位包含在标头块中。


您不需要16位来传递。最多只有一个棋子在最后一圈移动,因此四位就足够了(例如,1111对于“无可能”或以二进制数表示的列)。
彼得·泰勒

为什么典当在树上的位置更好?在通常情况下,它们是最常见的,但在极端情况下,R / B / N可能出现10次。
ugoren 2014年

@PeterTaylor,实际上3位就足够了。如果仅计算具有第5级黑色典当的列(假设白色移动),则8无效。
ugoren 2014年

1
请注意,实际上不可能发生最坏的情况。没有捕获就无法升级(每2次升级至少需要捕获一次)。
ugoren 2014年

2
请注意,要编码64个位置(对于白色或黑色国王),您需要6位(2 ** 6 = 64)。
lambruscoAcido14年

17

192位(最坏情况)

这是一个非常简单的存储方案,可以应对任意典当促销,并且永远不需要超过64 + 4×32 = 192位:

  • 前64位存储棋盘,告诉那里的作品是(但不是什么他们)。也就是说,我们为棋盘的每个正方形存储一位(从正方形a1开始,然后是b1,c1等,直到正方形h8),以使空闲的正方形由0表示,占用的正方形由1表示。

  • 接下来,对于在位板上标记为已占用的每个正方形,我们存储一个4位半字节对那个正方形上的片段进行编码。四个位中的第一位编码块的颜色(0 =白色,1 =黑色),其余三个位编码块的类型:

    +-----+-----+-----+-----+
    |Black|   Piece Type    |
    +-----+-----+-----+-----+
       4     3     2     1    Bits
    

    件类型

    0 =(正常)棋子
    1 =(正常)菜鸟
    2 =骑​​士
    3 =主教
    4 =女王
    5 =国王(下一位要移动的玩家)
    6 =国王(另一位玩家)

    请注意国王的两种编码,用于确定要移动哪个玩家的回合。(实际上,由于板上总是有两个国王,允许代码5和6的四种组合,因此我们可以在此处轻松地编码第二位信息。)

    Black Type Description
    +----+----+--------------------------------+
    |  0 | 5  | White King; White to move next |
    +----+----+--------------------------------+
    |  0 | 6  | White King                     |
    +----+----+--------------------------------+
    |  1 | 5  | Black King; Black to move next |
    +----+----+--------------------------------+
    |  1 | 6  | Black King                     |
    +----+----+--------------------------------+
    

    为了编码所需的额外信息顺便和castling规则,我们引入了一种附加的片段类型,根据其出现的行来表示典当还是白嘴鸦:

    7(在第1和8行上)=从未移动过且其国王也从未移动过的车队,因此有资格进行翻车
    7(在第4和5行上)=刚刚前进了两个方块的棋子;以及因此,可以捕获顺便

放在一起:

     Hex Description
    +---+---------------------------------------------+
    | 0 | White Pawn (normal)                         |
    | 1 | White Rook (has moved)                      |
    | 2 | White Knight                                |
    | 3 | White Bishop                                |
    | 4 | White Queen                                 |
    | 5 | White King; White to move next              |
    | 6 | White King                                  |
    | 7 | White Rook (pre castle) / Pawn (en Passant) |
    | 8 | Black Pawn (normal)                         |
    | 9 | Black Rook (has moved)                      |
    | A | Black Knight                                |
    | B | Black Bishop                                |
    | C | Black Queen                                 |
    | D | Black King; Black to move next              |
    | E | Black King                                  |
    | F | Black Rook (pre castle) / Pawn (en Passant) |
    +---+---------------------------------------------+

因此,对电路板状态进行编码所需的总位数为64 + 4×电路板上的块数。由于板上不能有超过32个片段,因此此编码的最大长度为192位。

例如,使用上述编码,电路板的初始状态将被编码为(为便于阅读而插入空白):

1111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111
0111 0010 0011 0100 0101 0011 0010 0111 0000 0000 0000 0000 0000 0000 0000 0000
1000 1000 1000 1000 1000 1000 1000 1000 1111 1010 1011 1100 1110 1011 1010 1111

或者,以十六进制表示:

FFFF 0000 0000 FFFF 7234 5327 0000 0000 8888 8888 FABC EBAF

2
“仅前进了两个正方形的棋子”和“从未移动过的钩子”可以共享相同的状态槽,因为它们根据它们所在的行是互斥的。额外的自由状态可用于编码“轮到的颜色之王”;您应该能够以这种方式摆脱悬挂的问题。
FireFly 2014年

您也可以通过只为位板存储63位,并从编码人员的数量推断出最后一位来节省一点。(我不清楚这是否作弊,因为它需要对位序列的长度进行外部编码)。而且,如果您放弃男人的状态6和7,则需要编码最多6 ^ 32,这需要82.7位。四舍五入到83,与使用状态6和​​7相比,它节省了13位,并且您可以仅将8位编码为相同的信息。
彼得·泰勒

谢谢@FireFly!我已经执行了您的建议。(当然,Peter Taylor的建议更为有效,但是到目前为止,我还没有使用它,因为我有点喜欢当前方案的简单二进制编码。您可以始终将其作为单独的条目提交...)
Ilmari Karonen 2014年

好的-这有点复杂。但是,请听我说。如果您只对转弯指示器施加1点命中,则可以用我称为pawn1的棋子代替转弯王。将pawn更改为pawn0。现在,只要有一个(并非易受攻击的)pawn,您就会在下一块获得一点信息(pawn0为0或pawn1为1)。因为有16个棋子,所以转向指示器的增益减少16位,即1。每当有容易受到传递的棋子时,您都必须在其后添加一点。但是由于必须立即发生,因此您的最小收益为14位。
user5957401

您也可以对主教之类的东西做类似的事情。假设您的主教不在其“特殊区域”(拐角处的10个斑点,侧面的中心排)标记为特殊。因为您知道它的位置-您知道它的主教。现在,您有两个主教,可以在下一张中给每个主教一个0或1。这又给了另外4位-但最坏的情况是在特殊区域中都有主教,却没有增益。
user5957401

14

160位最坏情况

在发布我之前的答案22个字节之后,我开始怀疑我们是否可以减少到21个字节。但是,当我看到Peter Taylor惊人的166个字节时,我以为“等等,看来可能有五个32位字!”

因此,经过一番思考,我想到了:159.91936391字节(非常紧密!)这种压缩级别需要一个相当复杂的程序,但是我已经考虑过如何使其在合理的时间内运行。

这将是一篇很长的文章,所以请耐心等待,我将尽我所能,并在不久后添加一些代码。

因此,这是操作方法:

通过无效位置对En Passant和Castling编码(0位)

恩人

如其他答案中所述,最多有5个可能易被传递的棋子站立的正方形。这些是轮到该玩家的棋手的棋子旁边的方块。

为了对此进行编码,将易受攻击的棋子与第一行或最后一行的正方形中的任何一个交换。这可能是一个人或一个空的正方形。由于典当不能在这些行上,因此会产生非法位置。解码器必须将典当返回其正确位置,以提取相关信息。

为了避免干扰小灵通的编码,重要的是不要扰乱游戏开始时国王所站在的方块,并且整个编码过程不要使国王彼此相邻,这将是非法的国王职位。为了满足这些要点中的第二个要求,编码器有两个选择,即可以交换哪个棋子。对于最多5个典当中的每个,首选方是A8,B8,C8,G8,H8。第二选择:A1,B1,C1,G1,H1。

卡斯特

根据定义,允许城堡的国王仍在他的最初广场上。白国王在他的第一个方格上,总共可以有63个方格,黑国王可以站在其中,其中58个是合法的(不允许他在白方旁边移动,因为他会检查自己。)如果允许白国王出入城堡,则可以允许他用左车子,右车子或两者一起进城。因此,有3x58 = 174种可能性,白国王可以城堡,另外174种黑人国王可以城堡,还有3x3 = 9两者都可以城堡,总共357种。

这两个国王在相邻的正方形上有420个非法排列:白国王在拐角处时为3x4 = 12,在边缘时为5x24 = 120,而在另一个正方形时为8x36 = 288。因此,容易有足够的非法位置来编码所有可能的铸造可能性。

如果允许至少一位国王出入城堡,编码器将在表格中查找那些不允许出入城堡的国王的铸造数据和位置数据(或者使用在此未指定的算法),并产生非法数据两位国王的位置。然后它将与这些广场上发生的一切交换国王。

重要的是,在通行代码之后对其进行编码和解码,否则会产生一些潜在的干扰。

比较方式

因此,到目前为止,我还没有使用任何工具!查看Peter的回答,我仍然需要进行以下编码:

Whose turn is it?                                   1.000 bits
Which squares are occupied by men of which colour? 91.552 bits 
Subtotal                                          *92.552 bits* 
For the two colours, which men and which order?   *68.613 bits* 
GRAND TOTAL                                       161.165 bits

这是针对29位男性的最坏情况(请参见Peter的答案。)下面,我将说明在**标记的两个点上我如何进行边际改进(至少对于29位男性而言)。

哪个方格被占用/轮到谁了?

编码占据哪个正方形的简单方法是使用64位网格。这也告诉我们占据了多少个正方形。但是,这有点浪费,因为不可能占用超过32个正方形。我的解决方案是在轮到White时使用1来编码占用的正方形,在轮到Black时使用0来编码占用的正方形。现在使用了所有组合,没有浪费。

因此,我们节省了一些存储转弯的时间:小于32 1的转弯是白色的转弯,大于32 1的转弯是黑色的转弯。唯一模棱两可的情况是,当所有人员都在棋盘上时,有32个1和32 0个。因此,仅在这种情况下需要额外的位。由于在抓捕之前没有晋升机会,因此这笔额外的费用不会影响整体上最坏的情况(抓捕3个人,还剩下29个人)。

占领广场的人的肤色

从上面我们知道有多少人。以下Pascal三角形的摘录告诉我们,黑色和白色的不同分布有多少种可能性。例如,对于3个男人,可能性是:3个黑人(1个排列)2个黑人,1个白色(3个排列),1个黑人,2个白色(3个排列),3个白色(1个排列)。总数为2 3 = 8。通常,对于人数较少的男人,有2 n种可能性。但是,全黑和全白的可能性是非法的(至少每边的国王都必须在棋盘上),因此合法排列的实际数量为2 n -2(忽略Pascals三角形上的1)。

对于总共超过16位的人员,还有一个额外的限制,即董事会上每种颜色的人员不得超过16位。因此,当所有32个人都在棋盘上时,每个人必须有16个人,总数为601080390,这比2 32要少得多。

1   1    1    1      1     1      1       1       1        1        1         1         1         1          1          1          1 
1   2    3    4     5      6      7       8       9       10       11        12        13        14         15         16         17
1   3    6   10    15     21     28      36      45       55       66        78        91       105        120        136        153
1   4   10   20    35     56     84     120     165      220      286       364       455       560        680        816        969
1   5   15   35    70    126    210     330     495      715     1001      1365      1820      2380       3060       3876       4845
1   6   21   56   126    252    462     792    1287     2002     3003      4368      6188      8568      11628      15504      20349
1   7   28   84   210    462    924    1716    3003     5005     8008     12376     18564     27132      38760      54264      74613
1   8   36  120   330    792   1716    3432    6435    11440    19448     31824     50388     77520     116280     170544     245157
1   9   45  165   495   1287   3003    6435   12870    24310    43758     75582    125970    203490     319770     490314     735471
1  10   55  220   715   2002   5005   11440   24310    48620    92378    167960    293930    497420     817190    1307504    2042975
1  11   66  286  1001   3003   8008   19448   43758    92378   184756    352716    646646   1144066    1961256    3268760    5311735
1  12   78  364  1365   4368  12376   31824   75582   167960   352716    705432   1352078   2496144    4457400    7726160   13037895
1  13   91  455  1820   6188  18564   50388  125970   293930   646646   1352078   2704156   5200300    9657700   17383860   30421755
1  14  105  560  2380   8568  27132   77520  203490   497420  1144066   2496144   5200300  10400600   20058300   37442160   67863915
1  15  120  680  3060  11628  38760  116280  319770   817190  1961256   4457400   9657700  20058300   40116600   77558760  145422675
1  16  136  816  3876  15504  54264  170544  490314  1307504  3268760   7726160  17383860  37442160   77558760  155117520  300540195
1  17  153  969  4845  20349  74613  245157  735471  2042975  5311735  13037895  30421755  67863915  145422675  300540195  601080390

通过将帕斯卡三角形的提取物的“行”相加,可以找到可能的数目(我指的是表格的NE-SW对角线,因为我将三角形逆时针旋转了45度以便于演示。因此,编码人的转弯,占据的正方形和颜色的方法如下:

最多25位男性:略少于64岁以上(男性人数)
超过25位男性:

men permutations  bits required  occupied sq+turn   
    of colours                   (bits required)  total bits
26   55791790     25.7335495      64              89.7335495
27  100960110     26.58921015     64              90.58921015
28  175844430     27.3897244      64              91.3897244
29  290845350     28.115677       64              92.115677   
30  445962870     28.73234836     64              92.73234836
31  601080390     29.16298271     64              93.16298271
32  601080390     29.16298271     65              94.16298271

对于这两种颜色,哪个人以哪个顺序?

根据先前的答案,只有当捕获被捕获后才能提升典当,因为每个典当都被同一列上相反颜色的典当所阻塞。彼得的回答认为(作为上限)每次捕获都可能导致被捕获方的一次晋升,而导致捕获边的两次晋升。但是,我们可以将其分为几种情况:

  1. 黑色典当捕获白色典当:现在,捕获典当可以自由升级,因为他现在在其他列上。他在同一专栏上的同事也可以晋升。白色棋子原始列上的黑色棋子也可以升级。这是唯一允许3次促销的情况。

  2. 黑色棋子经过相邻列上的白色棋子,然后捕获后面的白色棋子(棋子除外)。这使捕获的典当和原始列上的白色典当得以提升。每边一次晋升。

  3. 白色典当是按片段捕获的(典当除外)。这通常只允许黑色升级一次。唯一的例外是,它释放了已经被几次捕获将典当移动到同一列上而导致的阻塞的典当编队。

因此,作为一个基本案例,我们可以考虑每次捕获都允许双方晋升一次。如果被捕的人是典当,则对被捕方可能会有额外的提升。

我写了一个类似于彼得的程序。它有些粗糙,但是有一个重要的补充:它可以计算玩家开始时少于正常的8个棋子时可能的排列数量。这是程序产生的一些数据:

Max promotions   0            1            2             3             4              5 
8 PAWNS 
13 men    18725850    146911050    567991710    1373480394    2297173164     2902775304
14 men    36756720    339459120   1555313760    4501448952    9021804792    13325103792
15 men    60810750    660810150   3555401850   12144582450   28834205400    50030580600
16 men    64864800    843242400   5383778400   21810428640   61514893440    1.26476E+11
7 PAWNS                         
13 men    17760600    141003720    546949260    1321302840    2200401060     2761730400
14 men    30270240    287567280   1331890560    3852728880    7641553920    11068817760
15 men    32432400    372972600   2075673600    7209001800   17135118000    29315286000
6PAWNS                          
13 men    14054040    114594480    447026580    1069488420    1739577840     2113185360
14 men    15135120    151351200    718918200    2087805720    4073028960     5697051360                         
5 PAWNS                         
13 men     6486480     55135080    217297080     510630120     794233440      910235040

我们可以看到,对于8个典当,15个人,0个促销的情况,排列的数量仅略少于8个典当,16个人,0个促销的情况。但是,如果我们考虑一种情况,例如7个棋子,15个男人,0个晋升(这与考虑到被捕的男人肯定是一个棋子一样),那么我们得到的排列数大约是一半。

因此,对于黑人有16个人而白人有15个人的情况,我们可以考虑黑人有2个晋升,白人有1个晋升的上限估计:

5383778400 x 660810150 = 3.55766E+18 possibilities

但是,如果执行以下步骤,我们会做得更好。

A.假设白人失去的男人可以是任何一种,都可以考虑给黑人和白人各升职:

843242400 x 660810150 = 5.57223E+17 possibilities

B.如果黑人有两次晋升,请考虑他有两次晋升的可能性,再乘以他失去当兵的白人的可能性。

(5383778400-843242400) x 372972600 = 1.6935 E+18 possibilities.

将这两个加在一起,我们得到2.25072E + 18,这比3.55766E + 18小。下面列出了最多可进行3次抓捕(剩余29名男子)的所有可能性。

(Promotions, Pawns lost) possibilities

BLACK 16 MEN, WHITE 15 MEN. ESTIMATE   3.55766E+18 = 2^61.62563249
(1,0)   843242400 x (1,0)  660810150 = 5.57223E+17
(2,0)  4540536000 x (1,1)  372972600 = 1.6935 E+18
                               TOTAL   2.25072E+18 = 2^60.96509144


BLACK 16 MEN, WHITE 14 MEN. ESTIMATE   9.5675 E+19 = 2^66.3747752
(2,0)  5383778400 x (2,0) 1555313760 = 8.37346E+18
(3,0) 16426650240 x (2,1) 1331890560 = 2.18785E+19
(4,0) 39704464800 x (2,2)  718918200 = 2.85443E+19
                               TOTAL   5.87962E+19 = 2^65.67235739


BLACK 16 MEN, WHITE 13 MEN. ESTIMATE   2.69447E+20 = 2^67.86856193
(3,0) 21810428640 x (3,0) 1373480394 = 2.99562E+19
(4,0) 39704464800 x (3,1) 1321302840 = 5.24616E+19
(5,0) 64960896000 x (3,2) 1069488420 = 6.94749E+19
(6,0) 69702272640 x (3,3)  510630120 = 3.55921E+19
                               TOTAL   1.87485E+20 = 2^67.34533572


BLACK 15 MEN, WHITE 15 MEN. ESTIMATE   1.47491E+20 = 2^66.99918768
(2,0)  3555401850 x (2,0) 3555401850 = 1.26409E+19
(2,1)  2075673600 x (3,0) 8589180600 = 1.78283E+19
(3,0)  8589180600 x (2,1) 2075673600 = 1.78283E+19
(3,1)  5133328200 x (3,1) 5133328200 = 2.63511E+19
                  TOTAL BOTH COLUMNS   7.46486E+19 = 2^66.01674923


BLACK 15 MEN, WHITE 14 MEN. ESTIMATE   4.51366E+20 = 2^68.61286007      
(3,0) 12144582450 x (3,0) 4501448952 = 5.46682E+19
(3,1)  7209001800 x (4,0) 4520355840 = 3.25873E+19
(4,0) 16689622950 x (3,1) 3852728880 = 6.43006E+19
(4,1)  9926116200 x (4,1) 3788825040 = 3.76083E+19
(5,0) 21196375200 x (3,2) 2087805720 = 4.42539E+19
(5,1) 12180168000 x (4,2) 1985223240 = 2.41804E+19
                  TOTAL BOTH COLUMNS   2.57599E+20 = 2^67.80368692

因此,对于最坏的情况,一侧有15个人,而另一侧有14个人,我们需要67.804位。

将其加到指定哪个正方形和哪种颜色所需的92.116位,我们总共得到67.804 + 92.116 = 159.92位。


1
非常感谢@Einacio将我的十进制逗号更改为小数点。我在西班牙计算机上的Excel上处理了很多表格,并且发布这是一项艰巨的工作,因此修复此问题是我以后要去做的。正如我所说,我还没有完成这篇文章,我将在有空的时候添加我的排列计数程序和一些有关编码/解码的代码片段。PS。我不知道有这么多人正在读:-)
Level River St

最后,您设法达到了字节而不是比特的含义,这可能会给读者造成混乱
masterX244

13

177位最坏情况

这种算法虽然并不简单,但是却给出了177位最坏情况(实际上是184b = 23B),13b(16b = 2B)最坏情况(仅剩2个国王)的情况。

Bit     Description
  1     Turn (0=white 1=black)
  2-  7 White king position (2-4=letter, 5-7=number)
  8- 13 Black king position (8-10=letter, 11-13=number)
 14- 75 Which squares contain pieces (skipping the 2 king squares, so only 62)
        Ordered a1-h1,a2-h2,(...)
 76-105 Which color owns the square with their piece (0=white, 1=black)
        If there's LESS than 30 pieces (apart from kings), this area is
        smaller
106-end Square data

Square data has the following system:
Every square gets assigned a number which determines piece. Number is:
0 Queen
1 Rook
2 Bishop
3 Knight
4 Pawn OR allowed-castle rook depending on square
5 Pawn subject to potential enpassant

The first bits (max 13) is the potential enpassant slots from A-H, determined
from data of 1 + 14-105 for which of the squares has a piece, and which color
owns the piece and whose turn it is. For example, if turn is White (bit 1 is
0), all pieces on row 5 which is Black owned (determined from 14-105 metadata)
and has at least 1 adjacant (on the same row) square owned by White, is
explained in A-H order. A base 6 number is used which is converted to binary
for the storage. On reading, it's converted and read A-H according to the
numbers above (4 is obviously pawn in this case).
The second amount of bits takes care of the 1st and 8th row (not corners!)
in b1-g1,b8-g8. These only take up 2 bits since 4 or 5 is never needed
(pawn on 1st or 8th is invalid).
The third amount of bits takes care of the rest of the board, in the following
order: a1,h1,a2-h2,a3-h3,a4-h4,a5-h5,a6-h6,a7-h7,a8,h8 (skipping the
"enpassant" slots), in base 5 (since piece ID 0-4 are the only used) converted
to binary.

Best case: 13 bits (bit 1 for turn, bit 2-12 for kings)
Worst case: 177 bits
* 32 pieces with kings
* 5 viable enpassant pawns
* No pieces at 1st or 8th row (except if kings+rooks are at initial posions
whether or not they can castle)
In this case, the space as following:
  1   bit   turn
+ 12  bits  king positions
+ 62  bits  which squares have pieces
+ 30  bits  color of pieces
+ 13  bits  enpassant area
+ 0   bits  initial rows area
+ 59  bits  the rest of the area
= 177 bits  total

Potential optimizations but not really worth it IMO:
* Decrease average by make corners 2 bits as well if kings aren't at e1/e8
* Alter reading order to read b1-g1,b8-g8 last - decreases worst case to
  176 bits if the "which squares have pieces" area is cut off if 30 existing
  pieces has been defined already. Would actually save 8 bits on file but meh

非常好。通过使用基于多项式系数的编码替换位14-105(92位),可以使效率更高。sum_{i=0}^{15} sum_{j=0}^{15} 62! / (i! j! (62-i-j)!) < 2^87.45
彼得·泰勒

我唯一要更改的是为passpass区域创建一个相当简化的版本。例如:如果您以30为基数对30个片段进行编码,并且最多有5个伴随位置,那么您可以使5 ^ 31 <2 ^ 72。就像您将它们拆分为inpassant(13)和non-enpassant(59)一样,但是没有额外的复杂性。
Alin Stoian 2014年

这样做实际上会多使用1位。原因是可能(最坏的情况)有5种通过的可能性,但是我仍然需要声明“无通过”的可能性,即第六种状态。在这种情况下,额外的1位将用于声明是否可以通过(通过此方法,我还可以使用一种更简单的方法,对跳过了该块的30个编码进行编码,并分别使用3位进行检查,这将也导致+1位使用)。接下来的第5行将启用5个潜在的通过者(怀特的回合):BWBBWBBW
FIQ

你是对的。
Alin Stoian 2014年

7

166位

  • 1 位:轮到谁了?
  • 2位:哪些小选项已打开?(注意:仔细阅读问题后,只需要记录轮到该牌手的玩家的掷骰选项即可)。
  • lg 6 ~= 2.585位:这顺便选项是开放的?(请参阅我的其他答案)
  • lg sum_{i=1}^{16} sum_{j=1}^{16} 64! / (i! j! (64-i-j)! = lg 3629590441720924477681996172 ~= 91.552 位:哪个颜色的男人占据哪个正方形?
  • 在最差的lg 451366131803622235200 ~= 68.613位上指示出哪个人和哪个顺序(请参阅下文)

使用算术编码(由于每一步我们都应用均匀分布),我们可以实现 ceil(3 + 2.585 + 91.552 + 68.613) = 166位。

男装的编码:假设我们知道有多少个给定颜色的男装,我们可以轻松地枚举男装的所有可能分布/多集(例如,如果有5名男装,我们可能会有一位国王,一位女王,两位白嘴鸦和一名Pawn),然后我们可以考虑每种分布的所有可能排列。

但是,考虑到相互依赖性,我们可以做得更好。我只是在非常基本的水平上进行此操作:可能进行多少次促销?典当只能通过三种方式“通过”并能够提升:它捕获并移动到另一个列中;或其相对的典当捕获物,因此移至另一列;或捕获其相对的棋子。因此,捕获白色可能会为白色创建两个传递的棋子,为黑色创建一个传递的棋子。

我们可以建立促销上限的部分表:

(Max white promos, max black promos):

           White men
           16      15      14      13
Black men
       16  (0, 0)  (1, 2)  (2, 4)  (3, 6)
       15  (2, 1)  (3, 3)  (4, 5)  (5, 7)
       14  (4, 2)  (5, 4)  (6, 6)  (7, 8)
       13  (6, 3)  (7, 5)  (8, 7)  (8, 8)

我们还可以计算给定一个玩家有N男人并且只有P提升的棋子的排列数量:

Num of permutations (cumulative):
    max promotions: 0              1              2              3              4              5              6              7              8
 1 men              1              1              1              1              1              1              1              1              1
 2 men             10             10             10             10             10             10             10             10             10
 3 men             72             75             75             75             75             75             75             75             75
 4 men            436            496            500            500            500            500            500            500            500
 5 men           2305           3025           3120           3125           3125           3125           3125           3125           3125
 6 men          10746          17106          18606          18744          18750          18750          18750          18750          18750
 7 men          44170          88795         106260         109179         109368         109375         109375         109375         109375
 8 men         159832         415360         575240         619200         624744         624992         625000         625000         625000
 9 men         509841        1721961        2884815        3398769        3504735        3515301        3515616        3515625        3515625
10 men        1447200        6258240       13063080       17697780       19260180       19510320       19530840       19531230       19531240
11 men        3706065       20021265       52183395       85007571      102173181      106786581      107369592      107409918      107410281
12 men        8678340       57101220      183088620      364510476      509818716      570620556      584017632      585352152      585430164
13 men       18725850      146911050      567991710     1373480394     2297173164     2902775304     3107861328     3143928216     3146014014
14 men       36756720      339459120     1555313760     4501448952     9021804792    13325103792    15664512864    16283899632    16360920576
15 men       60810750      660810150     3555401850    12144582450    28834205400    50030580600    66655789200    73588394880    74576231730
16 men       64864800      843242400     5383778400    21810428640    61514893440   126475789440   196178062080   240747386880   253686232800

结合两者,我们可以根据两边的人数得出指定两个排列所需的位数:

           White men
           16      15      14      13      <13
Black men
       16  51.902  61.626  66.375  67.868  <=67.009
       15  --      67.000  68.613  67.534  <=65.243
       14  --      --      67.734  65.480  <=63.055
       13  --      --      --      63.102  <=60.676

如果不在表的此部分中,我们可以假设双方最多进行8次晋升,并且我们的表现仍然比最差的情况要好,当一个人有14个人而另一人有15个人时,则是68.613位。

请注意,这距离成为一个完美的表示还有很长的路要走,因为它允许许多非法职位。

用于计算置换表的代码:

import java.util.*;

public class ChessCombinatorics {
    public static void main(String[] args) {
        long[] f = new long[17];
        f[0] = 1;
        for (int i = 1; i < 17; i++) f[i] = i * f[i-1];

        // Indexed by num promotions, then total num men.
        long[][] distribs = new long[9][17];
        long[][] perms = new long[9][17];

        for (int promotedPawns = 0; promotedPawns < 9; promotedPawns++) {
            Map<Integer, Map<String, Long>> numCases = new HashMap<Integer, Map<String, Long>>();
            for (int i = 1; i < 17; i++) numCases.put(i, new HashMap<String, Long>());

            for (int extraQ = 0; extraQ <= promotedPawns; extraQ++) {
                for (int extraR = 0; extraR + extraQ <= promotedPawns; extraR++) {
                    for (int extraN = 0; extraN + extraR + extraQ <= promotedPawns; extraN++) {
                        int extraB = promotedPawns - extraN - extraR - extraQ;
                        int unpromotedPawns = 8 - promotedPawns;

                        // Promoted pawns should only count towards their new type if the existing ones are alive.
                        // Otherwise we double-count some cases.
                        int minQ, maxQ, minR, maxR, minN, maxN, minB, maxB;
                        if (extraQ == 0) {minQ = 0; maxQ = 1;} else {minQ = maxQ = 1 + extraQ;}
                        if (extraR == 0) {minR = 0; maxR = 2;} else {minR = maxR = 2 + extraR;}
                        if (extraN == 0) {minN = 0; maxN = 2;} else {minN = maxN = 2 + extraN;}
                        if (extraB == 0) {minB = 0; maxB = 2;} else {minB = maxB = 2 + extraB;}

                        for (int numQ = minQ; numQ <= maxQ; numQ++) {
                            for (int numR = minR; numR <= maxR; numR++) {
                                for (int numN = minN; numN <= maxN; numN++) {
                                    for (int numB = minB; numB <= maxB; numB++) {
                                        for (int numP = 0; numP <= unpromotedPawns; numP++) {
                                            // The number of possibilities at these values is (numK + numQ + numR + numN + numB + numP)! / (numK! numQ! numR! numN! numB! numP!)
                                            numCases.get(1+numQ+numR+numN+numB+numP).put(numQ+","+numR+","+numN+","+numB+","+numP, f[1 + numQ + numR + numN + numB + numP] / f[numQ] / f[numR] / f[numN] / f[numB] / f[numP]);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            for (int numMen = 1; numMen < 17; numMen++) {
                distribs[promotedPawns][numMen] = numCases.get(numMen).size();
                if (distribs[promotedPawns][numMen] > 0) {
                    for (Long l : numCases.get(numMen).values()) perms[promotedPawns][numMen] += l;
                }
            }
        }

        System.out.println("Num of permutations (cumulative):");
        System.out.println("    max promotions: 0              1              2              3              4              5              6              7              8");
        for (int numMen = 1; numMen < 17; numMen++) {
            System.out.print(String.format("%2d men", numMen));
            long cumul = 0;
            for (int promotedPawns = 0; promotedPawns < 9; promotedPawns++) {
                cumul += perms[promotedPawns][numMen];
                System.out.print(String.format("%15d", cumul));
            }
            System.out.println();
        }

        System.out.println("Entropy of permutations:");
        System.out.println("    max promotions: 0              1              2              3              4              5              6              7              8");
        for (int numMen = 1; numMen < 17; numMen++) {
            System.out.print(String.format("%2d men", numMen));
            long cumul = 0;
            for (int promotedPawns = 0; promotedPawns < 9; promotedPawns++) {
                cumul += perms[promotedPawns][numMen];
                System.out.print(String.format("  %6.3f", Math.log(cumul) / Math.log(2)));
            }
            System.out.println();
        }

    }
}

您如何推断国王的位置?您使用15个人进行计算,国王职位没有特殊的位。
Alin Stoian 2014年

@AlinStoian,哎呀。我在程序的输出循环中有一个<而不是一个<=。感谢您指出。我仍然可以通过对所有32位男士进行特殊包装来恢复以前的成绩,但是我现在不会这样做。
彼得·泰勒

有趣的数据!理论上最糟糕的情况,有3人被捕
级圣约翰河,

@steveverrill,我真正想做的是将典当位置和促销数量编码在一个“块”中,然后编码件的位置和值。但是,至少有2 ^ 38个棋子位置没有考虑晋升,到目前为止,有效地枚举它们已经使我逃脱了。
彼得·泰勒

@petertaylor如果板上只有16个棋子(限于48个正方形),则有48!/ 32!/ 8!/ 8!= 29019905518636890个可能性。略大于2 ^ 54!其中,有些是非法的,您不能将所有一种颜色的棋子都放在木板的一侧。
级圣河

5

178位(捏174!)最坏的情况

嗨,刚回到编码,那是我自大学以来从未真正做过的事情。我看到了这个网站,并认为这看起来很有趣。我做了一点理论验证,结果看来,完美算法至少需要146位,可能还要更多(我会在稍后的评论中解释)。

无论如何,这就是我构造数据的方式。基本概念以178位输入,但是如果带有一些锯齿扑克,则可以降低到174位(即21 3/4字节)。175稍微易于编程,更易于阅读,并且仍在22字节以内。

A)两位国王的位置:白色和黑色12位各6位

B)在剩下的62个方格中,哪些被占用?62位BITS矩阵

C)轮到谁了?1位

总计:75位

D)En Passant。如果轮到白色了,最多可以看到5个黑色的棋子被En Passant捕获。黑色的棋子必须位于第5行(从零开始,从下到上),并且旁边有白色的棋子。一种可能的捕获数量最多的情况如下所示:

BWBBWBBW

如果第5行上有6个黑棋子,那么白色棋子只能站立2个方格,并且只能威胁4个黑棋子,因此显然不可能同时有5个以上的黑棋子受到En Passant的威胁。因此,我们需要一个1到5的数字,以指示第5行的哪个棋子(最多5个)中有敌对棋子(在这种情况下为白色棋子)的棋子在最后一圈前进了2个正方形(如果没有,则为零)在这种情况下,是在最后一圈以这种方式移动的。)

E)在最多30个被占领的正方形(不包括国王)中,它们包含什么?

有10种可能性,每种可能性都用一个十进制数字表示。

最低有效位代表颜色。

因此,偶数是白色,奇数是黑色。

白/黑

兵0/1(或允许城堡的鲁克*)

骑士2/3

主教4/5

白嘴鸦6/7

女王8/9

*允许进入城堡的车队(因此从未从第一行或最后一行移出)由0或1而不是6或7表示。它不能与典当混淆,因为无法在第一个典当上找到典当或最后一行。

这给出了一个最多30位的十进制数字,我们可以将其乘以6,然后添加En passant的代码。结果数将适合103位,当加到上述75位时为103 + 75 = 178位。实际上,如果我们简单地乘以10而不是6,就不会对所使用的位数产生影响,并且解码会更容易。

这仅比22个字节多2位。但是,我们可以将其降低到174位,如下所述。

如果没有捕获到一块,那么就不可能提升典当

证明如下。想象一下,怀特从游戏一开始就迷恋在(例如)E栏推广他的棋子。在该棋子对面有一个黑色棋子。因此,要推广此典当,必须进行以下操作之一:

1)黑色典当被捕获。

2)黑色典当捕获了另一块,因此移开了。

3)白色棋子在相邻列(例如D列)上捕获了一个棋子。

4)白色棋子在相邻列上经过(或经过)黑色棋子,然后在同一相邻列上捕获一块,导致白色棋子改变了列。

案例4是最有趣的,因为不仅仅是从E列开始的白色典当现在有了明确的晋升途径。保留在E列上的列上的黑色棋子也可以升级。因此,一次捕获可以清除每种颜色的一个典当被提升的方式。

无论如何,只有在捕获到一块棋子后才能升职,这意味着我们不必存储第30条棋子。我们可以通过消除(或通过减法来计算),因为在游戏开始时完整的零件代号总和等于80。在游戏开始时的立场是首先扫描的对象(因为如果它们是最后一个,我们将不知道该车是否可以装城堡。)这很容易通过扫描第0行然后扫描第7至1行来完成:r = 8比1扫描行[r mod 8]。

因此,(B)中的位矩阵将告诉我们有多少个片段(不包括国王)。如果有完整的30个片段,则在编码时忽略最后一个片段,解码器将计算出原来的片段。现在,我们有一个最多29位的十进制数字,我们将其乘以6并添加到En Passant代码中。结果数将仅压缩为99位,总共为99 + 75 = 174位。

例如,这是实际位置。怀特刚刚迈出了第一步(高级国王的棋子),现在轮到布莱克了。

rnbqkbnr
pppppppp


    P

PPPP PPP
RNBQKBNR

A)国王的位置(白/黑八进制,12位):03 73 = 000011 111011

B)占据哪些方块?从第零行(底部行)开始,然后从上至下所有其他行,跳过国王:

1111 111

1111 111
11111111
00000000
00000000
00001000
00000000
11110111 

C)黑手:转位= 1

D)En Passant。黑色棋子旁边没有白色棋子,因此没有可以作为副棋的棋子(即使该棋子前进了最后一步),所以D = 0。如果我们不考虑仅在它们旁边有敌对兵的典当,而是考虑所有在两侧都没有友善棋子的兵,那么D将为1,因为在这种情况下有一个这样的兵典当确实在最后一轮被移动了。

E)同样,首先排在最下面,然后从上到下排在所有其他行,跳过国王,将四个未浇筑的车队称为0或1(通常用于典当的编号)。

RNBQ BNR =   0248 420
rnbq bnr =   1359 531
pppppppp =   11111111
PPPPPPPP = (0)0000000

可以丢弃第30个数字(在方括号中)。

尽管在这里不是很明显,但是White推进的棋子实际上位于棋子列表的一端,因为我们逐行扫描。

现在,我们的数据如下所示,其中包含29个用于表示正方形内容的代码以及En Passant代码:

 (0 discarded) 0000000 11111111 1359531 0248420 (0 en passant)

解码时最好从右向左扫描,编码时最好从左向右(反向)扫描。这意味着,当块数较少时,我们将保留较小的数目,同时保持最大的一致性(即,我们希望空格/零在前而不是在后,以压缩稀疏的木板。)当我们只有两个国王时在板上,我们将拥有上述75位,外加3位用于存储无用数据=最佳情况下为78位。每增加一块,其大小略低于3.5位(由于100 <128,所以2位可以7位存储。)

一个实际的问题是99位整数太大而无法容纳64位整数变量,这意味着许多编程语言不提供对此的支持(您不能简单地转换29-30位数字的字符串表示形式号转换成一个整数。)作为22个字节编码的一种简单的方法,我们可以打破30位数字(29个码+顺便代码)分为两个15位数字的每一个将适合在50个比特的每个(总共100个比特加上上面提到的75个,最坏的情况就是175位。)

对于最大压缩,如上所述,29个十进制数字加上En Passant码(6个可能的值)将恰好适合99位(总共174位),但是不支持该大小的整数的语言程序复杂。分离出29个颜色位并分别使用片段类型代码(5种可能性)和En Passant代码(6种可能性)与颜色(70位,几乎适合64位变量)比较容易。


和最后一个男人好戏。
彼得·泰勒

5

这是一个完整的解决方案,实际最坏的情况下是181位

这里的重点是一个您可以轻松理解的简单程序

输入是FEN,这里是开仓位置,它有六个字段(忽略5和6):

rnbqkbnr / pppppppp / 8/8/8/8 / PPPPPPPP / RNBQKBNR w KQkq-0 1

解析第一个字段(片段放置)

perl -pe 's/\d/"_"x$&/ge;s/\s.*//;s|/||g'

生产:

rnbqkbnrpppppppp________________________________PPPPPPPPRNBQKBNR

字段一:编码国王的位置(12位):

printf("%b",index('k',$_))
printf("%b",index('K',$_))

栏位2:编码片段(每片段最多5位):

s/_/0/g     Blank
s/P/100/g   From here, as normal chess meaning
s/p/101/g
s/Q/11000/g
s/q/11001/g
s/R/11010/g
s/r/11011/g
s/B/11100/g
s/b/11101/g
s/N/11110/g
s/n/11111/g
s/K//
s/k//

字段三:活动颜色(1位)

s/w/0/
s/b/1/

字段4:可播种可用性(4位)

m/K/?1:0
m/k/?1:0
m/Q/?1:0
m/q/?1:0

字段五:传递(零或3位)

printf("%b",ord($1)-ord("a")) unless m/-/
// The EP's rank is 3 or 6 based on active color, only need to encode file

天真的最坏情况200位

  • 两位国王的位置-12位
    • QRRBBNN QQQQQQQQ -75位
    • qrrbbnn qqqqqqqq -75位
    • 空方块-30位
  • 主动色-1位
  • 卡斯丁-4位
  • En Passant -3位

实际最坏的情况

每个玩家都不能在不捕获其他棋子的情况下提升所有棋子。这是片段捕获的熵效应:

  • PpR(3 + 3 + 5 = 11位)=> Qq_(5 + 5 + 1 = 11位)
  • PPpp(3 + 3 + 3 + 3 = 12位)=> QQq_(5 + 5 + 5 + 1 = 16位)

所以实际上最坏的情况是:

  • QRRBBNN QQQQQQQQ -75位
  • qrrbbnn qqqq -55位
  • 空方块-34位

最糟糕的情况是宣传所有作品,而不是将典当留给传人。

显示代码为 12 + 75 + 55 + 34 + 1 + 4的总实际最坏情况 = 181位

FIQ显示了此简单方案的两项改进,但很难编写:

  • 从第1行和第8行的片段编码中删除第2位,因为典当无法进入该行(最多节省16位)
  • 使用典当对可投放的菜鸟进行编码(节省4位)

为简洁起见,此答案中唯一未显示的剩余代码是:打破字段(split /\s/)中的输入FEN 和变量赋值。


玩家可以提升他的所有棋子(鉴于他能够捕获敌人的棋子);Qn4QQ / Qb6 / Qq1k4 / Qr6 / Qb6 / Qr6 / Qn4NK / RNB2B1R b--0 84
Krzysztof Szewczyk

@KrzysztofSzewczyk,是的,上面已在PPpp=>上指出QQq_
William Entriken,

4

总数据需要33个字节

(感谢评论中的某人,我意识到这不适用于典当推广。在我能解决此问题时会进行更新)

对于第一个字节,我们使用五个位:

  • 第一位:玩家回合,1 =白色
  • 第二位:黑王城堡,1 = can城堡
  • 第三位:黑皇后城堡,1 = can城堡
  • 第四位:白色国王方城堡,1 = can城堡
  • 第五位:白皇后城堡,1 = can城堡

接下来的32个字节用于以预定义的顺序表示每个棋子

  • 3位:代表行
  • 3位:代表列
  • 1位:表示合格,1 =可以合格
  • 1位:如果“可以通过”位为1:表示哪一侧,0 =左
    ,表示是否已捕获。0 =未捕获
    (如果可以通过,则肯定不会捕获)

一些C代码代表这个想法(实际上不起作用)

int main() {
    char b, c[32], i;

    //decode:

    FILE *p=fopen("/path/to/file.csv","r");
    fscanf(p,"%d,",&b);
    for(i=0;i<31;i++) fscanf(p,"%d,",&c[i]);
    fscanf(p,"%d",&c[31]);
    fclose(p);
    if(b&16) /* white's turn */
    else /* black's turn */
    if(b&8) /* black king side can castle */
    if(b&4) /* black queen side can castle */
    if(b&2) /* white king side can castle */
    if(b&1) /* white queen side can castle */

    for(i=0;i<32;i++) {
        int row, column;
        row=c[i]&7;
        column=c[i]&56;
        if(c[i]&64 && isPawn(c[i])) { //can en-passant
            if(c[i]&128) //can en-passant to the right
            else //can en-passant to the left
        }
        if(!(c[i]&64)) {
            if(c[i]&128) //captured
            else //not captured
        }
    }

    //encode:

    p=fopen("/path/to/file.csv","w");

    if(b&16) b&=239;
    else b|=16;
    if(black_king_side_cannot_castle) b&=247;
    if(black_queen_side_cannot_castle) b&=251;
    if(white_king_side_cannot_castle) b&=253;
    if(white_queen_side_cannot_castle) b&=254;

    for(i=0;i<32;i++) {
        c[i]=row;
        c[i]+=column*8;
        if(isPawn(c[i]) && can_en_Passant) {
            c[i]|=64;
            if(can_en_Passant_left) c[i]&=127;
            else c[i]|=128;
        }
        if(!(c[i]&64)) {
            if(isCaptured(c[i])) c[i]|=128;
            else c[i]&=127;
        }
    }
    fprintf(p,"%d,",b);
    for(i=0;i<31;i++) fprintf(p,"%d,",c[i]);
    fprintf(p,"%d",c[31]);
    fclose(p);
    return 0;
}

-1,这不是答案...
门把手

1
当典当达到第八名并晋升为骑士,主教,车夫或王后时,您的预定义顺序将无用。
吱吱作响的ossifrage,2014年

@Snow的@Doorknob好的我正在研究算法,只是晚上很晚,我很累,所以我做的要慢一些
ace_HongKongIndependence 2014年

那么,如果您没有解决方案,为什么还要发布此消息?
门把手

3
如果您仍然认为这个答案是废话,在这里没有任何价值,那么请继续投票删除它并对其投反对票,您也可以在这里删除我的帐户。我只想分享我的想法,你们为什么要这么刻薄?
ace_HongKongIndependence 2014年

4

256 242位

这是一个基本的压缩算法,可能会得到改进,因为它不会排除某些非法位置。

该评估板以5位标题信息开头,如下所示:

0 1 1 1 1
---------
1 2 3 4 5

1: Turn (black = 1, white = 0)
2: Black can castle queen-side
3: Black can castle king-side
4: White can castle queen-side
5: White can castle king-side

然后,一个12位的字符串表示国王的位置。

0 0 0 1 0 0 1 1 1 1 0 0
-----------------------
0 0 0 0 0 0 0 0 0 1 1 1
1 2 3 4 5 6 7 8 9 0 1 2

01 - 03: white king's rank
04 - 06: white king's file
07 - 09: white king's rank
10 - 12: white king's file

然后,在基数11中有一个巨大的64位数字,然后将其乘以9,以在末尾添加另一个数字,表示通过状态。底数11中的每个数字代表板上的一个正方形,具有以下可能的值:

0: empty

1: white pawn
2: white knight
3: white bishop
4: white rook
5: white queen

For the black equivalent of each white piece, add 5.

以及以9为底的数字:

0: no en-passant possible
1 - 8: en-passant on rank 1 - 8

11 64 ×9约为2 224.57,需要225位进行编码。加上顶部的17个标头位,总计242位。


感谢ugoren的改进。


这与ace的算法非常相似,但是它使用板的位置而不是按预定的顺序排列,这既避免了典当升级问题,又使数据被砍掉了一点。
Joe Z.

您可以将passant保存为0到8之间的一个数字(当前播放器可以捕获passant的列吗?)。13^64 * 9是239.99,节省11位。通过单独编码国王职位节省更多。
ugoren 2014年

根据Snow的Doorknob的评论,我的回答是“不是答案”。只是说。仅供参考,在我将C代码添加到答案中之前,他的评论已发布。
ace_HongKongIndependence

@ugoren:我忘记了。没错,我忘记了只能同时传递一个棋子。
Joe Z. 2014年

@ace:您的答案不是无效的,因为它不包括编码和解码代码;这是无效的,因为它没有考虑到典当促销的情况(在这种情况下,您预定义的件数没有任何作用)。这个问题的核心是需要一种数据编码方案。该程序只是与之交互的东西。
Joe Z.

3

?位

(≥217最差情况,17最佳情况,初始板为179)


编码说明

额外的元数据由轮流(一位)和铸造(四位,即国王的一方可以是白色城堡吗?皇后的一方可以是城堡,黑色也可以)组成。

对于棋盘位置本身,我们将其编码为一组活动棋子。好吧,实际上,由于我将稍作解释的原因,我们确保按特定顺序枚举片段。对于每件作品,我们都存储其颜色(一位),种类(三位涵盖6种,再加上一种额外的种类,以表示“可能被同伴带走的棋子”)及其位置。

这是一个有趣的部分:对片段的位置进行编码,而不是将其存储为坐标,而是以从左到右,从上到下的顺序枚举片段时,存储其与最后一块的相对距离。 ,B8,...,G1,H1)。此外,我们将距离存储为可变长度数字,1用来表示此片段与上一个片段正好相邻,0xx可以跳过1-3个,000xxx可以跳过4-10个,000000xxxx适合11-25,0000000000xxxxx适合26-56最后000000000000000xxx是57-62。

例子

我对使用此编码进行编码的某些职位做了要点,并用一些注释为初始职位添加了注释。

我不知道如何分析最坏情况下的位大小,但是根据要点,我相信该算法至少应该有一定效率。


解码器的实现

下面是用于此编码的快速解码器(如上面带注释的示例一样,将以文本编码的二进制数据作为输入,并跳过不是'0'或'1'的内容)。产生一个Unicode棋盘到stdout。

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

char buf[1024];
int wi = 0, ri = 0;

int read_n(int n) {
  int res = 0;
  for (int i = 0; i < n; i++) {
    res = res << 1 | (buf[ri++] == '1');
  }
  return res;
}

int read_varnum() {
  int v, c = 0;

  for (int i = 1; i <= 5; i++) {
    v = read_n(i);
    if (v != 0) return c + v;
    c += (1 << i) - 1;
  }

  assert(false); /* Shouldn't happen */
}

char *piece_to_str(int piece, int color) {       /* ↓ pawn that may be taken with en passant */
  char *pieces[] = { "♙", "♘", "♗", "♖", "♕", "♔", "♙",
                     "♟", "♞", "♝", "♜", "♛", "♚", "♟" };
  return pieces[color * 7 + piece];
}

int main(void) {
  int ch;
  while (ch = getchar(), ch != EOF) {
    if (ch == '0' || ch == '1') buf[wi++] = ch;
  }

  int board[64];
  memset(board, -1, 64 * sizeof(int));

  /* Read metadata */
  int is_white = read_n(1);
  int castling = read_n(4);

  /* Read the board state */
  int bi = -1;
  while (ri != wi) {
    int color = read_n(1);
    int kind  = read_n(3);
    int delta = read_varnum();
    board[bi + delta] = color << 8 | kind;
    bi += delta;
  }

  /* Print metadata */
  printf("  Current turn: %s's turn to play.\n", is_white? "white" : "black");
  printf("  Castling: White may castle? %s %s\n",
         castling & 0x8? "left" : "", castling & 0x4? "right" : "");
  printf("            Black may castle? %s %s\n",
         castling & 0x2? "left" : "", castling & 0x1? "right" : "");
  printf("\n");

  /* Print the board out */
  printf("+");
  for (int x = 0; x < 8; x++) printf("--");
  printf("-+\n");

  for (int y = 0; y < 8; y++) {
    printf("|");
    for (int x = 0; x < 8; x++) {
      int piece = board[y*8 + x],
          color = piece >> 8,
          kind  = piece & 0xFF;

      if (piece == -1) printf("  ");
      else printf(" %s", piece_to_str(kind, color));
    }
    printf(" |\n");
  }

  printf("+");
  for (int x = 0; x < 8; x++) printf("--");
  printf("-+\n");

  return 0;
}

我不知道您最坏情况的位数是多少,但我怀疑它远远超过179位。例如,您的算法将如何处理此布局?(顺便说一句,这有效的;我是使用Chess.com上的编辑器制作的
吱吱作响的ossifrage

@squeamishossifrage,似乎需要217位:gist.github.com/FireyFly/8639791(感谢测试用例,我想尝试一个更棘手的工具!)
FireFly 2014年

嘿,还不错。继续!
吱吱作响的ossifrage

2

最大值:184位,最小值:75位

@AdamSpeight的Huffman编码启发了我,尝试编写自己的方案。我怀疑这会赢,但是确实有可计算的限制。

该方案将象棋棋子视为11种不同类型的棋子。我大致遵循霍夫曼编码算法,将这些类按频率和实数进行分组,以生成以下编码:

 Piece Class                | Frequency Per Team | Encoding
============================+====================+==========
 Pawn, normal               | 0 - 8              | 0
 Pawn, jumped two last turn | 0 - 1              | 1111
 Knight                     | 0 - 2              | 1000
 Bishop                     | 0 - 2              | 1001
 Queen-side rook, unmoved   | 0 - 1              | 10100
 Queen-side rook, moved     | 0 - 1              | 10101
 King-side rook, unmoved    | 0 - 1              | 10110
 King-side rook, moved      | 0 - 1              | 10111
 Queen                      | 0 - 1              | 1110
 King, unmoved              | 0 - 1              | 1100
 King, moved                | 0 - 1              | 1101

每个棋子的代码之前都可以有两位,以显示其所属的团队(10对于白色,11对于黑色)。 0可用于编码空白。这些想法使我们能够使用所需的任何步骤为整个电路板构建平方编码。我将假设迭代顺序a1, b1, c1, ... f8, g8, h8。这意味着仅列出上述代码即可对所有信息进行编码,但轮到谁了。一个非常简单的国际象棋引擎可以将“类”用于棋子,新手和国王,从而确定cast和and是否合法。此外,该方案可以轻松处理典当促销活动。如果玩家将棋子提升为新手,则只要选择“移动”变体,就可以使用国王或王后码。

除了我认为不可能发生的病理位置之外,这种编码的最坏情况是,当所有棋子都还留在棋盘上,并且前一个玩家将棋子向前移动两个空格时。例如,下面的面板编码1. e4

1101010010100010100110111010110010100110100010101101001001001100100100100000000000000101111000000000000000000011011011011011011011011011101001110001110011111101111001110011110001110110
===========================================================================
                              Black's move
1110100 111000 111001 111110 111100 111001 111000 1110110 | r n b q k b n r
    110    110    110    110    110    110    110     110 | p p p p p p p p
      0      0      0      0      0      0      0       0 | . . . . . . . .
      0      0      0      0      0      0      0       0 | . . . . . . . .
      0      0      0      0 101111      0      0       0 | . . . . P . . .
      0      0      0      0      0      0      0       0 | . . . . . . . .
    100    100    100    110      0    100    100     100 | P P P P . P P P
1010100 101000 101001 101110 101100 101001 101000 1010110 | R N B Q K B N R

这意味着最坏情况的编码有184位:1用于指示玩家,32用于空白处,45用于未移动的棋子,6用于跳跃两次的棋子,24用于骑士,24用于主教,新秀车为28个,皇后车为12个,国王为12个。

随着片段的移动和捕获,编码的大小将减小。最好的情况是由板上的两个国王代表:1位用于指示玩家+ 62位用于指示空白方块+ 12位用于指示国王给出的最小长度为75位。

我将返回并使用一些代码来编辑此响应,这些代码将在今天或明天晚些时候演示该编码的实际作用。现在,我很想知道其他人对这种编码的看法。


1
您可以在小车上保存一些信息。您可以根据位置确定皇后和王朝,而无需知道移动的船员来自哪一侧。因此,您只需在车队上获取一点信息-已移动或未移动。
并不是查尔斯(Charles)

...实际上,将数据存储在片段级别更容易编码/解码,但是如果您只是在开始时存储了标记,则可能会在整体上节省最坏的情况(例如,标记11001意味着B'S MOVE W CAN KSC W CANT QSC B CANT KSC B CAN QSC)。这是4位,而不是编码中每侧5位,或者,如果消除了rook中的侧标记,则每侧3位。(KSC=国王的城堡。 QSC=女王的城堡)
不是查尔斯(Charles

另外,由于晋升,很有可能最多有9个皇后或10个骑士(或任何其他非常规,非典当的零件)
并不是查尔斯(Charles

1

184位= 23字节,是最坏的情况,并且不太复杂:

A.占据哪个正方形:64位= 8字节B. <= 32占据正方形的哪些颜色:32位= 4字节现在仅使用非国王占据的<= 30正方形:B.使用以基数编码的PNBRQ 5,有5 ^ 30种可能性;和32 * 31 * 2 * 4 * 4 * 9(国王职位),动色,白色和黑色铸造权利,直角正方形(8种可能性中,第9种都不存在);这个数字5 ^ 30 * 32 * 31 * 2 * 4 * 4 * 9 = 266075134277343750000000000 = 2 ^ 87.782适合88位,整个编码总共64 + 32 + 88 = 184位。

这可以减少,例如通过使用事实,可以将32 * 31 * 2 * 4 * 4 * 9 = 285696减少为2 *(32 * 31 + 31 * 3 + 31 * 3 + 3 * 3)* 6 = 14244最多有6位在世的受害人候选人(包括无候选人),并且使用事实cast窃权在同一集合内编码cast窃权和国王职位仅在最初的正方形上有国王时才重要。这样可以节省4位。

我有理由相信不可能达到18个字节,即合法象棋位置的总数超过2 ^ 144。

您的160位方案非常巧妙,但我认为必须先将其作为编码/解码程序使用,然后我才能完全相信它。


1

171位最坏情况:

我结合了一些想法以及一些自己的想法。

我们将从64位板开始。每一位代表板上的已占用空间。它们沿着行填充。所以开始看起来像:1111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111

现在,每个片段将由4位表示。第一位:颜色(0=white, 1=black)第二到第四位:8种类型之一。

0=king, 1=queen, 2=bishop0, 3=knight, 4=rook, 5=pawn0, 6=pawn1, 7=bishop1

最后,我们将对转弯进行一些说明。0=white, 1=black

4bits * 32个= 128位,我已经从转牌和局中获得了64 + 1。这样一来,总共有128 + 64 + 1 = 193,我什至还没有开始进行传授或铸造。什么也没超出我的极限-甚至没有转身。这是技巧的开始。

好的-您看到上面的那些类型了吗?Bishop0和Bishop1?Pawn0和pawn1?之所以指定这些类型,是因为它们告诉我们在此部分之后要插入的内容。所以Bishop0表示在它之后将是0-即下一块是白色。Bishop1告诉我们下一块是黑色的,pawn0和pawn1的工作原理相同。(如果这是最后列举的那一部分,那么它将告诉我们下一轮)。

我最坏的情况涉及董事会上的所有棋子,因此使用16个棋子和4个主教,可以为我节省20位。我只有173岁。

好的。在我最坏的情况下还有一点-一旦知道了16种颜色编码,我们就会停止对颜色进行编码-正如我们所知道的那样。我最坏的情况是现在有一块白色的东西到了远处,没有被捕获。在那里,我只保存一点。172。

我现在要更改我命名作品的顺序。我们将沿从外部开始的列开始命名它们。开始时命名的板将保持不变,但是当我在其上放置棋子时,我将从右下角的白色开始,然后向上移动该列。然后,我跳到布莱克的右下角,然后上那列。我跳到怀特右下角的未知单元格,依此类推-这意味着我最糟糕的情况还是重新开始。我的原因与我的无用技巧,我接下来失去的下两位以及cast狗有关。

现在,要推广典当(详细讨论过),必须捕获一块。因此,当我们知道有32件时,我们只需要表示它们中的31件即可。最后一块是唯一标识的。事实证明,对我而言,这只节省了2位-因为我的最后一块可能是一个典当/主教(通常我要花3位,因为我在下一块上节省了一位),其颜色已经确定,所以仅2位。我只有170位。

当典当得到提升时,它们只是改变其类型。对于每一个脱离董事会的棋子,我(最少)摆脱了3位,而两次典当促销使我损失了2位,所以我在(缓慢地)减少了晋升。

卡斯特 为使发生cast窃,国王和有关车手都可能没有动过。因此,当一个新车能够铸造时,它和国王都将处于其原始位置。因此,能够发出叫声的白嘴鸦将被列为与该色国王相同的地方。因为这是非法的(板上有两个国王或三个相同颜色的国王),并且每种颜色只能在三种可能的设置中发生(左,右,两个白名单都列为国王),所以完全可以解码。因此,我们没有编码比特。

En Passant在这里,我们还将使用非法职位。一次只能有一个棋子有传递危险。它必须在第四行。易受攻击的棋子将被“翻转”回到其本行-随处可见。由于该棋子位于其自己的第一行(即非法位置),对于解码器而言,情况将是显而易见的,它将颠倒该位置,并将棋子标记为容易受到攻击。

我们需要仔细考虑订单,因为最后一件必须“唯一标识”。按照标准的顺序,我们无法确定后角的车子是否可以掩盖-这是未知的。当我们从外到内工作时,我们保证无论是在“自己的角落”中还是在“白板”的中间,因为它是由一个易受攻击的当兵切换到的,在木板的中间,都会在后面列出一部分它-因此我们被告知菜鸟的类型。我们知道后面会有一个片断,因为,要使典当易受攻击,必须在其内部存在一个典当(稍后将严格按照上面的说明对其进行编码)。

哦,为确保最坏的情况涉及板上的所有棋子,一旦我们少于32棋子,我们可以使用64位棋盘来识别转弯和占用位置,方法是用0表示棋子,而白色棋子转为1时为黑色,转为1。

因此,我们免费获得了传授和cast职。我们免费拿起了最后一块,尽管花了一些时间才能使它与通行规则和cast割规则保持一致。我们在标准件上放弃了20位。我认为,最坏的情况是前进的中间白色棋子和棋子与女王之间的黑色棋子,所有棋子都在棋盘上。快速的双重检查:捕获第一块-称它为典当,在棋子上离开棋盘3位,最后一块在棋盘上3位,在转弯标记中消失1位。提升两个棋子2位回到板上。该死的,我是171。

编辑我在下面的R中为(工作的)解码器添加了代码。它需要布尔向量作为输入-(对不起-我无法在任何可以让我实际使用位的事情上进行良好的编码)我还包括了起始位置。

separate = function(vec){
    #Using a boolean vector (sorry R doesn't handle bits well and this will build quickest)
    board = matrix(vec[1:64],8,8,byrow=T)
    npieces = min(sum(board),64-sum(board))
    n = length(vec)
    a = vec[65:n]
    counter = 0
    pieces = list()
    white = 0
    Letters=c(letters,LETTERS)
    for (i in 1:npieces){
        col = ifelse(a[1],"black",{white=white+1;"white"})
        typ = a[2:4]
        a=a[-(1:4)]
        num = 4*typ[1] + 2*typ[2] + typ[3]
        type = switch(letters[num+1],a="king",b="queen",c="knight",d="rook",e="bishop0",f="bishop1",g="pawn0",h="pawn1")
        if (num > 3) {
            if(num%%2){
                a = c(T,a)
            } else {
                a = c(F,a)
            }
            type = substr(type,1,nchar(type)-1)
        }
        pieces[[Letters[i]]] = list(color=col,type=type)
        if (length(pieces)==31&&npieces==32) {
            col = ifelse(16-white,{white=white+1;"white"},"black")
            type = "TBD"
            pieces[[Letters[i+1]]] = list(color=col,type=type)
            break
        }
    }

    if (npieces==32) {
        f=function(y){sum(sapply(pieces,function(x)x$type==y))}
        if (f("pawn")<16) {pieces[[32]]$type="pawn"}
        if (f("bishop")<4) {pieces[[32]]$type="bishop"}
        if (f("knight")<4) {pieces[[32]]$type="knight"}
        if (f("queen")<2)  {pieces[[32]]$type="queen"}
        if (f("king")<2)   {pieces[[32]]$type="king"}
        if (f("rook")<(6-f("king"))) {pieces[[32]]$type="rook"}
    }
    return(list(board,pieces,turn=ifelse(a[length(a)],"black","white")))
}


fillboard = function(out) {
    board = out[[1]]
    pieces = out[[2]]
    turn = out[[3]]
    lpieces = lapply(pieces,function(x) paste(substr(x$color,1,1),x$type))
    game = matrix("     ",8,8)
    #Start with corners.
    a = c(1,57,8,64)
    #Then kings
    b = c(25,32)
    #Then rooks in en passant
    c = c(4,60,5,61)
    #Then kings in en passant
    d = 28:29
    exceptions = list(a,b,c,d)
    for (places in exceptions) {
        c= which(board[places])
        if (length(c)) {
            repl = lpieces[1:length(c)]
            game[places[c]] = unlist(repl)
            board[places] = F
            lpieces = lpieces[-(1:length(c))]
        }
    }
    #Loop through rows.
    for (i in c(1:4,8:5)) {
        j = which(board[i,])
        if (length(j)) {
            repl = lpieces[1:length(j)]
            game[i,j] = unlist(repl)
            board[i,j] = F
            lpieces = lpieces[-(1:length(j))]
        }
    }
    return(matrix(unlist(game),8,8,F))
}

swapillegal = function(matr) {
    mat = matr
    if (any(mat[8,]=="b pawn")) {
        j = which(mat[8,]=="b pawn")
        mat[8,j] = mat[5,j]
        mat[5,j] = "b pawn-e"
    }
    if (any(mat[1,]=="w pawn")) {
        j = which(mat[1,]=="w pawn")
        mat[1,j] = mat[4,j]
        mat[4,j] = "w pawn-e"
    }

    if (sum(mat[8,]=="b king") > 1) {
        j = which(mat[8,-4]=="b king")
        j[j==7] = 8
        mat[8,j] = "b rook-c"
    }
    if (sum(mat[1,]=="w king") >1) {
        j = which(mat[1,-4]=="w king")
        j[j==7] = 8
        mat[1,j] = "w rook-c"
    }
    return(mat)
}

decode = function(vec) {
    a = separate(vec)
    b = fillboard(a)
    c = swapillegal(b)
    list(board=c,turn=a[[3]])
}


startboard = c(rep(T,16),rep(F,32),rep(T,16))
#Re-ordering -- first spots will be the corners. Then kings. then en passant positions of those spots
pieces = c(F,F,T,T,F,F,T,T,T,F,T,T,T,F,T,T,F,F,F,F,T,F,F,F,F,F,T,F,F,T,F,F,F,F,T,F,T,F,F,F,T,F,F,T,T,F,T,T,F,T,T,F,T,T,F,T,T,F,T,T,F,T,T,F,T,T,T,F,T,F,T,T,F,T,F,F,T,T,T,F,T,F,T,F,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F)
########## w rook -w rook -B rook -B rook -W king -B king -w kni  -w bi 0 -w Q  -w Bi 0 -w kni-w p0   - 2   -   3 -  4  - 5   -  6  -  7  -w p1 -b kni-b bi1  -b q  -b bi1  -b kni-b p1   -2    - 3   - 4   - 5   - 6   - b p0- implicit b p0.
#After the kings come all the prior en passant positions. which are empty at start. Then we start at whites bottom right, and move across rows to middle. Then we go to blacks bottom left, and fill across to the middle.
#Changing start to properly encode rooks for castling
newpieces= c(F,F,F,F,F,F,F,F,T,F,F,F,T,F,F,F ,pieces[-(1:16)])
test2 = decode(c(startboard,newpieces))

此代码构建4个功能。它对棋子和棋盘结构进行了一些基本的分离,并找出棋子的类型和任何“隐式”棋子。下一个功能用那些有点怪异(与我最初的算法不同)的顺序(在代码注释中解释)用这些片段填充电路板结构。下一个功能将填补空缺并检测到任何非法位置-然后修复它们,并重命名容易受到破坏的pawn-“ x pawn-e”和任何可能掩盖“ x rook -c”的rook。最后一个功能是包装器,该包装器按顺序运行这些功能,并提供输出,该输出是当前板以及转向。

我还包括了起始位置的编码(尽管要看到它,您必须调用它c(startboard,newpieces),并且代码会在该位置调用包装函数。


这是有趣的。我很乐意将可行的实现视为概念证明。
Mego

实现通常超出概念验证的几步,但是我添加了一个解码器。它在R中,因此使用布尔值而不是位(对不起-不想使用太多时间)。但是我认为这应该是概念的证明。
user5957401

0

229/226位

事实证明这并不是非常成功,但是可能会挽救其他人走同样的道路。

简单版本:

  • 1 轮到谁了
  • 4 四种铸造可能性的钻头
  • 3带来无限可能。我一开始就了解到这一点。必须通过与捕获的棋子处于同一等级(行)的棋子来完成传递。案例分析表明,一旦我们知道有多少只上次移动的颜色的棋子正好前进了两个正方形,那么最多将有6个伴随事件(包括没有容易受到传递的棋子的情况)。最坏的情况是5个棋子(可能是所有弱点:例如PpPPpPPp有5个弱点P)。随着6个棋子最多有2周敌人的走卒在同级别,每一个都可以威胁到最多2个走卒顺便。因此,ceil(lg 6) = 3这里需要位。

然后是董事会。该板有64个正方形,因此正方形索引可以用6位编码。我们从国王开始,按等级,颜色交替列出人员。

  • 6位:白王的位置。(保证成为董事会成员)。
  • 6位:黑王的位置。(保证在棋盘上。在最坏的情况下,白王在角落里,他可能有60个可能的位置;在最好的情况下,白王不在边缘,有55个地方)。
  • 6位:白皇后的位置。如果没有白皇后,请重复白国王的位置作为信号。
  • 对于每个额外的白皇后,该1位置后面跟一个6位。
  • 一个0位。
  • 黑色女王的同上。
  • 车队,主教,骑士和兵的处理过程类似,但是如果我们已经有16个人使用该颜色,则可以跳过兵的颜色。
  • 删除最后0一位。

12国王要花一些钱,2*7*5-1 = 69其他人要花些钱。在最差的情况下,所有32个人都在棋盘上,7除了国王以外,每个人每个人都要花一点钱,总计为12 + 7*30 - 1 = 221 bits。因此,8对于全局状态的初始位,我们有229位


进阶版本:

通过使用算术编码,我们可以使用lg num_possibilities而不是ceil(lg num_possibilities),最后只取一个ceil

  • 1 轮到谁了
  • 4 四种铸造可能性的钻头
  • lg 6带来无限可能。
  • 6 白国王的位
  • lg 60 黑王的位(最坏的情况)
  • lg 63 白皇后的位子(因为我不想将其复杂化为查看支票的程度),如果没有白王位,则使用白王位
  • 每增加一个白色的女王,一个1位后跟随lg 62lg 61等位了她的位置。
  • 一个0位。
  • lg 63 黑皇后的位(如果有白皇后,则更少)。
  • 等等

实际出现的第n个人具有66-n可能的价值。如果一种颜色不存在某种类型,我们会花一点时间66-#men so far记录下来(加上一位用于分隔符)。极端情况是:

  1. 所有在场的人,包括每侧至少有一个未经推广的兵。我们5+lg 6在全局状态,6+lg 60国王,29分隔符位和SUM_{n=32}^{63} lg n位置位上花费。总计:ceil(225.82)位。令人失望
  2. 仅剩下未经推广的棋子。我们花在5+lg 6全局状态上,6+lg 60在国王上,29在分隔符上,8*lg 63说没有其他片段,以及SUM_{n=48}^{63} lg n在棋子的位置上。总计:ceil(188.94)位。更好-通过为每边节省第二个车队,骑士和主教,我们向前推进了一点。

因此,最坏的情况似乎是226位,仅节省了3位。

在一般情况下,我们可以通过在棋子之前对棋子进行编码来做得更好,因为它们被限制为48个正方形而不是完整的64个正方形。但是,在最坏的情况下,所有棋子都在棋盘上,所有棋子都得到了提升这最终将花费2位,因为我们需要一个“没有当兵”标志,而不是能够计算人员。


0

这是国际象棋界的讨论主题。

这是164位的一个非常简单的证明https://groups.google.com/forum/#!topic/rec.games.chess.computer/vmvI0ePH2kI 155显示在此处http://homepages.cwi.nl/~tromp /chess/chess.html

过度简化的策略:

  • 将棋子限制在可以找到棋子的地方
  • 限制军队考虑原始件和可能的典当促销
  • 认真考虑促销活动和无法进行促销的情况

2
仅链接的答案不是好的答案。您应该在帖子中提供完整的答案,而不仅仅是一些答案的链接。如果您的回答不是您自己的工作,则应将您的帖子设为社区Wiki。
ace_HongKong独立2014年

帖子是原创的。添加详细信息以作答。
William Entriken 2014年

1
这是为什么将内容包括在答案中的一个示例。第二个链接已死。是这个吗?tromp.github.io/chess/chess.html
mbomb007

-2

最小:0位

最高: 1734 243位(4.335 4.401位/板摊销)

预期: 351 177位(4.376 4.430位/板摊销)

由于我可以确定输入和输出,因此我希望直到现在为止都对游戏的历史进行编码。优点之一是,可以得到哪些人转身,被转为谁以及谁有能力隐瞒位置的附加信息可以被导出而不进行编码。

尝试1:

天真的我以为我可以将每个动作编码为12位,即4个三元组,形式为(开始x,开始y,结束x,结束y),其中每个都是3位。

我们将假定起始位置,并从该位置开始移动,白色先行。该板的排列方式是(0,0)是白色的左下角。

例如游戏:

  e4    e5
 Nf3    f6
Nxe5  fxe5
...    ...

将被编码为:

100001 100010 100110 100100
110000 101010 101110 101101
101010 100100 101101 100100
...

这导致了12 m位的编码,其中m是移动的数量

一方面,这可能会变得非常大;另一方面,您可以考虑将每一步视为自己的游戏,因此每种编码方式实际上都可以编码m个 “棋盘”。如果将其摊销,您将获得每个“棋盘”为12位。但是我觉得这有点作弊...

尝试2:

我意识到上一次尝试中的每个举动都会编码许多非法举动。因此,我决定只对法律行动进行编码。我们列举了可能的移动,如下所示,将每个正方形编号为(0,0)→0,(1,0)→1,(x,y)→x + 8 y。遍历图块,检查是否有一块块以及是否可以移动。如果是这样,可以添加职位。选择列表索引,它是您要执行的操作。将该数字加到以1加上可能移动数加权的连续移动总数中。

上面的示例:从起始位置开始,第一个可以移动的方块是正方形1上的骑士,它可以移动到正方形16或18上,因此将它们添加到列表中[(1,16),(1,18)]。接下来是6号广场上的骑士,添加其招式。总的来说,我们得到:

[(1,16),(1,18),(6,21),(6,23),(8,16),(8,24),(9,17),(9,25),(10,18),(10,26),(11,19),(11,27),(12,20),(12,28),(13,21),(13,29),(14,22),(14,30),(15,23),(15,31)]

由于我们需要移动(12,28),因此我们将其编码为基数20为13,因为有20种可能的移动。

现在我们得到游戏号码g 0 = 13

接下来,我们对黑色进行相同的操作,除了我们对图块进行反向编号(以使其更容易使用,而不是必需的)以获取移动列表:

[(1,16),(1,18),(6,21),(6,23),(8,16),(8,24),(9,17),(9,25),(10,18),(10,26),(11,19),(11,27),(12,20),(12,28),(13,21),(13,29),(14,22),(14,30),(15,23),(15,31)]

由于我们需要移动(11,27),因此我们将其编码为11以20为底数,因为有20种可能的移动。

所以现在我们得到游戏号码g 1 =(11⋅20)+ 13 = 233

接下来,我们获得以下白色动作列表:

[(1,16),(1,18),(3,12),(3,21),(3,30),(3,39),(4,12),(5,12),(5,19),(5,26),(5,33),(5,40),(6,12),(6,21),(6,23),(8,16),(8,24),(9,17),(9,25),(10,18),(10,26),(11,19),(11,27)(13,21),(13,29),(14,22),(14,30),(15,23),(15,31)]

因为我们要移动(6,21),所以我们在基数29中将此编码为13,因为有29种可能的移动。

所以现在我们得到游戏号码g 2 =((13⋅20)+ 11)20 + 13 = 5433

接下来,我们获得以下黑色举动列表: [(1,11),(1,16),(1,18),(2,11),(2,20),(2,29),(2,38),(2,47),(3,11),(4,11),(4,18),(4,25),(4,32),(6,21),(6,23),(8,16),(8,24),(9,17),(9,25),(10,18),(10,26),(12,20),(12,28),(13,21),(13,29),(14,22),(14,30),(15,23),(15,31)]

由于我们要移动$(10,18)$(10,18)

所以现在我们得到了游戏号码g 3 =(((19⋅29 + 13)20)+ 11)20 + 13 = 225833

并针对所有剩余的动作继续执行此过程。您可以将g视为函数g(x,y,z)= x y + z。因此g 0 = g(1,1,13),g 1 = g(g(1,1,11),20,13),g 2 = g(g(g(1,1,13),20, 11),20,13),g 3 = g(g(g(g(g(1,1,19),29,13),20,11),20,13)

为了解码游戏号g 0,我们从初始位置开始并列举所有可能的移动。然后我们计算g 1 = g 0 // lm 0 = g 0l,其中l是可能移动的数量,“ //”是整数除法运算符,“%”是模数运算符。应该认为g 0 = g 1 + m 0。接下来,我们移动m 0并重复。

从上面的示例中,如果g 0 = 225833,则g 1 = 225833 // 20 = 11291和m 0 = 225833%20 = 13.接下来g 2 = 11291 // 20 = 564和m 1 = 11291%20 = 11 g 3 = 11291 // 20 = 564和m 2 = 11291%20 =11。因此g 4 = 564 // 29 = 19 and_m_ 3 = 564%29 =13。最后,g 5 = 19 // 29 = 0和m 4 = 19%29 = 19。

那么,用这种方式对游戏进行编码的位数是多少?

为简单起见,假设每转总有20个动作,在最坏的情况下,我们总是选择最大的19个动作。我们得到的数字是19⋅20 m

+ 19⋅20 m-1 + 19⋅20 m-2 +⋯+ 19⋅20 + 19 = 20 m + 1-1其中_m是移动数。要对20 m + 1-1进行编码, 我们需要大约log 2 (20 m + 1 )位,大约为(m + 1)* log 2 (20)= 4.3219 *(m + 1)

平均而言,m = 80(每位玩家40步),因此编码需要351位。如果我们要录制很多游戏,我们将需要通用编码,因为我们不知道每个数字需要多少位

最糟糕的情况是m = 400(每位玩家200步),因此这将需要1734位进行编码。

注意,要编码的位置必须通过最短的路径提供给我们,以便遵循规则。例如,此处理论化的游戏不需要m = 11741即可编码最终位置。相反,我们运行“广度优先”搜索以找到到该位置的最短路径并对其进行编码。我不知道我们需要去列举所有棋位置有多深,但是我怀疑400是高估了。

快速计算:

有12个独特的棋子,或者正方形可以是空的,因此将其放置在棋盘上的数字是13 64。这是一个极大的高估,因为它包含许多无效职位。当我们运动进入游戏我们有大约20点创造的位置。所以我们正在寻找20 m = 13 64的时间。记录双方,得到m = 64 * log 20(13)= 54.797。这表明我们应该能够在55个动作中到达任何位置。

现在,我计算出最坏的情况是m = 55而不是m = 400,我将编辑结果。要编码m = 55 的位置需要243位。我还要说的是,平均情况约为m = 40,需要177位编码。

如果我们使用较早的摊销参数,则将1734位编码为400个“棋盘”,因此在最坏的情况下,每个“棋盘”都占用了4.335位。

请注意,g = 0表示有效的游戏,在该游戏中,最低方格上的棋子会移动到最低方格。

补充笔记:

如果要引用游戏中的特定位置,则可能需要对索引进行编码。这可以手动添加,例如将索引连接到游戏,也可以添加额外的“结束”动作作为每回合的最后一次可能动作。现在可以考虑让步的球员,或者连续2表示同意平局的球员。仅在游戏没有基于位置结束或陷入僵局的情况下才有必要,在这种情况下,这是隐含的。在这种情况下,平均需要的位数为356,在最坏的情况下为1762。


1
您的“摊销”论点不起作用。目的是对用户给定的板进行编码。您不会在此过程中可能会生成的400个辅助板中分配编码该板的成本。(这只有在用户允许独立选择这400个板块且不受组建国际象棋游戏的要求的约束的情况下才可以。)而且,理论上国际象棋游戏可以采取数千步动作,并且OP是很明显对最坏的情况感兴趣。
安德斯·卡塞格

@AndersKaseorg:非常正确。这实际上取决于目标。如果您尝试使用所有其他算法存储整个游戏,则将花费m * c个字节,其中m是移动数,c是输出结果的大小。因此,如果您尝试使用160位解决方案存储整个80移动游戏,则需要12800位,而我的仅需351位。出于竞争的考虑,我承认您是正确的,但我认为这是一个不错的选择需要指出的是,存储整个游戏而不仅仅是存储游戏板是很常见的。
edggy

目标是明确的。“目标是制作棋盘的最小表示,可以用它(一旦解码)来确定该回合玩家的所有移动可能性。......你的分数确定最糟糕的情况-在位最大可能的大小。”
安德斯Kaseorg

@AndersKaseorg:我从来没有说过它是模棱两可的。我只是决定采用另一种方法。要注意的一件事是为了使用我的算法找到最小的板,我需要最小的路径才能到达该位置。例如,在您链接到的同一个棋盘位置的11741回合游戏中,如果我们只关心棋盘,则无需遵循该路径。因此,为了对链接的游戏进行编码,我只找到最短的游戏,在这些正方形上剩下2个国王(可能只有200转或更少)。这可以通过深度优先搜索来完成。
edggy

如果您可以实际证明每个位置都可以在200转或更短时间内到达(现在看来只是个猜测),那么使用较短的等效游戏就可以了。您还需要通过100多个合法动作来说明象棋位置,除非您可以证明它们不在您的构造中出现。此挑战的规则仍然不允许将结果除以游戏中的移动次数。
Anders Kaseorg
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.