|
|
(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
| |