; package delivery problem
; by
; Jose M. Vidal
breed [ deliverators ]
;"The Deliverator stands tall, your pie in thirty minutes or you can have it free,
; shoot the driver, take his car, file a class-action suit."--Snow Crash.
deliverators-own [dist deliveries steps balance average-cost accepted-package packages helping behavior]
;dist is the total distance the agent has travelled
;deliveries are the set of deliveries left. They are given in [spoke distance] pairs.
;steps is used to keep track of the steps left towards the goal
;balance is a list of the balances (B in the paper). The i'th element refers to who-of agent = i.
;behavior is
; 0 - philanthropic
; 1 - selfish
; 2 - reciprocative
; 3 - individual
;
patches-own [name]
globals [max-distance delivery-distances spoke-mult distance-mult num-asks-multiplier average-total-cost average-total-dist num-deliverators]
;num-deliveries- deliveries are referred to as "tasks" in the paper.
;num-spokes- is the variable "R" in the paper
to setup
ca
initialize
end
to initialize
set max-distance 3 ;This is variable "D" in the paper
set spoke-mult 360 / num-spokes
set distance-mult 5
set num-asks-multiplier 10
set num-deliverators num-philanthropic + num-selfish + num-reciprocative + num-individual
create-custom-deliverators num-philanthropic + num-selfish + num-reciprocative + num-individual [
ifelse (who < num-philanthropic) [
set behavior 0 ]
[
ifelse (who < num-philanthropic + num-selfish) [
set behavior 1 ]
[
ifelse (who < num-philanthropic + num-selfish + num-reciprocative)[
set behavior 2]
[
set behavior 3]]]]
set delivery-distances make-list num-deliveries
ask deliverators [
set-deliveries
set heading 0]
ask (patch 0 0) [set name "depot"]
set-plot-pen-interval 1
print "Setup done."
end
to-report randomize-list [lst]
locals [i len num-reps a a-val b]
set len length lst
set num-reps len * 2
while [i < num-reps] [
set a random-int-or-float len
set a-val item a lst
set b random-int-or-float len
set lst replace-item a lst (item b lst)
set lst replace-item b lst a-val
set i i + 1
]
report lst
end
;Makes a list of len items by cycling over the possible distances of delivery.
;We assume only integer distances are allowed.
to-report make-list [len]
locals [i lst c]
set lst []
set i 0
set c round (max-distance / 2)
while [i < len] [
set lst fput c lst
set i i + 1
set c (c + 1) mod max-distance + 1]
report lst
end
to-report make-deliveries
locals [i res]
set res []
while [i < length delivery-distances][
set res fput list (random-int-or-float num-spokes) (item i delivery-distances) res
set i i + 1]
report randomize-list res
end
;agents in the depot exchange tasks
;I had to do this using a double loop because the parallelism introduced synchronization problems.
;without-interruption is not good enough to solve this.
to exchange-tasks
locals [i j owners traders]
ask deliverators with [name-of patch-here = "depot" and not empty? deliveries][
set accepted-package false ;true if agent has accepted a package from someone. If so, he will not try to trade his package away
set packages fput (item 1 first deliveries) [] ;A list of distances for the packages Im delivering, the first is my original one
set helping [] ] ;A list of who-of for the agents for whom agent has agreed to take a package
set owners values-from deliverators with [name-of patch-here = "depot" and
(not empty? deliveries) ] [who]
set owners randomize-list owners
while [not empty? owners][
set i (turtle first owners)
if (not accepted-package-of i and ;If i has not accepted a package then it can still try to give his away.
behavior-of i != 3)[ ;individual agents don't ask for help
set traders values-from deliverators with [name-of patch-here = "depot" and
not empty? deliveries and
who != who-of i and
first first deliveries = first first deliveries-of i] [who]
while [not empty? traders][
set j (turtle first traders)
ask i [
try-to-give-next-task-to-agent j]
ifelse (empty? packages-of i)[ ;if I gave it then Im done
set traders [] ]
[
set traders butfirst traders]
]
]
ask i [
if (not empty? packages) [
set steps max packages
set heading (first first deliveries) * spoke-mult
set deliveries but-first deliveries]]
set owners butfirst owners
]
end
to update
;turn of the infinity button if set
if (count deliverators with [empty? deliveries] = num-deliverators and
count deliverators with [name-of patch-here = "depot"] = num-deliverators)
[stop]
;agents in the depot
exchange-tasks
;Ask all deliverators to move one step, in parallel
ask deliverators [
deliver]
;Calculate average distance travelled
set average-total-dist mean values-from deliverators [dist]
plot average-total-dist
end
to evolve
ct cp
initialize
while [count deliverators with [empty? deliveries] != num-deliverators or
count deliverators with [name-of patch-here = "depot"] != num-deliverators][
update]
ask deliverators [evolve-behavior]
set num-philanthropic count deliverators with [behavior = 0]
set num-selfish count deliverators with [behavior = 1]
set num-reciprocative count deliverators with [behavior = 2]
set num-individual count deliverators with [behavior = 3]
end
to do-plot-deliveries
locals [inc]
set inc 10
set-current-plot "plot1"
clear-plot
set num-deliveries 10
repeat 25 [
ct cp
initialize
while [count deliverators with [empty? deliveries] != num-deliverators or
count deliverators with [name-of patch-here = "depot"] != num-deliverators][
update]
set-current-plot "plot1"
type num-deliveries
type " - "
print average-total-dist
plot average-total-dist
set num-deliveries num-deliveries + inc]
end
to do-plot-agents
locals [inc]
set inc 10
set-current-plot "plot1"
clear-plot
set num-reciprocative 10
set num-philanthropic 0
set num-selfish 0
set num-individual 0
repeat 25 [
ct cp
initialize
while [count deliverators with [empty? deliveries] != num-deliverators or
count deliverators with [name-of patch-here = "depot"] != num-deliverators][
update]
set-current-plot "plot1"
type num-reciprocative
type " - "
print average-total-dist
plot average-total-dist
set num-reciprocative num-reciprocative + inc
set num-deliverators num-reciprocative]
end
to-report get-costs-from [delivrs]
locals [res]
set res []
while [not empty? delivrs][
set res lput (item 1 first delivrs * 2) res
set delivrs butfirst delivrs]
report res
end
;Report the extra cost that agnt will incurr if it accepts task, given its current task (first deliverables)
;This method is explained in the 1996 paper.
to-report extra-cost [agnt task]
if (first first deliveries-of agnt != first task)[
report 0] ;only report cost if they are both on the same fin
ifelse (item 1 task <= item 1 first deliveries-of agnt)[
report item 1 task] ;if task is closer than mine then cost is just cost to get there
[
report 2 * item 1 task - item 1 first deliveries-of agnt] ;if task is farther than mine
end
;;;;;;
; Member functions for the Deliverators.
;;;;;;
to set-deliveries
locals [i]
set deliveries make-deliveries
set average-cost mean get-costs-from deliveries
set steps 0
set balance []
while [i < num-deliverators][
set i i + 1
set balance lput 0 balance]
end
;returns true if I am willing to take the next task from ag
;It implements eq.1 from the paper
to-report willing-to-take-next-task-from [ag]
locals [the-extra-cost his-task prob] ; C_{ij}^{kl} from the paper
report true ;philantropic behavior
set his-task first deliveries-of ag
set the-extra-cost max list 0.0 (item 1 his-task - item 1 first deliveries)
set prob 1.0 / (1.0 + exp ((the-extra-cost - beta * average-cost - item (who-of ag) balance)/ tau))
; type "Prob of "
; type who
; type " taking from "
; type who-of ag
; type " is "
; print prob
if (random-int-or-float 1.0 <= prob)[
report true]
report false
end
to-report reciprocating-agent-accepts-my-next-task? [ag]
locals [his-extra-cost his-task prob] ; C_{ij}^{kl} from the paper
set his-task first deliveries-of ag
set his-extra-cost extra-cost ag first deliveries
set prob 1.0 / (1.0 + exp ((his-extra-cost - beta * average-cost-of ag - item who (balance-of ag))/ tau))
if (random-int-or-float 1.0 <= prob)[
report true]
report false
end
to give-my-next-task-to-agent [to-agent]
locals [his-extra-cost task]
; type who
; type " giving package to "
; print who-of to-agent
set task first deliveries ;the task I am giving to-agent
set his-extra-cost extra-cost to-agent task
set (packages-of to-agent) lput (item 1 task) (packages-of to-agent)
; type "packages ="
; print (packages-of to-agent)
set (accepted-package-of to-agent) true
set (helping-of to-agent) lput who (helping-of to-agent)
set (balance-of to-agent) replace-item who (balance-of to-agent)
((item who balance-of to-agent) - his-extra-cost)
set balance replace-item (who-of to-agent) balance
((item (who-of to-agent) balance) + (2 * item 1 task)) ;update my balance of him..nice guy.
set deliveries butfirst deliveries
set packages []
end
;Assumes that the next task for me and ag is on the same spoke.
to try-to-give-next-task-to-agent [ag]
locals [my-cost his-cost]
if (empty? packages-of ag) [stop] ;if ag already gave away its package then we will not give it to him
if (behavior-of ag = 1 or behavior-of ag = 3)[ ;selfish or individuals do not accept packages
stop]
set my-cost 2 * item 1 first deliveries
if (my-cost > extra-cost ag first deliveries)[ ;as per the 1996 paper
ifelse (behavior-of ag = 0)[ ;philanthropic
give-my-next-task-to-agent ag]
[ ;reciprocating
if (reciprocating-agent-accepts-my-next-task? ag)[
give-my-next-task-to-agent ag]]]
end
to deliver
locals [willing-to-help]
if (empty? packages and name-of patch-here = "depot") [ ;nothing to deliver
stop]
if (steps = 0)[ ;at delivery site, turn around
set heading heading + 180
set packages [] ]
fd distance-mult
set dist dist + 1
set steps steps - 1
end
to evolve-behavior
locals [distances ap randa randb a b total-dist]
set total-dist sum values-from deliverators [dist]
set distances values-from deliverators [list who dist]
set randa random-int-or-float 1.0
set randb random-int-or-float 1.0
set ap 0
while [not empty? distances][
set ap ap + (item 1 first distances / total-dist)
if (randa < ap) [
set a first distances
set randa 10]
if (randb < ap)[
set b first distances
set randb 10]
set distances butfirst distances
]
ifelse (1.0 / item 1 a) > (1.0 / item 1 b) [
set behavior behavior-of (turtle first a) ]
[
set behavior behavior-of (turtle first b) ]
end
@#$#@#$#@
GRAPHICS-WINDOW
256
10
581
356
17
17
9.0
1
10
1
1
1
0
1
1
1
-17
17
-17
17
CC-WINDOW
5
393
793
488
Command Center
0
BUTTON
2
49
83
82
NIL
setup
NIL
1
T
OBSERVER
T
NIL
BUTTON
84
49
165
82
NIL
update
NIL
1
T
OBSERVER
T
NIL
BUTTON
166
49
247
82
NIL
update
T
1
T
OBSERVER
T
NIL
SLIDER
2
214
174
247
num-deliveries
num-deliveries
0
500
50
1
1
NIL
SLIDER
2
247
174
280
num-spokes
num-spokes
1
20
4
1
1
NIL
SLIDER
2
280
174
313
tau
tau
0
1
0.75
0.01
1
NIL
SLIDER
2
313
174
346
beta
beta
0
1
0.5
0.01
1
NIL
MONITOR
584
16
719
65
NIL
average-total-dist
3
1
PLOT
584
68
784
218
plot1
x
dist
0.0
100.0
0.0
100.0
true
false
BUTTON
2
346
126
379
Plot deliveries
do-plot-deliveries
NIL
1
T
OBSERVER
T
NIL
BUTTON
126
346
230
379
Plot agents
do-plot-agents
NIL
1
T
OBSERVER
T
NIL
SLIDER
2
115
182
148
num-philanthropic
num-philanthropic
0
100
0
1
1
NIL
SLIDER
2
82
174
115
num-selfish
num-selfish
0
100
0
1
1
NIL
SLIDER
2
148
181
181
num-reciprocative
num-reciprocative
0
100
40
1
1
NIL
SLIDER
2
181
174
214
num-individual
num-individual
0
100
0
1
1
NIL
BUTTON
174
214
255
247
NIL
evolve
NIL
1
T
OBSERVER
T
NIL
BUTTON
174
246
255
279
NIL
evolve
T
1
T
OBSERVER
T
NIL
@#$#@#$#@
Title: Reciprocity in Package Delivery
Author: Jose M Vidal
Description:
In the package delivery problem a the agents must deliver a set
of packages from a central depot. The destinations lie along one of several spokes
emaniting from the central depot. The agents can exchange packages only at the depot.
The cost to an agent is proportional to the number of packages carried.
The optimal solution to this problem is to choose one agent to go on each
spoke and deliver all the packages scheduled for it. However, this would
require completely selfless agents. In this program we study the dynamics that
emerege when using different
populations of selfish, philantropic, reciprocal, and individual agents behave.
These results were first presented in