Files
website/src/client/models/site/item_selector.coffee
T
Andrew Miner ed96cf0c71 Update SAYT algo to give better matches
The algorithm will now give higher rankings to items containing
multiple sequential letters which match, and higher rankings to
those whose length more closely matches the given term.
2017-01-28 19:59:18 -08:00

104 lines
3.3 KiB
CoffeeScript

#
# Crafting Guide - item_selector.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
BaseModel = require '../base_model'
ItemSlug = require '../game/item_slug'
########################################################################################################################
module.exports = class ItemSelector extends BaseModel
constructor: (attributes={}, options={})->
if not options.modPack? then throw new Error 'options.modPack is required'
options.isAcceptable ?= (item)-> return true # accept everything by default
super attributes, options
@_isAcceptable = options.isAcceptable
@_maxResults = 100
@_minHintLength = 3
@_modPack = options.modPack
@_results = []
# Property Methods #############################################################################
Object.defineProperties @prototype,
hint:
get: ->
return @_hint
set: (newHint)->
oldHint = @_hint
return if newHint is oldHint
@_hint = newHint.toLowerCase()
@trigger c.event.change + ':hint', this, oldHint, newHint
@_refreshResults()
@trigger c.event.change, this
results:
get: ->
return @_results
# Private Methods ##############################################################################
_computeScore: (name, itemSlug)->
hintIndex = 0
hintLetter = @_hint[hintIndex]
name = name.toLowerCase()
nextScore = 1
totalScore = 0
for nameIndex in [0...name.length] by 1
if name.charAt(nameIndex) is hintLetter
totalScore += nextScore
nextScore += 1
hintIndex += 1
if hintIndex is @_hint.length
item = @_modPack.findItem itemSlug
return 0 unless item?
return 0 unless @_isAcceptable item
break
hintLetter = @_hint[hintIndex]
else
nextScore = 1
return 0 if hintIndex < @_hint.length
totalScore *= @_hint.length / name.length
return totalScore
_refreshResults: ->
oldResults = @_results
scoredItems = []
count = 0
logger.verbose => "Looking for items which match: #{@_hint}"
if @_hint.length >= @_minHintLength
@_modPack.eachMod (mod)=>
return if count >= @_maxResults
return unless mod.enabled
mod.eachName (name, itemSlug)=>
return if scoredItems.length >= @_maxResults
return unless itemSlug.isQualified
score = @_computeScore name, itemSlug
if score >= @_hint.length
scoredItems.push score:score, itemSlug:itemSlug
scoredItems.sort (a, b)->
if a.score isnt b.score
return if a.score > b.score then -1 else +1
return ItemSlug.compare a.itemSlug, b.itemSlug
newResults = (e.itemSlug for e in scoredItems)
@_results = newResults
@trigger c.event.change + ':results', this, oldResults, newResults