aac-metrics 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/aac-metrics/loader.rb +49 -28
 - data/lib/aac-metrics/metrics.rb +139 -42
 - data/sets/base_words.en.json +894 -0
 - data/sets/pc36-6616542987.obfset +207 -0
 - data/sets/qc112-a67653dd81.en.obfset +4892 -2015
 - data/sets/qc24-bcbee2ea83.en.obfset +1331 -481
 - data/sets/qc40-5ceeb28275.obfset +11385 -0
 - data/sets/qc60-61bbf6171e.common.en.obfset +2714 -1224
 - data/sets/qc84-d83fa84056.obfset +24996 -0
 - data/sets/sfy-c45a81c416.common.en.obfset +863 -776
 - data/sets/vf112-253f46504a.obfset +28601 -0
 - data/sets/vf24-9d3442b880.obfset +13055 -0
 - data/sets/vf40-0355b86ede.obfset +16088 -0
 - data/sets/vf60-a37d6d6577.obfset +20557 -0
 - data/sets/vf84-02166358c0.obfset +25571 -0
 - data/sets/wp20-163743e671.obfset +39129 -0
 - data/sets/wp42-f67dc6f4a9.obfset +84948 -0
 - data/sets/wp60-ce5120b0d2.obfset +106281 -0
 - metadata +13 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 554fc52ede0b5dffdcf9315ec279a9f42a3ef2a44a324195018eda56538336b1
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 2b082df38edf52ddf495045969db894ef02f7718641658d8d1ca4b012456e14c
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 58c3c7e8128b0bee8754baa3b218a36491cf985ed752b917733a0508bc9a4c8abe8c3e2c40ddcb7cd9098bea51a6c3d9143f5ba4cbd2d9098f59f16899bf1e0b
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c9e2ec540feee357aaf6b1a20b11e5b938d2f4f8d28ba6d03eee80245167b1eb339ab5a2e1440588db6a31cffbe27bee37a44d6ef35f0078f5381d363c06226c
         
     | 
    
        data/lib/aac-metrics/loader.rb
    CHANGED
    
    | 
         @@ -37,6 +37,29 @@ module AACMetrics::Loader 
     | 
|
| 
       37 
37 
     | 
    
         
             
                  end
         
     | 
| 
       38 
38 
     | 
    
         
             
                  if obfset
         
     | 
| 
       39 
39 
     | 
    
         
             
                    json = JSON.parse(File.read(obfset))
         
     | 
| 
      
 40 
     | 
    
         
            +
                    relations_hash = {}
         
     | 
| 
      
 41 
     | 
    
         
            +
                    json.each do |board|
         
     | 
| 
      
 42 
     | 
    
         
            +
                      board['grid']['order'].each_with_index do |row, row_idx|
         
     | 
| 
      
 43 
     | 
    
         
            +
                        row.each_with_index do |id, col_idx|
         
     | 
| 
      
 44 
     | 
    
         
            +
                          button = id && board['buttons'].detect{|b| b['id'] == id }
         
     | 
| 
      
 45 
     | 
    
         
            +
                          if button && button['label'] && !button['clone_id']
         
     | 
| 
      
 46 
     | 
    
         
            +
                            ref = "#{board['grid']['rows']}x#{board['grid']['columns']}-#{row_idx}.#{col_idx}"
         
     | 
| 
      
 47 
     | 
    
         
            +
                            pre = 'c'
         
     | 
| 
      
 48 
     | 
    
         
            +
                            relations_hash["cpre#{ref}-#{button['label']}"] ||= []
         
     | 
| 
      
 49 
     | 
    
         
            +
                            relations_hash["cpre#{ref}-#{button['label']}"] << [board, button]
         
     | 
| 
      
 50 
     | 
    
         
            +
                          end
         
     | 
| 
      
 51 
     | 
    
         
            +
                        end
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                    relations_hash.each do |id, cells|
         
     | 
| 
      
 55 
     | 
    
         
            +
                      if cells.length > 1
         
     | 
| 
      
 56 
     | 
    
         
            +
                        cells.each do |board, button|
         
     | 
| 
      
 57 
     | 
    
         
            +
                          board['clone_ids'] ||= []
         
     | 
| 
      
 58 
     | 
    
         
            +
                          board['clone_ids'] << id
         
     | 
| 
      
 59 
     | 
    
         
            +
                          button['clone_id'] ||= id
         
     | 
| 
      
 60 
     | 
    
         
            +
                        end
         
     | 
| 
      
 61 
     | 
    
         
            +
                      end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
       40 
63 
     | 
    
         
             
                  elsif analysis
         
     | 
| 
       41 
64 
     | 
    
         
             
                    json = JSON.parse(File.read(analysis))
         
     | 
| 
       42 
65 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -84,6 +107,7 @@ module AACMetrics::Loader 
     | 
|
| 
       84 
107 
     | 
    
         
             
                  path = paths.shift
         
     | 
| 
       85 
108 
     | 
    
         
             
                  visited_paths[path] = idx
         
     | 
| 
       86 
109 
     | 
    
         
             
                  new_json = {
         
     | 
| 
      
 110 
     | 
    
         
            +
                    "format" => 'obs',
         
     | 
| 
       87 
111 
     | 
    
         
             
                    "id" => "brd#{idx}",
         
     | 
| 
       88 
112 
     | 
    
         
             
                    "buttons" => [],
         
     | 
| 
       89 
113 
     | 
    
         
             
                    "grid" => {},
         
     | 
| 
         @@ -158,28 +182,29 @@ module AACMetrics::Loader 
     | 
|
| 
       158 
182 
     | 
    
         
             
                            # treat action buttons for metrics
         
     | 
| 
       159 
183 
     | 
    
         
             
                            new_btn = nil
         
     | 
| 
       160 
184 
     | 
    
         
             
                          end
         
     | 
| 
       161 
     | 
    
         
            -
                          # temporarily save semantic_id and  
     | 
| 
      
 185 
     | 
    
         
            +
                          # temporarily save semantic_id and possibly clone_id for later use
         
     | 
| 
       162 
186 
     | 
    
         
             
                          # 1. Buttons in the same location with the same
         
     | 
| 
       163 
187 
     | 
    
         
             
                          # semantic_id should be marked in the obfset as having
         
     | 
| 
       164 
188 
     | 
    
         
             
                          # the same semantic_id
         
     | 
| 
       165 
189 
     | 
    
         
             
                          # 2. Buttons in the same location with the same label & voc
         
     | 
| 
       166 
190 
     | 
    
         
             
                          # and same load_board setting
         
     | 
| 
       167 
191 
     | 
    
         
             
                          # should be marked in the obfset as having the same clone_id
         
     | 
| 
       168 
     | 
    
         
            -
                          ref = "#{new_json['grid']['rows']}x#{new_json['grid']['columns']}-#{ 
     | 
| 
      
 192 
     | 
    
         
            +
                          ref = "#{new_json['grid']['rows']}x#{new_json['grid']['columns']}-#{row_idx}.#{col_idx}"
         
     | 
| 
       169 
193 
     | 
    
         
             
                          if btn['semantic_id']
         
     | 
| 
       170 
194 
     | 
    
         
             
                            relations_hash["s#{ref}-#{btn['semantic_id']}"] ||= []
         
     | 
| 
       171 
195 
     | 
    
         
             
                            relations_hash["s#{ref}-#{btn['semantic_id']}"] << [new_json['id'], new_btn['id']]
         
     | 
| 
       172 
196 
     | 
    
         
             
                          end
         
     | 
| 
       173 
     | 
    
         
            -
                          if new_btn['label']
         
     | 
| 
       174 
     | 
    
         
            -
                            #  
     | 
| 
       175 
     | 
    
         
            -
                            pre =  
     | 
| 
      
 197 
     | 
    
         
            +
                          if new_btn && new_btn['label']
         
     | 
| 
      
 198 
     | 
    
         
            +
                            #pre = new_btn['load_board'] ? 'cl' : 'c'
         
     | 
| 
      
 199 
     | 
    
         
            +
                            pre = 'c'
         
     | 
| 
       176 
200 
     | 
    
         
             
                            relations_hash["#{pre}#{ref}-#{new_btn['label']}"] ||= []
         
     | 
| 
       177 
     | 
    
         
            -
                            relations_hash["#{pre}#{ref}-#{new_btn['label']}"]  
     | 
| 
      
 201 
     | 
    
         
            +
                            relations_hash["#{pre}#{ref}-#{new_btn['label']}"] << [new_json['id'], new_btn['id']]
         
     | 
| 
       178 
202 
     | 
    
         
             
                          end
         
     | 
| 
       179 
     | 
    
         
            -
                          if do_ingest && new_btn['label']
         
     | 
| 
      
 203 
     | 
    
         
            +
                          if do_ingest && new_btn && new_btn['label']
         
     | 
| 
       180 
204 
     | 
    
         
             
                            str = new_btn['label'].downcase.sub(/^\s+/, '').sub(/\s+$/, '')
         
     | 
| 
       181 
205 
     | 
    
         
             
                            if str.scan(/\s+/).length < 2
         
     | 
| 
       182 
206 
     | 
    
         
             
                              word_hash = Digest::MD5.hexdigest(str)[0, 10]
         
     | 
| 
      
 207 
     | 
    
         
            +
                              # Will need to re-evaluate hash process if it ever finds a collision with an already-saved word
         
     | 
| 
       183 
208 
     | 
    
         
             
                              raise "collision!" if words[word_hash] && words[word_hash] != str
         
     | 
| 
       184 
209 
     | 
    
         
             
                              if add_words || words[word_hash]
         
     | 
| 
       185 
210 
     | 
    
         
             
                                words[word_hash] = str
         
     | 
| 
         @@ -197,9 +222,18 @@ module AACMetrics::Loader 
     | 
|
| 
       197 
222 
     | 
    
         
             
                    boards << new_json
         
     | 
| 
       198 
223 
     | 
    
         
             
                  end
         
     | 
| 
       199 
224 
     | 
    
         
             
                end
         
     | 
| 
      
 225 
     | 
    
         
            +
                # finalize all board paths once done iterating
         
     | 
| 
      
 226 
     | 
    
         
            +
                boards.each do |brd|
         
     | 
| 
      
 227 
     | 
    
         
            +
                  brd['buttons'].each do |btn|
         
     | 
| 
      
 228 
     | 
    
         
            +
                    if btn['load_board'] && btn['load_board']['tmp_path']
         
     | 
| 
      
 229 
     | 
    
         
            +
                      btn['load_board']['id'] = "brd#{visited_paths[btn['load_board']['tmp_path']]}" if visited_paths[btn['load_board']['tmp_path']]
         
     | 
| 
      
 230 
     | 
    
         
            +
                      btn['load_board'].delete('tmp_path')
         
     | 
| 
      
 231 
     | 
    
         
            +
                    end
         
     | 
| 
      
 232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
       200 
234 
     | 
    
         
             
                # any semantic_id or clone_id repeats must be recorded
         
     | 
| 
       201 
235 
     | 
    
         
             
                relations_hash.each do |id, btns|
         
     | 
| 
       202 
     | 
    
         
            -
                  if btns && btns.length >  
     | 
| 
      
 236 
     | 
    
         
            +
                  if btns && btns.length > 1
         
     | 
| 
       203 
237 
     | 
    
         
             
                    btns.each do |brd_id, btn_id|
         
     | 
| 
       204 
238 
     | 
    
         
             
                      brd = boards.detect{|b| b['id'] == brd_id }
         
     | 
| 
       205 
239 
     | 
    
         
             
                      if brd && brd['buttons']
         
     | 
| 
         @@ -220,32 +254,18 @@ module AACMetrics::Loader 
     | 
|
| 
       220 
254 
     | 
    
         
             
                    # 
         
     | 
| 
       221 
255 
     | 
    
         
             
                  end
         
     | 
| 
       222 
256 
     | 
    
         
             
                end
         
     | 
| 
       223 
     | 
    
         
            -
                boards.each do |brd|
         
     | 
| 
       224 
     | 
    
         
            -
                  brd['buttons'].each do |btn|
         
     | 
| 
       225 
     | 
    
         
            -
                    if btn['load_board'] && btn['load_board']['tmp_path']
         
     | 
| 
       226 
     | 
    
         
            -
                      btn['load_board']['id'] = "brd#{visited_paths[btn['load_board']['tmp_path']]}" if visited_paths[btn['load_board']['tmp_path']]
         
     | 
| 
       227 
     | 
    
         
            -
                      btn['load_board'].delete('tmp_path')
         
     | 
| 
       228 
     | 
    
         
            -
                    end
         
     | 
| 
       229 
     | 
    
         
            -
                  end
         
     | 
| 
       230 
     | 
    
         
            -
                end
         
     | 
| 
       231 
257 
     | 
    
         
             
                # TODO: record whether the board set is expected to have auto-home
         
     | 
| 
       232 
258 
     | 
    
         
             
                {boards: boards, words: words, words_path: words_path}
         
     | 
| 
       233 
259 
     | 
    
         
             
              end
         
     | 
| 
       234 
260 
     | 
    
         | 
| 
       235 
     | 
    
         
            -
              # TODO: Qualitative assessments of common vocabularies,
         
     | 
| 
       236 
     | 
    
         
            -
              # gather perspectives on what makes a "good" vocabulary
         
     | 
| 
       237 
     | 
    
         
            -
              # and collect reviews from field experts, also free
         
     | 
| 
       238 
     | 
    
         
            -
              # response sections.
         
     | 
| 
       239 
     | 
    
         
            -
              # Some criteria:
         
     | 
| 
       240 
     | 
    
         
            -
              # - works well for age group X, Y, Z
         
     | 
| 
       241 
     | 
    
         
            -
              # - works well for a beginning communicator
         
     | 
| 
       242 
     | 
    
         
            -
              # - allows long-term growth as-is
         
     | 
| 
       243 
     | 
    
         
            -
              # - comprehensive core
         
     | 
| 
       244 
     | 
    
         
            -
              # - 
         
     | 
| 
       245 
     | 
    
         
            -
             
     | 
| 
       246 
261 
     | 
    
         
             
              def self.ingest(fn, token=nil)
         
     | 
| 
       247 
262 
     | 
    
         
             
                output = nil
         
     | 
| 
       248 
263 
     | 
    
         
             
                boards = nil
         
     | 
| 
      
 264 
     | 
    
         
            +
                if fn.match(/manifest.json/)
         
     | 
| 
      
 265 
     | 
    
         
            +
                  json = JSON.parse(File.read(fn))
         
     | 
| 
      
 266 
     | 
    
         
            +
                  root_fn = json['root']
         
     | 
| 
      
 267 
     | 
    
         
            +
                  fn = fn.sub(/manifest.json/, root_fn)
         
     | 
| 
      
 268 
     | 
    
         
            +
                end
         
     | 
| 
       249 
269 
     | 
    
         
             
                if fn.match(/\.obfset$/)
         
     | 
| 
       250 
270 
     | 
    
         
             
                  boards = retrieve(fn, false)
         
     | 
| 
       251 
271 
     | 
    
         
             
                  output = fn
         
     | 
| 
         @@ -268,7 +288,8 @@ module AACMetrics::Loader 
     | 
|
| 
       268 
288 
     | 
    
         
             
                  end
         
     | 
| 
       269 
289 
     | 
    
         
             
                end
         
     | 
| 
       270 
290 
     | 
    
         
             
                if boards
         
     | 
| 
       271 
     | 
    
         
            -
                  analysis = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets',  
     | 
| 
      
 291 
     | 
    
         
            +
                  analysis = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', output.sub(/\.obfset$/, '.analysis')))
         
     | 
| 
      
 292 
     | 
    
         
            +
                  analysis = output.sub(/\.obfset$/, '.analysis')
         
     | 
| 
       272 
293 
     | 
    
         
             
                  res = AACMetrics::Metrics.analyze(boards, false)
         
     | 
| 
       273 
294 
     | 
    
         
             
                  f = File.open(analysis, 'w')
         
     | 
| 
       274 
295 
     | 
    
         
             
                  f.write(JSON.pretty_generate(res))
         
     | 
    
        data/lib/aac-metrics/metrics.rb
    CHANGED
    
    | 
         @@ -5,34 +5,38 @@ 
     | 
|
| 
       5 
5 
     | 
    
         
             
            # - the vocabulary organization of this set makes sense
         
     | 
| 
       6 
6 
     | 
    
         
             
            # - this set provides clear opportunities for user-specific words to be added
         
     | 
| 
       7 
7 
     | 
    
         
             
            # - grammatical forms, growth over time
         
     | 
| 
      
 8 
     | 
    
         
            +
            # - works well for age group X, Y, Z
         
     | 
| 
      
 9 
     | 
    
         
            +
            # - works well for a beginning communicator
         
     | 
| 
      
 10 
     | 
    
         
            +
            # - allows long-term growth as-is
         
     | 
| 
      
 11 
     | 
    
         
            +
            # - comprehensive core
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       8 
13 
     | 
    
         
             
            # Effort algorithms for scanning/eyes
         
     | 
| 
       9 
     | 
    
         
            -
            # TODO: manual way to flag button as conceptually
         
     | 
| 
       10 
     | 
    
         
            -
            #       related to the same-locaed button on the
         
     | 
| 
       11 
     | 
    
         
            -
            #       prior board, allowing for a discounted penalty
         
     | 
| 
       12 
14 
     | 
    
         
             
            module AACMetrics::Metrics
         
     | 
| 
       13 
     | 
    
         
            -
                #  
     | 
| 
       14 
     | 
    
         
            -
                # 1. When navigating from one board to the next, grid locations
         
     | 
| 
      
 15 
     | 
    
         
            +
                # A. When navigating from one board to the next, grid locations
         
     | 
| 
       15 
16 
     | 
    
         
             
                # with the same clone_id or semantic_id should result in a
         
     | 
| 
       16 
17 
     | 
    
         
             
                # discount to overall search based more on the number of
         
     | 
| 
       17 
18 
     | 
    
         
             
                # uncloned/unsemantic buttons than the number of total buttons
         
     | 
| 
       18 
19 
     | 
    
         
             
                # (perhaps also factoring in the percent of board with that
         
     | 
| 
       19 
20 
     | 
    
         
             
                # id present in the full board set)
         
     | 
| 
       20 
     | 
    
         
            -
                #  
     | 
| 
      
 21 
     | 
    
         
            +
                # B. When selecting a button with a semantic_id or clone_id,
         
     | 
| 
       21 
22 
     | 
    
         
             
                # a discount to both search and selection should
         
     | 
| 
       22 
23 
     | 
    
         
             
                # be applied based on the percent of boards that
         
     | 
| 
       23 
24 
     | 
    
         
             
                # contain the same id at that grid location
         
     | 
| 
       24 
     | 
    
         
            -
                #  
     | 
| 
      
 25 
     | 
    
         
            +
                # C. When selecting a button with a semantic_id or clone_id,
         
     | 
| 
       25 
26 
     | 
    
         
             
                # if the same id was present on the previous board,
         
     | 
| 
       26 
27 
     | 
    
         
             
                # an additional discount to search and selection should be applied
         
     | 
| 
       27 
     | 
    
         
            -
                 
     | 
| 
      
 28 
     | 
    
         
            +
                # D When selecting a button with a semantic_id or clone_id,
         
     | 
| 
      
 29 
     | 
    
         
            +
                # apply a steep discount to the button in the same location
         
     | 
| 
      
 30 
     | 
    
         
            +
                # as the link used to get there if they share an id
         
     | 
| 
      
 31 
     | 
    
         
            +
              def self.analyze(obfset, output=true, include_obfset=false)
         
     | 
| 
       28 
32 
     | 
    
         
             
                locale = nil
         
     | 
| 
       29 
33 
     | 
    
         
             
                buttons = []
         
     | 
| 
       30 
     | 
    
         
            -
                 
     | 
| 
      
 34 
     | 
    
         
            +
                set_refs = {}
         
     | 
| 
       31 
35 
     | 
    
         
             
                grid = {}
         
     | 
| 
       32 
36 
     | 
    
         | 
| 
       33 
37 
     | 
    
         
             
                if obfset.is_a?(Hash) && obfset['buttons']
         
     | 
| 
       34 
38 
     | 
    
         
             
                  locale = obfset['locale'] || 'en'
         
     | 
| 
       35 
     | 
    
         
            -
                   
     | 
| 
      
 39 
     | 
    
         
            +
                  set_refs = obfset['reference_counts']
         
     | 
| 
       36 
40 
     | 
    
         
             
                  grid = obfset['grid']
         
     | 
| 
       37 
41 
     | 
    
         
             
                  buttons = []
         
     | 
| 
       38 
42 
     | 
    
         
             
                  obfset['buttons'].each do |btn|
         
     | 
| 
         @@ -49,13 +53,14 @@ module AACMetrics::Metrics 
     | 
|
| 
       49 
53 
     | 
    
         
             
                else
         
     | 
| 
       50 
54 
     | 
    
         
             
                  visited_board_ids = {}
         
     | 
| 
       51 
55 
     | 
    
         
             
                  to_visit = [{board: obfset[0], level: 0, entry_x: 1.0, entry_y: 1.0}]
         
     | 
| 
       52 
     | 
    
         
            -
                   
     | 
| 
      
 56 
     | 
    
         
            +
                  set_refs = {}
         
     | 
| 
       53 
57 
     | 
    
         
             
                  rows_tally = 0.0
         
     | 
| 
       54 
58 
     | 
    
         
             
                  cols_tally = 0.0
         
     | 
| 
       55 
59 
     | 
    
         
             
                  root_rows = nil
         
     | 
| 
       56 
60 
     | 
    
         
             
                  root_cols = nil
         
     | 
| 
       57 
61 
     | 
    
         
             
                  # Gather repeated words/concepts
         
     | 
| 
       58 
62 
     | 
    
         
             
                  obfset.each do |board|
         
     | 
| 
      
 63 
     | 
    
         
            +
                    # try to figure out the average grid size for board set
         
     | 
| 
       59 
64 
     | 
    
         
             
                    root_rows ||= board['grid']['rows']
         
     | 
| 
       60 
65 
     | 
    
         
             
                    root_cols ||= board['grid']['columns']
         
     | 
| 
       61 
66 
     | 
    
         
             
                    rows_tally += board['grid']['rows']
         
     | 
| 
         @@ -63,25 +68,27 @@ module AACMetrics::Metrics 
     | 
|
| 
       63 
68 
     | 
    
         
             
                    # determine frequency within the board set
         
     | 
| 
       64 
69 
     | 
    
         
             
                    # for each semantic_id and clone_id
         
     | 
| 
       65 
70 
     | 
    
         
             
                    if board['clone_ids']
         
     | 
| 
       66 
     | 
    
         
            -
                       
     | 
| 
       67 
     | 
    
         
            -
                         
     | 
| 
       68 
     | 
    
         
            -
                         
     | 
| 
      
 71 
     | 
    
         
            +
                      board['clone_ids'].each do |id|
         
     | 
| 
      
 72 
     | 
    
         
            +
                        set_refs[id] ||= 0
         
     | 
| 
      
 73 
     | 
    
         
            +
                        set_refs[id] += 1
         
     | 
| 
       69 
74 
     | 
    
         
             
                      end
         
     | 
| 
       70 
75 
     | 
    
         
             
                    end
         
     | 
| 
       71 
76 
     | 
    
         
             
                    if board['semantic_ids']
         
     | 
| 
       72 
     | 
    
         
            -
                       
     | 
| 
       73 
     | 
    
         
            -
                         
     | 
| 
       74 
     | 
    
         
            -
                         
     | 
| 
      
 77 
     | 
    
         
            +
                      board['semantic_ids'].each do |id|
         
     | 
| 
      
 78 
     | 
    
         
            +
                        set_refs[id] ||= 0
         
     | 
| 
      
 79 
     | 
    
         
            +
                        set_refs[id] += 1
         
     | 
| 
       75 
80 
     | 
    
         
             
                      end
         
     | 
| 
       76 
81 
     | 
    
         
             
                    end
         
     | 
| 
       77 
82 
     | 
    
         
             
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  # If the average grid size is much different than the root
         
     | 
| 
      
 84 
     | 
    
         
            +
                  # grid size, only then use the average as the size for this board set
         
     | 
| 
       78 
85 
     | 
    
         
             
                  if (rows_tally / obfset.length.to_f - root_rows).abs > 3 || (cols_tally / obfset.length.to_f - root_cols).abs > 3
         
     | 
| 
       79 
86 
     | 
    
         
             
                    root_rows = (rows_tally / obfset.length.to_f).floor
         
     | 
| 
       80 
87 
     | 
    
         
             
                    root_cols = (cols_tally / obfset.length.to_f).floor
         
     | 
| 
       81 
88 
     | 
    
         
             
                  end
         
     | 
| 
       82 
     | 
    
         
            -
                   
     | 
| 
       83 
     | 
    
         
            -
                   
     | 
| 
       84 
     | 
    
         
            -
                     
     | 
| 
      
 89 
     | 
    
         
            +
                  set_pcts = {}
         
     | 
| 
      
 90 
     | 
    
         
            +
                  set_refs.each do |id, cnt|
         
     | 
| 
      
 91 
     | 
    
         
            +
                    set_pcts[id] = cnt.to_f / obfset.length.to_f
         
     | 
| 
       85 
92 
     | 
    
         
             
                  end
         
     | 
| 
       86 
93 
     | 
    
         
             
                  locale = obfset[0]['locale']
         
     | 
| 
       87 
94 
     | 
    
         
             
                  known_buttons = {}
         
     | 
| 
         @@ -102,20 +109,56 @@ module AACMetrics::Metrics 
     | 
|
| 
       102 
109 
     | 
    
         
             
                    # whose semantic_id or clone_id is repeated in the board set
         
     | 
| 
       103 
110 
     | 
    
         
             
                    #       -0.0025 (* pct of matching boards) for semantic_id
         
     | 
| 
       104 
111 
     | 
    
         
             
                    #       -0.005 (* pct of matching boards) for clone_id
         
     | 
| 
      
 112 
     | 
    
         
            +
                    reuse_discount = 0.0
         
     | 
| 
       105 
113 
     | 
    
         
             
                    board[:board]['grid']['rows'].times do |row_idx|
         
     | 
| 
       106 
114 
     | 
    
         
             
                      board[:board]['grid']['columns'].times do |col_idx|
         
     | 
| 
       107 
115 
     | 
    
         
             
                        button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
         
     | 
| 
       108 
116 
     | 
    
         
             
                        button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
         
     | 
| 
       109 
     | 
    
         
            -
                        if button && button['clone_id'] &&  
     | 
| 
       110 
     | 
    
         
            -
                           
     | 
| 
       111 
     | 
    
         
            -
                        elsif button && button['semantic_id'] &&  
     | 
| 
       112 
     | 
    
         
            -
                           
     | 
| 
      
 117 
     | 
    
         
            +
                        if button && button['clone_id'] && set_pcts[button['clone_id']]
         
     | 
| 
      
 118 
     | 
    
         
            +
                          reuse_discount += REUSED_CLONE_FROM_OTHER_DISCOUNT * set_pcts[button['clone_id']]
         
     | 
| 
      
 119 
     | 
    
         
            +
                        elsif button && button['semantic_id'] && set_pcts[button['semantic_id']]
         
     | 
| 
      
 120 
     | 
    
         
            +
                          reuse_discount += REUSED_SEMANTIC_FROM_OTHER_DISCOUNT * set_pcts[button['semantic_id']]
         
     | 
| 
       113 
121 
     | 
    
         
             
                        end
         
     | 
| 
       114 
122 
     | 
    
         
             
                      end
         
     | 
| 
       115 
123 
     | 
    
         
             
                    end
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
      
 124 
     | 
    
         
            +
                    board_effort -= reuse_discount
         
     | 
| 
       117 
125 
     | 
    
         
             
                    prior_buttons = 0
         
     | 
| 
       118 
126 
     | 
    
         | 
| 
      
 127 
     | 
    
         
            +
                    # Calculate the percent of links to this board
         
     | 
| 
      
 128 
     | 
    
         
            +
                    # that had or were linked by clone_ids or semantic_ids
         
     | 
| 
      
 129 
     | 
    
         
            +
                    board_pcts = {}
         
     | 
| 
      
 130 
     | 
    
         
            +
                    obfset.each do |brd|
         
     | 
| 
      
 131 
     | 
    
         
            +
                      brd['buttons'].each do |link_btn|
         
     | 
| 
      
 132 
     | 
    
         
            +
                        #  For every board that links to this board
         
     | 
| 
      
 133 
     | 
    
         
            +
                        if link_btn['load_board'] && link_btn['load_board']['id'] == board[:board]['id']
         
     | 
| 
      
 134 
     | 
    
         
            +
                          board_pcts['all'] ||= 0
         
     | 
| 
      
 135 
     | 
    
         
            +
                          board_pcts['all'] += 1
         
     | 
| 
      
 136 
     | 
    
         
            +
                          # Count how many of those links have a clone_id or semantic_id
         
     | 
| 
      
 137 
     | 
    
         
            +
                          if link_btn['clone_id']
         
     | 
| 
      
 138 
     | 
    
         
            +
                            board_pcts[link_btn['clone_id']] ||= 0
         
     | 
| 
      
 139 
     | 
    
         
            +
                            board_pcts[link_btn['clone_id']] += 1
         
     | 
| 
      
 140 
     | 
    
         
            +
                          end
         
     | 
| 
      
 141 
     | 
    
         
            +
                          if link_btn['semantic_id']
         
     | 
| 
      
 142 
     | 
    
         
            +
                            board_pcts[link_btn['semantic_id']] ||= 0
         
     | 
| 
      
 143 
     | 
    
         
            +
                            board_pcts[link_btn['semantic_id']] += 1
         
     | 
| 
      
 144 
     | 
    
         
            +
                          end
         
     | 
| 
      
 145 
     | 
    
         
            +
                          # Also count all the clone_ids and semantic_ids
         
     | 
| 
      
 146 
     | 
    
         
            +
                          # anywhere on the boards that link to this one
         
     | 
| 
      
 147 
     | 
    
         
            +
                          (brd['clone_ids'] || []).uniq.each do |cid|
         
     | 
| 
      
 148 
     | 
    
         
            +
                            board_pcts["upstream-#{cid}"] ||= 0
         
     | 
| 
      
 149 
     | 
    
         
            +
                            board_pcts["upstream-#{cid}"] += 1
         
     | 
| 
      
 150 
     | 
    
         
            +
                          end
         
     | 
| 
      
 151 
     | 
    
         
            +
                          (brd['semantic_ids'] || []).uniq.each do |sid|
         
     | 
| 
      
 152 
     | 
    
         
            +
                            board_pcts["upstream-#{sid}"] ||= 0
         
     | 
| 
      
 153 
     | 
    
         
            +
                            board_pcts["upstream-#{sid}"] += 1
         
     | 
| 
      
 154 
     | 
    
         
            +
                          end
         
     | 
| 
      
 155 
     | 
    
         
            +
                        end
         
     | 
| 
      
 156 
     | 
    
         
            +
                      end
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                    board_pcts.each do |id, cnt|
         
     | 
| 
      
 159 
     | 
    
         
            +
                      board_pcts[id] = board_pcts[id].to_f / board_pcts['all'].to_f
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
       119 
162 
     | 
    
         
             
                    board[:board]['grid']['rows'].times do |row_idx|
         
     | 
| 
       120 
163 
     | 
    
         
             
                      board[:board]['grid']['columns'].times do |col_idx|
         
     | 
| 
       121 
164 
     | 
    
         
             
                        button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
         
     | 
| 
         @@ -125,13 +168,34 @@ module AACMetrics::Metrics 
     | 
|
| 
       125 
168 
     | 
    
         
             
                        x = (btn_width / 2)  + (btn_width * col_idx)
         
     | 
| 
       126 
169 
     | 
    
         
             
                        y = (btn_height / 2) + (btn_height * row_idx)
         
     | 
| 
       127 
170 
     | 
    
         
             
                        # prior_buttons = (row_idx * board[:board]['grid']['columns']) + col_idx
         
     | 
| 
      
 171 
     | 
    
         
            +
                        # calculate the percentage of links that point to this button
         
     | 
| 
      
 172 
     | 
    
         
            +
                        # and match on semantic_id or clone_id
         
     | 
| 
       128 
173 
     | 
    
         
             
                        effort = 0
         
     | 
| 
       129 
     | 
    
         
            -
                        #  
     | 
| 
       130 
     | 
    
         
            -
                        # 
     | 
| 
       131 
     | 
    
         
            -
                        # 
     | 
| 
       132 
     | 
    
         
            -
                        # 
     | 
| 
       133 
     | 
    
         
            -
                        #        
     | 
| 
       134 
     | 
    
         
            -
                         
     | 
| 
      
 174 
     | 
    
         
            +
                        # Additional discount on board search effort,
         
     | 
| 
      
 175 
     | 
    
         
            +
                        # remember that semantic_id and clone_id are 
         
     | 
| 
      
 176 
     | 
    
         
            +
                        # keyed to the same grid location, so matches only
         
     | 
| 
      
 177 
     | 
    
         
            +
                        # apply to that specific location
         
     | 
| 
      
 178 
     | 
    
         
            +
                        #       - if this button's semantic_id or clone_id
         
     | 
| 
      
 179 
     | 
    
         
            +
                        #         was also present anywhere on the prior board
         
     | 
| 
      
 180 
     | 
    
         
            +
                        #         board_effort * 0.5 for semantic_id
         
     | 
| 
      
 181 
     | 
    
         
            +
                        #         board_effort * 0.33 for clone_id
         
     | 
| 
      
 182 
     | 
    
         
            +
                        #       - if this button's semantic_id or clone_id
         
     | 
| 
      
 183 
     | 
    
         
            +
                        #         is directly used to navigate to this board
         
     | 
| 
      
 184 
     | 
    
         
            +
                        #         board_effort * 0.1 for semantic_id
         
     | 
| 
      
 185 
     | 
    
         
            +
                        #         board_effort * 0.1 for clone_id
         
     | 
| 
      
 186 
     | 
    
         
            +
                        button_effort = board_effort
         
     | 
| 
      
 187 
     | 
    
         
            +
                        if board_pcts[button['semantic_id']]
         
     | 
| 
      
 188 
     | 
    
         
            +
                          # TODO: Pull out these magic numbers
         
     | 
| 
      
 189 
     | 
    
         
            +
                          button_effort = [button_effort, button_effort * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['semantic_id']]].min
         
     | 
| 
      
 190 
     | 
    
         
            +
                        elsif board_pcts["upstream-#{button['semantic_id']}"]
         
     | 
| 
      
 191 
     | 
    
         
            +
                          button_effort = [button_effort, button_effort * RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['semantic_id']}"]].min
         
     | 
| 
      
 192 
     | 
    
         
            +
                        end
         
     | 
| 
      
 193 
     | 
    
         
            +
                        if board_pcts[button['clone_id']]
         
     | 
| 
      
 194 
     | 
    
         
            +
                          button_effort = [button_effort, button_effort * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['clone_id']]].min
         
     | 
| 
      
 195 
     | 
    
         
            +
                        elsif board_pcts["upstream-#{button['clone_id']}"]
         
     | 
| 
      
 196 
     | 
    
         
            +
                          button_effort = [button_effort, button_effort * RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['clone_id']}"]].min
         
     | 
| 
      
 197 
     | 
    
         
            +
                        end
         
     | 
| 
      
 198 
     | 
    
         
            +
                        effort += button_effort
         
     | 
| 
       135 
199 
     | 
    
         
             
                        # add effort for percent distance from entry point
         
     | 
| 
       136 
200 
     | 
    
         
             
                        distance = distance_effort(x, y, board[:entry_x], board[:entry_y])
         
     | 
| 
       137 
201 
     | 
    
         
             
                        # TODO: decrease effective distance if the semantic_id or clone_id:
         
     | 
| 
         @@ -141,6 +205,24 @@ module AACMetrics::Metrics 
     | 
|
| 
       141 
205 
     | 
    
         
             
                        #       - was also present on the prior board (total)
         
     | 
| 
       142 
206 
     | 
    
         
             
                        #         distance * 0.5 for semantic_id
         
     | 
| 
       143 
207 
     | 
    
         
             
                        #         distance * 0.33 for clone_id
         
     | 
| 
      
 208 
     | 
    
         
            +
                        #       - is directly used to navigate to this board
         
     | 
| 
      
 209 
     | 
    
         
            +
                        #         distance * 0.1 * (pct of links that match) for semantic_id
         
     | 
| 
      
 210 
     | 
    
         
            +
                        #         distance * 0.1 * (pct of links that match) for clone_id
         
     | 
| 
      
 211 
     | 
    
         
            +
                        if board_pcts[button['semantic_id']]
         
     | 
| 
      
 212 
     | 
    
         
            +
                          distance = [distance, distance * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['semantic_id']]].min
         
     | 
| 
      
 213 
     | 
    
         
            +
                        elsif board_pcts["upstream-#{button['semantic_id']}"]
         
     | 
| 
      
 214 
     | 
    
         
            +
                          distance = [distance, distance * RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['semantic_id']}"]].min
         
     | 
| 
      
 215 
     | 
    
         
            +
                        elsif set_pcts[button['semantic_id']]
         
     | 
| 
      
 216 
     | 
    
         
            +
                          distance = [distance, distance * RECOGNIZABLE_SEMANTIC_FROM_OTHER_DISCOUNT / set_pcts[button['semantic_id']]].min
         
     | 
| 
      
 217 
     | 
    
         
            +
                        end
         
     | 
| 
      
 218 
     | 
    
         
            +
                        if board_pcts[button['clone_id']]
         
     | 
| 
      
 219 
     | 
    
         
            +
                          distance = [distance, distance * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['clone_id']]].min
         
     | 
| 
      
 220 
     | 
    
         
            +
                        elsif board_pcts["upstream-#{button['clone_id']}"]
         
     | 
| 
      
 221 
     | 
    
         
            +
                          distance = [distance, distance * RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['clone_id']}"]].min
         
     | 
| 
      
 222 
     | 
    
         
            +
                        elsif set_pcts[button['clone_id']]
         
     | 
| 
      
 223 
     | 
    
         
            +
                          distance = [distance, distance * RECOGNIZABLE_CLONE_FROM_OTHER_DISCOUNT / set_pcts[button['clone_id']]].min
         
     | 
| 
      
 224 
     | 
    
         
            +
                        end
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
       144 
226 
     | 
    
         
             
                        effort += distance
         
     | 
| 
       145 
227 
     | 
    
         
             
                        if distance > DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
         
     | 
| 
       146 
228 
     | 
    
         
             
                          # add small effort for every prior (visible) button when visually scanning
         
     | 
| 
         @@ -170,31 +252,39 @@ module AACMetrics::Metrics 
     | 
|
| 
       170 
252 
     | 
    
         
             
                          end
         
     | 
| 
       171 
253 
     | 
    
         
             
                          if try_visit
         
     | 
| 
       172 
254 
     | 
    
         
             
                            next_board = obfset.detect{|brd| brd['id'] == button['load_board']['id'] }
         
     | 
| 
       173 
     | 
    
         
            -
                             
     | 
| 
      
 255 
     | 
    
         
            +
                            change_effort = BOARD_CHANGE_PROCESSING_EFFORT
         
     | 
| 
       174 
256 
     | 
    
         
             
                            if next_board
         
     | 
| 
       175 
257 
     | 
    
         
             
                              to_visit.push({
         
     | 
| 
       176 
258 
     | 
    
         
             
                                board: next_board,
         
     | 
| 
       177 
259 
     | 
    
         
             
                                level: board[:level] + 1,
         
     | 
| 
       178 
     | 
    
         
            -
                                prior_effort: effort +  
     | 
| 
      
 260 
     | 
    
         
            +
                                prior_effort: effort + change_effort,
         
     | 
| 
       179 
261 
     | 
    
         
             
                                entry_x: x,
         
     | 
| 
       180 
     | 
    
         
            -
                                entry_y: y
         
     | 
| 
      
 262 
     | 
    
         
            +
                                entry_y: y,
         
     | 
| 
      
 263 
     | 
    
         
            +
                                entry_clone_id: button['clone_id'],
         
     | 
| 
      
 264 
     | 
    
         
            +
                                entry_semantic_id: button['semantic_id']
         
     | 
| 
       181 
265 
     | 
    
         
             
                              })
         
     | 
| 
       182 
266 
     | 
    
         
             
                            end
         
     | 
| 
       183 
267 
     | 
    
         
             
                          end
         
     | 
| 
       184 
268 
     | 
    
         
             
                        else
         
     | 
| 
       185 
269 
     | 
    
         
             
                          word = button['label']
         
     | 
| 
       186 
270 
     | 
    
         
             
                          existing = known_buttons[word]
         
     | 
| 
       187 
     | 
    
         
            -
                           
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
      
 271 
     | 
    
         
            +
                          if !existing || effort < existing[:effort] #board[:level] < existing[:level]
         
     | 
| 
      
 272 
     | 
    
         
            +
                            if board_pcts[button['clone_id']]
         
     | 
| 
      
 273 
     | 
    
         
            +
                              effort -= [BOARD_CHANGE_PROCESSING_EFFORT, BOARD_CHANGE_PROCESSING_EFFORT * 0.3 / board_pcts[button['clone_id']]].min
         
     | 
| 
      
 274 
     | 
    
         
            +
                            elsif board_pcts[button['semantic_id']]
         
     | 
| 
      
 275 
     | 
    
         
            +
                              effort -= [BOARD_CHANGE_PROCESSING_EFFORT, BOARD_CHANGE_PROCESSING_EFFORT * 0.5 / board_pcts[button['semantic_id']]].min
         
     | 
| 
      
 276 
     | 
    
         
            +
                            end
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
       190 
278 
     | 
    
         
             
                            known_buttons[word] = {
         
     | 
| 
       191 
279 
     | 
    
         
             
                              id: "#{button['id']}::#{board[:board]['id']}",
         
     | 
| 
       192 
280 
     | 
    
         
             
                              label: word,
         
     | 
| 
       193 
281 
     | 
    
         
             
                              level: board[:level],
         
     | 
| 
       194 
     | 
    
         
            -
                              effort: effort
         
     | 
| 
      
 282 
     | 
    
         
            +
                              effort: effort,
         
     | 
| 
       195 
283 
     | 
    
         
             
                            }
         
     | 
| 
       196 
284 
     | 
    
         
             
                          end
         
     | 
| 
       197 
285 
     | 
    
         
             
                        end
         
     | 
| 
      
 286 
     | 
    
         
            +
                        button['effort'] = effort
         
     | 
| 
      
 287 
     | 
    
         
            +
             
     | 
| 
       198 
288 
     | 
    
         
             
                      end
         
     | 
| 
       199 
289 
     | 
    
         
             
                    end
         
     | 
| 
       200 
290 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -212,7 +302,7 @@ module AACMetrics::Metrics 
     | 
|
| 
       212 
302 
     | 
    
         
             
                  locale: locale,
         
     | 
| 
       213 
303 
     | 
    
         
             
                  total_boards: total_boards,
         
     | 
| 
       214 
304 
     | 
    
         
             
                  total_buttons: buttons.length,
         
     | 
| 
       215 
     | 
    
         
            -
                  reference_counts:  
     | 
| 
      
 305 
     | 
    
         
            +
                  reference_counts: set_refs,
         
     | 
| 
       216 
306 
     | 
    
         
             
                  grid: {
         
     | 
| 
       217 
307 
     | 
    
         
             
                    rows: root_rows,
         
     | 
| 
       218 
308 
     | 
    
         
             
                    columns: root_cols
         
     | 
| 
         @@ -228,12 +318,19 @@ module AACMetrics::Metrics 
     | 
|
| 
       228 
318 
     | 
    
         | 
| 
       229 
319 
     | 
    
         
             
              SQRT2 = Math.sqrt(2)
         
     | 
| 
       230 
320 
     | 
    
         
             
              BUTTON_SIZE_MULTIPLIER = 0.09
         
     | 
| 
       231 
     | 
    
         
            -
              FIELD_SIZE_MULTIPLIER = 0. 
     | 
| 
       232 
     | 
    
         
            -
              VISUAL_SCAN_MULTIPLIER = 0. 
     | 
| 
      
 321 
     | 
    
         
            +
              FIELD_SIZE_MULTIPLIER = 0.005
         
     | 
| 
      
 322 
     | 
    
         
            +
              VISUAL_SCAN_MULTIPLIER = 0.015
         
     | 
| 
       233 
323 
     | 
    
         
             
              BOARD_CHANGE_PROCESSING_EFFORT = 1.0
         
     | 
| 
       234 
     | 
    
         
            -
              DISTANCE_MULTIPLIER = 0. 
     | 
| 
      
 324 
     | 
    
         
            +
              DISTANCE_MULTIPLIER = 0.4
         
     | 
| 
       235 
325 
     | 
    
         
             
              DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN = 0.1
         
     | 
| 
       236 
326 
     | 
    
         
             
              SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER = 0.5
         
     | 
| 
      
 327 
     | 
    
         
            +
              SAME_LOCATION_AS_PRIOR_DISCOUNT = 0.1
         
     | 
| 
      
 328 
     | 
    
         
            +
              RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT = 0.5
         
     | 
| 
      
 329 
     | 
    
         
            +
              RECOGNIZABLE_SEMANTIC_FROM_OTHER_DISCOUNT = 0.6
         
     | 
| 
      
 330 
     | 
    
         
            +
              RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT = 0.33
         
     | 
| 
      
 331 
     | 
    
         
            +
              RECOGNIZABLE_CLONE_FROM_OTHER_DISCOUNT = 0.4
         
     | 
| 
      
 332 
     | 
    
         
            +
              REUSED_SEMANTIC_FROM_OTHER_DISCOUNT = 0.0025
         
     | 
| 
      
 333 
     | 
    
         
            +
              REUSED_CLONE_FROM_OTHER_DISCOUNT = 0.005
         
     | 
| 
       237 
334 
     | 
    
         | 
| 
       238 
335 
     | 
    
         
             
              def self.button_size_effort(rows, cols)
         
     | 
| 
       239 
336 
     | 
    
         
             
                BUTTON_SIZE_MULTIPLIER * (rows + cols) / 2
         
     |