Difference between revisions of "User:Rysc00"

From Simulace.info
Jump to: navigation, search
(Blanked the page)
(Tag: Blanking)
Line 1: Line 1:
 +
=Problem definition=
  
 +
 +
=Method=
 +
 +
=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

Revision as of 19:06, 10 January 2025

Problem definition

Method

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