online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
import itertools import random import math import time from collections import deque # ========================================== # CONFIGURATION & INPUTS # ========================================== # 1. ENTER YOUR CURRENT RESOURCES HERE INVENTORY = { "carts": 6, "wagons": 3, "wells": 3, "sumps": 13, "pipes": 19 } # 2. CURRENT LAYOUT (New Section) # Enter the Node IDs where your structures are currently built. # Leave lists empty [] if you have nothing built. # Pipe Format: "NodeA-NodeB" (e.g. "V6H1-V6H2") CURRENT_LAYOUT = { "wells": ["V1H3", "V3H2"], "sumps": ["V1H1", "V1H2", "V2H1", "V2H2", "V3H1", "SPLIT_D2_T", "SPLIT_D2_B", "V4H1", "V4H2", "V5H0", "V5H1", "V5H2", "V5H3"], "pipes": ["V1H1-V2H1", "V1H2-V2H2", "V2H1-V3H1", "V2H1-V2H2", "V3H1-SPLIT_D2_T", "SPLIT_D2_T-V4H1", "SPLIT_D2_B-V4H2", "V4H1-V5H1", "V4H2-V5H2", "V5H1-V6H1", "V5H0-V5H1", "V5H1-V5H2", "V5H2-V5H3"] } # 3. SUMP EFFICIENCY CONFIGURATION SUMP_CONFIG = { "HIGH_OUTPUT": 11, # Rightmost 3 streets + D2 Split "MID_OUTPUT": 11, # Middle 2 streets "LOW_OUTPUT": 11 # Leftmost 2 streets + A2 Split } # 4. ENTER WATER DEMAND FOR EACH HOUSE DEMANDS = { "A1": 24, "B1": 25, "C1": 20, "D1": 21, "E1": 19, "F1": 20, "A2T": 22, "B2": 22, "C2": 23, "D2L": 19, "E2": 25, "F2": 23, "A2B": 22, "D2R": 21, "A3": 21, "B3": 22, "C3": 21, "D3": 17, "E3": 22, "F3": 24 } # ========================================== # GAME LOGIC & MAPPING # ========================================== HOUSES = list(DEMANDS.keys()) HOUSE_INDICES = {h: i for i, h in enumerate(HOUSES)} HOUSE_NEIGHBORS = { "A1": ["B1", "A2T"], "B1": ["A1", "C1", "B2"], "C1": ["B1", "D1", "C2"], "D1": ["C1", "E1", "D2L", "D2R"], "E1": ["D1", "F1", "E2"], "F1": ["E1", "F2"], "A2T": ["A1", "A2B", "B2"], "A2B": ["A2T", "A3", "B2"], "B2": ["B1", "B3", "C2", "A2T", "A2B"], "C2": ["C1", "C3", "B2", "D2L"], "D2L": ["D1", "D3", "C2", "D2R"], "D2R": ["D1", "D3", "E2", "D2L"], "E2": ["E1", "E3", "F2", "D2R"], "F2": ["F1", "F3", "E2"], "A3": ["B3", "A2B"], "B3": ["A3", "C3", "B2"], "C3": ["B3", "D3", "C2"], "D3": ["C3", "E3", "D2L", "D2R"], "E3": ["D3", "F3", "E2"], "F3": ["E3", "F2"] } # Pre-compute neighbor indices for faster lookup HOUSE_NEIGHBOR_INDICES = [ [HOUSE_INDICES[n] for n in HOUSE_NEIGHBORS[h]] for h in HOUSES ] NODES = [] NODE_ADJACENCY = {} NODE_SUMP_VALUE = {} NODE_NEIGHBORS = {} def setup_grid(): # 1. Standard Intersections for v in range(7): for h in range(4): node_id = f"V{v}H{h}" NODES.append(node_id) if v >= 4: val = SUMP_CONFIG["HIGH_OUTPUT"] elif v >= 2: val = SUMP_CONFIG["MID_OUTPUT"] else: val = SUMP_CONFIG["LOW_OUTPUT"] NODE_SUMP_VALUE[node_id] = val adj = [] def get_house(c, r): if c < 0 or c > 5 or r < 0 or r > 2: return None if c == 0 and r == 1: return ["A2T", "A2B"] if c == 3 and r == 1: return ["D2L", "D2R"] cols = "ABCDEF" return [f"{cols[c]}{r+1}"] # Quadrant checks hl = get_house(v-1, h-1) if hl: for hx in hl: if hx == "A2T" and h == 2: continue if hx == "A2B" and h == 1: continue if hx == "D2L" and v == 4: continue if hx == "D2R" and v == 3: continue adj.append(hx) hr = get_house(v, h-1) if hr: for hx in hr: if hx == "A2T" and h == 2: continue if hx == "A2B" and h == 1: continue if hx == "D2L" and v == 4: continue if hx == "D2R" and v == 3: continue adj.append(hx) bl = get_house(v-1, h) if bl: for hx in bl: if hx == "A2T" and h == 2: continue if hx == "A2B" and h == 1: continue if hx == "D2L" and v == 4: continue if hx == "D2R" and v == 3: continue adj.append(hx) br = get_house(v, h) if br: for hx in br: if hx == "A2T" and h == 2: continue if hx == "A2B" and h == 1: continue if hx == "D2L" and v == 4: continue if hx == "D2R" and v == 3: continue adj.append(hx) NODE_ADJACENCY[node_id] = list(set(adj)) # 2. Special Split Nodes nid = "SPLIT_A2_L" NODES.append(nid) NODE_SUMP_VALUE[nid] = SUMP_CONFIG["LOW_OUTPUT"] NODE_ADJACENCY[nid] = ["A2T", "A2B"] nid = "SPLIT_A2_R" NODES.append(nid) NODE_SUMP_VALUE[nid] = SUMP_CONFIG["LOW_OUTPUT"] NODE_ADJACENCY[nid] = ["A2T", "A2B", "B2"] nid = "SPLIT_D2_T" NODES.append(nid) NODE_SUMP_VALUE[nid] = SUMP_CONFIG["HIGH_OUTPUT"] NODE_ADJACENCY[nid] = ["D1", "D2L", "D2R"] nid = "SPLIT_D2_B" NODES.append(nid) NODE_SUMP_VALUE[nid] = SUMP_CONFIG["HIGH_OUTPUT"] NODE_ADJACENCY[nid] = ["D3", "D2L", "D2R"] # 3. Graph Connections for n in NODES: NODE_NEIGHBORS[n] = [] def connect(n1, n2): if n1 in NODES and n2 in NODES: NODE_NEIGHBORS[n1].append(n2) NODE_NEIGHBORS[n2].append(n1) for v in range(7): for h in range(4): curr = f"V{v}H{h}" if v < 6: connect(curr, f"V{v+1}H{h}") if h < 3: connect(curr, f"V{v}H{h+1}") connect("SPLIT_A2_L", "V0H1") connect("SPLIT_A2_L", "V0H2") connect("SPLIT_A2_L", "SPLIT_A2_R") connect("SPLIT_A2_R", "V1H1") connect("SPLIT_A2_R", "V1H2") connect("SPLIT_D2_T", "V3H1") connect("SPLIT_D2_T", "V4H1") connect("SPLIT_D2_T", "SPLIT_D2_B") connect("SPLIT_D2_B", "V3H2") connect("SPLIT_D2_B", "V4H2") setup_grid() MILL_NODE = "V6H1" # ========================================== # VALIDATION UTILS # ========================================== def get_connected_sumps_from_layout(layout): if not layout['sumps'] or not layout['pipes']: return [] adj = {n: [] for n in NODES} for p in layout['pipes']: parts = p.split('-') if len(parts) != 2: continue u, v = parts[0].strip(), parts[1].strip() if u in adj and v in adj: adj[u].append(v) adj[v].append(u) connected = set() queue = deque([MILL_NODE]) visited = {MILL_NODE} while queue: curr = queue.popleft() connected.add(curr) for n in adj[curr]: if n not in visited: visited.add(n) queue.append(n) active_sumps = [s for s in layout['sumps'] if s in connected] inactive_sumps = [s for s in layout['sumps'] if s not in connected] if len(inactive_sumps) > 0: print(f" ! Warning: {inactive_sumps} not connected to Mill.") return active_sumps def check_theoretical_pipe_validity(sump_nodes, available_pipes): if not sump_nodes: return True connected_nodes = {MILL_NODE} target_sumps = set(sump_nodes) if MILL_NODE in target_sumps: target_sumps.remove(MILL_NODE) pipes_used = 0 while target_sumps: best_path = [] found = False queue = deque([(n, []) for n in connected_nodes]) visited = set(connected_nodes) while queue: curr, path = queue.popleft() if curr in target_sumps: best_path = path chosen_sump = curr found = True break for neighbor in NODE_NEIGHBORS[curr]: if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, path + [neighbor])) if not found: return False pipes_used += len(best_path) for node in best_path: connected_nodes.add(node) target_sumps.remove(chosen_sump) return pipes_used <= available_pipes # ========================================== # SOLVER ENGINE # ========================================== def calculate_supply_from_structures(active_structures): """Calculate base supply from wells/sumps only.""" supply = [0] * len(HOUSES) for node, s_type in active_structures.items(): if s_type == 'well': amt = 2 else: amt = NODE_SUMP_VALUE[node] for house_str in NODE_ADJACENCY[node]: idx = HOUSE_INDICES[house_str] supply[idx] += amt return supply def add_employee_supply(base_supply, cart_locs, wagon_locs): """ Adds employee contribution to a base supply list. Accepts lists of locations (e.g. ['A1', 'A1', 'B2']) """ # Copy strictly required? depends on context. # In loops we usually copy before calling this or modify a temp array. supply = list(base_supply) # Wagons for h_idx in wagon_locs: supply[h_idx] += 8 for n_idx in HOUSE_NEIGHBOR_INDICES[h_idx]: supply[n_idx] += 6 # Carts for h_idx in cart_locs: supply[h_idx] += 3 for n_idx in HOUSE_NEIGHBOR_INDICES[h_idx]: supply[n_idx] += 2 return supply def get_score_from_supply_array(supply, demand_array): inf = 0 shortfall = 0 for i in range(len(supply)): diff = supply[i] - demand_array[i] if diff >= 0: inf += 1 elif diff == -1: pass # +0 else: inf -= 2 if diff < 0: shortfall += abs(diff) return inf, shortfall def calculate_full_score(assignments, active_structures, demands_dict): """ assignments: {'carts': ['A1',...], 'wagons': ['B2',...]} """ # Build Demand Array dem_arr = [demands_dict[h] for h in HOUSES] # 1. Structure Supply base_supply = calculate_supply_from_structures(active_structures) # 2. Employee Supply # Convert house strings to indices c_idxs = [HOUSE_INDICES[h] for h in assignments['carts']] w_idxs = [HOUSE_INDICES[h] for h in assignments['wagons']] final_supply = add_employee_supply(base_supply, c_idxs, w_idxs) # 3. Score inf, short = get_score_from_supply_array(final_supply, dem_arr) # Convert back to dict for display supply_dict = {h: final_supply[i] for i, h in enumerate(HOUSES)} return inf, short, supply_dict # ========================================== # OPTIMIZATION PHASES # ========================================== def solve_phase_fixed_structures(demands, n_carts, n_wagons, fixed_structures): """ Brute Force with Unlimited Stacking (Combinations with Replacement). Optimized to run fast by pre-calculating structure supply. """ print(" (Calculating combinations... this might take a moment)") # Pre-calc base base_supply = calculate_supply_from_structures(fixed_structures) demand_arr = [demands[h] for h in HOUSES] house_indices = list(range(len(HOUSES))) best_inf = -999 best_shortfall = 999 best_assignment = {'carts': [], 'wagons': []} # 1. Iterate Wagons (Combinations with replacement allows stacking) # Using indices for speed for wagon_indices in itertools.combinations_with_replacement(house_indices, n_wagons): # Calculate supply just from wagons + base current_supply_w = list(base_supply) for w_idx in wagon_indices: current_supply_w[w_idx] += 8 for n_idx in HOUSE_NEIGHBOR_INDICES[w_idx]: current_supply_w[n_idx] += 6 # 2. Iterate Carts for cart_indices in itertools.combinations_with_replacement(house_indices, n_carts): # Add carts to the wagon+base supply final_supply = list(current_supply_w) for c_idx in cart_indices: final_supply[c_idx] += 3 for n_idx in HOUSE_NEIGHBOR_INDICES[c_idx]: final_supply[n_idx] += 2 # Score inline inf = 0 shortfall = 0 possible_max = 20 # Unrolling loop slightly for speed/fail-fast? # Not easy in Python, but let's just do standard iteration for i in range(20): diff = final_supply[i] - demand_arr[i] if diff >= 0: inf += 1 elif diff == -1: possible_max -= 1 # Can't reach 20 else: inf -= 2 possible_max -= 3 # Lost 1 potential point plus 2 penalty if diff < 0: shortfall += abs(diff) # Pruning: If we can't beat current best, stop checking this combo # (Simple pruning logic is hard here without calculating full score) if inf > best_inf or (inf == best_inf and shortfall < best_shortfall): best_inf = inf best_shortfall = shortfall best_assignment = { 'wagons': [HOUSES[i] for i in wagon_indices], 'carts': [HOUSES[i] for i in cart_indices] } if best_inf == 20: return best_assignment, best_inf return best_assignment, best_inf def solve_phase_sa(demands, n_carts, n_wagons, n_wells, n_sumps, n_pipes, mode='move_wells', fixed_sumps=[]): """ Simulated Annealing handling Multi-Unit Stacking. modes: 'move_wells', 'move_all' """ msg = "Sumps & Pipes" if mode == 'move_all' else "Wells (Fixed Sumps)" print(f"--- Phase SA: Relocating {msg} ---") # Init State # Randomly assign units (allowing stacking) current_assign = {'carts': [], 'wagons': []} for _ in range(n_carts): current_assign['carts'].append(random.choice(HOUSES)) for _ in range(n_wagons): current_assign['wagons'].append(random.choice(HOUSES)) # Init Structures current_structs = {} if mode == 'move_wells': for s in fixed_sumps: current_structs[s] = 'sump' avail = [n for n in NODES if n not in current_structs] random.shuffle(avail) for _ in range(n_wells): if avail: current_structs[avail.pop()] = 'well' else: avail = NODES[:] random.shuffle(avail) for _ in range(n_wells): if avail: current_structs[avail.pop()] = 'well' for _ in range(n_sumps): if avail: current_structs[avail.pop()] = 'sump' def get_neighbor(assign, struct): new_a = {'carts': assign['carts'][:], 'wagons': assign['wagons'][:]} new_s = struct.copy() r = random.random() # Mutation 1: Move a Unit # Pick a type, pick an index, change location if r < 0.4: u_type = 'carts' if (new_a['carts'] and random.random()<0.5) else 'wagons' if not new_a['carts'] and new_a['wagons']: u_type = 'wagons' if new_a['carts'] and not new_a['wagons']: u_type = 'carts' if new_a[u_type]: idx = random.randrange(len(new_a[u_type])) new_a[u_type][idx] = random.choice(HOUSES) # Move to ANY house (stacking allowed) # Mutation 2: Move/Swap Structure elif r < 0.9: if new_s: n_from = random.choice(list(new_s.keys())) # If mode is move_wells, we cannot move fixed sumps if mode == 'move_wells' and n_from in fixed_sumps: pass # Skip else: sType = new_s.pop(n_from) # Find empty spot occupied = set(new_s.keys()) if mode == 'move_wells': occupied.update(fixed_sumps) empty = [n for n in NODES if n not in occupied] if empty: new_s[random.choice(empty)] = sType else: new_s[n_from] = sType # Revert if full return new_a, new_s # Initial Score curr_inf, _, _ = calculate_full_score(current_assign, current_structs, demands) if mode == 'move_all': sumps = [n for n, t in current_structs.items() if t == 'sump'] if not check_theoretical_pipe_validity(sumps, n_pipes): curr_inf -= 10 best_inf = -999 best_res = (current_assign, current_structs) temp = 10.0 cooling = 0.995 steps = 8000 if mode == 'move_wells' else 12000 for _ in range(steps): na, ns = get_neighbor(current_assign, current_structs) ni, _, _ = calculate_full_score(na, ns, demands) valid = True if mode == 'move_all': sumps = [n for n, t in ns.items() if t == 'sump'] if not check_theoretical_pipe_validity(sumps, n_pipes): valid = False ni -= 10 delta = ni - curr_inf if delta > 0 or random.random() < math.exp(delta/temp): current_assign = na current_structs = ns curr_inf = ni if valid and ni > best_inf: best_inf = ni best_res = ({'carts': na['carts'][:], 'wagons': na['wagons'][:]}, ns.copy()) if best_inf == 20: break temp *= cooling return best_res[0], best_res[1], best_inf # ========================================== # MAIN EXECUTION # ========================================== def print_solution(assign, struct, demands, supply_details, phase_msg): print("\n" + "="*50) print(f"SOLUTION FOUND: {phase_msg}") print("="*50) inf, _, supply = calculate_full_score(assign, struct, demands) print(f"Projected Influence: {inf}/20") print("\n--- Employee Assignments (Stacked) ---") # Group by house for cleaner printing # e.g. A1: 2 Carts, 1 Wagon grouped = {h: {'carts': 0, 'wagons': 0} for h in HOUSES} for c in assign['carts']: grouped[c]['carts'] += 1 for w in assign['wagons']: grouped[w]['wagons'] += 1 has_units = False for h in sorted(HOUSES): c = grouped[h]['carts'] w = grouped[h]['wagons'] if c > 0 or w > 0: has_units = True parts = [] if w > 0: parts.append(f"{w} Wagon(s)") if c > 0: parts.append(f"{c} Cart(s)") print(f" {h}: " + ", ".join(parts)) if not has_units: print(" None") print("\n--- Structure Placements ---") if not struct: print(" None") for n, sType in struct.items(): val = NODE_SUMP_VALUE[n] if sType=='sump' else 2 print(f" {sType.upper().ljust(6)} -> {n} (Water: {val})") print("\n--- House Status ---") print(f" {'House':<6} {'Dem':<5} {'Sup':<5} {'Diff':<5} {'Infl'}") print("-" * 35) total_inf = 0 ids = sorted(HOUSES) for h in ids: d = demands[h] s = supply[h] diff = s - d if diff >= 0: i = 1 elif diff == -1: i = 0 else: i = -2 total_inf += i print(f" {h:<6} {d:<5} {s:<5} {diff:<5} {i}") print("-" * 35) print(f" TOTAL INFLUENCE: {total_inf}") def main(): print("Starting Water Supply Optimization...") start_time = time.time() # ---------------------------------------------------- # PHASE 1: CURRENT SETUP + EMPLOYEE OPTIMIZATION # ---------------------------------------------------- print("--- Phase 1: Checking Current Structure Setup ---") active_sumps = get_connected_sumps_from_layout(CURRENT_LAYOUT) fixed_structs = {} for w in CURRENT_LAYOUT['wells']: fixed_structs[w] = 'well' for s in active_sumps: fixed_structs[s] = 'sump' if len(active_sumps) < len(CURRENT_LAYOUT['sumps']): print(f" ! Warning: Only {len(active_sumps)} of {len(CURRENT_LAYOUT['sumps'])} sumps are connected to Mill.") assign, inf = solve_phase_fixed_structures(DEMANDS, INVENTORY['carts'], INVENTORY['wagons'], fixed_structs) final_structs = fixed_structs solution_phase = "Phase 1 (Current Setup)" # ---------------------------------------------------- # PHASE 2: MOVE WELLS (Fixed Sumps) # ---------------------------------------------------- if inf < 20 and INVENTORY['wells'] > 0: print(f" Result: {inf}/20. Attempting to relocate Wells...") assign_2, struct_2, inf_2 = solve_phase_sa( DEMANDS, INVENTORY['carts'], INVENTORY['wagons'], INVENTORY['wells'], 0, 0, mode='move_wells', fixed_sumps=active_sumps ) if inf_2 > inf: assign = assign_2 final_structs = struct_2 inf = inf_2 solution_phase = "Phase 2 (Relocated Wells)" # ---------------------------------------------------- # PHASE 3: MOVE EVERYTHING # ---------------------------------------------------- if inf < 20 and INVENTORY['sumps'] > 0 and INVENTORY['pipes'] > 0: print(f" Result: {inf}/20. Attempting to relocate Sumps & Pipes...") assign_3, struct_3, inf_3 = solve_phase_sa( DEMANDS, INVENTORY['carts'], INVENTORY['wagons'], INVENTORY['wells'], INVENTORY['sumps'], INVENTORY['pipes'], mode='move_all' ) if inf_3 > inf: assign = assign_3 final_structs = struct_3 inf = inf_3 solution_phase = "Phase 3 (New Layout)" print(f"\nCalculation finished in {round(time.time() - start_time, 2)} seconds.") _, _, final_supply = calculate_full_score(assign, final_structs, DEMANDS) print_solution(assign, final_structs, DEMANDS, final_supply, solution_phase) if __name__ == "__main__": main()

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue