id rather be famous instead
parent
8a93dc36f0
commit
2474b474e6
|
|
@ -0,0 +1,2 @@
|
||||||
|
__pycache__
|
||||||
|
.venv
|
||||||
|
|
@ -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
46
data.py
|
|
@ -1,8 +1,8 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from engine import Recipe
|
from engine import Collection, Recipe
|
||||||
|
|
||||||
class Item(Enum):
|
class Items(Collection):
|
||||||
IronBar = "Iron Bar"
|
IronBar = "Iron Bar"
|
||||||
CopperBar = "Copper Bar"
|
CopperBar = "Copper Bar"
|
||||||
SteelBar = "Steel Bar"
|
SteelBar = "Steel Bar"
|
||||||
|
|
@ -23,28 +23,28 @@ class Item(Enum):
|
||||||
SteamCrusher = "Steam Crusher"
|
SteamCrusher = "Steam Crusher"
|
||||||
Pipe = "Pipe"
|
Pipe = "Pipe"
|
||||||
|
|
||||||
class Recipes(Enum):
|
class Recipes(Collection):
|
||||||
IronPlate = Recipe([Item.IronBar], [Item.IronPlate], "Iron Plate")
|
IronPlate = Recipe([Items.IronBar], [Items.IronPlate], "Iron Plate")
|
||||||
CopperPlate = Recipe([Item.CopperBar], [Item.CopperPlate], "Copper Plate")
|
CopperPlate = Recipe([Items.CopperBar], [Items.CopperPlate], "Copper Plate")
|
||||||
IronBeam = Recipe([Item.IronBar], [Item.IronBeam], "Iron Beam")
|
IronBeam = Recipe([Items.IronBar], [Items.IronBeam], "Iron Beam")
|
||||||
CopperBeam = Recipe([Item.CopperBar], [Item.CopperBeam], "Copper Beam")
|
CopperBeam = Recipe([Items.CopperBar], [Items.CopperBeam], "Copper Beam")
|
||||||
SteelBeam = Recipe([Item.SteelBar], [Item.SteelBeam], "Steel Beam")
|
SteelBeam = Recipe([Items.SteelBar], [Items.SteelBeam], "Steel Beam")
|
||||||
IronMechanicalParts = Recipe([Item.IronBar], [Item.IronMechanicalParts], "Iron Mechanical Parts")
|
IronMechanicalParts = Recipe([Items.IronBar], [Items.IronMechanicalParts], "Iron Mechanical Parts")
|
||||||
CopperMechanicalParts = Recipe([Item.CopperBar], [Item.CopperMechanicalParts], "Copper Mechanical Parts")
|
CopperMechanicalParts = Recipe([Items.CopperBar], [Items.CopperMechanicalParts], "Copper Mechanical Parts")
|
||||||
SteelMechanicalParts = Recipe([Item.SteelBar], [Item.SteelMechanicalParts], "Steel Mechanical Parts")
|
SteelMechanicalParts = Recipe([Items.SteelBar], [Items.SteelMechanicalParts], "Steel Mechanical Parts")
|
||||||
Pipe = Recipe([Item.IronPlate, Item.IronMechanicalParts], [Item.Pipe], "Pipe")
|
Pipe = Recipe([Items.IronPlate, Items.IronMechanicalParts], [Items.Pipe], "Pipe")
|
||||||
SteamEngine = Recipe([Item.Pipe, Item.CopperMechanicalParts], [Item.SteamEngine], "Steam Engine")
|
SteamEngine = Recipe([Items.Pipe, Items.CopperMechanicalParts], [Items.SteamEngine], "Steam Engine")
|
||||||
SteamAssembler = Recipe([
|
SteamAssembler = Recipe([
|
||||||
Item.Pipe,
|
Items.Pipe,
|
||||||
Item.CopperMechanicalParts,
|
Items.CopperMechanicalParts,
|
||||||
Item.SteamEngine,
|
Items.SteamEngine,
|
||||||
Item.CopperBeam
|
Items.CopperBeam
|
||||||
], [Item.SteamAssembler], "Steam Assembler")
|
], [Items.SteamAssembler], "Steam Assembler")
|
||||||
SteamCrusher = Recipe([
|
SteamCrusher = Recipe([
|
||||||
Item.Pipe,
|
Items.Pipe,
|
||||||
Item.IronMechanicalParts,
|
Items.IronMechanicalParts,
|
||||||
Item.SteamEngine,
|
Items.SteamEngine,
|
||||||
Item.CopperBeam
|
Items.CopperBeam
|
||||||
], [Item.SteamCrusher], "Steam Crusher")
|
], [Items.SteamCrusher], "Steam Crusher")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
128
engine.py
128
engine.py
|
|
@ -1,7 +1,20 @@
|
||||||
from typing import List, Set, Tuple
|
from typing import List, Set, Tuple
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.patches import Rectangle
|
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:
|
class Item:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
|
|
@ -9,8 +22,8 @@ class Item:
|
||||||
|
|
||||||
class Recipe:
|
class Recipe:
|
||||||
def __init__(self, inputs: List[Item], outputs: List[Item], name: str) -> None:
|
def __init__(self, inputs: List[Item], outputs: List[Item], name: str) -> None:
|
||||||
self.inputs = inputs
|
self.inputs = set(inputs)
|
||||||
self.outputs = outputs
|
self.outputs = set(outputs)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
class World:
|
class World:
|
||||||
|
|
@ -38,20 +51,23 @@ def find_valid_recipes(output: Item, world: World) -> List[Recipe]:
|
||||||
class Assembler:
|
class Assembler:
|
||||||
def __init__(self, recipe: Recipe, world: World) -> None:
|
def __init__(self, recipe: Recipe, world: World) -> None:
|
||||||
self.recipe = recipe
|
self.recipe = recipe
|
||||||
self.input_links = [] # List[Assembler]
|
self.input_links: Set[Assembler] = set() # List[Assembler]
|
||||||
self.world = world
|
self.world = world
|
||||||
|
|
||||||
def link_input(self, assembler: "Assembler") -> None:
|
def link_input(self, assembler: "Assembler") -> None:
|
||||||
self.input_links.append(assembler)
|
self.input_links.append(assembler)
|
||||||
|
|
||||||
def get_unlinked_inputs(self) -> List[Item]:
|
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 [
|
return [
|
||||||
item
|
item
|
||||||
for item in self.recipe.inputs
|
for item in self.recipe.inputs
|
||||||
if item not in [
|
if item not in recv
|
||||||
assembler.recipe.outputs
|
|
||||||
for assembler in self.input_links
|
|
||||||
].flatten()
|
|
||||||
and item not in self.world.bus
|
and item not in self.world.bus
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -67,9 +83,40 @@ class Graph:
|
||||||
]
|
]
|
||||||
self.world = world
|
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":
|
def create_graph(world: World, recipe: Recipe) -> "Graph":
|
||||||
graph = Graph(world)
|
graph = Graph(world)
|
||||||
graph.grid[1][world.size // 2] = recipe
|
graph.grid[1][world.size // 2] = Assembler(recipe, world)
|
||||||
|
print(recipe)
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
def get_coordinates_of_assembler(self, assembler: Assembler) -> (int, int):
|
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])
|
adjacent_coordinates = self.get_adjacent_coordinates(a[0], a[1])
|
||||||
return b in adjacent_coordinates
|
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:
|
def draw(self) -> None:
|
||||||
plt.figure()
|
plt.figure()
|
||||||
|
|
||||||
|
|
@ -156,21 +218,45 @@ class Graph:
|
||||||
plt.ylim(-1, 11)
|
plt.ylim(-1, 11)
|
||||||
|
|
||||||
# Show the plot
|
# Show the plot
|
||||||
plt.show()
|
plt.show(block=False)
|
||||||
|
plt.pause(5)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
def get_solutions(self) -> List["Graph"]:
|
def solve(graph: Graph) -> Set["Graph"]:
|
||||||
if self.is_solved():
|
|
||||||
return []
|
|
||||||
|
|
||||||
graphs: List["Graph"] = []
|
|
||||||
|
|
||||||
for x in range(len(self.grid)):
|
if graph.is_solved():
|
||||||
for y in range(len(self.grid)):
|
return set([graph])
|
||||||
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:
|
if assembler == None:
|
||||||
continue
|
continue
|
||||||
print(assembler)
|
for item in assembler.get_unlinked_inputs():
|
||||||
# for item in assembler.get_unlinked_inputs():
|
print(item)
|
||||||
# for recipes in find_valid_recipes(item):
|
# for recipes in find_valid_recipes(item):
|
||||||
# graph = self.add_assembler(assembler, x, y)
|
# graph = self.add_assembler(assembler, x, y)
|
||||||
# graphs.append(graph)
|
# graphs.append(graph)
|
||||||
|
|
|
||||||
28
main.py
28
main.py
|
|
@ -1,18 +1,22 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
from data import Item, Recipes
|
from data import Items, Recipes
|
||||||
from engine import Graph
|
from engine import Graph, World, solve
|
||||||
|
|
||||||
bus_resources = [
|
world = World(
|
||||||
Item.IronBar,
|
bus=[
|
||||||
Item.CopperBar,
|
Items.IronBar,
|
||||||
Item.SteelBar,
|
Items.CopperBar,
|
||||||
]
|
Items.SteelBar,
|
||||||
|
],
|
||||||
|
items=Items.as_set(),
|
||||||
|
recipes=Recipes.as_set(),
|
||||||
|
size=10
|
||||||
|
)
|
||||||
|
|
||||||
|
solutions = solve(Graph.create_graph(world, Recipes.IronPlate))
|
||||||
|
|
||||||
|
if not solutions:
|
||||||
|
print("No solutions found")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
solutions = Graph.create_graph(10, Recipes.IronPlate).get_solutions()
|
|
||||||
for solution in solutions:
|
for solution in solutions:
|
||||||
solution.draw()
|
solution.draw()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue