id rather be famous instead

stable
Valerie (Bronwen) 2023-11-23 18:14:39 -05:00
parent 8a93dc36f0
commit 2474b474e6
5 changed files with 164 additions and 56 deletions

2
.gitignore vendored 100644
View File

@ -0,0 +1,2 @@
__pycache__
.venv

16
.vscode/launch.json vendored 100644
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "main.py",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

46
data.py
View File

@ -1,8 +1,8 @@
from enum import Enum
from engine import Recipe
from engine import Collection, Recipe
class Item(Enum):
class Items(Collection):
IronBar = "Iron Bar"
CopperBar = "Copper Bar"
SteelBar = "Steel Bar"
@ -23,28 +23,28 @@ class Item(Enum):
SteamCrusher = "Steam Crusher"
Pipe = "Pipe"
class Recipes(Enum):
IronPlate = Recipe([Item.IronBar], [Item.IronPlate], "Iron Plate")
CopperPlate = Recipe([Item.CopperBar], [Item.CopperPlate], "Copper Plate")
IronBeam = Recipe([Item.IronBar], [Item.IronBeam], "Iron Beam")
CopperBeam = Recipe([Item.CopperBar], [Item.CopperBeam], "Copper Beam")
SteelBeam = Recipe([Item.SteelBar], [Item.SteelBeam], "Steel Beam")
IronMechanicalParts = Recipe([Item.IronBar], [Item.IronMechanicalParts], "Iron Mechanical Parts")
CopperMechanicalParts = Recipe([Item.CopperBar], [Item.CopperMechanicalParts], "Copper Mechanical Parts")
SteelMechanicalParts = Recipe([Item.SteelBar], [Item.SteelMechanicalParts], "Steel Mechanical Parts")
Pipe = Recipe([Item.IronPlate, Item.IronMechanicalParts], [Item.Pipe], "Pipe")
SteamEngine = Recipe([Item.Pipe, Item.CopperMechanicalParts], [Item.SteamEngine], "Steam Engine")
class Recipes(Collection):
IronPlate = Recipe([Items.IronBar], [Items.IronPlate], "Iron Plate")
CopperPlate = Recipe([Items.CopperBar], [Items.CopperPlate], "Copper Plate")
IronBeam = Recipe([Items.IronBar], [Items.IronBeam], "Iron Beam")
CopperBeam = Recipe([Items.CopperBar], [Items.CopperBeam], "Copper Beam")
SteelBeam = Recipe([Items.SteelBar], [Items.SteelBeam], "Steel Beam")
IronMechanicalParts = Recipe([Items.IronBar], [Items.IronMechanicalParts], "Iron Mechanical Parts")
CopperMechanicalParts = Recipe([Items.CopperBar], [Items.CopperMechanicalParts], "Copper Mechanical Parts")
SteelMechanicalParts = Recipe([Items.SteelBar], [Items.SteelMechanicalParts], "Steel Mechanical Parts")
Pipe = Recipe([Items.IronPlate, Items.IronMechanicalParts], [Items.Pipe], "Pipe")
SteamEngine = Recipe([Items.Pipe, Items.CopperMechanicalParts], [Items.SteamEngine], "Steam Engine")
SteamAssembler = Recipe([
Item.Pipe,
Item.CopperMechanicalParts,
Item.SteamEngine,
Item.CopperBeam
], [Item.SteamAssembler], "Steam Assembler")
Items.Pipe,
Items.CopperMechanicalParts,
Items.SteamEngine,
Items.CopperBeam
], [Items.SteamAssembler], "Steam Assembler")
SteamCrusher = Recipe([
Item.Pipe,
Item.IronMechanicalParts,
Item.SteamEngine,
Item.CopperBeam
], [Item.SteamCrusher], "Steam Crusher")
Items.Pipe,
Items.IronMechanicalParts,
Items.SteamEngine,
Items.CopperBeam
], [Items.SteamCrusher], "Steam Crusher")

126
engine.py
View File

@ -1,7 +1,20 @@
from typing import List, Set, Tuple
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from typing import List, Any
from typing import List, Generic, TypeVar, Callable, Set, Tuple, Dict, Any
T = TypeVar("T")
class Collection(Generic[T]):
@classmethod
def as_set(clazz) -> set[T]:
return {
key: value
for (key, value) in clazz.__dict__.items()
if not key.startswith("__")
and not hasattr(Collection, key)
}
class Item:
def __init__(self, name: str) -> None:
@ -9,8 +22,8 @@ class Item:
class Recipe:
def __init__(self, inputs: List[Item], outputs: List[Item], name: str) -> None:
self.inputs = inputs
self.outputs = outputs
self.inputs = set(inputs)
self.outputs = set(outputs)
self.name = name
class World:
@ -38,20 +51,23 @@ def find_valid_recipes(output: Item, world: World) -> List[Recipe]:
class Assembler:
def __init__(self, recipe: Recipe, world: World) -> None:
self.recipe = recipe
self.input_links = [] # List[Assembler]
self.input_links: Set[Assembler] = set() # List[Assembler]
self.world = world
def link_input(self, assembler: "Assembler") -> None:
self.input_links.append(assembler)
def get_unlinked_inputs(self) -> List[Item]:
print(type(self.recipe))
recv = [
output
for assembler in self.input_links
for output in assembler.recipe.outputs
]
return [
item
for item in self.recipe.inputs
if item not in [
assembler.recipe.outputs
for assembler in self.input_links
].flatten()
if item not in recv
and item not in self.world.bus
]
@ -67,9 +83,40 @@ class Graph:
]
self.world = world
def __hash__(self) -> int:
recipes = []
for x in range(len(self.grid)):
for y in range(len(self.grid)):
assembler = self.get_assembler_at(x, y)
if assembler is not None:
recipes.append(assembler.recipe)
return hash((
tuple(self.world.bus),
self.world.size,
tuple(self.world.recipes),
tuple(self.world.items),
tuple(recipes)
))
def __eq__(self, other: "Graph") -> bool:
if self.world != other.world:
return False
for x in range(len(self.grid)):
for y in range(len(self.grid)):
if self.get_assembler_at(x, y).recipe != other.get_assembler_at(x, y).recipe:
return False
a_linked_coords = self.get_assembler_at(x, y).input_links.map(lambda assembler: self.get_coordinates_of_assembler(assembler))
b_linked_coords = other.get_assembler_at(x, y).input_links.map(lambda assembler: other.get_coordinates_of_assembler(assembler))
if a_linked_coords != b_linked_coords:
return False
return True
def create_graph(world: World, recipe: Recipe) -> "Graph":
graph = Graph(world)
graph.grid[1][world.size // 2] = recipe
graph.grid[1][world.size // 2] = Assembler(recipe, world)
print(recipe)
return graph
def get_coordinates_of_assembler(self, assembler: Assembler) -> (int, int):
@ -132,6 +179,21 @@ class Graph:
adjacent_coordinates = self.get_adjacent_coordinates(a[0], a[1])
return b in adjacent_coordinates
def get_assembler_at(self, x: int, y: int) -> Assembler:
if x < 0 or x >= len(self.grid) or y < 0 or y >= len(self.grid):
return None
if self.grid[x][y] is None:
return None
return self.grid[x][y]
def get_adjacent_assemblers(self, x: int, y: int) -> List[Assembler]:
assemblers = []
for (x, y) in self.get_adjacent_coordinates(x, y):
assembler = self.get_assembler_at(x, y)
if assembler is not None:
assemblers.append(assembler)
return assemblers
def draw(self) -> None:
plt.figure()
@ -156,21 +218,45 @@ class Graph:
plt.ylim(-1, 11)
# Show the plot
plt.show()
plt.show(block=False)
plt.pause(5)
plt.close()
def get_solutions(self) -> List["Graph"]:
if self.is_solved():
return []
def solve(graph: Graph) -> Set["Graph"]:
graphs: List["Graph"] = []
if graph.is_solved():
return set([graph])
for x in range(len(self.grid)):
for y in range(len(self.grid)):
assembler = self.grid[x][y]
graphs: Set["Graph"] = set([graph])
def count_unsolved_graphs(graphs: Set[Graph]) -> int:
count = 0
for graph in graphs:
if not graph.is_solved():
count += 1
return count
def get_unsolved_graph(graphs: Set[Graph]) -> Graph:
print(f"grabbing next unsolved graph ({count_unsolved_graphs(graphs)})")
selected = None
for graph in graphs:
if not graph.is_solved():
selected = graph
break
if selected is not None:
graphs.remove(selected)
return selected
while count_unsolved_graphs(graphs) > 0:
graph = get_unsolved_graph(graphs)
for x in range(len(graph.grid)):
for y in range(len(graph.grid)):
assembler = graph.get_assembler_at(x, y)
if assembler == None:
continue
print(assembler)
# for item in assembler.get_unlinked_inputs():
# for recipes in find_valid_recipes(item):
for item in assembler.get_unlinked_inputs():
print(item)
# for recipes in find_valid_recipes(item):
# graph = self.add_assembler(assembler, x, y)
# graphs.append(graph)

28
main.py
View File

@ -1,18 +1,22 @@
from typing import List
from data import Item, Recipes
from engine import Graph
from data import Items, Recipes
from engine import Graph, World, solve
bus_resources = [
Item.IronBar,
Item.CopperBar,
Item.SteelBar,
]
world = World(
bus=[
Items.IronBar,
Items.CopperBar,
Items.SteelBar,
],
items=Items.as_set(),
recipes=Recipes.as_set(),
size=10
)
solutions = solve(Graph.create_graph(world, Recipes.IronPlate))
solutions = Graph.create_graph(10, Recipes.IronPlate).get_solutions()
if not solutions:
print("No solutions found")
exit(1)
for solution in solutions:
solution.draw()