diff --git a/blueprint_engine.py b/blueprint_engine.py index d1a8ede..3436f13 100644 --- a/blueprint_engine.py +++ b/blueprint_engine.py @@ -40,106 +40,134 @@ RESEARCH_LEVELS = { def evaluate_blueprints(conn): blueprints = conn.execute('SELECT town_id, blueprint_name FROM town_blueprints WHERE is_active = 1').fetchall() + print(f"[blueprint] Active blueprints: {len(blueprints)}") if not blueprints: return for row in blueprints: town_id = str(row['town_id']) - town_row = conn.execute('SELECT data, player_id, town_name FROM town_state WHERE town_id = ?', (town_id,)).fetchone() + print(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: + print(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'] - + print(f"[blueprint] Town: {town_name_db}, player_id={player_id}") + try: town = json.loads(town_row['data']) - except Exception: + except Exception as e: + print(f"[blueprint] Failed to parse town data JSON: {e}") continue - + build_queue = town.get('buildingOrder', []) buildings = town.get('buildings', {}) build_data = town.get('buildData', {}) - + + print(f"[blueprint] buildings keys: {list(buildings.keys())}") + print(f"[blueprint] buildData keys: {list(build_data.keys())}") + print(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 FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", (town_id,)).fetchall() + db_pending = conn.execute( + "SELECT id FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", + (town_id,) + ).fetchall() if db_pending: + print(f"[blueprint] Already has {len(db_pending)} pending/executing commands — skipping") 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 - + + print(f"[blueprint] future_levels: {future_levels}") + # Find next required building target_building = None phase_incomplete = False blocked_phases = 0 - - for phase in STANDARD_BLUEPRINT: + + 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 + print(f"[blueprint] Phase {phase_idx} is incomplete. Missing: {incomplete_buildings}") waiting_for_resources = False - - # Try to find a building in this phase that we can upgrade right now + for b_name in incomplete_buildings: b_info = build_data.get(b_name) - if b_info and not b_info.get('has_max_level'): - # A missing_dependencies object that is empty means dependencies are met - deps = b_info.get('missing_dependencies') + if b_info is None: + print(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') + print(f"[blueprint] {b_name}: has_max={has_max}, deps={deps}, enough_resources={enough}") + + if not has_max: if not deps: - if b_info.get('enough_resources') != False: + if enough != False: target_building = b_name + print(f"[blueprint] -> SELECTED {b_name}") break else: + print(f"[blueprint] -> waiting for resources for {b_name}") waiting_for_resources = True - + if target_building: - # Found something we can build right now break elif waiting_for_resources: - # We meet the dependencies for at least one building, but lack resources. - # We should wait for resources to accumulate instead of spending them on future phases. + print(f"[blueprint] Phase {phase_idx}: blocked by resources, stopping lookahead") break else: - # ALL incomplete buildings in this phase are blocked by missing dependencies. - # We look ahead up to 2 additional phases to build the required dependencies. + print(f"[blueprint] Phase {phase_idx}: all blocked by deps, looking ahead (blocked_phases={blocked_phases+1})") blocked_phases += 1 if blocked_phases > 2: + print(f"[blueprint] Too many blocked phases, giving up") break - + # Handle Academy Tech Research target_research = None - # Only queue research if we aren't about to queue a building, to prevent double-booking if not target_building: academy_level = future_levels.get('academy', 0) researched = town.get('researches', {}) - # We don't have a reliable research queue from the state yet, so we just check what's researched. - # But we should ensure we don't queue multiple of the same. The DB check above handles 'research' pending commands. - 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 + print(f"[blueprint] -> Research target: {r_name}") break + print(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)) + print(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)) + print(f"[blueprint] Inserted research command: {target_research} for {town_name_db}") + else: + print(f"[blueprint] Nothing to do for {town_name_db}")