docs: def all_legal_moves(self):
          'Returns a np.array of size go.N**2 + 1, with 1 = legal, 0 = illegal'
          # by default, every move is legal
          legal_moves = np.ones([N, N], dtype=np.int8)
          # ...unless there is already a stone there
          legal_moves[self.board != EMPTY] = 0
          # calculate which spots have 4 stones next to them
          # padding is because the edge always counts as a lost liberty.
          adjacent = np.ones([N + 2, N + 2], dtype=np.int8)
          adjacent[1:-1, 1:-1] = np.abs(self.board)
          num_adjacent_stones = (adjacent[:-2, 1:-1] + adjacent[1:-1, :-2] +
                                 adjacent[2:, 1:-1] + adjacent[1:-1, 2:])
          # Surrounded spots are those that are empty and have 4 adjacent stones.
          surrounded_spots = np.multiply(
              (self.board == EMPTY),
              (num_adjacent_stones == 4))
          # Such spots are possibly illegal, unless they are capturing something.
          # Iterate over and manually check each spot.
          for coord in np.transpose(np.nonzero(surrounded_spots)):
              if self.is_move_suicidal(tuple(coord)):
                  legal_moves[tuple(coord)] = 0
  
          # ...and retaking ko is always illegal
          if self.ko is not None:
              legal_moves[self.ko] = 0
  
          # and pass is always legal
          return np.concatenate([legal_moves.ravel(), [1]])