Skip to content

Instantly share code, notes, and snippets.

@josher19
Created April 3, 2013 09:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josher19/5299885 to your computer and use it in GitHub Desktop.
Save josher19/5299885 to your computer and use it in GitHub Desktop.
Ask questions about the weather using Fuzzy Logic and LiveScript
fuzzylogic = require 'fuzzylogic/lib/fuzzylogic'
temps = {"freezing": [ 'reverseGrade', 0, 5 ], "cold":["triangle", 0,15],"cool":["triangle",10,20],"roomTemp":["triangle",16,26],"warm":["triangle",21,32],"hot":["grade",27,32]}
temps.is = (temp, description, verbose) ->
[fuzfcn,...args] = this[description];
fcn = fuzzylogic[fuzfcn] || fuzzylogic.triangle;
args.unshift(temp);
args=[temp,args[1],(args[1]+args[2])/2,args[2]] if args.length==3 && fcn && fcn.length == 4;
if verbose
[fcn.apply(fuzzylogic,args),fuzfcn,args, fcn == fuzzylogic.triangle]
else
fcn.apply(fuzzylogic,args)
temps.toF = (c) -> c * 9/5 + 32
temps.toC = (f) -> (f - 32) * 5/9
temps.ofList = (d) -> [["Fahrenheit", this.toF(d)]].concat([ [temp,this.is(d, temp)] if typeof this[temp] != "function" for temp of this])
temps.of = (d) ->
res={};
for own temp of this
res[temp] = this.is(d, temp) if typeof this[temp] != "function";
res
very = (x) -> x*x
somewhat = (x) -> Math.pow(x, 0.5)
propositions = { very: very, somewhat: somewhat }
truthiness = (prop) ->
| prop >= 1 => "definitely"
| prop <= 0 => "definitely not"
| prop >= 0.75 => "yes"
| prop <= 0.25 => "no"
| prop >= 0.67 => "probably"
| prop <= 0.33 => "probably not"
| otherwise => "maybe"
temps.ask = (msg) -> msg.match(/\s*(\w+)\s+(-?\d+\.?\d*)(\s+degrees\s*)?\s*(F\s*|C\s+|celsius\s+)?(somewhat|very)?\s*(\w+)?/i)
answer = (msg, dom=temps) ->
wtype = []
try [message, q, deg, degrees, ForC, prep, weather ] = dom.ask msg
wtype = [weather] if weather?
deg = dom.toC(deg) if dom.toC && ForC && ForC.charAt(0).toUpperCase() == "F"
prob = dom[q]?(deg, weather)
if prob?.shift
wtype = prob
prob = wtype.shift()
mod = propositions[prep]
prob = mod prob if typeof mod is "function"
[ prob, truthiness(prob) ].concat(wtype)
temps.highest = (choices) ->
max = -Infinity
weatherType = null
for own temp, deg of choices
if deg > max
max = deg
weatherType = temp
[max, weatherType]
temps.find = (deg, weather) ->
choices = this.of(deg);
choices[weather] or this.highest(choices);
# TODO: seperate domain and verbs, fuzzification
domain = (low, hi, desc, width) ->
range = hi - low;
width = width or (range / (desc.length-1));
chunks = [['triangle', here - width, here, here + width] for here from low to hi by width];
chunks[0] = ['reverseGrade', low, low + width]
chunks[chunks.length-1] = ['grade', hi - width, hi]
dom = Object.create(temps) || {};
i=0;
for d in desc then dom[d] = chunks[i++];
dom;
/**
livescript> answer "is 31 degrees hot?"
[ 0.7999999999999998,
'yes',
'hot' ]
livescript> answer "is 32 degrees hot?"
[ 1, 'definitely', 'hot' ]
livescript> answer "is 31 F hot?"
[ 0, 'definitely not', 'hot' ]
livescript> answer "is 31 F cold?"
[ 0, 'definitely not', 'cold' ]
livescript> answer "find 31 F ?"
[ 1, 'definitely', 'freezing' ]
livescript> answer "find 32?"
[ 1, 'definitely', 'hot' ]
livescript> d = domain(0,42,["freezing","cold","cool","roomTemp","warm","hot","blazing"])
{ freezing: [ 'reverseGrade', 0, 7 ],
cold: [ 'triangle', 0, 7, 14 ],
cool: [ 'triangle', 7, 14, 21 ],
roomTemp: [ 'triangle', 14, 21, 28 ],
warm: [ 'triangle', 21, 28, 35 ],
hot: [ 'triangle', 28, 35, 42 ],
blazing: [ 'grade', 35, 42 ] }
livescript> d.is(40, "blazing")
0.7142857142857144
livescript> answer "is 40 blazing", d
[ 0.7142857142857144,
'probably',
'blazing' ]
*/
module?.exports = answer
answer.is = temps.is.bind(temps)
answer.find = temps.find.bind(temps)
answer.temperatures = temps
answer.domain = domain
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment