Files
grepo-remote/blueprint_engine.py
2026-05-02 01:07:01 +03:00

178 lines
7.5 KiB
Python

import json
import logging
log = logging.getLogger(__name__)
STANDARD_BLUEPRINT = [
{"barracks": 1, "farm": 3, "lumber": 2, "stoner": 2, "ironer": 2, "storage": 2, "main": 2, "temple": 1},
{"barracks": 1, "farm": 3, "lumber": 3, "stoner": 3, "ironer": 3, "storage": 6, "main": 8},
{"farm": 8, "lumber": 8, "ironer": 8, "stoner": 8, "market": 5, "temple": 5, "barracks": 5},
{"academy": 13},
{"storage": 12, "farm": 12},
{"main": 25},
{"storage": 21, "farm": 15},
{"lumber": 15, "stoner": 10, "ironer": 12},
{"docks": 10},
{"academy": 30},
{"farm": 20, "storage": 25},
{"market": 15, "trade_office": 1, "hide": 10},
{"market": 30, "farm": 35, "thermal": 1, "academy": 36},
{"farm": 45, "storage": 35, "lumber": 40, "ironer": 40, "stoner": 40},
{"temple": 30}
]
RESEARCH_LIST = [
"booty", "pottery", "architecture", "building_crane",
"shipwright", "plow", "mathematics", "combat_experience",
"strong_wine", "take_over", "colonize_ship"
]
RESEARCH_LEVELS = {
"booty": 7,
"pottery": 7,
"architecture": 10,
"building_crane": 13,
"shipwright": 13,
"plow": 22,
"mathematics": 25,
"combat_experience": 34,
"strong_wine": 34,
"take_over": 28,
"colonize_ship": 13
}
def evaluate_blueprints(conn):
blueprints = conn.execute('SELECT town_id, blueprint_name FROM town_blueprints WHERE is_active = 1').fetchall()
log.warning(f"[blueprint] Active blueprints: {len(blueprints)}")
if not blueprints:
return
for row in blueprints:
town_id = str(row['town_id'])
log.warning(f"[blueprint] Evaluating town_id={town_id}")
town_row = conn.execute(
'SELECT data, player_id, town_name FROM town_state WHERE town_id = ?', (town_id,)
).fetchone()
if not town_row:
log.warning(f"[blueprint] No town_state row found for town_id={town_id} — skipping")
continue
player_id = town_row['player_id']
town_name_db = town_row['town_name']
log.warning(f"[blueprint] Town: {town_name_db}, player_id={player_id}")
try:
town = json.loads(town_row['data'])
except Exception as e:
log.warning(f"[blueprint] Failed to parse town data JSON: {e}")
continue
build_queue = town.get('buildingOrder', [])
buildings = town.get('buildings', {})
build_data = town.get('buildData', {})
log.warning(f"[blueprint] buildings keys: {list(buildings.keys())}")
log.warning(f"[blueprint] buildData keys: {list(build_data.keys())}")
log.warning(f"[blueprint] buildingOrder length: {len(build_queue)}")
# Don't queue anything if there's already a pending/executing command in DB
db_pending = conn.execute(
"SELECT id, type, status FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')",
(town_id,)
).fetchall()
if db_pending:
details = [(r['id'], r['type'], r['status']) for r in db_pending]
log.warning(f"[blueprint] Already has {len(db_pending)} pending/executing commands — skipping. Commands: {details}")
continue
# Calculate Future Levels based on current + game queue
future_levels = {k: v for k, v in buildings.items()}
for q_item in build_queue:
b_type = q_item.get('building_type') or q_item.get('name')
if b_type:
future_levels[b_type] = future_levels.get(b_type, 0) + 1
log.warning(f"[blueprint] future_levels: {future_levels}")
# Find next required building
target_building = None
phase_incomplete = False
blocked_phases = 0
for phase_idx, phase in enumerate(STANDARD_BLUEPRINT):
incomplete_buildings = []
for b_name, req_level in phase.items():
if future_levels.get(b_name, 0) < req_level:
incomplete_buildings.append(b_name)
if incomplete_buildings:
phase_incomplete = True
log.warning(f"[blueprint] Phase {phase_idx} is incomplete. Missing: {incomplete_buildings}")
waiting_for_resources = False
for b_name in incomplete_buildings:
b_info = build_data.get(b_name)
if b_info is None:
log.warning(f"[blueprint] {b_name}: no buildData entry — skipping")
continue
has_max = b_info.get('has_max_level', False)
deps = b_info.get('missing_dependencies')
enough = b_info.get('enough_resources')
log.warning(f"[blueprint] {b_name}: has_max={has_max}, deps={deps}, enough_resources={enough}")
if not has_max:
if not deps:
if enough != False:
target_building = b_name
log.warning(f"[blueprint] -> SELECTED {b_name}")
break
else:
log.warning(f"[blueprint] -> waiting for resources for {b_name}")
waiting_for_resources = True
if target_building:
break
elif waiting_for_resources:
log.warning(f"[blueprint] Phase {phase_idx}: blocked by resources, stopping lookahead")
break
else:
log.warning(f"[blueprint] Phase {phase_idx}: all blocked by deps, looking ahead (blocked_phases={blocked_phases+1})")
blocked_phases += 1
if blocked_phases > 2:
log.warning(f"[blueprint] Too many blocked phases, giving up")
break
# Handle Academy Tech Research
target_research = None
if not target_building:
academy_level = future_levels.get('academy', 0)
researched = town.get('researches', {})
for r_name in RESEARCH_LIST:
if not researched.get(r_name):
req_level = RESEARCH_LEVELS.get(r_name, 99)
if academy_level >= req_level:
target_research = r_name
log.warning(f"[blueprint] -> Research target: {r_name}")
break
log.warning(f"[blueprint] Final: target_building={target_building}, target_research={target_research}")
if target_building:
payload_str = json.dumps({"building_id": target_building})
conn.execute('''
INSERT INTO commands (town_id, town_name, type, payload, status, player_id)
VALUES (?, ?, ?, ?, ?, ?)
''', (town_id, town_name_db, 'build', payload_str, 'pending', player_id))
log.warning(f"[blueprint] Inserted build command: {target_building} for {town_name_db}")
elif target_research:
payload_str = json.dumps({"research_id": target_research})
conn.execute('''
INSERT INTO commands (town_id, town_name, type, payload, status, player_id)
VALUES (?, ?, ?, ?, ?, ?)
''', (town_id, town_name_db, 'research', payload_str, 'pending', player_id))
log.warning(f"[blueprint] Inserted research command: {target_research} for {town_name_db}")
else:
log.warning(f"[blueprint] Nothing to do for {town_name_db}")