Marijn
Marijn

Reputation: 61

NetLogo sort list of agents by a specific value

I have a model with 5000+ farmers and several factories. Sometimes a new factory is built, at which point I want the factory to do the following:

Create a list with all farmers and then sort that list according to distance of farmer to the factory (from low to high).

I have tried doing this with a table,

ask factory 1 [ask farmers [set distance-to-factory distance myself]]
ask factory 1 [set a table:group-agents farmers [distance-to-factory]]

but then the resulting agentsets are not ordered from low to high or vice versa. Moreover, I want the factory to be able afterwards to ask single agents from the ordered table (or list) to do something:

After ordering the farmers by their distance-to-factory, I want the factory to be able to ask farmers from that list to deliver their goods (i.e. the closest farmer is asked first, but when it has no goods, the second closest farmer is asked and so on).

Your help is much appreciated!

Upvotes: 2

Views: 543

Answers (3)

JenB
JenB

Reputation: 17678

You need to create an agent variable for the factory that stores the list of farmers in distance order. Here is a full example, run it and inspect a factory to convince yourself that it works.

breed [factories factory]
breed [farmers farmer]

factories-own [my-farmers]

to setup
  clear-all
  create-farmers 100
  [ setxy random-xcor random-ycor
    set color yellow
    set shape "circle"
    set size 0.5
  ]
  create-factories 3
  [ setxy random-xcor random-ycor
    set color red
    set shape "house"
    set size 2
    initialise-factory
  ]
  reset-ticks
end

to initialise-factory
  set my-farmers sort-on [distance myself] farmers
end

Look at the initialise-factory procedure. The sort-on primitive operating on an agentset returns a list. And the [distance myself] of ... is calculating the distance back to the factory (because the factory is doing the asking and is therefore myself). So the list is sorted by distance to the factory.

Once you have created the list, you use list procedures (eg the item primitive) to do the asking specific farmers.

Upvotes: 4

Marijn
Marijn

Reputation: 61

There are indeed multiple factories and the distance-to-factory is to the most recently created factory, but the my-farmers list that each factory has doesn't change.

I already used the profiler extension on a previous version of the model and that one was very slow because of every factory asking each farmer every time (once a year) if they had goods:

let closest-farmer (min-one-of farmers with [status = 0] [distance myself])

That is why I thought of every factory creating a list of farmers from closest to furthest, so that factories can run through that list. Below you find a more elaborate piece of code, I hope this helps you to get a better image.

breed [factories factory]
breed [farmers farmer]

globals [
  count-down 
  total-capacity-factories 
  price-goods
]

farmers-own [
  area 
  goods-per-area 
  goods 
  distance-to-factory 
  status
]

factories-own [
  my-farmers 
  goods-delivered 
  capacity
  revenues-this-year
  total-revenues
]

to setup
  clear-all
  create-farmers 1000 [
    setxy random-xcor random-ycor
    set area one-of [10 50 100]
    set goods-per-area one-of [5 10 15]
    set color yellow
    set shape "circle"
    set size 0.5
  ]
  create-factories 20 [
    setxy random-xcor random-ycor
    set color red
    set shape "house"
    set size 2
    initialise-factory
  ]
  set market-demand-goods 250000
  set total-capacity-factories sum [capacity] of factories
  set count-down 11
  reset-ticks
end

to go
  if count-down = 11 [
    change-market-demand
    ask farmers [
      set status 0
      set goods 0
    ]
  ]
  if count-down = 10 [
    ask farmers [
      sow-seeds
    ]
  ]
  if count-down = 5 [
    if market-demand-goods - [capacity] of one-of factories > total-capacity-factories [
      build-factory
    ]
  ]
  if count-down = 2 [
    ask farmers [ 
      harvest-goods
    ]
    ask factories [
      receive-harvested-goods
      sell-goods
    ]
  ]
  set count-down count-down - 1
  if count-down = 0 [
    set count-down 11
  ]
end

to initialise-factory
  set capacity 10000
  ask farmers [
    set distance-to-factory distance myself
  ]
  set my-farmers (sort-on [distance-to-factory] farmers)
end

to change-market-demand
  let chance random 100
  if chance < 33 [
    set market-demand-goods market-demand-goods - 50000
  ]
  if chance >= 33 and chance < 66 [
    set market-demand-goods market-demand-goods + 10000
  ]
  if chance >= 66 [
    set market-demand-goods market-demand-goods + 50000
  ]
  let chance2 random 100
  if chance2 < 50 [
    set price-goods price-goods + 1
  ]
  if chance2 >= 50 [
    set price-goods price-goods - 1
  ]
end

to sow-seeds
  set color brown
end

to build-factory
  loop [
    if total-capacity-factories >= market-demand-goods [stop]
    create-factory 1 [
      setxy random-xcor random-ycor
      set color red
      set shape "house"
      set size 2
      initialise-factory
      set total-capacity-factories (total-capacity-factories + [capacity] of myself
    ]
end

to harvest-goods
  set goods area * goods-per-area
end

to receive-harvested-goods
  let i 0
  loop [
    if goods-delivered >= capacity [stop]
    let closest-farmer-with-goods (item i my-farmers)
    ifelse [status] of closest-farmer-with-goods = 0 [
      set goods-delivered + [goods] of closest-farmer-with-goods
      ask closest-farmer-with-goods [
        set status "goods-delivered"
      ]
    ]
    [set i i + 1]
  ]
end

to sell-goods
  set revenues-this-year goods-delivered * price-goods
  set total-revenues total-revenues + revenues-this-year
end

Upvotes: 1

Marijn
Marijn

Reputation: 61

@JenB, thanks for the help! I also found out that it can be done as follows:

hatch factory 1 [
  ask farmers [set distance-to-factory distance myself]
  set my-farmers (sort-on [distance-to-factory] farmers)
]

Then I designed the following code for asking from the my-farmers list:

let i 0
loop [
  if goods-delivered > capacity [stop]
  let closest-farmer-with-goods (item i my-farmers)
  ifelse [status] of closest-farmer-with-goods = 0 [ (; aka goods are not delivered yet to another factory) 
    set goods-delivered + [goods] of closest-farmer-with-goods
  ]
  [set i i + 1] ; *else*
]

But this makes the model quite slow. Do you know how to make this simpler?

Upvotes: 0

Related Questions