text: EMPTY = 0
  BLACK = 1
  WHITE = 2
  BORDER = 3
  FLOODFILL = 4
  import numpy as np
  import random
  import copy
  from pattern import pat3set
  import sys
  
  
  class GoBoardUtil(object):
      
      @staticmethod
      def generate_legal_moves(board,color):
          """
          generate a list of legal moves
  
          Arguments
          ---------
          board : np.array
              a SIZExSIZE array representing the board
          color : {'b','w'}
              the color to generate the move for.
          """
          moves = board.get_empty_points()
          num_moves = len(moves)
          np.random.shuffle(moves)
          illegal_moves = []
  
          for i in range(num_moves):
              if board.check_legal(moves[i],color):
                  continue
              else:
                  illegal_moves.append(i)
          legal_moves = np.delete(moves,illegal_moves)
          gtp_moves=[]
          for point in legal_moves:
              x,y = board._point_to_coord(point)
              gtp_moves.append(GoBoardUtil.format_point((x,y)))
          sorted_moves = ' '.join(sorted(gtp_moves))
          return sorted_moves
      
      
      @staticmethod       
      def generate_random_move(board, color, is_eye_filter):
          """
          generate a random move
  
          Arguments
          ---------
          board : np.array
              a SIZExSIZE array representing the board
          color : {'b','w'}
              the color to generate the move for.
          """
          moves = board.get_empty_points()
          num_moves = len(moves)
          np.random.shuffle(moves)
          for i in range(num_moves):
              if is_eye_filter and board.is_eye(moves[i],color):
                  continue
              move = moves[i]
              legal = board.check_legal(move,color)
              if not legal:
                  continue
              return move
          return None
      
  
      @staticmethod
      def format_point(move):
          """
          Return coordinates as a string like 'a1', or 'pass'.
  
          Arguments
          ---------
          move : (row, col), or None for pass
  
          Returns
          -------
          The move converted from a tuple to a Go position (e.g. D4)
          """
          column_letters = "abcdefghjklmnopqrstuvwxyz"
          if move is None:
              return "pass"
          row, col = move
          if not 0 <= row < 25 or not 0 <= col < 25:
              raise ValueError
          return    column_letters[col-1]+ str(row) 
          
      @staticmethod
      def move_to_coord(point, board_size):
          """
          Interpret a string representing a point, as specified by GTP.
  
          Arguments
          ---------
          point : str
              the point to convert to a tuple
          board_size : int
              size of the board
  
          Returns
          -------
          a pair of coordinates (row, col) in range(1, board_size+1)
  
          Raises
          ------
          ValueError : 'point' isn't a valid GTP point specification for a board of size 'board_size'.
          """
          if not 0 < board_size <= 25:
              raise ValueError("board_size out of range")
          try:
              s = point.lower()
          except Exception:
              raise ValueError("invalid point")
          if s == "pass":
              return None
          try:
              col_c = s[0]
              if (not "a" <= col_c <= "z") or col_c == "i":
                  raise ValueError
              if col_c > "i":
                  col = ord(col_c) - ord("a")
              else:
                  col = ord(col_c) - ord("a") + 1
              row = int(s[1:]) 
              if row < 1:
                  raise ValueError
          except (IndexError, ValueError):
              raise ValueError("invalid point: '%s'" % s)
          if not (col <= board_size and row <= board_size):
              raise ValueError("point is off board: '%s'" % s)
          return row, col
      
      @staticmethod
      def opponent(color):
          opponent = {WHITE:BLACK, BLACK:WHITE} 
          try:
              return opponent[color]    
          except:
              raise ValueError("Wrong color provided for opponent function")
              
      @staticmethod
      def color_to_int(c):
          """convert character representing player color to the appropriate number"""
          color_to_int = {"b": BLACK , "w": WHITE, "e":EMPTY, "BORDER":BORDER, "FLOODFILL":FLOODFILL}
          try:
             return color_to_int[c] 
          except:
              raise ValueError("Valid color characters are: b, w, e, BORDER and FLOODFILL. please provide the input in this format ")
      
      @staticmethod
      def int_to_color(i):
          """convert number representing player color to the appropriate character """
          int_to_color = {BLACK:"b", WHITE:"w", EMPTY:"e", BORDER:"BORDER", FLOODFILL:"FLOODFILL"}
          try:
             return int_to_color[i] 
          except:
              raise ValueError("Provided integer value for color is invalid")
           
      @staticmethod
      def copyb2b(board,copy_board):
          """Return an independent copy of this Board."""
          copy_board.__dict__ = copy.deepcopy(board.__dict__)
          assert copy_board.board.all() == board.board.all()
          return copy_board
  
      @staticmethod
      def sorted_point_string(points, ns):
          result = []
          for point in points:
              x, y = GoBoardUtil.point_to_coord(point, ns)
              result.append(GoBoardUtil.format_point((x, y)))
          return ' '.join(sorted(result))
  
      @staticmethod
      def generate_pattern_moves(board):
          color = board.current_player
          pattern_checking_set = board.last_moves_empty_neighbors()
          moves = []
          for p in pattern_checking_set:
              if (board.neighborhood_33(p) in pat3set):
                  assert p not in moves
                  assert board.board[p] == EMPTY
                  moves.append(p)
          return moves
          
      @staticmethod
      def generate_all_policy_moves(board,pattern,check_selfatari):
          """
              generate a list of policy moves on board for board.current_player.
              Use in UI only. For playing, use generate_move_with_filter
              which is more efficient
          """
          if pattern:
              pattern_moves = []
              pattern_moves = GoBoardUtil.generate_pattern_moves(board)
              pattern_moves = GoBoardUtil.filter_moves(board, pattern_moves, check_selfatari)
              if len(pattern_moves) > 0:
                  return pattern_moves, "Pattern"
          return GoBoardUtil.generate_random_moves(board,True), "Random"
  
      @staticmethod 
      def filter_moves_and_generate(board, moves, check_selfatari):
          color = board.current_player
          while len(moves) > 0:
              candidate = random.choice(moves)
              if GoBoardUtil.filter(board, candidate, color, check_selfatari):
                  moves.remove(candidate)
              else:
                  return candidate
          return None
  
      @staticmethod
      def filter_moves(board, moves, check_selfatari):
          color = board.current_player
          good_moves = []
          for move in moves:
              if not GoBoardUtil.filter(board,move,color,check_selfatari):
                  good_moves.append(move)
          return good_moves
  
      # return True if move should be filtered
      @staticmethod
      def filleye_filter(board, move, color):
          assert move != None
          return not board.check_legal(move, color) or board.is_eye(move, color)
      
      # return True if move should be filtered
      @staticmethod
      def selfatari_filter(board, move, color):
          return (  GoBoardUtil.filleye_filter(board, move, color)
                 or GoBoardUtil.selfatari(board, move, color)
                 )
  
      # return True if move should be filtered
      @staticmethod
      def filter(board, move, color, check_selfatari):
          if check_selfatari:
              return GoBoardUtil.selfatari_filter(board, move, color)
          else:
              return GoBoardUtil.filleye_filter(board, move, color)
  
      @staticmethod
      def generate_random_moves(board,is_eye_filter):
          empty_points = board.get_empty_points()
          color = board.current_player
          moves = []
          for move in empty_points:
              if is_eye_filter and board.is_eye(move,color):
                  continue 
              if board.check_legal(move, color):
                  moves.append(move)
          return moves
  
      @staticmethod 
      def generate_move_with_filter(board, use_pattern, check_selfatari):
          """
              Arguments
              ---------
              check_selfatari: filter selfatari moves?
                  Note that even if True, this filter only applies to pattern moves
              use_pattern: Use pattern policy?
          """
          move = None
          if use_pattern:
              moves = GoBoardUtil.generate_pattern_moves(board)
              move = GoBoardUtil.filter_moves_and_generate(board, moves, 
                                                           check_selfatari)
          if move == None:
              move = GoBoardUtil.generate_random_move(board, board.current_player,True)
          return move 
      
      @staticmethod
      def selfatari(board, move, color):
          max_old_liberty = GoBoardUtil.blocks_max_liberty(board, move, color, 2)
          if max_old_liberty > 2:
              return False
          cboard = board.copy()
          # swap out true board for simulation board, and try to play the move
          isLegal = cboard.move(move, color) 
          if isLegal:               
              new_liberty = cboard._liberty(move,color)
              if new_liberty==1:
                  return True 
          return False
  
      @staticmethod
      def blocks_max_liberty(board, point, color, limit):
          assert board.board[point] == EMPTY
          max_lib = -1 # will return this value if this point is a new block
          neighbors = board._neighbors(point)
          for n in neighbors:
              if board.board[n] == color:
                  num_lib = board._liberty(n,color) 
                  if num_lib > limit:
                      return num_lib
                  if num_lib > max_lib:
                      max_lib = num_lib
          return max_lib
  
      @staticmethod
      def point_to_coord(point, ns):
          """
          Transform one dimensional point presentation to two dimensional.
  
          Arguments
          ---------
          point
  
          Returns
          -------
          x , y : int
                  coordinates of the point  1 <= x, y <= size
          """
          if point is None:
              return 'pass'
          row, col = divmod(point, ns)
          return row,col
  
      @staticmethod
      def playGame(board, color, **kwargs):
          komi = kwargs.pop('komi', 0)
          limit = kwargs.pop('limit', 1000)
          random_simulation = kwargs.pop('random_simulation',True)
          use_pattern = kwargs.pop('use_pattern',True)
          check_selfatari = kwargs.pop('check_selfatari',True)
          if kwargs:
              raise TypeError('Unexpected **kwargs: %r' % kwargs)
          for _ in range(limit):
              if random_simulation:
                  move = GoBoardUtil.generate_random_move(board,color,True)
              else:
                  move = GoBoardUtil.generate_move_with_filter(board,use_pattern,check_selfatari)
              isLegalMove = board.move(move,color)
              assert isLegalMove
              if board.end_of_game():
                  break
              color = GoBoardUtil.opponent(color)
          winner,_ = board.score(komi)  
          return winner
  
  