Tender-Simulation-ABM

Tender-Simulation-ABM preview image

1 collaborator

Default-person Riccardo Pizzuti (Author)

Tags

(This model has yet to be categorized with any tags)
Visible to everyone | Changeable by everyone
Model was written in NetLogo 6.4.0 • Viewed 10 times • Downloaded 0 times • Run 0 times
Download the 'Tender-Simulation-ABM' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


Info tab cannot be displayed because of an encoding error

Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TENDER SIMULATION PROCESS ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GLOBAL VARIABLES AND CONSTANTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

extensions [stats]

globals [
  ;; Simulation state
  id-active-tender
  winning-bids-list

  ;; Configuration constants (initialized in setup)
  MIN-QUALITY
  MAX-QUALITY
  BASE-TENDER-VALUE
  TENDER-VALUE-VARIANCE

  ;; Experience thresholds
  EXPERIENCE-THRESHOLD-LOW
  EXPERIENCE-THRESHOLD-HIGH

  ;; Quality-price ratio thresholds
  THRESHOLD-HIGH-MAX
  THRESHOLD-HIGH-MIN
  THRESHOLD-MED-MAX
  THRESHOLD-MED-MIN
  THRESHOLD-LOW-MAX
  THRESHOLD-LOW-MIN

  ;; Data collection variables
  market-statistics
  round-statistics


  ;; Economic modeling constants
  MIN-PROFIT-MARGIN     ; 15%
  MAX-PROFIT-MARGIN     ; 40%
  TENDER-COMPLEXITY-DISCOUNT  ; margin reduction factor for complex tenders
]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; AGENT BREEDS AND PROPERTIES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

breed [tenders tender]
breed [evaluators evaluator]
breed [players player]

tenders-own [
  id
  value
  tender-type        ; "small", "medium", "large"
  complexity-factor  ; affects quality requirements
  winner
  bids
  estimated-cost     ; estimated cost to complete tender
]

evaluators-own [
  attitude
  rates-list
  expertise-level    ; affects evaluation consistency
  ;; Individual evaluator thresholds (personalized from global defaults)
  eval-threshold-high-max
  eval-threshold-high-min
  eval-threshold-med-max
  eval-threshold-med-min
  eval-threshold-low-max
  eval-threshold-low-min
]

players-own [
  ;; Core attributes
  experience
  base-quality       ; inherent firm quality capability
  current-quality    ; quality offered in current bid (varies)
  risk-attitude

  ;; Bidding behavior and strategy
  my-bid
  ideal-bid          ; The bid calculated for profit before competitive adjustments
  bid-strategy
  bid-adjustment     ; corrective action based on bid-to-winning ratio
  bidding-archetype  ; "aggressive", "conservative", "adaptive", "follower"
  strategy-confidence ; how confident player is in their current strategy
  player-id          ; Stable ID for visualization, avoiding 'who' issues

  ;; Learning and memory
  history-bids
  history-qualities    ; track quality history for recent rounds
  winner?
  win-count
  total-bids

  ;; Enhanced metrics
  profitability      ; track estimated profit margins
  market-position    ; competitive standing

  ;; Social learning and market intelligence
  observed-strategies    ; strategies observed from other players
  social-influence       ; susceptibility to social learning
  market-knowledge       ; partial information about market conditions
  learning-partners      ; list of players this agent learns from
  strategy-adaptation-rate ; how quickly player adapts strategy
  competitive-response   ; reaction to competitive pressure
  strategy-imitation-cooldown

  ;; Performance tracking
  recent-performance     ; performance over last N rounds
  performance-trend      ; improving, stable, or declining
  market-share          ; estimated market share
  overall-performance-metric ; Combined metric for win-rate and profitability

  ;; Economic modeling and profit targeting
  target-profit-margin   ; desired profit margin (15-40%)
  current-profit-margin  ; actual achieved margin
  cost-estimation-accuracy ; how well player estimates costs (0.0-1.0)
  profit-history         ; list of actual profit margins achieved
  margin-adjustment-rate ; how quickly player adjusts profit targets
  risk-premium          ; additional margin required for risk tolerance
  learning-curve-speed  ; how fast this player learns (0.1-1.0)
  market-intelligence   ; limited knowledge: own bids, tender values, winner bids
  tender-cost-estimates ; estimates of what tenders should cost
  margin-sensitivity    ; how sensitive margins are to tender complexity
]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MAIN SIMULATION PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to setup
  clear-all
  initialize-globals
  build-players
  build-evaluators
  initialize-data-collection
  initialize-plots

  ; Set up circular market visualization
  ; Players are arranged in a circle, evaluators are hidden
  ; Tender will appear in center when simulation starts
  display-visualization-legend

  reset-ticks
end 

to display-visualization-legend
  ; Display helpful information about the circular market visualization
  output-print "=================="
  output-print "CIRCULAR MARKET VISUALIZATION"
  output-print "=================="
  output-print "• Players arranged in circle around market center"
  output-print "• Player colors: Red=Aggressive, Blue=Conservative, Orange=Adaptive, Yellow=Follower"
  output-print "• Player size increases with market share"
  output-print "• Winners become stars and move closer to center"
  output-print "• Tender appears in center with type-based colors:"
  output-print "  - Green = Small tenders"
  output-print "  - Orange = Medium tenders"
  output-print "  - Red = Large tenders"
  output-print "• Player labels show current bids"
  output-print "=================="
end 

to simulate-n-rounds
  repeat number-rounds [go]
  finalize-statistics
end 

to go
  if ticks >= number-rounds [stop]
  simulate-tender
  make-bids
  evaluate-winner

  ; Apply social learning before strategy updates
  apply-social-learning

  update-strategies
  collect-round-data

  update-performance-tracking

  update-player-visualization

  ; Update plots with current round data
  refresh-plots

  tick
end 

to update-player-visualization
  ; Update player appearance based on performance and market dynamics
  ask players [
    ; Update size based on market share (1.0 to 3.5 scale)
    let size-factor 1.0 + (market-share * 5)  ; minimum 1.0, scales with market share
    if size-factor > 3.5 [ set size-factor 3.5 ]  ; cap at 3.5
    set size size-factor

    ; Update position slightly based on performance (winners move closer to center)
    let base-angle (360 / number-players) * player-id
    let performance-factor market-position - 0.5  ; -0.5 to 1.5 range
    let radius-adjustment performance-factor * 2   ; ±2 units from base radius
    let new-radius 12 + radius-adjustment

    ; Ensure radius stays reasonable
    if new-radius < 6 [ set new-radius 6 ]
    if new-radius > 16 [ set new-radius 16 ]

    ; Calculate new position
    let x new-radius * cos base-angle
    let y new-radius * sin base-angle
    setxy x y

    ; Update label to show current bid (if participating)
    if my-bid > 0 [
      set label precision my-bid 1
    ]

    ; Highlight winner with different shape temporarily
    ifelse winner? [
      set shape "star"
      set size size-factor * 1.3  ; make winners slightly larger
    ] [
      set shape "person"
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZATION PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to initialize-globals
  ;; Initialize simulation state
  set id-active-tender 0
  set winning-bids-list []
  set market-statistics []
  set round-statistics []

  ;; Initialize configuration constants
  set MIN-QUALITY 5
  set MAX-QUALITY 10
  set BASE-TENDER-VALUE 100
  set TENDER-VALUE-VARIANCE 0.3    ; 30% variance in tender values

  ;; Initialize experience thresholds
  set EXPERIENCE-THRESHOLD-LOW 5
  set EXPERIENCE-THRESHOLD-HIGH 10

  ;; Initialize quality-price ratio thresholds (based on realistic market ranges)
  set THRESHOLD-HIGH-MAX 0.2       ; max = (10/50 = 0.2)
  set THRESHOLD-HIGH-MIN 0.15
  set THRESHOLD-MED-MAX 0.15       ; medium threshold from 0.1 to 0.14
  set THRESHOLD-MED-MIN 0.1
  set THRESHOLD-LOW-MAX 0.1        ; low threshold from 0.05 to 0.09
  set THRESHOLD-LOW-MIN 0.05

  ;; Initialize economic modeling constants
  set MIN-PROFIT-MARGIN 0.15      ; 15% minimum profit margin
  set MAX-PROFIT-MARGIN 0.40      ; 40% maximum profit margin
  set TENDER-COMPLEXITY-DISCOUNT 0.05  ; 5% margin reduction per complexity unit

  ;; Validate and normalize MEAT criteria weights (sliders should sum to 1.0)
  let weight-sum meat-price-weight + meat-quality-weight + meat-experience-weight
  if abs(weight-sum - 1.0) > 0.01 [
    output-print (word "Warning: MEAT weights sum to " precision weight-sum 3 " instead of 1.0")
    output-print "Normalizing weights automatically for accurate evaluation"
    let normalization-factor 1.0 / weight-sum
    set meat-price-weight meat-price-weight * normalization-factor
    set meat-quality-weight meat-quality-weight * normalization-factor
    set meat-experience-weight meat-experience-weight * normalization-factor
    output-print (word "Normalized weights - Price: " precision meat-price-weight 3 ", Quality: " precision meat-quality-weight 3 ", Experience: " precision meat-experience-weight 3)
  ]
end 

to initialize-data-collection
  ; Initialize tracking lists for enhanced metrics
  set market-statistics (list
    "round" "avg-bid" "min-bid" "max-bid" "bid-spread"
    "avg-quality" "winner-experience" "market-concentration"
  )
end 

to build-evaluators
  create-evaluators 3 [
    set shape "person"
    set color red
    ; Hide evaluators from view - they work in background
    set hidden? true
    set attitude one-of ["extreme" "medium"]
    set expertise-level random-float 1.0  ; 0.0 to 1.0, affects evaluation consistency

    ; Set individual thresholds with some variation
    set eval-threshold-high-max THRESHOLD-HIGH-MAX + random-float 0.02 - 0.01
    set eval-threshold-high-min THRESHOLD-HIGH-MIN + random-float 0.02 - 0.01
    set eval-threshold-med-max THRESHOLD-MED-MAX + random-float 0.02 - 0.01
    set eval-threshold-med-min THRESHOLD-MED-MIN + random-float 0.02 - 0.01
    set eval-threshold-low-max THRESHOLD-LOW-MAX + random-float 0.02 - 0.01
    set eval-threshold-low-min THRESHOLD-LOW-MIN + random-float 0.02 - 0.01
  ]
end 

to build-players
  create-players number-players [
  set shape "person"
  set color green
  ]

  ; Assign player-id separately to ensure it's contiguous from 0 to N-1
  ; This avoids issues with using non-contiguous 'who' numbers for calculations.
  let i 0
  foreach sort players [ p ->
    ask p [
      set player-id i

      ; Arrange players in a circle around the center
      let angle (360 / number-players) * player-id  ; Use stable player-id
      let radius 12  ; Distance from center
      let center-x 0
      let center-y 0

      ; Calculate circular position
      let x center-x + (radius * cos angle)
      let y center-y + (radius * sin angle)
      setxy x y

      ; Initialize core attributes
      set experience 1
      set base-quality MIN-QUALITY + ifelse-value (quality-base > 0) [random quality-base] [0] ; firm's inherent capability
      set bid-strategy (random 51) + 50  ; value between 50 and 100
      set risk-attitude 0
      set bid-adjustment 0
      calculate-risk-attitude

      ; Initialize tracking variables
      set history-bids []
      set history-qualities []
      set win-count 0
      set total-bids 0
      set profitability 0
      set market-position 0.5  ; neutral starting position

      ; Initialize bidding archetypes and social learning
      assign-bidding-archetype
      set strategy-confidence 0.5 + random-float 0.3  ; 0.5-0.8 initial confidence
      set observed-strategies []
      set social-influence 0.2 + random-float 0.6  ; 0.2-0.8 susceptibility to social learning
      set market-knowledge 0.1 + random-float 0.3  ; 0.1-0.4 initial market knowledge
      set learning-partners []
      set strategy-adaptation-rate 0.05 + random-float 0.15  ; 0.05-0.2 adaptation rate
      set competitive-response 0.3 + random-float 0.4  ; 0.3-0.7 competitive response
      set recent-performance []
      set performance-trend "stable"
      set market-share 1 / number-players  ; equal initial market share

      initialize-economic-attributes
    ]
    set i i + 1
  ]

  establish-learning-networks
end 

to initialize-economic-attributes
  ; Initialize profit targeting and learning attributes
  set target-profit-margin MIN-PROFIT-MARGIN + random-float (MAX-PROFIT-MARGIN - MIN-PROFIT-MARGIN)
  set current-profit-margin 0
  set cost-estimation-accuracy 0.3 + random-float 0.5  ; 0.3-0.8 accuracy
  set profit-history []

  ; Learning curve speed - randomly distributed (some learn faster than others)
  ; Using exponential distribution to create realistic learning curve differences
  let random-factor random-float 1.0
  set learning-curve-speed 0.1 + (0.9 * (1 - exp (- random-factor * 2)))  ; 0.1-1.0, exponentially distributed

  set margin-adjustment-rate learning-curve-speed * 0.1  ; tied to learning speed

  ; Risk premium based on archetype
  if bidding-archetype = "aggressive" [
    set risk-premium 0.08 + random-float 0.04  ; 8-12% additional margin for profit maximization
  ]
  if bidding-archetype = "conservative" [
    set risk-premium 0.02 + random-float 0.03  ; 2-5% additional margin for market share focus
  ]
  if bidding-archetype = "adaptive" [
    set risk-premium 0.04 + random-float 0.04  ; 4-8% moderate risk premium
  ]
  if bidding-archetype = "follower" [
    set risk-premium 0.05 + random-float 0.03  ; 5-8% risk premium, follows others
  ]

  ; Market intelligence: limited knowledge
  set market-intelligence (list
    []                        ; own-bid-history (index 0)
    []                        ; tender-values (index 1)
    []                        ; winner-bids (index 2)
  )

  set tender-cost-estimates []

  ; Margin sensitivity to tender complexity (varies by player capability)
  set margin-sensitivity 0.5 + random-float 0.5  ; 0.5-1.0, how much margins shrink for complex tenders
end 

to assign-bidding-archetype
  ; Assign bidding archetypes based on initial characteristics and realistic business behavior
  let archetype-prob random-float 1.0

  ifelse archetype-prob < 0.25 [
    set bidding-archetype "aggressive"
    set color red
    ; Aggressive = Profit maximizers, often high-quality providers
    set bid-strategy bid-strategy * 1.10  ; higher base strategy for premium pricing
    ; Bias towards higher quality for aggressive players (quality commands premium)
    if base-quality > (MIN-QUALITY + quality-base * 0.6) [
      set base-quality base-quality + random-float 1.0  ; quality boost for premium providers
    ]
  ] [
    ifelse archetype-prob < 0.5 [
      set bidding-archetype "conservative"
      set color blue
      ; Conservative = Market share focused, compete more on price
      set bid-strategy bid-strategy * 0.90  ; lower base strategy for competitive pricing
    ] [
      ifelse archetype-prob < 0.75 [
        set bidding-archetype "adaptive"
        set color orange
        ; Moderate base strategy, will adapt based on learning and market conditions
      ] [
        set bidding-archetype "follower"
        set color yellow
        set social-influence social-influence * 1.5  ; more susceptible to social learning
      ]
    ]
  ]
end 

to establish-learning-networks
  ; Create learning partnerships between players
  ask players [
    ; Each player learns from 1-3 other players
    let num-partners 1 + random 3
    let potential-partners other players

    repeat num-partners [
      if any? potential-partners [
        let partner one-of potential-partners
        set learning-partners lput [player-id] of partner learning-partners
        set potential-partners potential-partners with [who != [who] of partner]
      ]
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SOCIAL LEARNING PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to apply-social-learning
  ; Players observe and learn from their learning partners
  ask players [
    ; Observe strategies of learning partners
    observe-partner-strategies

    ; Update market knowledge based on observations
    update-market-knowledge

    ; Consider strategy imitation if performance is poor
    consider-strategy-imitation

    ; Update archetype-specific behaviors
    update-archetype-behavior
  ]
end 

to observe-partner-strategies
  ; Observe bidding strategies and outcomes of learning partners
  foreach learning-partners [ partner-id ->
    let partner one-of players with [player-id = partner-id]
    if partner != nobody [
      let partner-strategy [bid-strategy] of partner
      let partner-performance [overall-performance-metric] of partner
      let partner-archetype [bidding-archetype] of partner

      ; Store observation with decay (recent observations are more important)
      let observation (list partner-id partner-strategy partner-performance partner-archetype ticks)
      set observed-strategies lput observation observed-strategies

      ; Keep only recent observations (last 10 rounds)
      if length observed-strategies > 10 [
        set observed-strategies but-first observed-strategies
      ]
    ]
  ]
end 

to update-market-knowledge
  ; Update understanding of market conditions based on observations
  if length observed-strategies > 0 [
    let avg-observed-strategy mean map [obs -> item 1 obs] observed-strategies
    let avg-observed-performance mean map [obs -> item 2 obs] observed-strategies

    ; Gradually update market knowledge
    let knowledge-update-rate market-intelligence-level * strategy-adaptation-rate
    set market-knowledge market-knowledge + (knowledge-update-rate * (avg-observed-performance - market-knowledge))

    ; Bounds checking
    if market-knowledge < 0 [ set market-knowledge 0 ]
    if market-knowledge > 1 [ set market-knowledge 1 ]

    ; Also learn from average observed bidding strategy
    let strategy-learning-rate market-intelligence-level * strategy-adaptation-rate
    set bid-strategy bid-strategy + (avg-observed-strategy - bid-strategy) * strategy-learning-rate
  ]
  ; Ensure market-intelligence lists are accessed safely
  let own-bid-data item 0 market-intelligence
  if length own-bid-data > 0 [
    ; Only perform operations if data exists
    ; Currently, no specific operation needed here, but structure ensures safety
  ]
  let tender-data item 1 market-intelligence
  if length tender-data > 0 [
    ; Only perform operations if data exists
  ]
  let winner-data item 2 market-intelligence
  if length winner-data > 0 [
    ; Only perform operations if data exists
  ]
end 

to consider-strategy-imitation
  ; Consider imitating successful strategies if own performance is poor
  let my-performance overall-performance-metric

  if length observed-strategies > 0 [
    ; Find best performing observed strategy
    let best-performance max map [obs -> item 2 obs] observed-strategies
    let best-strategy-obs filter [obs -> item 2 obs = best-performance] observed-strategies

    if length best-strategy-obs > 0 [
      let best-obs one-of best-strategy-obs
      let best-strategy item 1 best-obs
      let best-archetype item 3 best-obs

      ; Consider imitation based on performance gap and social influence
      let performance-gap best-performance - my-performance
      let imitation-probability social-influence * performance-gap

      ; Check if player is in cooldown period for strategy imitation (minimum 3 rounds since last imitation)
      if not is-number? strategy-imitation-cooldown [ set strategy-imitation-cooldown -999 ]  ; Initialize if not set
      let rounds-since-last-imitation ticks - strategy-imitation-cooldown
      let cooldown-period 3  ; Minimum rounds before another imitation

      if imitation-probability > strategy-imitation-threshold and random-float 1.0 < imitation-probability and rounds-since-last-imitation >= cooldown-period [
        ; Imitate the successful strategy (partial imitation based on social influence)
        let strategy-adjustment (best-strategy - bid-strategy) * social-influence * social-learning-rate
        set bid-strategy bid-strategy + strategy-adjustment

        ; Update confidence based on imitation
        set strategy-confidence strategy-confidence * 0.9  ; reduce confidence when imitating

        ; Set cooldown to current tick
        set strategy-imitation-cooldown ticks

        output-print (sentence "Player" who "(" bidding-archetype ") imitates strategy from Player"
                      item 0 best-obs "(" best-archetype ")")
      ]
    ]
  ]
end 

to update-archetype-behavior
  ; Update behavior based on archetype and market conditions
  let performance-trend-factor calculate-performance-trend

  if bidding-archetype = "aggressive" [
    ; Aggressive players are profit-maximizers: maintain high margins even under pressure
    if performance-trend-factor < 0 [
      ; Only modest reduction in margins when losing - they prefer fewer wins at good margins
      let adjustment-factor (0.99 - competitive-response * 0.01)
      ; Ensure adjustment factor is reasonable
      if adjustment-factor < 0.8 [ set adjustment-factor 0.8 ]
      if adjustment-factor > 1.2 [ set adjustment-factor 1.2 ]
      set bid-strategy bid-strategy * adjustment-factor
    ]
  ]

  if bidding-archetype = "conservative" [
    ; Conservative players are market-share focused: adjust pricing to maintain competitiveness
    let market-volatility calculate-market-volatility
    if market-volatility > 0.3 [
      ; When market is volatile, become more competitive to secure work
      let adjustment-factor (0.98 - market-volatility * 0.02)
      ; Ensure adjustment factor is reasonable
      if adjustment-factor < 0.5 [ set adjustment-factor 0.5 ]
      set bid-strategy bid-strategy * adjustment-factor
    ]
  ]

  if bidding-archetype = "adaptive" [
    ; Adaptive players adjust based on market trends
    ; Limit performance trend factor to prevent extreme adjustments
    if performance-trend-factor > 0.5 [ set performance-trend-factor 0.5 ]
    if performance-trend-factor < -0.5 [ set performance-trend-factor -0.5 ]

    let adjustment-factor (1 + performance-trend-factor * strategy-adaptation-rate)
    ; Ensure adjustment factor is reasonable
    if adjustment-factor < 0.5 [ set adjustment-factor 0.5 ]
    if adjustment-factor > 1.5 [ set adjustment-factor 1.5 ]
    set bid-strategy bid-strategy * adjustment-factor
  ]

  if bidding-archetype = "follower" [
    ; Followers heavily weight social learning
    if length observed-strategies > 2 [
      let social-strategy mean map [obs -> item 1 obs] observed-strategies
      let adjustment (social-strategy - bid-strategy) * social-influence * 0.3
      ; Limit adjustment to prevent extreme changes
      if adjustment > bid-strategy * 0.2 [ set adjustment bid-strategy * 0.2 ]
      if adjustment < bid-strategy * -0.2 [ set adjustment bid-strategy * -0.2 ]
      set bid-strategy bid-strategy + adjustment
    ]
  ]

  ; Apply global bounds to prevent bid-strategy from becoming too extreme
  if bid-strategy < 10 [ set bid-strategy 10 ]      ; minimum reasonable bid strategy
  if bid-strategy > 200 [ set bid-strategy 200 ]  ; maximum reasonable bid strategy
end 

to-report calculate-performance-trend
  ; Calculate performance trend over recent rounds
  if length recent-performance > 2 and total-bids >= 5 [
    let recent-avg mean recent-performance
    let overall-avg 0
    set overall-avg win-count / total-bids
    report recent-avg - overall-avg
  ]
  report 0
end 

to-report calculate-market-volatility
  ; Calculate market volatility based on bid spread variations
  if length round-statistics > 3 [
    let recent-spreads sublist (map [r -> item 4 r] round-statistics)
                               (max (list 0 (length round-statistics - 5)))
                               (length round-statistics)
    if length recent-spreads > 1 [
      let avg-spread mean recent-spreads
      if avg-spread > 0 [
        let spread-variance variance recent-spreads
        report spread-variance / avg-spread
      ]
    ]
  ]
  report 0.2  ; default moderate volatility
end 

to update-performance-tracking
  ; Update performance tracking for all players
  ask players [
    ; Update recent performance (last 5 rounds)
    let current-round-performance ifelse-value (winner?) [1] [0]
    set recent-performance lput current-round-performance recent-performance

    if length recent-performance > 5 [
      set recent-performance but-first recent-performance
    ]

    ; Update performance trend
    if length recent-performance >= 3 [
      let recent-avg mean recent-performance
      let overall-performance ifelse-value (total-bids > 0) [win-count / total-bids] [0]

      if recent-avg > overall-performance + 0.1 [
        set performance-trend "improving"
      ]
      if recent-avg < overall-performance - 0.1 [
        set performance-trend "declining"
      ]
      if abs (recent-avg - overall-performance) <= 0.1 [
        set performance-trend "stable"
      ]
    ]

    ; Update market share based on recent wins across all players
    let total-recent-wins sum [sum recent-performance] of players
    if total-recent-wins > 0 [
      set market-share sum recent-performance / total-recent-wins
    ]

    ; Update strategy confidence based on performance
    if performance-trend = "improving" [
      set strategy-confidence min (list 1.0 (strategy-confidence + 0.05))
    ]
    if performance-trend = "declining" [
      set strategy-confidence max (list 0.1 (strategy-confidence - 0.05))
    ]

    ; Calculate overall performance metric, balancing win-rate and profitability
    let win-rate-component ifelse-value (total-bids > 0) [win-count / total-bids] [0]

    let profitability-component 0
    if length profit-history > 0 [
      let avg-profit mean profit-history
      ; Score profitability based on proximity to the target margin
      if target-profit-margin > 0 [
        set profitability-component (avg-profit / target-profit-margin)
        ; Cap reward for exceeding target to avoid rewarding overly conservative bidding
        if profitability-component > 1.2 [ set profitability-component 1.2 ]
        if profitability-component < 0 [ set profitability-component 0 ]
      ]
    ]

    ; Combine win-rate and profitability into a single performance metric.
    ; A weight of 0.5 balances win-rate and profitability.
    set overall-performance-metric (win-rate-component * 0.5) + (profitability-component * 0.5)
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TENDER SIMULATION PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to simulate-tender
  ask tenders [die]

  ; Reset player shapes from previous round
  ask players [
    set shape "person"
    set label ""  ; Clear previous bid labels
  ]

  set id-active-tender id-active-tender + 1

  create-tenders 1 [
    set id id-active-tender
    set shape "target"  ; Changed from "flag" to "target" for market center
    set color lime     ; Bright color to stand out (will be overridden by tender type)
    set size 3         ; Default size (will be overridden by tender type)

    ; Position at market center
    setxy 0 0

    ; Variable tender values based on type and complexity
    generate-tender-characteristics
  ]
end 

to generate-tender-characteristics
  ; Determine tender type and corresponding value
  let available-tender-types ["small" "medium" "large"]
  let type-weights [0.4 0.4 0.2]  ; 40% small, 40% medium, 20% large

  let random-val random-float 1.0
  ifelse random-val < 0.4 [
    set tender-type "small"
    set value BASE-TENDER-VALUE * (0.5 + random-float 0.5)  ; 50-100% of base
    set complexity-factor 0.7 + random-float 0.3  ; 0.7-1.0
    set color green   ; Small tenders are green
    set size 2.5
  ] [
    ifelse random-val < 0.8 [
      set tender-type "medium"
      set value BASE-TENDER-VALUE * (0.8 + random-float 0.4)  ; 80-120% of base
      set complexity-factor 0.8 + random-float 0.4  ; 0.8-1.2
      set color orange  ; Medium tenders are orange
      set size 3.0
    ] [
      set tender-type "large"
      set value BASE-TENDER-VALUE * (1.2 + random-float 0.8)  ; 120-200% of base
      set complexity-factor 1.0 + random-float 0.5  ; 1.0-1.5
      set color red     ; Large tenders are red
      set size 3.5
    ]
  ]

  ; Add random variance
  set value value * (1 + (random-float (2 * TENDER-VALUE-VARIANCE) - TENDER-VALUE-VARIANCE))
  set value precision value 2

  ; Estimate realistic cost to complete tender
  ; Cost should be 60-85% of tender value depending on complexity
  let base-cost-ratio 0.60 + (complexity-factor - 0.7) * 0.25  ; 0.60-0.85 ratio
  set estimated-cost value * base-cost-ratio
  set estimated-cost precision estimated-cost 2

  ; Update label with tender information, using precision for display only
  set label (word tender-type " (M$" precision value 2 ")")
  set label-color white
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BIDDING PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to make-bids
  let current-tender one-of tenders with [id = id-active-tender]
  let v [value] of current-tender
  let complexity [complexity-factor] of current-tender
  let actual-cost [estimated-cost] of current-tender

  output-print "=================="
  output-print "Bidding phase"
  output-print (word "Tender ID: " [id] of current-tender)
  output-print (word "Tender type: " [tender-type] of current-tender)
  output-print (word "Tender value: " v)
  output-print (word "Complexity factor: " precision complexity 2)
  output-print (word "Estimated cost: " actual-cost)
  output-print "=================="

  ask players [
    ; Enhanced quality modeling
    calculate-current-quality complexity

    ; Update market intelligence with current tender value directly
    let tender-data item 1 market-intelligence
    set tender-data lput v tender-data
    if length tender-data > 10 [
      set tender-data but-first tender-data
    ]
    set market-intelligence replace-item 1 market-intelligence tender-data

    ; Estimate tender cost based on player's capability and knowledge, passing tender value
    let my-cost-estimate estimate-tender-cost v complexity

    ; Calculate profit-margin-based bid
    calculate-profit-based-bid my-cost-estimate complexity
    set ideal-bid my-bid ; Store the ideal bid before adjustments

    ; Apply bidding strategy as a multiplier, connecting the social learning model to behavior.
    set my-bid my-bid * (bid-strategy / 100)

    ; Apply traditional risk attitude and adjustments
    apply-risk-adjustments

    ; Adjust bid to not exceed tender value based on market awareness
    ; Players inherently consider tender value as upper limit based on market knowledge
    let awareness-factor market-knowledge * 0.5 + 0.5  ; Ranges from 0.5 to 1.0 based on market knowledge
    if my-bid > v [
      let overbid-adjustment (my-bid - v) / my-bid  ; Proportion by which bid exceeds tender value
      set my-bid v * (1 - overbid-adjustment * (1 - awareness-factor))  ; Adjust bid down, more aware players adjust closer to tender value
      if my-bid <= 0 [ set my-bid 1 ]  ; Ensure minimum bid after adjustment
    ]

    ; Calculate and track actual profit margin based on estimated cost
    let estimated-profit-margin calculate-estimated-margin my-cost-estimate
    set current-profit-margin estimated-profit-margin

    ; Show bid as player label instead of creating links
    set label precision my-bid 1
    set label-color white

    ; Update tracking
    set total-bids total-bids + 1

    output-print (sentence "Player" player-id "bid" precision my-bid 2
                  "with quality" precision current-quality 1
                  "target margin" precision (target-profit-margin * 100) 1 "%"
                  "estimated margin" precision (estimated-profit-margin * 100) 1 "%")
  ]
end 

to-report estimate-tender-cost [tender-value complexity]
  ; Players estimate cost based on their experience and cost estimation accuracy
  let base-cost-ratio 0.70  ; default baseline cost ratio
  let current-tender one-of tenders with [id = id-active-tender]
  let tender-type-val [tender-type] of current-tender

  ; Adjust base cost ratio based on tender type for realism (small tenders have higher relative costs, large have lower due to economies of scale)
  if tender-type-val = "small" [
    set base-cost-ratio 0.75 + random-float 0.10  ; 75-85% for small tenders (less economy of scale)
  ]
  if tender-type-val = "medium" [
    set base-cost-ratio 0.65 + random-float 0.10  ; 65-75% for medium tenders
  ]
  if tender-type-val = "large" [
    set base-cost-ratio 0.55 + random-float 0.10  ; 55-65% for large tenders (better economy of scale)
  ]

  let base-cost-estimate tender-value * base-cost-ratio  ; apply adjusted baseline

  ; Adjust for complexity
  let complexity-adjustment complexity * 0.05 * tender-value  ; more complex = higher cost

  ; Apply player's cost estimation accuracy (error factor)
  let estimation-error (1 - cost-estimation-accuracy) * 0.3  ; max 30% error
  let error-factor 1 + (random-float (2 * estimation-error) - estimation-error)

  let final-estimate (base-cost-estimate + complexity-adjustment) * error-factor

  ; Store cost estimate for learning
  set tender-cost-estimates lput final-estimate tender-cost-estimates
  if length tender-cost-estimates > 5 [
    set tender-cost-estimates but-first tender-cost-estimates
  ]

  report final-estimate
end 

to calculate-profit-based-bid [cost-estimate complexity]
  ; Calculate bid based on desired profit margin, adjusted for tender complexity and risk

  ; Adjust target margin for tender complexity (larger/more complex = smaller margins)
  let complexity-adjustment 0
  let current-tender one-of tenders with [id = id-active-tender]
  let tender-type-val [tender-type] of current-tender

  if tender-type-val = "medium" [
    set complexity-adjustment TENDER-COMPLEXITY-DISCOUNT
  ]
  if tender-type-val = "large" [
    set complexity-adjustment TENDER-COMPLEXITY-DISCOUNT * 2
  ]

  ; Further adjust for specific complexity factor
  set complexity-adjustment complexity-adjustment + ((complexity - 1.0) * margin-sensitivity * TENDER-COMPLEXITY-DISCOUNT)

  ; Calculate adjusted target margin
  let adjusted-target-margin target-profit-margin - complexity-adjustment

  ; Apply risk premium based on archetype (realistic behavior: aggressive = profit-maximizers, conservative = market-share focused)
  if bidding-archetype = "aggressive" [
    ; Aggressive players are profit-maximizers: higher margins, fewer wins
    set adjusted-target-margin adjusted-target-margin + risk-premium
  ]
  if bidding-archetype = "conservative" [
    ; Conservative players are market-share focused: lower margins, more wins
    set adjusted-target-margin adjusted-target-margin - risk-premium
  ]

  ; Ensure margin stays within reasonable bounds
  if adjusted-target-margin < MIN-PROFIT-MARGIN [ set adjusted-target-margin MIN-PROFIT-MARGIN ]
  if adjusted-target-margin > MAX-PROFIT-MARGIN [ set adjusted-target-margin MAX-PROFIT-MARGIN ]

  ; Calculate base bid from cost and margin
  set my-bid cost-estimate / (1 - adjusted-target-margin)

  output-print (sentence "Player" player-id "cost estimate:" precision cost-estimate 2
                "adjusted margin target:" precision (adjusted-target-margin * 100) 1 "%"
                "base profit bid:" precision my-bid 2)
end 

to apply-risk-adjustments
  ; Apply traditional risk attitude adjustments on top of profit-based bid
  let risk-factor 0
  let experience-factor experience / 20  ; small experience bonus/penalty

  ; Apply risk attitude (legacy system, but now as fine-tuning)
  if experience <= EXPERIENCE-THRESHOLD-LOW [
    set risk-factor (my-bid * risk-attitude * 0.5)
    set my-bid my-bid + risk-factor + experience-factor
  ]

  if experience > EXPERIENCE-THRESHOLD-LOW [
    set risk-factor (my-bid * risk-attitude * 0.5)  ; reduced impact
    set my-bid my-bid - risk-factor + experience-factor
  ]

  ; Apply bid adjustment from historical performance
  set my-bid my-bid * (1 + bid-adjustment)

  ; Add small random variation to represent market uncertainty
  set my-bid my-bid * (0.98 + random-float 0.04)  ; ±2% random variation
  set my-bid precision my-bid 2
end 

to-report calculate-estimated-margin [cost-estimate]
  ; Calculate estimated profit margin based on bid and cost estimate
  if my-bid > 0 and cost-estimate > 0 [
    let margin (my-bid - cost-estimate) / my-bid
    if margin < 0 [ set margin 0 ]  ; no negative margins
    if margin > 1 [ set margin 1 ]  ; cap at 100%
    report margin
  ]
  report 0
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EVALUATION PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to evaluate-winner
  output-print "=================="
  output-print "Evaluation phase"
  output-print (word "MEAT Criteria - Price: " (meat-price-weight * 100) "%, Quality: " (meat-quality-weight * 100) "%, Experience: " (meat-experience-weight * 100) "%")
  output-print "=================="

  let players-list sort-by [[p1 p2] -> [player-id] of p1 < [player-id] of p2] players with [my-bid > 0]

  ; Only proceed with evaluation if there are actual bidders
  if not empty? players-list [
    let bids-list []
    let players-exp []
    let quality-list []

    ; Collect player data for evaluation
    foreach players-list [
      player-agent ->
      if [my-bid] of player-agent > 0 [
        set bids-list lput [my-bid] of player-agent bids-list
        set players-exp lput [experience] of player-agent players-exp
        set quality-list lput ([current-quality] of player-agent) quality-list
      ]
    ]

    ; Calculate MEAT scores using weighted criteria
    let meat-scores calculate-meat-scores players-list bids-list quality-list players-exp

    ; Enhanced evaluator assessment with MEAT integration
    ask evaluators [
      set rates-list []
      foreach meat-scores [ meat-score ->
        let base-rating calculate-base-rating meat-score
        let final-rating apply-evaluator-bias base-rating
        set rates-list lput final-rating rates-list
      ]

      output-print (sentence "Evaluator" who "(" attitude ") MEAT-adjusted rates:"
                    map [r -> precision r 1] rates-list)
    ]

    output-print "=================="
    output-print "Final Decision"
    output-print "=================="

    ; Calculate final scores combining MEAT and evaluator assessments
    let total-scores []
    foreach players-list [
      player-agent ->
      let index position player-agent players-list
      let meat-score item index meat-scores
      let evaluator-score mean [item index rates-list] of evaluators
      let final-score (meat-score * 0.6) + (evaluator-score * 0.4)  ; 60% MEAT, 40% evaluator judgment
      set total-scores lput final-score total-scores

      output-print (sentence "Player" [who] of player-agent "- MEAT score:" precision meat-score 2
                    "Evaluator score:" precision evaluator-score 1
                    "Final score:" precision final-score 2)
    ]

    ; Determine winner(s)
    determine-winner players-list total-scores
  ]
end 

to-report calculate-meat-scores [players-list bids-list quality-list experience-list]
  ; Calculate MEAT scores based on weighted criteria
  let meat-scores []

  ; Normalize each criterion (0-1 scale)
  let max-bid 0
  let min-bid 0
  if length bids-list > 0 [
    set max-bid max bids-list
    set min-bid min bids-list
  ]
  let current-max-quality 0
  let current-min-quality 0
  if length quality-list > 0 [
    set current-max-quality max quality-list
    set current-min-quality min quality-list
  ]
  let max-experience 0
  let min-experience 0
  if length experience-list > 0 [
    set max-experience max experience-list
    set min-experience min experience-list
  ]

  ; Normalize the MEAT weights themselves to ensure they sum to 1, preventing distorted scales.
  let weight-sum meat-price-weight + meat-quality-weight + meat-experience-weight
  if weight-sum = 0 [ set weight-sum 1 ] ; Avoid division by zero
  let norm-price-weight meat-price-weight / weight-sum
  let norm-quality-weight meat-quality-weight / weight-sum
  let norm-experience-weight meat-experience-weight / weight-sum

  foreach players-list [
    p ->
    let index position p players-list
    let bid item index bids-list
    let quality item index quality-list
    let player-exp item index experience-list

    ; Normalize scores (higher is better for all criteria)
    let price-score 0
    ifelse max-bid > min-bid [
      set price-score (max-bid - bid) / (max-bid - min-bid)  ; lower bid = higher score
    ] [
      set price-score 1 ; All bids are the same, give full score.
    ]

    let quality-score 0
    ifelse current-max-quality > current-min-quality [
      set quality-score (quality - current-min-quality) / (current-max-quality - current-min-quality)
    ] [
      set quality-score 1 ; All qualities are the same, give full score.
    ]

    let experience-score 0
    ifelse max-experience > min-experience [
      set experience-score (player-exp - min-experience) / (max-experience - min-experience)
    ] [
      set experience-score 1 ; All experiences are the same, give full score.
    ]

    ; Calculate weighted MEAT score using normalized weights
    let meat-score (price-score * norm-price-weight) +
                   (quality-score * norm-quality-weight) +
                   (experience-score * norm-experience-weight)

    set meat-scores lput meat-score meat-scores
  ]

  report meat-scores
end 

to-report calculate-base-rating [meat-score]
  ; Convert MEAT score to evaluator rating scale (1-10)
  let base-rating 1 + (meat-score * 9)  ; scale 0-1 MEAT score to 1-10 rating
  report base-rating
end 

to-report apply-evaluator-bias [base-rating]
  ; Apply evaluator-specific bias and expertise variation
  let rating base-rating

  ; Apply attitude bias
    if attitude = "extreme" [
    if base-rating > 7 [
      set rating rating * 1.2  ; amplify high scores
    ]
    if base-rating < 4 [
      set rating rating * 0.8  ; reduce low scores further
    ]
  ]

  ; Apply expertise variation
  let expertise-variation (1 - expertise-level) * 0.3 * rating
  set rating rating + (random-float (2 * expertise-variation) - expertise-variation)

  ; Ensure rating bounds
  if rating < 1 [ set rating 1 ]
  if rating > 10 [ set rating 10 ]

  report rating
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WINNER DETERMINATION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to determine-winner [players-list total-scores]
  if not empty? total-scores [
    let max-score max total-scores

    ; Correctly identify all players with the max score
    let potential-winners []
    foreach (range length players-list) [ i ->
      if (item i total-scores) = max-score [
        set potential-winners lput (item i players-list) potential-winners
      ]
    ]

    ; Reset final winner status for all players before assigning
    ask players [ set winner? false ]

    ; Determine final winner
    if length potential-winners = 1 [
      let winner-agent first potential-winners
      ask winner-agent [
        set winner? true
        set win-count win-count + 1
      ]
      output-print (sentence "Winner: Player" [player-id] of winner-agent
                    "with score" precision max-score 1
                    "and experience" precision [experience] of winner-agent 1)
    ]

    if length potential-winners > 1 [
      ; Tie-breaking: first use experience among the tied players
      let max-exp max map [p -> [experience] of p] potential-winners
      let exp-tied-winners filter [p -> [experience] of p = max-exp] potential-winners

      if length exp-tied-winners = 1 [
        ask first exp-tied-winners [
          set winner? true
          set win-count win-count + 1
        ]
        output-print (sentence "Tie broken by experience. Winner: Player"
                      [player-id] of first exp-tied-winners)
      ]
      if length exp-tied-winners > 1 [
        ; If still tied on experience, use lowest bid as final tie-breaker
        let min-bid min map [p -> [my-bid] of p] exp-tied-winners
        let final-winner one-of filter [p -> [my-bid] of p = min-bid] exp-tied-winners
        ask final-winner [
          set winner? true
          set win-count win-count + 1
        ]
        output-print (sentence "Tie broken by experience and lowest bid. Winner: Player"
                      [player-id] of final-winner "with bid" precision [my-bid] of final-winner 2)
      ]
    ]
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STRATEGY UPDATE PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to update-strategies
  ; Update bidding histories
  ask players [
    set history-bids fput my-bid history-bids
    ; Cap history to prevent memory issues on long runs
    if length history-bids > 50 [
      set history-bids but-last history-bids
    ]

    ; Update experience based on outcome
    ifelse winner? = true [
      set experience experience + winner-exp
      update-winner-strategy
    ] [
      set experience experience + loser-exp
      update-loser-strategy
    ]

    ; Calculate risk attitude *after* updating experience to use the latest value.
    calculate-risk-attitude

    ; Calculate bid adjustment based on historical performance
    calculate-bid-adjustment

    update-market-position
  ]

  ; Efficiently get winner bid once, then update all players' intelligence
  let winner-bid -1 ; Default to -1 if no winner
  if any? players with [winner?] [
    set winner-bid [my-bid] of one-of players with [winner?]
  ]
  update-profit-learning winner-bid

  ; Update global winning bids list
  if winner-bid != -1 [
    set winning-bids-list fput winner-bid winning-bids-list
    ; Cap the list to prevent memory issues on long runs
    if length winning-bids-list > 500 [
      set winning-bids-list but-last winning-bids-list
    ]
  ]
end 

to update-winner-strategy
  ; Winners become slightly more conservative to maintain competitive edge
  set bid-strategy bid-strategy * 0.995  ; slight reduction in base strategy
  if bid-strategy < 20 [ set bid-strategy 20 ]  ; enforce minimum bid strategy

  ; Update profitability estimate (simplified)
  let tender-value [value] of one-of tenders with [id = id-active-tender]
  set profitability ((tender-value - my-bid) / tender-value) * 100
end 

to update-loser-strategy
  ; Losers adjust strategy more aggressively
  if total-bids > 1 [
    let current-performance win-count / total-bids
    if current-performance < 0.2 [  ; if winning less than 20%
      set bid-strategy bid-strategy * 0.99  ; more aggressive bidding
    ]
  ]
end 

to calculate-bid-adjustment
  ; Enhanced bid adjustment calculation
  if length winning-bids-list > 0 and length history-bids > 0 [
    let adjustment-sum 0
    let comparison-rounds min (list length winning-bids-list length history-bids)

    foreach range comparison-rounds [
      i ->
      let winning-bid item i winning-bids-list
      let my-historical-bid item i history-bids
      if winning-bid > 0 [
        set adjustment-sum adjustment-sum + (my-historical-bid / winning-bid)
      ]
    ]

    if comparison-rounds > 0 [
      let average-ratio adjustment-sum / comparison-rounds
      set bid-adjustment (1 - average-ratio) * 0.5  ; damped adjustment

      ; Bounds checking
      if bid-adjustment > 0.2 [ set bid-adjustment 0.2 ]
      if bid-adjustment < -0.2 [ set bid-adjustment -0.2 ]
    ]
  ]
end 

to update-market-position
  ; Calculate relative market position (0 = worst, 1 = best)
  let my-win-rate 0
  if total-bids > 0 [ set my-win-rate win-count / total-bids ]

  let total-wins sum [win-count] of players
  let total-bids-all sum [total-bids] of players
  if total-bids-all > 0 [
    let avg-win-rate total-wins / total-bids-all
    set market-position my-win-rate / avg-win-rate
    if market-position > 2 [ set market-position 2 ]  ; cap at 2x average
  ]
end 

to calculate-risk-attitude
  ; Risk attitude based on experience levels with smoother transitions
  if experience <= EXPERIENCE-THRESHOLD-LOW [
    set risk-attitude propension  ; young firms are more aggressive (higher bids)
  ]

  if experience > EXPERIENCE-THRESHOLD-LOW and experience <= EXPERIENCE-THRESHOLD-HIGH [
    set risk-attitude risk-aversion-medium-exp  ; moderate experience, moderate aversion
  ]

  if experience > EXPERIENCE-THRESHOLD-HIGH [
    set risk-attitude risk-aversion-high-exp  ; experienced firms are more conservative
  ]
end 

to calculate-current-quality [complexity]
  ; Quality offered varies based on:
  ; 1. Base quality capability
  ; 2. Tender complexity requirements with player-specific penalty
  ; 3. Random variation representing effort/focus

  let quality-effort random-float 1.0  ; 0.0 to 1.0 effort level
  ; Player-specific complexity penalty: lower base quality players are more penalized by complexity
  let quality-capability-factor base-quality / MAX-QUALITY  ; 0.5 to 1.0 based on base quality relative to max
  let complexity-penalty (1 - quality-capability-factor) * complexity * 0.3  ; Penalty scales with complexity and inversely with quality capability
  let complexity-adjustment (complexity * 0.2) - complexity-penalty  ; Base adjustment reduced by penalty for lower quality players

  set current-quality base-quality + (quality-effort * quality-base) + complexity-adjustment

  ; Ensure quality stays within bounds
  if current-quality < MIN-QUALITY [ set current-quality MIN-QUALITY ]
  if current-quality > MAX-QUALITY [ set current-quality MAX-QUALITY ]

  ; Track quality history
  set history-qualities fput current-quality history-qualities
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROFIT MARGIN LEARNING AND ADAPTATION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to update-profit-learning [winner-bid]
  ; Players learn and adapt their profit targeting based on outcomes
  ask players [
    ; Calculate actual profit margin if won
    if winner? and total-bids > 0 [
      let actual-profit calculate-actual-profit-margin
      set profit-history lput actual-profit profit-history

      ; Keep only recent profit history
      if length profit-history > 10 [
        set profit-history but-first profit-history
      ]

      ; Adjust target profit margin based on learning curve
      adapt-profit-targets
    ]

    ; Update cost estimation accuracy based on experience
    improve-cost-estimation

    ; Update market intelligence with winner information
    if winner-bid != -1 [
      let winner-data item 2 market-intelligence
      set winner-data lput winner-bid winner-data
      if length winner-data > 10 [
        set winner-data but-first winner-data
      ]
      set market-intelligence replace-item 2 market-intelligence winner-data
    ]

    ; Update own bid history in market intelligence
    let own-bid-data item 0 market-intelligence
    set own-bid-data lput my-bid own-bid-data
    if length own-bid-data > 10 [
      set own-bid-data but-first own-bid-data
    ]
    set market-intelligence replace-item 0 market-intelligence own-bid-data
  ]
end 

to-report calculate-actual-profit-margin
  ; Calculate actual profit margin achieved
  let current-tender one-of tenders with [id = id-active-tender]
  let actual-cost [estimated-cost] of current-tender

  if my-bid > actual-cost [
    report (my-bid - actual-cost) / my-bid
  ]
  report 0
end 

to adapt-profit-targets
  ; Adapt profit targets based on learning curve and recent performance
  if length profit-history > 2 [
    let recent-avg-profit mean profit-history
    let target-gap recent-avg-profit - target-profit-margin

    ; Adjust target based on learning curve speed and direction
    let adjustment target-gap * margin-adjustment-rate * learning-curve-speed

    ; Apply archetype-specific learning patterns
    if bidding-archetype = "adaptive" [
      set adjustment adjustment * 1.5  ; adaptive players learn faster
    ]
    if bidding-archetype = "conservative" [
      set adjustment adjustment * 0.5  ; conservative players change slowly
    ]

    set target-profit-margin target-profit-margin + adjustment

    ; Ensure target stays within reasonable bounds
    if target-profit-margin < MIN-PROFIT-MARGIN [ set target-profit-margin MIN-PROFIT-MARGIN ]
    if target-profit-margin > MAX-PROFIT-MARGIN [ set target-profit-margin MAX-PROFIT-MARGIN ]
  ]
end 

to improve-cost-estimation
  ; Gradually improve cost estimation accuracy through experience
  let improvement-rate learning-curve-speed * 0.01  ; 1% per round maximum
  set cost-estimation-accuracy cost-estimation-accuracy + improvement-rate

  ; Cap at maximum accuracy
  if cost-estimation-accuracy > 0.95 [ set cost-estimation-accuracy 0.95 ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DATA COLLECTION AND ANALYSIS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to collect-round-data
  ; Collect enhanced statistics for each round
  let current-bids [my-bid] of players
  let current-qualities [current-quality] of players
  let current-winner-exp [experience] of one-of players with [winner? = true]

  let avg-bid mean current-bids
  let min-bid min current-bids
  let max-bid max current-bids
  let bid-spread max-bid - min-bid
  let avg-quality mean current-qualities

  ; Calculate market concentration (simplified HHI)
  let current-market-shares []
  ask players [
    let share 0
    if total-bids > 0 [ set share win-count / total-bids ]
    set current-market-shares lput share current-market-shares
  ]
  let hhi sum map [s -> s * s] current-market-shares

  ; Store round statistics
  let round-data (list
    ticks avg-bid min-bid max-bid bid-spread
    avg-quality current-winner-exp hhi
  )
  set round-statistics lput round-data round-statistics
  ; Cap the list to prevent memory issues on long runs
  if length round-statistics > 500 [
    set round-statistics but-last round-statistics
  ]

  ; Also append to market-statistics for full round-by-round data
  set market-statistics lput round-data market-statistics
  if length market-statistics > 500 [
    set market-statistics but-last market-statistics
  ]
end 

to finalize-statistics
  ; Output final summary statistics
  output-print "=================="
  output-print "SIMULATION SUMMARY"
  output-print "=================="

  ask players [
    let win-rate 0
    if total-bids > 0 [ set win-rate precision (win-count / total-bids * 100) 1 ]
    output-print (sentence "Player" who ": " win-count "wins /" total-bids "bids (" win-rate "%)")
  ]

  if length round-statistics > 0 [
    let final-avg-bid mean map [r -> item 1 r] round-statistics
    let final-avg-quality mean map [r -> item 5 r] round-statistics
    output-print (sentence "Average bid across all rounds:" precision final-avg-bid 2)
    output-print (sentence "Average quality across all rounds:" precision final-avg-quality 2)
  ]
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ENHANCED REPORTING FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report bids-bs
  report [my-bid] of players
end 

to-report win-bid
  report [my-bid] of players with [winner? = true]
end 

to-report bid-adj
  report [bid-adjustment * 100] of players
end 

to-report quality-offered
  report [current-quality] of players
end 

to-report base-quality-levels
  report [base-quality] of players
end 

to-report experience-levels
  report [experience] of players
end 

to-report win-rates
  report [ifelse-value (total-bids > 0) [win-count / total-bids * 100] [0]] of players
end 

to-report market-positions
  report [market-position] of players
end 

to-report tender-values
  if any? tenders [ report [value] of tenders ]
  report []
end 

to-report tender-types
  if any? tenders [ report [tender-type] of tenders ]
  report []
end 

to-report average-bid-per-round
  if length round-statistics > 0 [
    report map [r -> item 1 r] round-statistics
  ]
  report []
end 

to-report bid-spread-per-round
  if length round-statistics > 0 [
    report map [r -> item 4 r] round-statistics
  ]
  report []
end 

to-report bidding-archetypes
  report [bidding-archetype] of players
end 

to-report strategy-confidence-levels
  report [strategy-confidence] of players
end 

to-report social-influence-levels
  report [social-influence] of players
end 

to-report market-knowledge-levels
  report [market-knowledge] of players
end 

to-report performance-trends
  report [performance-trend] of players
end 

to-report market-shares
  report [market-share] of players
end 

to-report learning-network-size
  report [length learning-partners] of players
end 

to-report archetype-distribution
  let archetypes bidding-archetypes
  let aggressive length filter [a -> a = "aggressive"] archetypes
  let conservative length filter [a -> a = "conservative"] archetypes
  let adaptive length filter [a -> a = "adaptive"] archetypes
  let follower length filter [a -> a = "follower"] archetypes
  report (list aggressive conservative adaptive follower)
end 

to-report meat-weights
  report (list meat-price-weight meat-quality-weight meat-experience-weight)
end 

to-report social-learning-stats
  report (list social-learning-rate market-intelligence-level strategy-imitation-threshold)
end 

to-report average-strategy-confidence
  report mean [strategy-confidence] of players
end 

to-report market-concentration-hhi
  let shares [market-share] of players
  report sum map [s -> s * s] shares
end 

to-report experience-performance-data
  ; Returns list of [experience win-rate] pairs for correlation analysis
  let data []
  ask players [
    let win-rate ifelse-value (total-bids > 0) [win-count / total-bids] [0]
    set data lput (list experience win-rate) data
  ]
  report data
end 

to-report experience-performance-correlation
  ; Calculate correlation coefficient between experience and performance
  let data experience-performance-data
  if length data < 2 [ report 0 ]

  ; The stats extension requires a table to calculate the correlation matrix.
  let tbl stats:newtable-from-row-list data
  let corr-matrix stats:correlation tbl

  ; In a 2x2 correlation matrix, the off-diagonal element is the Pearson coefficient.
  ; We can take the element at row 0, column 1. We add checks to prevent runtime errors.
  if is-list? corr-matrix and length corr-matrix >= 2 [
    let first-row item 0 corr-matrix
    if is-list? first-row and length first-row >= 2 [
      report item 1 first-row
    ]
  ]

  report 0 ; Return 0 if something went wrong or correlation is not possible.
end 

to-report average-experience-by-performance-tier
  ; Returns average experience for high, medium, and low performers
  let performers experience-performance-data
  if length performers = 0 [ report [0 0 0] ]

  ; Sort by performance (win rate)
  let sorted-performers sort-by [[d1 d2] -> (item 1 d1) > (item 1 d2)] performers
  let num-players length sorted-performers

  ; Divide into tiers
  let high-tier-size max (list 1 (floor (num-players / 3)))
  let low-tier-size max (list 1 (floor (num-players / 3)))

  let high-performers sublist sorted-performers 0 high-tier-size
  let low-performers sublist sorted-performers (num-players - low-tier-size) num-players
  let mid-performers sublist sorted-performers high-tier-size (num-players - low-tier-size)

  let high-avg-exp ifelse-value (length high-performers > 0) [mean map [d -> item 0 d] high-performers] [0]
  let mid-avg-exp ifelse-value (length mid-performers > 0) [mean map [d -> item 0 d] mid-performers] [0]
  let low-avg-exp ifelse-value (length low-performers > 0) [mean map [d -> item 0 d] low-performers] [0]

  report (list high-avg-exp mid-avg-exp low-avg-exp)
end 

;; Economic modeling and profit targeting reporters

to-report target-profit-margins
  report [target-profit-margin * 100] of players  ; return as percentages
end 

to-report current-profit-margins
  report [current-profit-margin * 100] of players  ; return as percentages
end 

to-report cost-estimation-accuracies
  report [cost-estimation-accuracy * 100] of players  ; return as percentages
end 

to-report learning-curve-speeds
  report [learning-curve-speed] of players
end 

to-report risk-premiums
  report [risk-premium * 100] of players  ; return as percentages
end 

to-report margin-sensitivity-levels
  report [margin-sensitivity] of players
end 

to-report average-target-profit-margin
  report precision (mean [target-profit-margin] of players * 100) 1
end 

to-report average-achieved-profit-margin
  let achieved-margins [current-profit-margin] of players with [current-profit-margin > 0]
  if length achieved-margins > 0 [
    report precision (mean achieved-margins * 100) 1
  ]
  report 0
end 

to-report average-cost-estimation-accuracy
  report precision (mean [cost-estimation-accuracy] of players * 100) 1
end 

to-report average-learning-speed
  report precision (mean [learning-curve-speed] of players) 2
end 

to-report profit-margin-variance
  let margins [target-profit-margin] of players
  if length margins > 1 [
    report precision (variance margins * 100 * 100) 1  ; convert to percentage variance
  ]
  report 0
end 

to-report archetype-profit-performance
  ; Returns profit performance by archetype
  let aggressive-margins []
  let conservative-margins []
  let adaptive-margins []
  let follower-margins []

  ask players [
    if current-profit-margin > 0 [
      if bidding-archetype = "aggressive" [
        set aggressive-margins lput current-profit-margin aggressive-margins
      ]
      if bidding-archetype = "conservative" [
        set conservative-margins lput current-profit-margin conservative-margins
      ]
      if bidding-archetype = "adaptive" [
        set adaptive-margins lput current-profit-margin adaptive-margins
      ]
      if bidding-archetype = "follower" [
        set follower-margins lput current-profit-margin follower-margins
      ]
    ]
  ]

  let agg-avg ifelse-value (length aggressive-margins > 0) [mean aggressive-margins * 100] [0]
  let con-avg ifelse-value (length conservative-margins > 0) [mean conservative-margins * 100] [0]
  let ada-avg ifelse-value (length adaptive-margins > 0) [mean adaptive-margins * 100] [0]
  let fol-avg ifelse-value (length follower-margins > 0) [mean follower-margins * 100] [0]

  report (list precision agg-avg 1 precision con-avg 1 precision ada-avg 1 precision fol-avg 1)
end 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PLOTTING PROCEDURES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to refresh-plots
  ; Update all plots with current round data
  update-bid-trends-plot
  update-bid-spread-plot
  update-archetype-win-rates-plot
  update-strategy-confidence-plot
  update-experience-performance-plot
  update-profit-margins-plot
end 

to update-bid-trends-plot
  ; Plot average, min, and max bids over time
  if any? players with [my-bid > 0] [
    let current-bids [my-bid] of players with [my-bid > 0]
    let current-ideal-bids [ideal-bid] of players with [ideal-bid > 0]

    set-current-plot "Bid Trends (Ideal vs Actual)"

    set-current-plot-pen "avg-ideal-bid"
    if any? players with [ideal-bid > 0] [
      plot precision mean current-ideal-bids 3
    ]

    set-current-plot-pen "avg-bid"
    plot precision mean current-bids 3

    set-current-plot-pen "min-bid"
    plot precision min current-bids 3

    set-current-plot-pen "max-bid"
    plot precision max current-bids 3
  ]
end 

to update-bid-spread-plot
  ; Plot bid spread (max - min bid) over time
  if any? players with [my-bid > 0] [
    let current-bids [my-bid] of players with [my-bid > 0]
    let spread max current-bids - min current-bids

    set-current-plot "Bid Spread Over Time"
    set-current-plot-pen "spread"
    plot precision spread 3
  ]
end 

to update-archetype-win-rates-plot
  ; Plot share of total wins for each bidding archetype
  set-current-plot "Win Rates by Archetype"

  ; Calculate total wins so far across all players
  let total-wins-so-far sum [win-count] of players
  if total-wins-so-far = 0 [ stop ] ; Stop if no wins have occurred yet

  ; Get player sets for each archetype
  let aggressive-players players with [bidding-archetype = "aggressive"]
  let conservative-players players with [bidding-archetype = "conservative"]
  let adaptive-players players with [bidding-archetype = "adaptive"]
  let follower-players players with [bidding-archetype = "follower"]

  ; --- Plot share of wins for each archetype ---

  set-current-plot-pen "aggressive"
  let aggressive-wins sum [win-count] of aggressive-players
  plot precision (aggressive-wins / total-wins-so-far * 100) 3

  set-current-plot-pen "conservative"
  let conservative-wins sum [win-count] of conservative-players
  plot precision (conservative-wins / total-wins-so-far * 100) 3

  set-current-plot-pen "adaptive"
  let adaptive-wins sum [win-count] of adaptive-players
  plot precision (adaptive-wins / total-wins-so-far * 100) 3

  set-current-plot-pen "follower"
  let follower-wins sum [win-count] of follower-players
  plot precision (follower-wins / total-wins-so-far * 100) 3
end 

to update-strategy-confidence-plot
  ; Plot average strategy confidence over time
  set-current-plot "Average Strategy Confidence"
  set-current-plot-pen "confidence"
  plot precision average-strategy-confidence 3
end 

to update-experience-performance-plot
  ; Plot experience vs performance analysis
  set-current-plot "Experience vs Performance"

  ; Plot correlation coefficient over time
  set-current-plot-pen "correlation"
  let correlation experience-performance-correlation
  plot precision correlation 3

  ; Plot average experience by performance tier
  let tier-data average-experience-by-performance-tier

  set-current-plot-pen "high-performers"
  plot precision (item 0 tier-data) 3

  set-current-plot-pen "mid-performers"
  plot precision (item 1 tier-data) 3

  set-current-plot-pen "low-performers"
  plot precision (item 2 tier-data) 3

  ; Plot average experience vs average performance
  set-current-plot-pen "avg-experience"
  let avg-exp mean [experience] of players
  plot precision (avg-exp / 10) 3  ; normalize to 0-1 scale for better visualization
end 

to update-profit-margins-plot
  ; Plot average target profit margins for each archetype over time
  set-current-plot "Profit Margins by Archetype"

  ; Calculate average target margins for each archetype
  let aggressive-players players with [bidding-archetype = "aggressive"]
  let conservative-players players with [bidding-archetype = "conservative"]
  let adaptive-players players with [bidding-archetype = "adaptive"]
  let follower-players players with [bidding-archetype = "follower"]

  ; Plot aggressive target margins
  set-current-plot-pen "aggressive"
  if any? aggressive-players [
    let avg-margin mean [target-profit-margin * 100] of aggressive-players
    plot precision avg-margin 1
  ]

  ; Plot conservative target margins
  set-current-plot-pen "conservative"
  if any? conservative-players [
    let avg-margin mean [target-profit-margin * 100] of conservative-players
    plot precision avg-margin 1
  ]

  ; Plot adaptive target margins
  set-current-plot-pen "adaptive"
  if any? adaptive-players [
    let avg-margin mean [target-profit-margin * 100] of adaptive-players
    plot precision avg-margin 1
  ]

  ; Plot follower target margins
  set-current-plot-pen "follower"
  if any? follower-players [
    let avg-margin mean [target-profit-margin * 100] of follower-players
    plot precision avg-margin 1
  ]
end 

to initialize-plots
  ; Initialize all plots
  clear-all-plots

  ; Setup Average Bid Trends plot
  set-current-plot "Bid Trends (Ideal vs Actual)"
  set-plot-x-range 0 number-rounds

  ; Setup Bid Spread plot
  set-current-plot "Bid Spread Over Time"
  set-plot-x-range 0 number-rounds

  ; Setup Archetype Win Rates plot
  set-current-plot "Win Rates by Archetype"
  set-plot-x-range 0 number-rounds
  set-plot-y-range 0 100

  ; Setup Strategy Confidence plot
  set-current-plot "Average Strategy Confidence"
  set-plot-x-range 0 number-rounds
  set-plot-y-range 0 1

  ; Setup Profit Margins by Archetype plot
  set-current-plot "Profit Margins by Archetype"
  set-plot-x-range 0 number-rounds
  set-plot-y-range 0 50  ; profit margins range from 0% to 50%

  ; Setup Experience vs Performance plot
  set-current-plot "Experience vs Performance"
  set-plot-x-range 0 number-rounds
  set-plot-y-range -1 1   ; correlation (Pearson r) ranges from -1 to 1
end 

There is only one version of this model, created 3 days ago by Riccardo Pizzuti.

Attached files

File Type Description Last updated
Tender-Simulation-ABM.png preview Preview for 'Tender-Simulation-ABM' 3 days ago, by Riccardo Pizzuti Download

This model does not have any ancestors.

This model does not have any descendants.