Difference between revisions of "User:Rysc00"

From Simulace.info
Jump to: navigation, search
(Overview of the Model)
(Blanked the page)
(Tag: Blanking)
 
(38 intermediate revisions by the same user not shown)
Line 1: Line 1:
=Problem definition=
 
  
The supermarket faces inefficiencies in its process at checkouts, leading to customer dissatisfaction. Currently, there are no easily applicable rules for opening new checkouts, which results in problems with too long queues during peak hours and low customer satisfaction.
 
 
The goal of this project is to develop data-based rules for opening new checkouts, ensuring that customer satisfaction remains consistently above 70%. The rule for opening checkouts must be easily applicable during store operations - it has to be based on visible elements.
 
 
=Method=
 
The simulation of the customer checkout process at the supermarket Tesco Vodňany is based on an agent-based simulation approach implemented in NetLogo. This methodology was chosen due to its ability to model complex systems and individual interactions among agents with varying behaviors, allowing for the abstraction and simplification of real-world dynamics.
 
Netlogo was chosen due to its linkup on the course 4IT496.
 
 
=Model=
 
===Overview of the Model===
 
 
The model is designed to replicate the customer checkout processes, focusing on interactions between customers and the store environment. It incorporates dynamic customer behaviors, such as queue selection, willingness to switch checkouts, and the impact of these decisions on their satisfaction.
 
 
The simulation integrates varying customer arrival rates throughout the day and models the performance differences between regular and self-service checkouts. By analyzing the effects of these factors, the model enables exploration of strategies for improving customer satisfaction in a supermarket setting.
 
 
===Agents and Their Characteristics=== 
 
===Processes in the Model=== 
 
===Assumptions and Limitations=== 
 
===Simulation Environment=== 
 
===Validation of the Model===
 
 
=Results=
 
 
=Conclusion=
 
 
=Code=
 
 
globals [
 
  satisfaction-threshold ;; Prahová hodnota pro spokojenost
 
  total-satisfaction    ;; Celková průměrná spokojenost zákazníků
 
  total-satisfaction-population
 
  simulation-running    ;; Stav simulace (true = běží, false = zastaveno)
 
  shared-self-checkout-queue
 
  last-20-satisfactions
 
]
 
 
 
breed [customers customer]
 
breed [checkouts checkout]
 
 
customers-own [
 
  items                    ;; Počet položek, které zákazník nakupuje
 
  checkout-preference      ;; Preference (regular/self-service)
 
  willingness-to-switch    ;; Ochota přejít k jiné pokladně
 
  satisfaction            ;; Aktuální úroveň spokojenosti
 
  time-spent-in-queue      ;; Čas strávený ve frontě
 
  current-checkout
 
  remaining-service-time  ;; Celková zbývající doba obsluhy
 
]
 
 
checkouts-own [
 
  queue-length            ;; Délka fronty na pokladně
 
  checkout-type            ;; Typ pokladny (regular nebo self-service)
 
  status                  ;; true = otevřená, false = zavřená
 
  queue-start-position    ;; Souřadnice začátku fronty jako seznam [x y]
 
  currently-served-customer ;; Zákazník, který je aktuálně obsluhován (nebo `nobody`, pokud nikdo)
 
]
 
 
 
to setup
 
  clear-all
 
  set satisfaction-threshold 70
 
  set total-satisfaction 0
 
  set last-20-satisfactions []
 
  set total-satisfaction-population 0
 
  set simulation-running true ;; Simulace je připravena k běhu
 
  setup-checkouts
 
  set last-20-satisfactions []
 
  reset-ticks
 
end
 
 
 
to setup-checkouts
 
  clear-patches
 
  set shared-self-checkout-queue (list 13 -5)
 
 
  ;; Regular checkouts (modré pokladny)
 
  let regular-checkout-positions [
 
    [-17 13] [-12 13] [-7 13] [-2 13] [3 13]
 
  ]
 
  create-checkouts 5 [
 
    set shape "square"
 
    set size 3.5
 
    set color blue
 
    set checkout-type "regular"
 
    set queue-length 0
 
    set currently-served-customer nobody
 
    ;; První regular checkout otevřená, ostatní zavřené
 
    ifelse who = 0 [
 
      set status true
 
    ] [
 
      set status false
 
    ]
 
    ;; Použij hardcodované souřadnice
 
    let pos item who regular-checkout-positions
 
    setxy item 0 pos item 1 pos
 
    ;; Nastav počáteční bod individuální fronty
 
    set queue-start-position (list item 0 pos (item 1 pos - 2)) ;; Fronta začíná 2 jednotky pod pokladnou
 
  ]
 
 
  ;; Self-service checkouts (zelené pokladny)
 
  let self-checkout-positions [
 
    [8 13] [18 13]    ;; První řada
 
    [8 8] [18 8]      ;; Druhá řada
 
    [8 3] [18 3]      ;; Třetí řada
 
    [8 -2] [18 -2]    ;; Čtvrtá řada
 
  ]
 
  create-checkouts 8 [
 
    set shape "square"
 
    set size 3.5
 
    set color green
 
    set checkout-type "self-service"
 
    set queue-length 0
 
    set currently-served-customer nobody
 
    set status true
 
    ;; Použij hardcodované souřadnice
 
    let pos item (who - 5) self-checkout-positions
 
    setxy item 0 pos item 1 pos
 
 
  ;; Nastav počáteční bod fronty na základě ycor a obarvi záplatu
 
  ifelse [ycor] of self = 13 [
 
    ;; Pokud je pokladna v první řadě (y = 13)
 
    let queue-x [xcor] of self
 
    let queue-y ([ycor] of self - 2)
 
    set queue-start-position (list queue-x queue-y)
 
  ] [
 
    ifelse [ycor] of self = 8 [
 
      ;; Pokud je pokladna v druhé řadě (y = 8)
 
      let queue-x [xcor] of self
 
      let queue-y ([ycor] of self - 2)
 
      set queue-start-position (list queue-x queue-y)
 
    ] [
 
      ifelse [ycor] of self = 3 [
 
        ;; Pokud je pokladna ve třetí řadě (y = 3)
 
        let queue-x [xcor] of self
 
        let queue-y ([ycor] of self - 2)
 
        set queue-start-position (list queue-x queue-y)
 
      ] [
 
        ;; Pokud je pokladna ve čtvrté řadě (y = -2)
 
        let queue-x [xcor] of self
 
        let queue-y ([ycor] of self - 2)
 
        set queue-start-position (list queue-x queue-y)
 
      ]
 
    ]
 
  ]
 
]
 
 
 
  update-checkout-colors
 
end
 
 
 
to update-checkout-colors
 
  ;; Iterace přes všechny pokladny
 
  ask checkouts [
 
    ifelse status [
 
      ;; Pokud je pokladna otevřená
 
      ifelse checkout-type = "regular" [
 
        set color blue ;; Modrá pro regular
 
      ] [
 
        set color green ;; Zelená pro self-service
 
      ]
 
    ] [
 
      ;; Pokud je pokladna zavřená
 
      set color gray ;; Šedá barva pro zavřenou pokladnu
 
    ]
 
  ]
 
end
 
 
to go
 
  if not simulation-running [stop]
 
  ifelse ticks <= 780 [
 
    if any? customers [
 
      consider-switching
 
    ]
 
    generate-customers
 
    assign-checkouts
 
    increment-time-spent-in-queue
 
    process-checkouts
 
    served-customers-leave
 
    update-queue
 
    adjust-checkouts
 
    update-checkout-colors ;; Dynamická aktualizace barev
 
    tick
 
  ] [
 
    ifelse ticks >= 795 [
 
      ask checkouts [
 
        set status false
 
        update-checkout-colors
 
      ]
 
      stop
 
    ] [
 
      increment-time-spent-in-queue
 
      process-checkouts
 
      served-customers-leave
 
      update-queue
 
      adjust-checkouts
 
      update-checkout-colors
 
      tick ;; Přidání volání tick v této části
 
    ]
 
  ]
 
end
 
 
 
to-report random-lognormal [mu sigma]
 
  ;; Vygeneruje náhodnou hodnotu z lognormálního rozdělení
 
  let normal-sample random-normal mu sigma
 
  report exp normal-sample
 
end
 
 
to-report random-poisson-lognormal [mu sigma]
 
  ;; Výpočet lognormálních parametrů
 
  let lognormal-std-dev sqrt (ln (1 + ((sigma ^ 2) / (mu ^ 2)))) ;; Přesná std dev pro lognormál
 
  let lognormal-mean (ln mu - ((lognormal-std-dev ^ 2) / 2))    ;; Přesná mean pro lognormál
 
 
 
  ;; Generování lognormálního vzorku
 
  let lognormal-sample exp (random-normal lognormal-mean lognormal-std-dev)
 
 
 
  ;; Zaokrouhlení na celá čísla (Poisson-lognormál výstup)
 
  let result round lognormal-sample
 
 
 
if result > 80 [
 
    set result random-poisson-lognormal mu sigma
 
]
 
 
  ;; Zajištění minimální hodnoty 1
 
  report ifelse-value (result < 1) [1] [result]
 
end
 
 
 
to generate-customers
 
  let arrivals max list 0 floor random-normal
 
  (ifelse-value
 
    (ticks mod 780 >= 0 and ticks mod 780 < 180) or (ticks mod 780 >= 540 and ticks mod 780 < 660)
 
    [4] ; Špičkové hodiny: 8:00-10:00 a 16:00-18:00
 
    [2] ; Mimo špičku: 10:00-16:00 a 18:00-20:00
 
  ) 1
 
 
 
  ;; Vytvoř zákazníky specifického plemene "customers"
 
  create-customers arrivals [
 
    ;; Nastavení počtu položek
 
  set items random-poisson-lognormal 14 15
 
 
    set remaining-service-time -1
 
 
    ;; Generování preference pokladny na škále 1–100
 
    if items > 20 [
 
      set checkout-preference 1 ;; Pouze regular checkouts
 
    ]
 
    if items <= 10 [
 
    let preference random-lognormal 3.8 0.9
 
      set checkout-preference max list 1 (min list (100 - preference) 100)
 
    ]
 
    if items > 10 and items <= 20 [
 
      let preference random-lognormal 3.9 0.9
 
      set checkout-preference max list 1 (min list preference 100)
 
    ]
 
 
 
    ;; Generování willingness-to-switch (lognormální rozdělení)
 
    set willingness-to-switch min list 100 max list 0 round random-lognormal 2.9 0.4
 
 
    ;; Nastavení vzhledu zákazníků
 
    set shape "person"
 
    set size 1.5
 
    ifelse checkout-preference <= 50 [
 
    set color blue ;; Barva se může změnit dle pokladny
 
      ] [
 
    set color green
 
  ]
 
 
    ;; Umístění zákazníků na spodní stěnu
 
    let x-start random-xcor
 
    setxy x-start min-pycor
 
 
    ;; Další vlastnosti zákazníků
 
    set satisfaction random-normal 80 5
 
    set time-spent-in-queue 0
 
    set current-checkout nobody
 
  ]
 
end
 
 
to consider-switching
 
  ask customers [
 
    ;; Spočítají si, jak by byli spokojeni na všech dostupných pokladnách
 
    let best-checkout choose-checkout self ;; Vybere pokladnu s nejvyšším potenciálem spokojenosti
 
    if best-checkout != current-checkout [
 
      ;; Spočítá potenciální spokojenost na nejlepší pokladně
 
      let potential-satisfaction (satisfaction + potential-satisfaction-change-on-checkout-x best-checkout self)
 
 
      ;; Spočítá aktuální spokojenost v současné frontě na základě pozice
 
      let current-queue-satisfaction calculate-current-queue-satisfaction self
 
 
      ;; Spočítá penalizaci za přechod na základě willingness-to-switch
 
      let switch-penalty 0
 
      if willingness-to-switch > 80 [
 
        set switch-penalty 0 ;; Žádná penalizace
 
      ]
 
      if willingness-to-switch <= 80 and willingness-to-switch > 60 [
 
        set switch-penalty random 5 + 1 ;; Penalizace 1–5 bodů
 
      ]
 
      if willingness-to-switch <= 60 and willingness-to-switch > 40 [
 
        set switch-penalty random 5 + 3 ;; Penalizace 3–7 bodů
 
      ]
 
      if willingness-to-switch <= 40 and willingness-to-switch > 20 [
 
        set switch-penalty random 5 + 5 ;; Penalizace 5–9 bodů
 
      ]
 
      if willingness-to-switch <= 20 [
 
        set switch-penalty random 5 + 7 ;; Penalizace 7–11 bodů
 
      ]
 
 
      ;; Zvažuje, zda přejít: nová spokojenost musí být vyšší než aktuální
 
      let new-satisfaction (potential-satisfaction - switch-penalty)
 
      if new-satisfaction > current-queue-satisfaction [
 
        ;; Rozhodne se, zda přejde (pravděpodobnost dle willingness-to-switch)
 
        if random-float 100 <= willingness-to-switch [
 
          ;; Přechod na novou pokladnu
 
          move-to-checkout best-checkout
 
          ask current-checkout [
 
            set queue-length queue-length - 1
 
          ]
 
          set current-checkout best-checkout
 
          ask current-checkout [
 
            set queue-length queue-length + 1
 
          ]
 
 
 
          ;; Snížení willingness-to-switch na polovinu
 
          set willingness-to-switch willingness-to-switch / 2
 
 
          ;; Snížení spokojenosti podle penalizace
 
          set satisfaction current-queue-satisfaction - switch-penalty
 
 
          ;; Ujisti se, že spokojenost zůstane v rozsahu 0–100
 
          if satisfaction > 100 [ set satisfaction 100 ]
 
          if satisfaction < 0 [ set satisfaction 0 ]
 
        ]
 
      ]
 
    ]
 
  ]
 
end
 
 
to-report calculate-current-queue-satisfaction [currently-handled-customer]
 
  ;; Spočítá celkový odhadovaný čas, který zákazník stráví čekáním ve frontě
 
  let total-wait-time 0
 
  let handled-customer currently-handled-customer
 
 
  ;; Najdi aktuální pokladnu zákazníka
 
  let current-checkout-1 [current-checkout] of self
 
 
  ;; Spočítej zákazníky ve frontě před tímto zákazníkem
 
  let queue-start [queue-start-position] of current-checkout-1
 
  let queue-x item 0 queue-start
 
  let queue-y item 1 queue-start
 
 
  ;; Najdi zákazníky v aktuální frontě seřazené podle pozice ve frontě
 
  let queue-customers customers with [
 
    current-checkout-1 = myself and xcor = queue-x and ycor <= queue-y
 
  ]
 
  let sorted-queue sort-on [ycor] queue-customers
 
 
  ;; Iterace přes zákazníky ve frontě
 
  let position-in-customer-queue 1
 
  foreach sorted-queue [
 
    let customer-1 self ;;PROBLÉM?
 
    ifelse ycor <= [ycor] of handled-customer [
 
    ] [
 
    ifelse position-in-customer-queue = 1 [
 
      ;; První ve frontě: přičti zbývající dobu obsluhy
 
      set total-wait-time total-wait-time + [remaining-service-time] of customer-1
 
    ] [
 
      ;; Další zákazníci: přičti odhadovanou dobu obsluhy
 
      set total-wait-time total-wait-time + estimate-service-time current-checkout-1 customer-1
 
    ]
 
    ]
 
    set position-in-customer-queue position-in-customer-queue + 1
 
  ]
 
 
  ;; Spočítej satisfaction na základě čekací doby
 
  ifelse total-wait-time <= 3 [
 
    report satisfaction ;; Spokojenost se nezmění
 
  ] [
 
    report satisfaction - ((10 + (total-wait-time - 3)) * ln (1 + (total-wait-time - 3))) ;; Spokojenost klesá s čekací dobou
 
  ]
 
end
 
 
to assign-checkouts
 
  ask customers with [current-checkout = nobody] [
 
    let chosen-checkout choose-checkout self
 
    if chosen-checkout != nobody [
 
      set current-checkout chosen-checkout
 
 
      ;; Přesuň zákazníka na pozici na konci fronty vybrané pokladny
 
      move-to-checkout chosen-checkout
 
 
      ifelse [checkout-type] of chosen-checkout = "regular" [
 
        ;; Zvýšení délky fronty pouze u vybraného regular checkoutu
 
        ask chosen-checkout [
 
          set queue-length queue-length + 1
 
        ]
 
      ] [
 
        ;; Zvýšení délky fronty o 1 u všech self-service checkoutů
 
        ask checkouts with [checkout-type = "self-service"] [
 
          set queue-length queue-length + 1
 
         
 
        ]
 
      ]
 
    ]
 
  ]
 
end
 
 
to move-to-checkout [chosen-checkout]
 
  ;; Získání cílové souřadnice na konci fronty
 
  let queue-start-of-checkout [queue-start-position] of chosen-checkout
 
  let queue-position [queue-length] of chosen-checkout
 
  if [checkout-type] of chosen-checkout = "self-service" [
 
    set queue-start-of-checkout shared-self-checkout-queue
 
  ]
 
  let target-x (item 0 queue-start-of-checkout)
 
  let target-y (item 1 queue-start-of-checkout - queue-position * 0.5)
 
  if target-y <= min-pycor [
 
    set target-y min-pycor + 0.5
 
  ]
 
 
  ;; Pohyb směrem k cíli po malých krocích
 
  while [distancexy target-x target-y > 0.5] [
 
    facexy target-x target-y
 
    fd 0.5 ;; Délka kroku
 
    display ;; Aktualizuje vizuální zobrazení při každém kroku
 
  ]
 
 
  ;; Jakmile je dostatečně blízko, nastav souřadnice na cílové místo
 
  setxy target-x target-y
 
end
 
 
to move-to-self-checkout [chosen-checkout]
 
  ;; Získání cílové souřadnice na konci fronty
 
  let queue-start-of-checkout [queue-start-position] of chosen-checkout
 
  let target-x (item 0 queue-start-of-checkout)
 
  let target-y (item 1 queue-start-of-checkout)
 
 
  ;; Pohyb směrem k cíli po malých krocích
 
  while [distancexy target-x target-y > 0.5] [
 
    facexy target-x target-y
 
    fd 0.5 ;; Délka kroku
 
    display ;; Aktualizuje vizuální zobrazení při každém kroku
 
  ]
 
 
  ;; Jakmile je dostatečně blízko, nastav souřadnice na cílové místo
 
  setxy target-x target-y
 
end
 
 
 
to-report choose-checkout [customer-to-handle]
 
  let handled-customer customer-to-handle
 
  ;; Inicializace proměnných pro sledování nejlepší satisfaction a pokladen s touto hodnotou
 
  let best-satisfaction -1000
 
  let best-checkouts []
 
 
  ;; Projdi všechny otevřené pokladny
 
  ask checkouts with [status = true] [
 
    let potential-satisfaction potential-satisfaction-change-on-checkout-x self handled-customer
 
 
    ;; Pokud je satisfaction vyšší než aktuální nejlepší, aktualizuj seznam pokladen
 
    if potential-satisfaction > best-satisfaction [
 
      set best-satisfaction potential-satisfaction
 
      set best-checkouts (list self) ;; Resetuj seznam a přidej tuto pokladnu
 
    ]
 
    ;; Pokud je satisfaction stejná jako aktuální nejlepší, přidej pokladnu do seznamu
 
    if potential-satisfaction = best-satisfaction [
 
      set best-checkouts lput self best-checkouts
 
    ]
 
  ]
 
 
  ;; Jinak vrať náhodnou z nejlepších
 
  report one-of best-checkouts
 
end
 
 
 
 
 
to update-satisfaction [leaving-customer]
 
 
 
  let checkout-type-satisfaction 0
 
  ;; Výpočet satisfaction z typu pokladny
 
  ask leaving-customer[
 
  set checkout-type-satisfaction satisfaction-change-from-checkout-type [checkout-type] of current-checkout self
 
  ]
 
  ;; Výpočet satisfaction z čekací doby
 
  let wait-time-satisfaction 0
 
  if time-spent-in-queue > 3 [
 
    set wait-time-satisfaction (-(10 * ln (1 + (time-spent-in-queue - 3))))
 
  ]
 
 
  ;; Aktualizace spokojenosti
 
  set satisfaction (satisfaction + wait-time-satisfaction + checkout-type-satisfaction)
 
 
 
  if satisfaction > 100 [set satisfaction 100]
 
  if satisfaction < 0 [ set satisfaction 0 ]
 
 
  ;; Aktualizace celkové spokojenosti všech zákazníků
 
  set total-satisfaction (((total-satisfaction * total-satisfaction-population) + satisfaction) / (total-satisfaction-population + 1))
 
  set total-satisfaction-population (total-satisfaction-population + 1)
 
 
 
  set last-20-satisfactions lput satisfaction last-20-satisfactions
 
  if length last-20-satisfactions > 20 [
 
    set last-20-satisfactions but-first last-20-satisfactions
 
  ]
 
 
 
end
 
 
 
to increment-time-spent-in-queue
 
  ;; Pro všechny zákazníky zvyšte hodnotu time-spent-in-queue o 1
 
  ask customers [
 
    set time-spent-in-queue time-spent-in-queue + 1
 
  ]
 
end
 
 
 
to process-checkouts
 
  ;; Projdi všechny otevřené pokladny
 
  ask checkouts with [status = true] [
 
    ;; Ulož odkaz na aktuální pokladnu
 
    let current-checkout-1 self
 
 
    ifelse [checkout-type] of current-checkout-1 = "regular" [
 
      ;; Logika pro regular checkouts
 
      if currently-served-customer = nobody [
 
        ;; Najdi souřadnice začátku fronty
 
        let queue-start [queue-start-position] of current-checkout-1
 
        let queue-start-x item 0 queue-start
 
        let queue-start-y item 1 queue-start
 
 
        ;; Najdi zákazníka, který stojí na začátku fronty
 
        let first-in-line one-of customers with [
 
          xcor = queue-start-x and
 
          ycor = queue-start-y
 
        ]
 
 
        if first-in-line != nobody [
 
          ;; Nastav tohoto zákazníka jako obsluhovaného
 
          set currently-served-customer first-in-line
 
          ask first-in-line [
 
            let service-time estimate-service-time current-checkout-1 first-in-line
 
            set remaining-service-time service-time
 
            set willingness-to-switch 0
 
          ]
 
        ]
 
      ]
 
 
      ;; Načti a nastav zbývající dobu obsluhy zákazníka
 
      if currently-served-customer != nobody [
 
        ask currently-served-customer [
 
          set remaining-service-time (remaining-service-time - 1)
 
        ]
 
      ]
 
    ] [
 
      ;; Logika pro self-service checkouts
 
      if currently-served-customer = nobody [
 
        ;; Sdílená fronta: najdi zákazníka na vrcholu fronty
 
        let queue-start shared-self-checkout-queue
 
        let queue-start-x item 0 queue-start
 
        let queue-start-y item 1 queue-start
 
 
        ;; Najdi zákazníka na vrcholu sdílené fronty
 
        let first-in-line one-of customers with [
 
          xcor = queue-start-x and
 
          ycor = queue-start-y
 
        ]
 
 
        if first-in-line != nobody [
 
          ;; Nastav tohoto zákazníka jako obsluhovaného
 
          set currently-served-customer first-in-line
 
          ask first-in-line [
 
            move-to-self-checkout current-checkout-1
 
            let service-time estimate-service-time current-checkout-1 first-in-line
 
            set remaining-service-time service-time
 
            set willingness-to-switch 0
 
          ]
 
          update-self-checkouts
 
          ask checkouts with [checkout-type = "self-service"] [
 
          set queue-length (queue-length - 1)
 
          ]
 
        ]
 
      ]
 
      ;; Načti a nastav zbývající dobu obsluhy zákazníka
 
      if currently-served-customer != nobody [
 
        ask currently-served-customer [
 
          set remaining-service-time (remaining-service-time - 1)
 
        ]
 
      ]
 
    ]
 
  ]
 
end
 
 
 
 
 
 
to served-customers-leave
 
  ;; Najdi všechny zákazníky, jejichž zbývající doba obsluhy klesla na 0
 
  ask customers with [remaining-service-time = 0] [
 
    if current-checkout = nobody [
 
      die
 
    ]
 
 
    ;; Aktualizuj pokladny, které obsluhují tohoto zákazníka
 
    ask checkouts with [currently-served-customer = myself] [
 
      if [checkout-type] of self = "regular" [
 
        ;; Snížení délky fronty u regular pokladny
 
        set queue-length (queue-length - 1)
 
      ]
 
      set currently-served-customer nobody
 
    ]
 
 
    update-satisfaction self
 
    die
 
  ]
 
end
 
 
 
to update-queue
 
  update-regular-checkouts
 
  update-self-checkouts
 
end
 
 
to update-regular-checkouts
 
  ;; Iterace přes všechny regular pokladny
 
  ask checkouts with [checkout-type = "regular"] [
 
    ;; Ulož odkaz na aktuální pokladnu
 
    let checkout-reference self
 
 
    ;; Najdi pozici začátku fronty této pokladny
 
    let queue-start [queue-start-position] of checkout-reference
 
    let queue-start-x item 0 queue-start
 
    let queue-start-y item 1 queue-start
 
 
    if currently-served-customer = nobody [
 
      let current-ycor queue-start-y - 0.5
 
 
      ;; Iterace pro kontrolu fronty a posun zákazníků
 
      while [any? customers with [xcor = queue-start-x and ycor = current-ycor]] [
 
        ;; Posuň zákazníka na této pozici o 0.5 nahoru
 
        ask customers with [xcor = queue-start-x and ycor = current-ycor] [
 
            set ycor ycor + 0.5
 
        ]
 
        ;; Pokračuj kontrolou další pozice o 0.5 níže
 
        set current-ycor current-ycor - 0.5
 
       
 
      ]
 
    ]
 
  ]
 
end
 
 
to update-self-checkouts
 
  ;; Pohyb zákazníků ve sdílené frontě self-service pokladen
 
  let queue-start shared-self-checkout-queue
 
  let queue-start-x item 0 queue-start
 
  let queue-start-y item 1 queue-start
 
  let current-ycor queue-start-y
 
 
 
  ifelse any? customers with [xcor = queue-start-x and ycor = current-ycor] [
 
  ][
 
    set current-ycor current-ycor - 0.5
 
   
 
    ;; Procházej zákazníky ve sdílené frontě (od začátku fronty dolů)
 
    while [any? customers with [xcor = queue-start-x and ycor = current-ycor]] [
 
      let customer-on-spot one-of customers with [xcor = queue-start-x and ycor = current-ycor]
 
 
      ;; Najdi volnou pozici nad zákazníkem
 
      let target-ycor current-ycor + 0.5
 
      if not any? customers with [xcor = queue-start-x and ycor = target-ycor] [
 
        ;; Posuň zákazníka na volné místo
 
        ask customer-on-spot [
 
          set ycor target-ycor
 
        ]
 
      ]
 
 
      ;; Posuň se o 0.5 níže ve frontě
 
      set current-ycor current-ycor - 0.5
 
    ]
 
  ]
 
end
 
 
 
to-report potential-satisfaction-change-on-checkout-x [target-checkout customer-to-handle]
 
  let handled-customer customer-to-handle
 
  ;; Spočítání satisfaction z čekací doby
 
  let satisfaction-from-wait satisfaction-change-from-wait-time target-checkout
 
 
  ;; Spočítání satisfaction z typu pokladny a preference
 
  let satisfaction-from-type satisfaction-change-from-checkout-type [checkout-type] of target-checkout handled-customer
 
 
  ;; Celková potenciální změna satisfaction
 
  report satisfaction-from-wait + satisfaction-from-type
 
end
 
 
to-report satisfaction-change-from-wait-time [target-checkout]
 
  ;; Inicializace proměnných
 
  let queue-start [queue-start-position] of target-checkout
 
 
 
  if [checkout-type] of target-checkout = "self-service" [
 
    set queue-start shared-self-checkout-queue
 
  ]
 
 
 
  let current-x item 0 queue-start
 
  let current-y item 1 queue-start
 
  let total-wait-time 0
 
 
  ;; Iterace přes pozice fronty
 
  while [any? customers with [xcor = current-x and ycor = current-y]] [
 
    ;; Najdi zákazníka na aktuální pozici
 
    let customer-on-spot one-of customers with [xcor = current-x and ycor = current-y]
 
 
    ;; Přidej odhadovaný čas obsluhy zákazníka k celkovému času čekání
 
    set total-wait-time total-wait-time + estimate-service-time target-checkout customer-on-spot
 
 
    ;; Posuň se na další pozici
 
    set current-y current-y - 0.5
 
  ]
 
 
 
if [checkout-type] of target-checkout = "self-service" [
 
    let served-customer [currently-served-customer] of target-checkout
 
    ifelse served-customer = nobody [
 
      set total-wait-time total-wait-time / 8
 
    ][
 
      let current-customer [currently-served-customer] of target-checkout
 
      let ticks-before-leave [remaining-service-time] of current-customer
 
      set total-wait-time ((total-wait-time / 8) + ticks-before-leave)
 
    ]
 
  ]
 
 
  ;; Výpočet satisfaction
 
  ifelse total-wait-time <= 3 [
 
    report 0
 
  ] [
 
    report (-((10 + (total-wait-time - 3)) * ln (1 + (total-wait-time - 3))))
 
  ]
 
end
 
 
to-report satisfaction-change-from-checkout-type [target-checkout-type customer-to-handle]
 
  let handled-customer customer-to-handle
 
  ;; Získání preference zákazníka
 
  let preference [checkout-preference] of handled-customer ;; Preference je atribut zákazníka
 
  let result 0
 
 
  ;; Výpočet vlivu typu pokladny na satisfaction
 
  if preference > 60 [
 
    if target-checkout-type = "self-service" [
 
      set result (random 2 + 2) ;; Zvýšení o 2–3 body
 
    ]
 
    if target-checkout-type = "regular" [
 
      set result (-(random 6 + 5)) ;; Snížení o 5–10 bodů
 
    ]
 
  ]
 
  if preference <= 60 and preference > 50 [
 
    if target-checkout-type = "self-service" [
 
      set result (random 2 + 1) ;; Zvýšení o 1–2 body
 
    ]
 
    if target-checkout-type = "regular" [
 
      set result (-(random 5 + 1)) ;; Snížení o 1–5 bodů
 
    ]
 
  ]
 
  if preference <= 50 and preference > 40 [
 
    if target-checkout-type = "self-service" [
 
      set result (-(random 5 + 1)) ;; Snížení o 1–5 bodů
 
    ]
 
    if target-checkout-type = "regular" [
 
      set result (random 2 + 1) ;; Zvýšení o 1–2 body
 
    ]
 
  ]
 
  if preference <= 40 [
 
    if target-checkout-type = "self-service" [
 
      set result (-(random 6 + 5)) ;; Snížení o 5–10 bodů
 
    ]
 
    if target-checkout-type = "regular" [
 
      set result (random 2 + 2) ;; Zvýšení o 2–3 body
 
    ]
 
  ]
 
  report result
 
end
 
 
 
to-report estimate-service-time [target-checkout target-customer]
 
  ;; Inicializace proměnné pro odhadovaný čas
 
  let estimated-time 0
 
 
  ;; Pravděpodobnost chyby a čas řešení
 
  let error-probability ifelse-value ([checkout-type] of target-checkout = "regular") [0.001] [0.01]
 
 
  ;; Výpočet na základě typu pokladny
 
  if [checkout-type] of target-checkout = "regular" [
 
    set estimated-time
 
      (random-normal 1 0.2 +
 
      0.8 * [items] of target-customer * random-normal 0.06 0.01 +
 
      0.2 * [items] of target-customer * random-normal 0.07 0.01)
 
  ]
 
  if [checkout-type] of target-checkout = "self-service" [
 
    set estimated-time
 
      (random-normal 1.2 0.3 +
 
      0.8 * [items] of target-customer * random-normal 0.07 0.02 +
 
      0.2 * [items] of target-customer * random-normal 0.08 0.02)
 
  ]
 
 
  ;; Přidání času na řešení chyby, pokud k ní dojde
 
  let error-opportunities [items] of target-customer
 
  if error-opportunities > 0 [
 
    if random-float 1 < error-probability [
 
      let error-resolution-time random-normal 0.5 0.1
 
      set estimated-time estimated-time + error-resolution-time
 
    ]
 
    set error-opportunities error-opportunities - 1
 
  ]
 
 
  ;; Vrácení odhadovaného času
 
  report ceiling(estimated-time)
 
end
 
 
to open-new-checkouts
 
  if not empty? last-20-satisfactions [
 
  let average-last-20 mean last-20-satisfactions
 
 
  if average-last-20 < satisfaction-threshold [
 
    let closed-checkouts checkouts with [status = false]
 
    if any? closed-checkouts [
 
      let checkout-to-open one-of closed-checkouts
 
      ask checkout-to-open [
 
        set status true
 
      ]
 
    ]
 
  ]
 
  ]
 
end
 
 
to close-checkouts
 
  ;; Najdi otevřené regular pokladny
 
  let opened-checkouts sort [self] of checkouts with [
 
    checkout-type = "regular" and
 
    status = true
 
  ]
 
 
 
  let opened-checkouts-without-customers []
 
 
 
  foreach opened-checkouts [checkout1 ->
 
    let queue-start [queue-start-position] of checkout1
 
    let queue-start-x item 0 queue-start
 
    let queue-start-y item 1 queue-start
 
    let first-in-line one-of customers with [xcor = queue-start-x and ycor = queue-start-y]
 
    if first-in-line = nobody [
 
      set opened-checkouts-without-customers lput checkout1 opened-checkouts-without-customers
 
    ]
 
  ]
 
 
 
  ;; Projdi všechny pokladny bez zákazníků
 
  foreach opened-checkouts-without-customers [checkout1 ->
 
    ;; Spočítej aktuální počet otevřených regular pokladen
 
    let num-opened-regular-checkouts length opened-checkouts
 
 
    ;; Zavři pokladnu, pokud je otevřených více než 1
 
    if num-opened-regular-checkouts > 1 [
 
      ask checkout1 [
 
        set status false
 
      ]
 
 
      ;; Aktualizuj seznam otevřených regular pokladen
 
      set opened-checkouts sort [self] of checkouts with [
 
        checkout-type = "regular" and
 
        status = true
 
      ]
 
    ]
 
  ]
 
end
 
 
to adjust-checkouts
 
  close-checkouts
 
  open-new-checkouts
 
end
 
 
 
 
to stop-simulation
 
  set simulation-running false ;; Nastaví stav simulace na zastaveno
 
end
 

Latest revision as of 23:43, 10 January 2025