python으로 체스 게임 작성 [4일째]

17465 단어 codenewbiepython
코로나로 인해 우리는 여전히 봉쇄 상태로, 나는 이 시간을 내 12yo로 텍스트 장기 게임을 구현하고 있다.이 시리즈에서 나는 우리가 무엇을 했는지, 그리고 프로젝트가 어떻게 발전했는지 공유할 것이다.
우리는 에서 효과적인 이동 검사 테스트 세트를 끝냈는데, 흔히 볼 수 있는 장면 (목표 블록이 경계를 초과하거나 같은 색깔의 바둑알이 차지함) 과 rook 이동에 사용된다.
오늘 우리는 칠판 위에서 차 한 대를 옮기기로 결정했다.
우리는 이 인터페이스를 정의했다.
def move(board, piece, target_rank, target_file):
  pass
우리가 그것을 실현하기 위해 무엇을 필요로 하는지 고려할 때, 우리는 하나의 동작이 효과가 있는지 검사하는 것이 부족하다는 것을 깨닫고, 효과적인 부분이 어디에 있는지 알아야 한다.is_valid_move은 이 위치를 검증검사의 일부로 계산하기 때문에 검증방법 이외의 두 번째 방법이 아닌 둘을 결합시키는 것이 더 의미가 있다.
솔직히 말해서, 나는 우리가 결국 이렇게 할 것이라는 것을 알고 있지만, 나는 그로 하여금 스스로 생각하게 했다. 이것은 학습 과정의 일부분이다.또 다른 장점은 테스트의 가치를 보여주는 것이다. 우리가 다른 내용을 되돌리기 위해 방법을 바꾼 후에 우리는 기존의 테스트에 대해 약간의 변경을 했다. 테스트가 통과되었을 때 우리는 자신감을 가지게 되고 기능이 어떠한 후퇴도 일으키지 않을 것이다.
다음은 우리가 바꾼 것이다.
def find_valid_move(board, piece, target_rank, target_file):
  # out of bounds
  if  target_rank < 0 or target_rank > 7:
    return -1, -1
  if  target_file < 0 or target_file > 7:
    return -1, -1
  # piece with same color is in the target square
  if board[target_rank][target_file][0] == piece[0]:
    return -1, -1

  if piece in ("WR", "BR"):
    return find_valid_rook_move(board, piece, target_rank, target_file)

  return -1, -1

def find_valid_rook_move(board, piece, target_rank, target_file):
  found_rank = -1
  found_file = -1
  for i in range(8):
    if board[target_rank][i] == piece:
      found_rank = target_rank
      found_file = i
      break

  if found_rank == -1:
    for i in range(8):
      if board[i][target_file] == piece:
        found_rank = i
        found_file = target_file
        break

  if found_rank < 0 or found_file < 0:
    return -1, -1 

  if found_rank == target_rank:
    start_file = min(target_file+1, found_file+1)
    end_file = max(target_file, found_file)
    for i in range(start_file, end_file):
        if board[target_rank][i] != EMPTY:
          return -1, -1
  else: # found_file == target_file
    start_rank = min(target_rank+1, found_rank+1)
    end_rank = max(target_rank, found_rank)
    for i in range(start_rank, end_rank):
        if board[i][target_file] != EMPTY:
          return -1, -1
  return found_rank, found_file
이것은 완전히 같은 논리입니다. 유효한 부분을 찾지 못했을 때만 되돌아갑니다. (-1, -1) 유효한 부분을 찾으면 되돌아갑니다. (find rank,find file)
다음은 다음과 같이 move 로직을 구현했습니다.
def move(board, piece, target_rank, target_file):
  found_rank, found_file = find_valid_move(board, piece, 
  target_rank, target_file)
  if (found_rank, found_file) != (-1, -1):
    board[target_rank][target_file] = piece
    board[found_rank][found_file] = EMPTY
    return True
  return False
다음은 move이 예상대로 작동하는지 확인하기 위한 소형 수동 테스트입니다.
board = create_board()
board[1][0] = EMPTY
result = move(board, "WR", 5, 0)
print_board(board)
놀라운 결과:
   ---- ---- ---- ---- ---- ---- ---- ----
8 | BR | BN | BB | BQ | BK | BB | BN | BR |
   ---- ---- ---- ---- ---- ---- ---- ----
7 | BP | BP | BP | BP | BP | BP | BP | BP |
   ---- ---- ---- ---- ---- ---- ---- ----
6 | WR |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
5 | WR |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
4 | WR |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
3 | WR |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
2 |    | WP | WP | WP | WP | WP | WP | WP |
   ---- ---- ---- ---- ---- ---- ---- ----
1 |    | WN | WB | WQ | WK | WB | WN | WR |
   ---- ---- ---- ---- ---- ---- ---- ----
    a    b    c    d    e    f    g    h  
12yo는 이 점을 이해할 수 없지만, 나는 곧 무슨 일이 일어났는지 깨달았다. 나는 더 잘 알아야 한다.아이고.다음 명령을 사용하여 보드를 초기화했습니다.
board = [[EMPTY]*8]*8
이것은 목록을 만들고 8번 복사합니다.우리가 5위를 바꿀 때, 그것은 모든 중간 줄에서 바뀌었다. (다른 줄은 조각이 포함된 줄로 바뀌었다.)크게 틀리다.그러지 마.
12yo는 어떤 부분에 문제가 생겼는지, 왜 문제가 생겼는지 이해할 때 어려움을 겪었다. 그래서 나는 간단한 예시를 만들어서 설명했다.
sub_list = [1,2,3]
test_list = [sub_list]*3
print(test_list)
sub_list[0] = 5
print(test_list)
그는 "아, 그래...모든 목록의 항목을 바로 바꿀 거야."라고 말했기 때문에 나는 그가 이해했다고 생각한다.
출력:
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
[[5, 2, 3], [5, 2, 3], [5, 2, 3]]
일단 우리가 이 문제를 해결하면, 그것은 정상적으로 일할 것이다.
12yo는 다음 테스트를 작성했습니다.
def test_move_rook():
  board = create_board()
  board[1][0] = EMPTY
  result = move(board, "WR", 5, 0)
  assert result
  print("test_move_rook passed")
괜찮다그러나 나는 그에게 이것이 우리가 수동으로 테스트를 실행할 때 본 문제를 발견할 수 있느냐고 물었다.그는'아니야'라고 말하고 싶었다.테스트에 expected_board비트를 추가했습니다.
def test_move_rook():
  board = create_board()
  board[1][0] = EMPTY

  expected_board = create_board()
  expected_board[1][0] = EMPTY
  expected_board[0][0] = EMPTY
  expected_board[5][0] = "WR"

  result = move(board, "WR", 5, 0)
  assert result
  assert board == expected_board
  print("test_move_rook passed")
곧 올 거라는 건 알지만, 너무 빨리 합류하고 싶지 않아...그러나 지금은 때가 되었다. 만약 두 개의 효과적인 장면이 있다면 무슨 일이 일어날까?12yo는 가능한 위치를 계산하는 것이 아니라 우리가 공작물을 어디에서 이동할 것인지, 목표를 미리 지정하는 나의 최초의 생각을 받아들이지 않은 것을 후회하기 시작했다. 그러나 나는 지금 포기하지 않을 것이다.
그는 두 개의 일치하는 바둑알이 있을 때, 유저는 바둑알의 등급을 지정하지만, 일을 간단하게 하기 위해서, 나는 우리가 등급을 지정하는 것을 건의한다.그가 본 바와 같이, 나의 건의는 통상적으로 합리적이어서, 그는 이 생각을 받아들이기로 결정했다.find_valid_rook_move 방법을 다음과 같이 변경합니다.
  • 이동할 항목을 지정할 수 있도록 source_ranksource_file을 추가했습니다.
  • 일치하는 부품을 찾은 후 중지하지 않고 found_pieces 목록에 추가했습니다.
  • 조각을 찾지 못하면 -1, -1
  • 으로 돌아가기
  • 우리가 찾은 것이 한 가지가 아니라면 주어진 source_ranksource_file이 일치하는 것을 찾아보세요.
  • 일치하는 부품이 하나만 있는 경우 사용합니다.
  • 그리고 계속해서 다른 부품이 그것을 막지 않았는지 확인하세요.
  • def find_valid_rook_move(board, piece, target_rank, target_file, source_rank=-1, source_file=-1):
      found_pieces = []
      for i in range(8):
        if board[target_rank][i] == piece:
          found_rank = target_rank
          found_file = i
          found_pieces.append((found_rank, found_file))
    
      for i in range(8):
        if board[i][target_file] == piece:
          found_rank = i
          found_file = target_file
          found_pieces.append((found_rank, found_file))
    
      if len(found_pieces) == 0:
        return -1, -1 
    
      # don't know which item to choose
      found_rank = -1
      found_file = -1
      if (len(found_pieces) > 1):
        if source_rank < 0 or source_file < 0:
          return -1, -1
        for rank, file in found_pieces:
          if rank == source_rank and file == source_file:
            found_rank = rank
            found_file = file
      else:
        found_rank, found_file = found_pieces[0]
    
      if (found_rank, found_file) == (-1, -1):
        return -1, -1 
    
      if found_rank == target_rank:
        start_file = min(target_file+1, found_file+1)
        end_file = max(target_file, found_file)
        for i in range(start_file, end_file):
            if board[target_rank][i] != EMPTY:
              return -1, -1
      else: # found_file == target_file
        start_rank = min(target_rank+1, found_rank+1)
        end_rank = max(target_rank, found_rank)
        for i in range(start_rank, end_rank):
            if board[i][target_file] != EMPTY:
              return -1, -1
      return found_rank, found_file
    
    테스트가 하나 더 추가되었습니다.
    def test_double_move_rook():
      board = create_board()
      board[1][0] = EMPTY
      board[0][7] = EMPTY
      board[5][0] = "WR"
      result = move(board, "WR", 3, 0)
      assert not result
    
      expected_board = create_board()
      expected_board[1][0] = EMPTY
      expected_board[0][7] = EMPTY
      expected_board[5][0] = "WR"
      expected_board[3][0] = "WR"
      expected_board[0][0] = EMPTY
      result = move(board, "WR", 3, 0, 0, 0)
      assert result
      assert board == expected_board
    
      print("test_double_move_rook passed")
    
    우리가 검사해야 할 장면이 하나 더 있는데, 나는 이미 그것이 통과하지 못할 것이라는 것을 알고 있다.만약 회로판에 일치하는 부품이 두 개 있는데, 그 중 하나가 막히면 어떤 상황이 발생합니까?나는 12yo에게 테스트를 작성하라고 한 후에 실패의 원인을 찾아내려고 했다.
      ...
      board = create_board()
      board[0][7] = EMPTY
      board[5][0] = "WR"
      expected_board = create_board()
      expected_board[0][7] = EMPTY
      expected_board[3][0] = "WR"
      expected_board[0][0] = "WR"
      result = move(board, "WR", 3, 0)
      assert result
      assert board == expected_board
    
    우리는 print의 서로 다른 위치에 find_valid_rook_move 명령을 추가했다. 우리는 우리가 공작물을 선택한 후에 유효성을 검사했기 때문에 우리가 충분한 정보(즉 source_ranksource_file이 너무 이르다는 결론을 얻었다.
    우리가 valid_piecessource_rank이 필요하다는 것을 검증하기 전에 우리는 논리를 바꾸었고 source_file을 찾았으며 성공했다(12yo는 내가 논리를 완성하도록 인도한 후에 이러한 변경을 했다)👏):
    def find_valid_rook_move(board, piece, target_rank, target_file, source_rank=-1, source_file=-1):
      found_pieces = []
      for i in range(8):
        if board[target_rank][i] == piece:
          found_rank = target_rank
          found_file = i
          found_pieces.append((found_rank, found_file))
    
      for i in range(8):
        if board[i][target_file] == piece:
          found_rank = i
          found_file = target_file
          found_pieces.append((found_rank, found_file))
    
      if len(found_pieces) == 0:
        return -1, -1 
    
      valid_pieces = []
    
      for found_rank, found_file in found_pieces:
        is_valid = True
        if found_rank == target_rank:
          start_file = min(target_file+1, found_file+1)
          end_file = max(target_file, found_file)
          for i in range(start_file, end_file):
              if board[target_rank][i] != EMPTY:
                is_valid = False
                break
        else: # found_file == target_file
          start_rank = min(target_rank+1, found_rank+1)
          end_rank = max(target_rank, found_rank)
          for i in range(start_rank, end_rank):
              if board[i][target_file] != EMPTY:
                is_valid = False
                break
        if is_valid:
          valid_pieces.append((found_rank, found_file))
    
      # don't know which item to choose
      found_rank = -1
      found_file = -1
      if len(valid_pieces) > 1:
        if source_rank < 0 or source_file < 0:
          return -1, -1
        for rank, file in valid_pieces:
          if rank == source_rank and file == source_file:
            found_rank = rank
            found_file = file
      elif len(valid_pieces) == 1:
        found_rank, found_file = valid_pieces[0]
    
      return found_rank, found_file
    
    현재 우리는 모두 템플릿을 설치하여 모든 다른 부분을 이동할 수 있기 때문에 나는 우리가 다음 부분을 더욱 빨리 완성할 수 있기를 바란다.
    다음에 또 오세요!
    오늘의 최종 코드(수정된 테스트 포함):
    
    EMPTY = "  "
    
    def print_board(board):
      row_number = 8
      print("  ", end="")
      print(" ----"*8)
      for row in reversed(board):
          print(row_number, end=" ")
          row_number -= 1
          for cell in row:
              print("| {} ".format(cell), end="")
          print("|")
          print("  ", end="")
          print(" ----"*8)
      print("  ", end="")
      for letter in ['a','b','c','d','e','f','g','h']:
          print("  {}  ".format(letter), end="")
      print("")
    
    def create_board():
      board = []
    
      board.append(["WR","WN","WB","WQ","WK","WB","WN","WR"])
      board.append(["WP","WP","WP","WP","WP","WP","WP","WP"])
      for i in range(2, 6):
        board.append([EMPTY]*8)
      board.append(["BP","BP","BP","BP","BP","BP","BP","BP"])
      board.append(["BR","BN","BB","BQ","BK","BB","BN","BR"])
    
      return board
    
    def find_valid_move(board, piece, target_rank, target_file, source_rank=-1, source_file=-1):
      # out of bounds
      if  target_rank < 0 or target_rank > 7:
        return -1, -1
      if  target_file < 0 or target_file > 7:
        return -1, -1
      # piece with same color is in the target cell
      if board[target_rank][target_file][0] == piece[0]:
        return -1, -1
    
      if piece in ("WR", "BR"):
        return find_valid_rook_move(board, piece, target_rank, target_file, source_rank, source_file)
    
      return -1, -1
    
    def find_valid_rook_move(board, piece, target_rank, target_file, source_rank=-1, source_file=-1):
      found_pieces = []
      for i in range(8):
        if board[target_rank][i] == piece:
          found_rank = target_rank
          found_file = i
          found_pieces.append((found_rank, found_file))
    
      for i in range(8):
        if board[i][target_file] == piece:
          found_rank = i
          found_file = target_file
          found_pieces.append((found_rank, found_file))
    
      if len(found_pieces) == 0:
        return -1, -1 
    
      valid_pieces = []
    
      for found_rank, found_file in found_pieces:
        is_valid = True
        if found_rank == target_rank:
          start_file = min(target_file+1, found_file+1)
          end_file = max(target_file, found_file)
          for i in range(start_file, end_file):
              if board[target_rank][i] != EMPTY:
                is_valid = False
                break
        else: # found_file == target_file
          start_rank = min(target_rank+1, found_rank+1)
          end_rank = max(target_rank, found_rank)
          for i in range(start_rank, end_rank):
              if board[i][target_file] != EMPTY:
                is_valid = False
                break
        if is_valid:
          valid_pieces.append((found_rank, found_file))
    
      # don't know which item to choose
      found_rank = -1
      found_file = -1
      if len(valid_pieces) > 1:
        if source_rank < 0 or source_file < 0:
          return -1, -1
        for rank, file in valid_pieces:
          if rank == source_rank and file == source_file:
            found_rank = rank
            found_file = file
      elif len(valid_pieces) == 1:
        found_rank, found_file = valid_pieces[0]
    
      return found_rank, found_file
    
    
    def move(board, piece, target_rank, target_file, source_rank=-1, source_file=-1):
      found_rank, found_file = find_valid_move(board, piece, 
      target_rank, target_file, source_rank, source_file)
      if (found_rank, found_file) != (-1, -1):
        board[target_rank][target_file] = piece
        board[found_rank][found_file] = EMPTY
        return True
      return False
    
    # ----------------------- tests -------------------
    
    def test_move_rook():
      board = create_board()
      board[1][0] = EMPTY
    
      expected_board = create_board()
      expected_board[1][0] = EMPTY
      expected_board[0][0] = EMPTY
      expected_board[5][0] = "WR"
    
      result = move(board, "WR", 5, 0)
      assert result
      assert board == expected_board
      print("test_move_rook passed")
    
    def test_double_move_rook():
      board = create_board()
      board[1][0] = EMPTY
      board[0][7] = EMPTY
      board[5][0] = "WR"
      result = move(board, "WR", 3, 0)
      assert not result
    
      expected_board = create_board()
      expected_board[1][0] = EMPTY
      expected_board[0][7] = EMPTY
      expected_board[5][0] = "WR"
      expected_board[3][0] = "WR"
      expected_board[0][0] = EMPTY
      result = move(board, "WR", 3, 0, 0, 0)
      assert result
      assert board == expected_board
    
      board = create_board()
      board[0][7] = EMPTY
      board[5][0] = "WR"
      expected_board = create_board()
      expected_board[0][7] = EMPTY
      expected_board[3][0] = "WR"
      expected_board[0][0] = "WR"
      result = move(board, "WR", 3, 0)
      assert result
      assert board == expected_board
      print("test_double_move_rook passed")
    
    def test_is_rank_valid():
      board = create_board()
      result = find_valid_move(board, "xx", 22, 4)
      assert result == (-1, -1)
      result = find_valid_move(board, "xx", -2020, 4)
      assert result == (-1, -1)
      print("test_is_rank_valid passed")
    
    def test_is_file_valid():
      board = create_board()
      result = find_valid_move(board, "xx", 0, 22)
      assert result == (-1, -1)
      result = find_valid_move(board, "xx", 0, -2020)
      assert result == (-1, -1)
      print("test_is_file_valid passed")
    
    def test_is_target_square_taken():
      board = create_board()
      result = find_valid_move(board, "Wx", 0, 1)
      assert result == (-1, -1)
      print("test_is_target_square_taken passed")
    
    def test_rook_is_blocked():
      board = create_board()
      result = find_valid_rook_move(board, "BR", 5, 0)
      assert result == (-1, -1)
      board[7][5] = EMPTY
      result = find_valid_rook_move(board, "BR", 7, 5)
      assert result == (-1, -1)
      print("test_rook_is_blocked passed")
    
    def test_rook_is_not_blocked():
      board = create_board()
      board[1][0] = EMPTY
      result = find_valid_rook_move(board, "WR", 2, 0)
      assert result == (0,0)
      board[0][1] = EMPTY
      result = find_valid_rook_move(board, "WR", 0, 1)
      assert result == (0,0)
      print("test_rook_is_not_blocked passed")
    
    def run_tests():
      test_is_rank_valid()
      test_is_file_valid()
      test_is_target_square_taken()
      test_rook_is_blocked()
      test_rook_is_not_blocked()
      test_move_rook()
      test_double_move_rook()
    run_tests()
    

    좋은 웹페이지 즐겨찾기