如何知道FEN职位何时合法?


19

我正在做一个个人项目,在这一点上我需要验证FEN职位,我从一些基础检查开始,例如检查是否有国王,并确保没有多余的行或列,以及那种东西。

但是,我应该做哪些其他检查以完全确保FEN合法?

Answers:


18

这是一个井井有条的清单,可以验证99.99%+的普通职位:

板:

  • 恰好有8个列
  • 每个等级(行)的空平方和块的总和为8。
  • 空正方形没有连续的数字

国王:

  • 看看是否恰好有一个w_king和一个b_king
  • 确保国王分开1个正方形

检查:

  • 非活动颜色不在检查中
  • 活动颜色检查少于3次(不可能进行三次检查);如果为2,则永远不会当兵+(当兵,主教,骑士),主教+主教,骑士+骑士

典当:

  • 每种颜色的棋子不超过8个
  • 由于行列位置错误或应该被提升,所以没有第一行或最后一行(行)的棋子。
  • 如果是直角正方形;看看它是否是合法创建的(例如,它必须在x3x6等级上,并且它前面必须有一个棋子(使用正确的颜色),而无用的正方形和后面的那个是空的)
  • 防止促销物品比缺少的典当多(例如extra_pieces = Math.max(0, num_queens-1) + Math.max(0, num_rooks-2)...,然后extra_pieces <= (8-num_pawns)),也应该对主教进行特殊计算。如果您有两个(或更多)同一个正方形的主教,则只能通过典当促销来创建这些主教,并且应包括将此信息以某种方式添加到上面的公式中
  • 典当的编队是可以到达的(例如,在一个col中有多个典当的情况下,必须缺少足够的敌人碎片才能组成该编队),以下是一些有用的规则:
    1. 单个文件(列)中不能有超过6个典当(因为典当不能存在于首尾)
    2. 在一个col中达到多个棋子的敌人丢失棋子的最小数量B to G 2=1, 3=2, 4=4, 5=6, 6=9 ___ A and H 2=1, 3=3, 4=6, 5=10, 6=15,例如,如果您在A或H中看到5个棋子,则其他玩家必须从他的15个可捕获棋子中至少丢失10个棋子
    3. 如果a2和a3中有白色棋子,那么b2中就不能合法地存在一个棋子,这个想法可以进一步扩展以涵盖更多可能性

卡斯特:

  • 如果国王或新手不在起始位置;该方的掷球能力会丢失(在国王的情况下,两者都会丢失)

主教:

  • 在未被移动的棋子困住的第一排和最后一排(行)中寻找主教,例如:
    1. 被困三只棋子后面的主教(任何颜色)
    2. 一个被困 2个非敌方典当后面的主教(不是被敌人的典当所包围,因为我们可以通过不足地推动典当来达到该位置,但是如果我们检查典当的数量,extra_pieces可以确定是否可能发生这种情况)

非跳线:

  • (如果要验证Fisher's Chess960,请避免使用此方法。)如果国王与车队之间有非跳跃者的敌方棋子,并且仍然有一些棋子无法移动;检查这些敌人的碎片是否可以合法进入那里。另外,问问自己:国王或白手起家是否需要移动以产生该位置?(如果是的话,我们需要确保投射能力反映出这一点)
  • 如果所有8个棋子都仍处于起始位置,则所有非跳跃者都不得离开其初始等级(非跳跃者的敌人也可能无法合法进入),还有其他类似的想法,例如白色-pawn移过一次后,白嘴鸦仍应留在pawn的内部,依此类推。

半/全动时钟:

  • 如果是方形,则半走时时钟必须等于0
  • HalfMoves <= ((FullMoves-1)*2)+(if BlackToMove 1 else 0),则+1或+0取决于移动的一侧
  • HalfMoves必须x >= 0和FullMovesx >= 1

其他:

  • 确保FEN包含所有需要的部分(例如,活动颜色,铸造能力,直角正方形等)

注意:因为“不超过8个棋子” + “防止额外提升的棋子” + “恰好一个国王”的分数已经被覆盖,所以不需要进行“玩家的棋子不超过16件”的检查

注意2:这些规则旨在验证由正常国际象棋的起始位置引起的位置,某些规则将使Chess960中的某些位置无效(从N°518开始的情况除外),并且会生成拼图,因此请避免使用它们来获得功能验证器。


1
您也可以检查pawn的结构,例如白色pawn永远不能位于a2,a3和b2上;在a3和b2上都不可能有一个棋子。
Akavall

就是说FEN头寸只能从初始头寸中获得吗?如果我想以FEN表示拼图位置该怎么办?有时,它们是以无法在实际游戏中达到的方式创建的……
tbischel

@tbischel我从正常的国际象棋角度制定了这些规则(不适用于Chess960或其他生成的位置),谢谢,我可能会指出这一点以使其更清楚
ajax333221 2013年

即使是普通的国际象棋,您可能也不想做所有这些检查。您最终得到的程序不能代表FEN的非法职位。但是它们确实在实践中发生-有时只有在比赛结束后才注意到非法举动。是否应该不可能从此类游戏中显示图表等等?
RemcoGerlich 2013年

1
@ ajax333221:此页面提供了合法的游戏中,白色沾到了超过5个走卒a文件。

10
\s*([rnbqkpRNBQKP1-8]+\/){7}([rnbqkpRNBQKP1-8]+)\s[bw-]\s(([a-hkqA-HKQ]{1,4})|(-))\s(([a-h][36])|(-))\s\d+\s\d+\s*

这是一个用于确保FEN字符串实际上有效的正则表达式。它不会对合法/非法职位进行任何测试,但这是一个很好的起点。


我认为必须使用主动色彩(您允许-),我认为半/全时钟有时是可选的。另外,我不了解a-h有关轮转能力的部分,我将其重写为/^\s*([rnbqkpRNBQKP1-8]+\/){7}([rnbqkpRNBQKP1-8]+)\s[bw]\s(-|K?Q?k?q?)\s(-|[a-h][36])/
ajax333221,2013年

我刚刚说过,我们可以使用“ ([rnbqkRNBQK1-8]+\/)([rnbqkpRNBQKP1-8]+\/){6}([rnbqkRNBQK1-8]+) ....
ajax333221

同样对于时钟来说,这可能很好,(0|[1-9][0-9]*)\s([1-9][0-9]*)因为移动不能有前导零并且全动不能是0或以0开始(代码信用)
ajax333221 2013年

5

对于其他功能,Stockfish引擎中有一个简单的函数,可验证FEN字符串。

bool Position::is_valid_fen(const std::string &fen) {
   std::istringstream iss(fen);
   std::string board, side, castleRights, ep;

   if (!iss) return false;

   iss >> board;

   if (!iss) return false;

   iss >> side;

   if (!iss) {
      castleRights = "-";
      ep = "-";
   } else {
      iss >> castleRights;
      if (iss)
         iss >> ep;
      else
         ep = "-";
   }

   // Let's check that all components of the supposed FEN are OK.
   if (side != "w" && side != "b") return false;
   if (castleRights != "-" && castleRights != "K" && castleRights != "Kk"
       && castleRights != "Kkq" && castleRights != "Kq" && castleRights !="KQ"
       && castleRights != "KQk" && castleRights != "KQq" && castleRights != "KQkq"
       && castleRights != "k" && castleRights != "q" && castleRights != "kq"
       && castleRights != "Q" && castleRights != "Qk" && castleRights != "Qq"
       && castleRights != "Qkq")
      return false;
   if (ep != "-") {
      if (ep.length() != 2) return false;
      if (!(ep[0] >= 'a' && ep[0] <= 'h')) return false;
      if (!((side == "w" && ep[1] == '6') || (side == "b" && ep[1] == '3')))
         return false;
   }

   // The tricky part: The board.
   // Seven slashes?
   if (std::count(board.begin(), board.end(), '/') != 7) return false;
   // Only legal characters?
   for (int i = 0; i < board.length(); i++)
      if (!(board[i] == '/' || (board[i] >= '1' && board[i] <= '8')
            || piece_type_is_ok(piece_type_from_char(board[i]))))
         return false;
   // Exactly one king per side?
   if (std::count(board.begin(), board.end(), 'K') != 1) return false;
   if (std::count(board.begin(), board.end(), 'k') != 1) return false;
   // Other piece counts reasonable?
   size_t wp = std::count(board.begin(), board.end(), 'P'),
      bp = std::count(board.begin(), board.end(), 'p'),
      wn = std::count(board.begin(), board.end(), 'N'),
      bn = std::count(board.begin(), board.end(), 'n'),
      wb = std::count(board.begin(), board.end(), 'B'),
      bb = std::count(board.begin(), board.end(), 'b'),
      wr = std::count(board.begin(), board.end(), 'R'),
      br = std::count(board.begin(), board.end(), 'r'),
      wq = std::count(board.begin(), board.end(), 'Q'),
      bq = std::count(board.begin(), board.end(), 'q');
   if (wp > 8 || bp > 8 || wn > 10 || bn > 10 || wb > 10 || bb > 10
       || wr > 10 || br > 10 || wq > 9 || bq > 10
       || wp + wn + wb + wr + wq > 15 || bp + bn + bb + br + bq > 15)
      return false;

   // OK, looks close enough to a legal position. Let's try to parse
   // the FEN and see!
   Position p;
   p.from_fen(board + " " + side + " " + castleRights + " " + ep);
   return p.is_ok(true);
}

1
看起来所有实际的验证都在中完成position.is_okay()。此处的代码仅进行了一些基本检查,以确保其格式正确,并且值得进行真正的验证(即,显然不是非法的)。
undergroundmonorail

4

这是一个简单的回溯算法,只要您具有可以检查每个董事会状态(也称为头寸)的反向合法移动的功能:

function is_legal_state(state,move)

   //Terminate if a starting state was found. This immediately implies there
   //was a legal game that generated this state, in fact the backtracking
   //can tell you precisely such a game       
   if (state in starting board state)
     return true

   //Apply some move to get to a new state, state is a persistent object
   apply_reverse_move(state,move)

   //Generate all legal "reverse" moves, that is, moves that could have
   //been performed to get to the current state from another position,
   //provided the previous position was valid. You do not have to check the
   //validness of the previous state, you just have to make sure the
   //transitioning move was valid
   legalmoves = enumerate_all_reverse_moves( state )

   for local_move in legalmoves:
     return is_legal_state(state,local_move)

   //Reverse the move that was previously applied so backtracking can
   //work properly 
   reverse_reverse_move(state,move)

   return false

1

FEN规范中没有任何内容表示必须从初始数组可以到达所表示的位置。证明从初始数组可以到达给定位置是一个未解决的问题。

在有效的FEN字符串中,半移计数必须与所传递的目标平方一致;如果存在目标方块,则半移动计数必须为零。半步数也必须与全步数一致;例如,半个走步计数为10的数字与3个完整走动的数字不兼容。


1

迟到聚会。

无法100%验证职位是否合法,但为什么验证很重要?我们可以下棋,无论位置是否恰好是从起始位置派生的(所谓的“游戏数组”)。可能有一个非常有趣的位置需要分析,但是恰好这是非法的。

所以我只检查:

  • 双方各有一位国王吗?
  • 排名第一或第八的棋子没有吗?
  • 移动端已经不给支票了吗?

如果那是三个,则我们可以从该图中向前下棋。甚至这份简短的条件清单,我们都可能会放松。

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.