Spaces:
Sleeping
Sleeping
Aryanshh commited on
Commit ·
c547cb1
1
Parent(s): 262e2fd
fix: Decouple orders from time advance and implement cart cancellation
Browse files- dashboard/app.js +15 -17
- netzero_nav/env.py +55 -32
- netzero_nav/models.py +1 -0
dashboard/app.js
CHANGED
|
@@ -62,24 +62,18 @@ async function updateState() {
|
|
| 62 |
document.getElementById('chips-count').textContent = data.inventory.chips;
|
| 63 |
document.getElementById('sensors-count').textContent = data.inventory.sensors;
|
| 64 |
|
| 65 |
-
// Update Cart
|
| 66 |
const cartContainer = document.getElementById('cart-container');
|
| 67 |
if (data.active_shipments && data.active_shipments.length > 0) {
|
| 68 |
-
|
| 69 |
-
const grouped = data.active_shipments.reduce((acc, ship) => {
|
| 70 |
-
const key = `${ship.part}-${ship.eta}`;
|
| 71 |
-
if (!acc[key]) {
|
| 72 |
-
acc[key] = { ...ship };
|
| 73 |
-
} else {
|
| 74 |
-
acc[key].quantity += ship.quantity;
|
| 75 |
-
}
|
| 76 |
-
return acc;
|
| 77 |
-
}, {});
|
| 78 |
-
|
| 79 |
-
cartContainer.innerHTML = Object.values(grouped).map(ship => `
|
| 80 |
<div class="cart-item">
|
| 81 |
-
<div class="cart-type">
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
<div class="arrival-day">Arrives in: ${ship.eta} days</div>
|
| 84 |
</div>
|
| 85 |
`).join('');
|
|
@@ -149,7 +143,7 @@ function log(message, type = 'system') {
|
|
| 149 |
}
|
| 150 |
|
| 151 |
// Global Execute Function
|
| 152 |
-
window.execute = async function(type, event) {
|
| 153 |
document.querySelector('main').classList.add('transitioning');
|
| 154 |
|
| 155 |
let actionObj = { action_type: type };
|
|
@@ -165,6 +159,9 @@ window.execute = async function(type, event) {
|
|
| 165 |
actionObj.offset_amount = 100.0;
|
| 166 |
} else if (type === 'skip') {
|
| 167 |
actionObj.action_type = 'skip';
|
|
|
|
|
|
|
|
|
|
| 168 |
}
|
| 169 |
|
| 170 |
try {
|
|
@@ -178,7 +175,8 @@ window.execute = async function(type, event) {
|
|
| 178 |
if (result.info && result.info.error) {
|
| 179 |
log(`ERROR: ${result.info.error}`, 'error');
|
| 180 |
} else {
|
| 181 |
-
|
|
|
|
| 182 |
}
|
| 183 |
|
| 184 |
await updateState();
|
|
|
|
| 62 |
document.getElementById('chips-count').textContent = data.inventory.chips;
|
| 63 |
document.getElementById('sensors-count').textContent = data.inventory.sensors;
|
| 64 |
|
| 65 |
+
// Update Cart
|
| 66 |
const cartContainer = document.getElementById('cart-container');
|
| 67 |
if (data.active_shipments && data.active_shipments.length > 0) {
|
| 68 |
+
cartContainer.innerHTML = data.active_shipments.map(ship => `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
<div class="cart-item">
|
| 70 |
+
<div class="cart-type">
|
| 71 |
+
${ship.part} (x${ship.quantity})
|
| 72 |
+
<button onclick="execute('cancel', event, '${ship.id}')" class="btn btn-ghost small" style="float: right; color: #ff4136;">✕</button>
|
| 73 |
+
</div>
|
| 74 |
+
<div class="cart-meta">Mode: ${ship.mode.toUpperCase()}
|
| 75 |
+
<span style="float:right; color:var(--primary-green); font-weight:600;">$${ship.cost.toFixed(0)}</span>
|
| 76 |
+
</div>
|
| 77 |
<div class="arrival-day">Arrives in: ${ship.eta} days</div>
|
| 78 |
</div>
|
| 79 |
`).join('');
|
|
|
|
| 143 |
}
|
| 144 |
|
| 145 |
// Global Execute Function
|
| 146 |
+
window.execute = async function(type, event, shipment_id = null) {
|
| 147 |
document.querySelector('main').classList.add('transitioning');
|
| 148 |
|
| 149 |
let actionObj = { action_type: type };
|
|
|
|
| 159 |
actionObj.offset_amount = 100.0;
|
| 160 |
} else if (type === 'skip') {
|
| 161 |
actionObj.action_type = 'skip';
|
| 162 |
+
} else if (type === 'cancel') {
|
| 163 |
+
actionObj.action_type = 'cancel';
|
| 164 |
+
actionObj.shipment_id = shipment_id;
|
| 165 |
}
|
| 166 |
|
| 167 |
try {
|
|
|
|
| 175 |
if (result.info && result.info.error) {
|
| 176 |
log(`ERROR: ${result.info.error}`, 'error');
|
| 177 |
} else {
|
| 178 |
+
if (type === 'cancel') log(`SUCCESS: Canceled Shipment`, 'action');
|
| 179 |
+
else log(`SUCCESS: ${type === 'skip' ? 'Skipped Day' : 'Executed ' + type}`, 'action');
|
| 180 |
}
|
| 181 |
|
| 182 |
await updateState();
|
netzero_nav/env.py
CHANGED
|
@@ -50,44 +50,45 @@ class AtlasEcoEnv:
|
|
| 50 |
)
|
| 51 |
|
| 52 |
def step(self, action: Action) -> Tuple[Observation, float, bool, dict]:
|
| 53 |
-
self.step_count += 1
|
| 54 |
reward = 0.0
|
| 55 |
news = None
|
| 56 |
-
|
| 57 |
-
if self.step_count < self.sea_blocked_until:
|
| 58 |
-
news = "EMERGENCY: Suez Canal blocked! Sea routes offline."
|
| 59 |
-
|
| 60 |
info = {}
|
| 61 |
|
| 62 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
if action.action_type == ActionType.ORDER_PARTS:
|
| 64 |
reward += self._handle_order_parts(action, info)
|
| 65 |
elif action.action_type == ActionType.PRODUCE:
|
| 66 |
reward += self._handle_production(action, info)
|
| 67 |
elif action.action_type == ActionType.OFFSET:
|
| 68 |
reward += self._handle_offset(action, info)
|
| 69 |
-
elif action.action_type == ActionType.
|
| 70 |
-
reward += self.
|
| 71 |
-
|
| 72 |
-
# 2. Advance Shipments
|
| 73 |
-
for ship in self.active_shipments:
|
| 74 |
-
ship.eta -= 1
|
| 75 |
-
if ship.eta <= 0:
|
| 76 |
-
self._receive_shipment(ship)
|
| 77 |
-
|
| 78 |
-
self.active_shipments = [s for s in self.active_shipments if s.eta > 0]
|
| 79 |
|
| 80 |
-
# 3. Check Order Deadlines
|
| 81 |
-
for order in self.pending_orders:
|
| 82 |
-
if self.step_count > order.due_date:
|
| 83 |
-
reward -= 50.0 # Late penalty
|
| 84 |
-
|
| 85 |
# 4. Check Termination
|
| 86 |
if self.step_count >= 50 or not self.pending_orders:
|
| 87 |
self.done = True
|
| 88 |
info["final_score"] = self._calculate_final_score()
|
| 89 |
|
| 90 |
-
return self._get_obs(), reward, self.done, info
|
| 91 |
|
| 92 |
def _handle_order_parts(self, action: Action, info: dict) -> float:
|
| 93 |
if not action.part_type or not action.mode or not action.quantity:
|
|
@@ -110,18 +111,40 @@ class AtlasEcoEnv:
|
|
| 110 |
self.cash_balance -= total_cost
|
| 111 |
self.carbon_total += carbon * action.quantity
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
part=action.part_type
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
return 2.0
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
def _receive_shipment(self, ship: Shipment):
|
| 126 |
current_val = getattr(self.inventory, ship.part.value)
|
| 127 |
setattr(self.inventory, ship.part.value, current_val + ship.quantity)
|
|
|
|
| 50 |
)
|
| 51 |
|
| 52 |
def step(self, action: Action) -> Tuple[Observation, float, bool, dict]:
|
|
|
|
| 53 |
reward = 0.0
|
| 54 |
news = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
info = {}
|
| 56 |
|
| 57 |
+
# Day Advancement Loop
|
| 58 |
+
if action.action_type == ActionType.SKIP:
|
| 59 |
+
self.step_count += 1
|
| 60 |
+
if self.step_count < self.sea_blocked_until:
|
| 61 |
+
news = "EMERGENCY: Suez Canal blocked! Sea routes offline."
|
| 62 |
+
|
| 63 |
+
# 2. Advance Shipments
|
| 64 |
+
for ship in self.active_shipments:
|
| 65 |
+
ship.eta -= 1
|
| 66 |
+
if ship.eta <= 0:
|
| 67 |
+
self._receive_shipment(ship)
|
| 68 |
+
|
| 69 |
+
self.active_shipments = [s for s in self.active_shipments if s.eta > 0]
|
| 70 |
+
|
| 71 |
+
# 3. Check Order Deadlines
|
| 72 |
+
for order in self.pending_orders:
|
| 73 |
+
if self.step_count > order.due_date:
|
| 74 |
+
reward -= 50.0 # Late penalty
|
| 75 |
+
|
| 76 |
+
# 1. Process Actions (No Time Advancement)
|
| 77 |
if action.action_type == ActionType.ORDER_PARTS:
|
| 78 |
reward += self._handle_order_parts(action, info)
|
| 79 |
elif action.action_type == ActionType.PRODUCE:
|
| 80 |
reward += self._handle_production(action, info)
|
| 81 |
elif action.action_type == ActionType.OFFSET:
|
| 82 |
reward += self._handle_offset(action, info)
|
| 83 |
+
elif action.action_type == ActionType.CANCEL:
|
| 84 |
+
reward += self._handle_cancel(action, info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
# 4. Check Termination
|
| 87 |
if self.step_count >= 50 or not self.pending_orders:
|
| 88 |
self.done = True
|
| 89 |
info["final_score"] = self._calculate_final_score()
|
| 90 |
|
| 91 |
+
return self._get_obs(news=news), reward, self.done, info
|
| 92 |
|
| 93 |
def _handle_order_parts(self, action: Action, info: dict) -> float:
|
| 94 |
if not action.part_type or not action.mode or not action.quantity:
|
|
|
|
| 111 |
self.cash_balance -= total_cost
|
| 112 |
self.carbon_total += carbon * action.quantity
|
| 113 |
|
| 114 |
+
merged = False
|
| 115 |
+
for ship in self.active_shipments:
|
| 116 |
+
if ship.part == action.part_type and ship.mode == action.mode and ship.eta == eta:
|
| 117 |
+
ship.quantity += action.quantity
|
| 118 |
+
ship.cost += total_cost
|
| 119 |
+
ship.carbon_impact += carbon * action.quantity
|
| 120 |
+
merged = True
|
| 121 |
+
break
|
| 122 |
+
|
| 123 |
+
if not merged:
|
| 124 |
+
new_ship = Shipment(
|
| 125 |
+
id=f"SHP_{random.randint(1000, 9999)}",
|
| 126 |
+
part=action.part_type,
|
| 127 |
+
quantity=action.quantity,
|
| 128 |
+
mode=action.mode,
|
| 129 |
+
eta=eta,
|
| 130 |
+
carbon_impact=carbon * action.quantity,
|
| 131 |
+
cost=total_cost
|
| 132 |
+
)
|
| 133 |
+
self.active_shipments.append(new_ship)
|
| 134 |
return 2.0
|
| 135 |
|
| 136 |
+
def _handle_cancel(self, action: Action, info: dict) -> float:
|
| 137 |
+
if not action.shipment_id: return 0.0
|
| 138 |
+
|
| 139 |
+
for i, ship in enumerate(self.active_shipments):
|
| 140 |
+
if ship.id == action.shipment_id:
|
| 141 |
+
# Refund
|
| 142 |
+
self.cash_balance += ship.cost
|
| 143 |
+
self.carbon_total = max(0.0, self.carbon_total - ship.carbon_impact)
|
| 144 |
+
self.active_shipments.pop(i)
|
| 145 |
+
return 0.0
|
| 146 |
+
return 0.0
|
| 147 |
+
|
| 148 |
def _receive_shipment(self, ship: Shipment):
|
| 149 |
current_val = getattr(self.inventory, ship.part.value)
|
| 150 |
setattr(self.inventory, ship.part.value, current_val + ship.quantity)
|
netzero_nav/models.py
CHANGED
|
@@ -52,6 +52,7 @@ class ActionType(str, Enum):
|
|
| 52 |
PRODUCE = "produce"
|
| 53 |
OFFSET = "offset"
|
| 54 |
SKIP = "skip"
|
|
|
|
| 55 |
|
| 56 |
class Action(BaseModel):
|
| 57 |
action_type: ActionType
|
|
|
|
| 52 |
PRODUCE = "produce"
|
| 53 |
OFFSET = "offset"
|
| 54 |
SKIP = "skip"
|
| 55 |
+
CANCEL = "cancel"
|
| 56 |
|
| 57 |
class Action(BaseModel):
|
| 58 |
action_type: ActionType
|