RosettaCodeData/Task/Stable-marriage-problem/CoffeeScript/stable-marriage-problem.coffee

129 lines
4.4 KiB
CoffeeScript

class Person
constructor: (@name, @preferences) ->
@mate = null
@best_mate_rank = 0
@rank = {}
for preference, i in @preferences
@rank[preference] = i
preferred_mate_name: =>
@preferences[@best_mate_rank]
reject: =>
@best_mate_rank += 1
set_mate: (mate) =>
@mate = mate
offer_mate: (free_mate, reject_mate_cb) =>
if @mate
if @covets(free_mate)
console.log "#{free_mate.name} steals #{@name} from #{@mate.name}"
reject_mate_cb @mate
free_mate.set_mate @
@set_mate free_mate
else
console.log "#{free_mate.name} cannot steal #{@name} from #{@mate.name}"
reject_mate_cb free_mate
else
console.log "#{free_mate.name} gets #{@name} first"
free_mate.set_mate @
@set_mate free_mate
happiness: =>
@rank[@mate.name]
covets: (other_mate) =>
@rank[other_mate.name] <= @rank[@mate.name]
persons_by_name = (persons) ->
hsh = {}
for person in persons
hsh[person.name] = person
hsh
mate_off = (guys, gals) ->
free_pursuers = (guy for guy in guys)
guys_by_name = persons_by_name guys
gals_by_name = persons_by_name gals
while free_pursuers.length > 0
free_pursuer = free_pursuers.shift()
gal_name = free_pursuer.preferred_mate_name()
gal = gals_by_name[gal_name]
reject_mate_cb = (guy) ->
guy.reject()
free_pursuers.push guy
gal.offer_mate free_pursuer, reject_mate_cb
report_on_mates = (guys) ->
console.log "\n----Marriage Report"
for guy, i in guys
throw Error("illegal marriage") if guy.mate.mate isnt guy
console.log guy.name, guy.mate.name, \
"(his choice #{guy.happiness()}, her choice #{guy.mate.happiness()} )"
report_potential_adulteries = (guys) ->
for guy1, i in guys
gal1 = guy1.mate
for j in [0...i]
guy2 = guys[j]
gal2 = guy2.mate
if guy1.covets(gal2) and gal2.covets(guy1)
console.log "#{guy1.name} and #{gal2.name} would stray"
if guy2.covets(gal1) and gal1.covets(guy2)
console.log "#{guy2.name} and #{gal1.name} would stray"
perturb = (guys) ->
# mess up marriages by swapping two couples...this is mainly to drive
# out that report_potential_adulteries will actually work
guy0 = guys[0]
guy1 = guys[1]
gal0 = guy0.mate
gal1 = guy1.mate
console.log "\nPerturbing with #{guy0.name}, #{gal0.name}, #{guy1.name}, #{gal1.name}"
guy0.set_mate gal1
guy1.set_mate gal0
gal1.set_mate guy0
gal0.set_mate guy1
Population = ->
guy_preferences =
abe: ['abi', 'eve', 'cath', 'ivy', 'jan', 'dee', 'fay', 'bea', 'hope', 'gay']
bob: ['cath', 'hope', 'abi', 'dee', 'eve', 'fay', 'bea', 'jan', 'ivy', 'gay']
col: ['hope', 'eve', 'abi', 'dee', 'bea', 'fay', 'ivy', 'gay', 'cath', 'jan']
dan: ['ivy', 'fay', 'dee', 'gay', 'hope', 'eve', 'jan', 'bea', 'cath', 'abi']
ed: ['jan', 'dee', 'bea', 'cath', 'fay', 'eve', 'abi', 'ivy', 'hope', 'gay']
fred: ['bea', 'abi', 'dee', 'gay', 'eve', 'ivy', 'cath', 'jan', 'hope', 'fay']
gav: ['gay', 'eve', 'ivy', 'bea', 'cath', 'abi', 'dee', 'hope', 'jan', 'fay']
hal: ['abi', 'eve', 'hope', 'fay', 'ivy', 'cath', 'jan', 'bea', 'gay', 'dee']
ian: ['hope', 'cath', 'dee', 'gay', 'bea', 'abi', 'fay', 'ivy', 'jan', 'eve']
jon: ['abi', 'fay', 'jan', 'gay', 'eve', 'bea', 'dee', 'cath', 'ivy', 'hope']
gal_preferences =
abi: ['bob', 'fred', 'jon', 'gav', 'ian', 'abe', 'dan', 'ed', 'col', 'hal']
bea: ['bob', 'abe', 'col', 'fred', 'gav', 'dan', 'ian', 'ed', 'jon', 'hal']
cath: ['fred', 'bob', 'ed', 'gav', 'hal', 'col', 'ian', 'abe', 'dan', 'jon']
dee: ['fred', 'jon', 'col', 'abe', 'ian', 'hal', 'gav', 'dan', 'bob', 'ed']
eve: ['jon', 'hal', 'fred', 'dan', 'abe', 'gav', 'col', 'ed', 'ian', 'bob']
fay: ['bob', 'abe', 'ed', 'ian', 'jon', 'dan', 'fred', 'gav', 'col', 'hal']
gay: ['jon', 'gav', 'hal', 'fred', 'bob', 'abe', 'col', 'ed', 'dan', 'ian']
hope: ['gav', 'jon', 'bob', 'abe', 'ian', 'dan', 'hal', 'ed', 'col', 'fred']
ivy: ['ian', 'col', 'hal', 'gav', 'fred', 'bob', 'abe', 'ed', 'jon', 'dan']
jan: ['ed', 'hal', 'gav', 'abe', 'bob', 'jon', 'col', 'ian', 'fred', 'dan']
guys = (new Person(name, preferences) for name, preferences of guy_preferences)
gals = (new Person(name, preferences) for name, preferences of gal_preferences)
[guys, gals]
do ->
[guys, gals] = Population()
mate_off guys, gals
report_on_mates guys
report_potential_adulteries guys
perturb guys
report_on_mates guys
report_potential_adulteries guys