Python中FEN字符串坐标索引问题:移动棋子时添加逻辑出错
问题描述
国际象棋中FEN字符串用于描述棋盘配置。我编写了ChangeFEN函数,尝试根据棋子的棋盘坐标从字符串中移除棋子,再将其添加到新坐标位置。移除棋子的索引查找循环运行正常,但添加棋子的循环无法正常工作,每次移动棋子时代码都会计算错误的索引。
当前FEN字符串:
curFEN = "r5k1/5ppp/1p6/p1p5/7b/1PPrqPP1/1PQ4P/R4R1K"
函数代码:
def ChangeFEN(Orig, New): global curFEN PrevX, PrevY = Orig NewX, NewY = New rows = curFEN.split("/") # delete piece from FEN string index = 0 for i in range(PrevY-1): index += len(rows[i]) + 1 space = 0 index2 = 0 #print(rows[PrevY-1]) for index2, i in enumerate(rows[PrevY-1]): if i.isdigit() == True: space += int(i) else: if space == PrevX-1: break else: space += 1 index3 = index + index2 piece = curFEN[index3] if index3 == 0: a = "/" else: a = curFEN[index3 - 1] try: b = curFEN[index3 + 1] except IndexError: b = "/" if a == "/": if curFEN[index3 + 1].isdigit() == True: #print("letter, number") curFEN = curFEN[0 : index3] + str(1 + int(curFEN[index3 + 1])) + curFEN[index3 + 2 :] else: #print("letter, letter") curFEN = curFEN[0 : index3] + "1" + curFEN[index3 + 1 :] elif b == "/": if curFEN[index3 - 1].isdigit() == True: #print("number, letter") curFEN = curFEN[0 : index3 - 1] + str(int(curFEN[index3 - 1]) + 1) + curFEN[index3 + 1 :] else: #print("letter, letter") curFEN = curFEN[0 : index3] + "1" + curFEN[index3 + 1 :] else: if curFEN[index3 - 1].isdigit() == True: if curFEN[index3 + 1].isdigit() == True: #print("number, letter, number") curFEN = curFEN[0 : index3 - 1] + str(int(curFEN[index3 - 1]) + 1 + int(curFEN[index3 + 1])) + curFEN[index3 + 2 : ] else: #print("number, letter, letter") curFEN = curFEN[0 : index3 - 1] + str(int(curFEN[index3 - 1]) + 1) + curFEN[index3 + 1 :] else: if curFEN[index3 + 1].isdigit() == True: #print("letter, letter, number") curFEN = curFEN[0 : index3] + str(1 + int(curFEN[index3 + 1])) + curFEN[index3 + 2 :] else: #print("letter, letter, letter") curFEN = curFEN[0 : index3] + "1" + curFEN[index3 + 1 :] # add piece to FEN string index = 0 for i in range(NewY-1): index += len(rows[i]) + 1 #index = index - 1 space = 0 index2 = 0 #print(rows[NewY-1]) for index2, i in enumerate(rows[NewY-1]): if i.isdigit() == True: for j in range(int(i)): space += 1 if space == NewX: break else: if space == NewX: break else: space += 1 #print(index) #print(index2) index3 = index + index2 #- NewY #print(curFEN[index3])
问题原因
添加棋子部分存在三个核心错误:
- 移除棋子后
curFEN已被修改,但仍使用原始的rows变量计算新坐标索引,导致基于旧棋盘数据的位置计算完全错误。 - 新坐标的
space计数逻辑混乱:遇到数字时逐个计数,但未处理“计数到目标位置后,确定在数字中的偏移”,循环结束后无法正确定位插入点。 - 缺失插入棋子后的数字拆分/合并逻辑,比如插入位置是数字时,未将数字拆分为对应空格数的前后部分,直接插入会破坏FEN格式。
修复方案
重新设计添加棋子的逻辑,基于修改后的curFEN重新计算位置,并处理数字的拆分与合并:
def ChangeFEN(Orig, New): global curFEN PrevX, PrevY = Orig NewX, NewY = New # --- 保留原移除棋子逻辑(确认正常工作)--- rows = curFEN.split("/") index = 0 for i in range(PrevY-1): index += len(rows[i]) + 1 space = 0 index2 = 0 for index2, i in enumerate(rows[PrevY-1]): if i.isdigit(): space += int(i) else: if space == PrevX-1: break else: space += 1 index3 = index + index2 piece = curFEN[index3] left_char = curFEN[index3-1] if index3 > 0 else "/" right_char = curFEN[index3+1] if index3+1 < len(curFEN) else "/" if left_char == "/": if right_char.isdigit(): curFEN = curFEN[:index3] + str(1 + int(right_char)) + curFEN[index3+2:] else: curFEN = curFEN[:index3] + "1" + curFEN[index3+1:] elif right_char == "/": if left_char.isdigit(): curFEN = curFEN[:index3-1] + str(int(left_char) + 1) + curFEN[index3+1:] else: curFEN = curFEN[:index3] + "1" + curFEN[index3+1:] else: if left_char.isdigit() and right_char.isdigit(): curFEN = curFEN[:index3-1] + str(int(left_char) + 1 + int(right_char)) + curFEN[index3+2:] elif left_char.isdigit(): curFEN = curFEN[:index3-1] + str(int(left_char) + 1) + curFEN[index3+1:] elif right_char.isdigit(): curFEN = curFEN[:index3] + str(1 + int(right_char)) + curFEN[index3+2:] else: curFEN = curFEN[:index3] + "1" + curFEN[index3+1:] # --- 修复后的添加棋子逻辑 --- # 基于修改后的FEN重新拆分行 new_rows = curFEN.split("/") target_row = new_rows[NewY-1] row_start_index = 0 # 计算目标行在FEN字符串中的起始位置 for i in range(NewY-1): row_start_index += len(new_rows[i]) + 1 space_count = 0 insert_pos_in_row = 0 digit_offset = 0 # 记录插入点在数字字符内的偏移 for idx, c in enumerate(target_row): if c.isdigit(): digit_val = int(c) if space_count + digit_val >= NewX: # 插入点在当前数字内部 insert_pos_in_row = idx digit_offset = NewX - space_count break space_count += digit_val else: space_count += 1 if space_count == NewX: insert_pos_in_row = idx + 1 digit_offset = 0 break # 修改目标行 modified_row = "" if digit_offset > 0: # 拆分数字,插入棋子 digit = int(target_row[insert_pos_in_row]) left_digit = digit - digit_offset right_digit = digit_offset - 1 # 插入棋子后空格数减1 modified_row = ( target_row[:insert_pos_in_row] + (str(left_digit) if left_digit > 0 else "") + piece + (str(right_digit) if right_digit > 0 else "") + target_row[insert_pos_in_row+1:] ) else: if insert_pos_in_row < len(target_row) and target_row[insert_pos_in_row].isdigit(): # 插入点在数字前,将数字减1 digit = int(target_row[insert_pos_in_row]) new_digit = digit - 1 modified_row = ( target_row[:insert_pos_in_row] + piece + (str(new_digit) if new_digit > 0 else "") + target_row[insert_pos_in_row+1:] ) else: # 直接插入棋子 modified_row = target_row[:insert_pos_in_row] + piece + target_row[insert_pos_in_row:] # 拼接新的FEN字符串 new_rows[NewY-1] = modified_row curFEN = "/".join(new_rows)
关键修复点
- 移除棋子后立即基于更新后的
curFEN重新拆分行,避免使用旧棋盘数据。 - 重构插入位置的计数逻辑,精准判断插入点是在数字内部还是字符间隙。
- 处理数字拆分/合并:插入点在数字内时拆分数字并插入棋子;插入点在数字前时将数字减1后拼接棋子,保证FEN格式合法。
内容的提问来源于stack exchange,提问作者Coder
相关产品推荐
相关产品推荐

