Guest User

Untitled

a guest
Dec 19th, 2022
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.03 KB | None | 0 0
  1. # Input: Lines like "Blueprint <#>: Each ore robot costs <#> ore.
  2. #                    Each clay robot costs <#> ore.
  3. #                    Each obsidian robot costs <#> ore and <#> clay.
  4. #                    Each geode robot costs <#> ore and <#> obsidian."
  5. # Output: Starting with one ore-collecting robot and one blueprint,
  6. #         spending 1 minute to build any one robot,
  7. #         how many geodes could you collect within 32 minutes?
  8. #         Calculate product(max geodes) over first three blueprints
  9.  
  10. from functools import cache
  11.  
  12. blueprints = []
  13.  
  14. input_data = open(r"c:\users\edward\desktop\personal\aoc2022\19_input.txt")
  15. for input_line in input_data.read().splitlines():
  16.     input_line = input_line.replace("\n", "")
  17.     parts = input_line.split(": Each ore robot costs ")
  18.     parts[0] = parts[0].replace("Blueprint ", "")
  19.     blueprint_index = int(parts[0])
  20.     rest_of_line = parts[1]
  21.     parts = rest_of_line.split(" ore. Each clay robot costs ")
  22.     ore_robot_cost_ore = int(parts[0])
  23.     rest_of_line = parts[1]
  24.     parts = rest_of_line.split(" ore. Each obsidian robot costs ")
  25.     clay_robot_cost_ore = int(parts[0])
  26.     rest_of_line = parts[1]
  27.     parts = rest_of_line.split(" clay. Each geode robot costs ")
  28.     # At this point, parts[0] = "<#> ore and <#>" [clay]
  29.     #            and parts[1] = "<#> ore and <#> obsidian."
  30.     obsidian_parts = parts[0].split(" ore and ")
  31.     obsidian_robot_cost_ore = int(obsidian_parts[0])
  32.     obsidian_robot_cost_clay = int(obsidian_parts[1])
  33.     geode_parts = parts[1].split(" ore and ")
  34.     geode_parts[1] = geode_parts[1].replace(" obsidian.", "")
  35.     geode_robot_cost_ore = int(geode_parts[0])
  36.     geode_robot_cost_obsidian = int(geode_parts[1])
  37.     blueprints.append([
  38.         blueprint_index,
  39.         ore_robot_cost_ore,
  40.         clay_robot_cost_ore,
  41.         obsidian_robot_cost_ore,
  42.         obsidian_robot_cost_clay,
  43.         geode_robot_cost_ore,
  44.         geode_robot_cost_obsidian
  45.     ])
  46.  
  47. @cache
  48. def max_geodes(
  49.     blueprint_index,
  50.     ore_robot_cost_ore,
  51.     clay_robot_cost_ore,
  52.     obsidian_robot_cost_ore,
  53.     obsidian_robot_cost_clay,
  54.     geode_robot_cost_ore,
  55.     geode_robot_cost_obsidian,
  56.     ore,
  57.     clay,
  58.     obsidian,
  59.     ore_robots,
  60.     clay_robots,
  61.     obsidian_robots,
  62.     geode_robots,
  63.     minutes_left
  64. ):
  65.  
  66.     if minutes_left == 0:
  67.         return 0
  68.  
  69.     # Each minute:
  70.     #   * Optionally start building 1 type of robot, materials permitting.
  71.     #   * Each ore robot collects 1 ore.
  72.     #   * Each clay robot collects 1 clay.
  73.     #   * Each obsidian robot collects 1 obsidian.
  74.     #   * Each geode robot collects 1 geode.
  75.  
  76.     # If building a geode robot each turn wouldn't beat a previous result,
  77.     #   then it's a dead end
  78.     possible_geodes = 0
  79.     for i in range(0, minutes_left - 1):
  80.         possible_geodes += geode_robots + i
  81.     if possible_geodes < best_so_far[minutes_left]:
  82.         return 0
  83.  
  84.     # If we can build a geode robot, then do so
  85.     if ore >= geode_robot_cost_ore and obsidian >= geode_robot_cost_obsidian:
  86.         return geode_robots + max_geodes(
  87.             blueprint_index,
  88.             ore_robot_cost_ore,
  89.             clay_robot_cost_ore,
  90.             obsidian_robot_cost_ore,
  91.             obsidian_robot_cost_clay,
  92.             geode_robot_cost_ore,
  93.             geode_robot_cost_obsidian,
  94.             ore - geode_robot_cost_ore + ore_robots,
  95.             clay + clay_robots,
  96.             obsidian - geode_robot_cost_obsidian + obsidian_robots,
  97.             ore_robots,
  98.             clay_robots,
  99.             obsidian_robots,
  100.             geode_robots + 1,
  101.             minutes_left - 1
  102.         )
  103.  
  104.     # Otherwise, if we can't build a geode robot by next-to-last minute
  105.     # even if we built nothing but obsidian robots in between
  106.     # then current geode robots are all we'll get
  107.     possible_obsidian = obsidian
  108.     for i in range(0, minutes_left - 2):
  109.         possible_obsidian += obsidian_robots + i
  110.     if possible_obsidian < geode_robot_cost_obsidian:
  111.         return geode_robots * minutes_left
  112.  
  113.     # Otherwise, compile all other options and determine the best result
  114.     options = []
  115.  
  116.     # Option: Build an obsidian robot
  117.     # If current obsidian robots are enough to cover obsidian cost of all other robot types,
  118.     # then there's no value in making more obsidian robots
  119.     if ore >= obsidian_robot_cost_ore and clay >= obsidian_robot_cost_clay and obsidian_robots < geode_robot_cost_obsidian:
  120.         options.append(max_geodes(
  121.             blueprint_index,
  122.             ore_robot_cost_ore,
  123.             clay_robot_cost_ore,
  124.             obsidian_robot_cost_ore,
  125.             obsidian_robot_cost_clay,
  126.             geode_robot_cost_ore,
  127.             geode_robot_cost_obsidian,
  128.             ore - obsidian_robot_cost_ore + ore_robots,
  129.             clay - obsidian_robot_cost_clay + clay_robots,
  130.             obsidian + obsidian_robots,
  131.             ore_robots,
  132.             clay_robots,
  133.             obsidian_robots + 1,
  134.             geode_robots,
  135.             minutes_left - 1
  136.         ))
  137.  
  138.     # Option: Build a clay robot
  139.     # If current clay robots are enough to cover clay cost of all other robot types,
  140.     # then there's no value in making more clay robots
  141.     if ore >= clay_robot_cost_ore and clay_robots < obsidian_robot_cost_clay:
  142.         options.append(max_geodes(
  143.             blueprint_index,
  144.             ore_robot_cost_ore,
  145.             clay_robot_cost_ore,
  146.             obsidian_robot_cost_ore,
  147.             obsidian_robot_cost_clay,
  148.             geode_robot_cost_ore,
  149.             geode_robot_cost_obsidian,
  150.             ore - clay_robot_cost_ore + ore_robots,
  151.             clay + clay_robots,
  152.             obsidian + obsidian_robots,
  153.             ore_robots,
  154.             clay_robots + 1,
  155.             obsidian_robots,
  156.             geode_robots,
  157.             minutes_left - 1
  158.         ))
  159.  
  160.     # Option: Build an ore robot
  161.     # If current ore robots are enough to cover ore cost of all other robot types,
  162.     # then there's no value in making more ore robots
  163.     if ore >= ore_robot_cost_ore and ore_robots < max(clay_robot_cost_ore, obsidian_robot_cost_ore, geode_robot_cost_ore):
  164.         options.append(max_geodes(
  165.             blueprint_index,
  166.             ore_robot_cost_ore,
  167.             clay_robot_cost_ore,
  168.             obsidian_robot_cost_ore,
  169.             obsidian_robot_cost_clay,
  170.             geode_robot_cost_ore,
  171.             geode_robot_cost_obsidian,
  172.             ore - ore_robot_cost_ore + ore_robots,
  173.             clay + clay_robots,
  174.             obsidian + obsidian_robots,
  175.             ore_robots + 1,
  176.             clay_robots,
  177.             obsidian_robots,
  178.             geode_robots,
  179.             minutes_left - 1
  180.         ))
  181.  
  182.     # Option: Don't build a robot
  183.     # May be better to save up for a robot type that we can't build yet
  184.     options.append(max_geodes(
  185.         blueprint_index,
  186.         ore_robot_cost_ore,
  187.         clay_robot_cost_ore,
  188.         obsidian_robot_cost_ore,
  189.         obsidian_robot_cost_clay,
  190.         geode_robot_cost_ore,
  191.         geode_robot_cost_obsidian,
  192.         ore + ore_robots,
  193.         clay + clay_robots,
  194.         obsidian + obsidian_robots,
  195.         ore_robots,
  196.         clay_robots,
  197.         obsidian_robots,
  198.         geode_robots,
  199.         minutes_left - 1
  200.     ))
  201.  
  202.     return geode_robots + max(options)
  203.  
  204. count = 0
  205. total = 1
  206. for blueprint in blueprints:
  207.     count += 1
  208.     best_so_far = []
  209.     for minutes_left in range(0, 33):
  210.         best_so_far.append(0)
  211.     best = max_geodes(
  212.         blueprint[0], # index
  213.         blueprint[1], # ore robot cost (ore)
  214.         blueprint[2], # clay robot cost (ore)
  215.         blueprint[3], # obsidian robot cost (ore)
  216.         blueprint[4], # obsidian robot cost (clay)
  217.         blueprint[5], # geode robot cost (ore)
  218.         blueprint[6], # geode robot cost (obsidian)
  219.         0, # ore
  220.         0, # clay
  221.         0, # obsidian
  222.         1, # ore robots
  223.         0, # clay robots
  224.         0, # obsidian robots
  225.         0, # geode robots
  226.         32 # minutes left
  227.     )
  228.     total *= best
  229.     if count == 3:
  230.         break
  231.  
  232. print (total)
  233.  
  234.  
Add Comment
Please, Sign In to add comment