factorio things
This commit is contained in:
parent
acff1f3d57
commit
aa10b7f249
300
factorio-clone/main.py
Normal file
300
factorio-clone/main.py
Normal file
@ -0,0 +1,300 @@
|
||||
import pygame, sys, math
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#! Note: Broken
|
||||
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Configuration & Constants
|
||||
# ---------------------------
|
||||
CELL_SIZE = 50
|
||||
GRID_WIDTH = 16
|
||||
GRID_HEIGHT = 12
|
||||
SCREEN_WIDTH = CELL_SIZE * GRID_WIDTH
|
||||
SCREEN_HEIGHT = CELL_SIZE * GRID_HEIGHT
|
||||
FPS = 60
|
||||
|
||||
MIN_DISTANCE = 20 # minimum spacing (in pixels) between items on a belt
|
||||
|
||||
# Direction vectors for movement
|
||||
DIRECTIONS = {
|
||||
"right": (1, 0),
|
||||
"left": (-1, 0),
|
||||
"up": (0, -1),
|
||||
"down": (0, 1),
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Simulation Object Classes
|
||||
# ---------------------------
|
||||
class Item:
|
||||
def __init__(self, x, y, dx, dy, speed=60):
|
||||
# Position in pixels; starts at the center of a grid cell.
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.dx = dx # normalized direction vector (e.g., from DIRECTIONS)
|
||||
self.dy = dy
|
||||
self.speed = speed
|
||||
|
||||
def draw(self, surface):
|
||||
# Draw the item as a small circle (gold color)
|
||||
pygame.draw.circle(surface, (255, 215, 0), (int(self.x), int(self.y)), 10)
|
||||
|
||||
|
||||
class Conveyor:
|
||||
def __init__(self, grid_x, grid_y, direction):
|
||||
self.grid_x = grid_x
|
||||
self.grid_y = grid_y
|
||||
self.direction = direction # e.g. "right", "down", etc.
|
||||
self.rect = pygame.Rect(grid_x * CELL_SIZE, grid_y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
|
||||
|
||||
def update(self, dt):
|
||||
# Conveyors in this demo do not update objects directly.
|
||||
pass
|
||||
|
||||
def draw(self, surface, debug=False):
|
||||
# Draw the cell as a light gray block
|
||||
pygame.draw.rect(surface, (169, 169, 169), self.rect)
|
||||
# Draw an arrow indicating the conveyor's direction
|
||||
center = self.rect.center
|
||||
offset = 15
|
||||
dx, dy = DIRECTIONS[self.direction]
|
||||
end_point = (center[0] + dx * offset, center[1] + dy * offset)
|
||||
pygame.draw.line(surface, (0, 0, 0), center, end_point, 3)
|
||||
if debug:
|
||||
font = pygame.font.SysFont("monospace", 12)
|
||||
text = font.render(f"{self.grid_x},{self.grid_y}", True, (0, 0, 0))
|
||||
surface.blit(text, (self.rect.x + 2, self.rect.y + 2))
|
||||
|
||||
|
||||
class Arm:
|
||||
def __init__(self, grid_x, grid_y, pick_pos, place_pos):
|
||||
self.grid_x = grid_x
|
||||
self.grid_y = grid_y
|
||||
self.pick_pos = pick_pos # grid cell from which to pick an item
|
||||
self.place_pos = place_pos # grid cell where the item is dropped
|
||||
self.state = "idle" # states: "idle", "picking", "placing"
|
||||
self.holding_item = None
|
||||
self.timer = 0
|
||||
|
||||
def update(self, dt, items):
|
||||
# When idle, scan the pick cell for an item to pick up.
|
||||
if self.state == "idle":
|
||||
for item in items:
|
||||
cell_x = int(item.x // CELL_SIZE)
|
||||
cell_y = int(item.y // CELL_SIZE)
|
||||
if (cell_x, cell_y) == self.pick_pos:
|
||||
self.holding_item = item
|
||||
items.remove(item)
|
||||
self.state = "picking"
|
||||
self.timer = 0.5 # time to pick up
|
||||
break
|
||||
elif self.state == "picking":
|
||||
self.timer -= dt
|
||||
if self.timer <= 0:
|
||||
self.state = "placing"
|
||||
self.timer = 0.5 # time to place the item
|
||||
elif self.state == "placing":
|
||||
self.timer -= dt
|
||||
if self.timer <= 0:
|
||||
# Drop the item at the center of the place cell, with a new direction.
|
||||
if self.holding_item:
|
||||
self.holding_item.x = self.place_pos[0] * CELL_SIZE + CELL_SIZE / 2
|
||||
self.holding_item.y = self.place_pos[1] * CELL_SIZE + CELL_SIZE / 2
|
||||
# For this layout, the vertical belt moves items downward.
|
||||
self.holding_item.dx, self.holding_item.dy = DIRECTIONS["down"]
|
||||
items.append(self.holding_item)
|
||||
self.holding_item = None
|
||||
self.state = "idle"
|
||||
|
||||
def draw(self, surface, debug=False):
|
||||
# Base center of the arm (drawn as a circle)
|
||||
base_center = (self.grid_x * CELL_SIZE + CELL_SIZE // 2, self.grid_y * CELL_SIZE + CELL_SIZE // 2)
|
||||
pygame.draw.circle(surface, (135, 206, 235), base_center, CELL_SIZE // 3)
|
||||
|
||||
# Compute centers for pick and place zones
|
||||
pick_center = (self.pick_pos[0] * CELL_SIZE + CELL_SIZE // 2, self.pick_pos[1] * CELL_SIZE + CELL_SIZE // 2)
|
||||
place_center = (self.place_pos[0] * CELL_SIZE + CELL_SIZE // 2, self.place_pos[1] * CELL_SIZE + CELL_SIZE // 2)
|
||||
|
||||
# Draw the arm's "arm" as a line indicating its action.
|
||||
if self.state == "picking":
|
||||
pygame.draw.line(surface, (255, 0, 0), base_center, pick_center, 4)
|
||||
elif self.state == "placing":
|
||||
pygame.draw.line(surface, (0, 255, 0), base_center, place_center, 4)
|
||||
else:
|
||||
# Idle: draw faint guide lines toward both pick and place positions.
|
||||
pygame.draw.line(surface, (100, 100, 100), base_center, pick_center, 2)
|
||||
pygame.draw.line(surface, (100, 100, 100), base_center, place_center, 2)
|
||||
|
||||
if debug:
|
||||
font = pygame.font.SysFont("monospace", 12)
|
||||
text = font.render(f"Arm: {self.state}", True, (0, 0, 0))
|
||||
surface.blit(text, (self.grid_x * CELL_SIZE, self.grid_y * CELL_SIZE))
|
||||
# Draw outlines for the pick (green) and place (red) zones.
|
||||
pick_rect = pygame.Rect(self.pick_pos[0] * CELL_SIZE, self.pick_pos[1] * CELL_SIZE, CELL_SIZE, CELL_SIZE)
|
||||
place_rect = pygame.Rect(self.place_pos[0] * CELL_SIZE, self.place_pos[1] * CELL_SIZE, CELL_SIZE, CELL_SIZE)
|
||||
pygame.draw.rect(surface, (0, 255, 0), pick_rect, 2)
|
||||
pygame.draw.rect(surface, (255, 0, 0), place_rect, 2)
|
||||
|
||||
|
||||
class Factory:
|
||||
def __init__(self, grid_x, grid_y, output_direction="right"):
|
||||
self.grid_x = grid_x
|
||||
self.grid_y = grid_y
|
||||
self.output_direction = output_direction
|
||||
self.rect = pygame.Rect(grid_x * CELL_SIZE, grid_y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
|
||||
self.production_timer = 1.0 # seconds until next item production
|
||||
self.production_interval = 0.5
|
||||
|
||||
def update(self, dt, items):
|
||||
self.production_timer -= dt
|
||||
if self.production_timer <= 0:
|
||||
# Create a new item at the center of the factory cell.
|
||||
center_x = self.grid_x * CELL_SIZE + CELL_SIZE / 2
|
||||
center_y = self.grid_y * CELL_SIZE + CELL_SIZE / 2
|
||||
dx, dy = DIRECTIONS[self.output_direction]
|
||||
new_item = Item(center_x, center_y, dx, dy, speed=60)
|
||||
items.append(new_item)
|
||||
self.production_timer = self.production_interval
|
||||
|
||||
def draw(self, surface, debug=False):
|
||||
pygame.draw.rect(surface, (192, 192, 192), self.rect)
|
||||
if debug:
|
||||
font = pygame.font.SysFont("monospace", 12)
|
||||
text = font.render(f"{self.production_timer:.1f}", True, (0, 0, 0))
|
||||
surface.blit(text, (self.rect.x + 2, self.rect.y + 2))
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Helper: Draw Grid Lines
|
||||
# ---------------------------
|
||||
def draw_grid(surface):
|
||||
for x in range(0, SCREEN_WIDTH, CELL_SIZE):
|
||||
pygame.draw.line(surface, (200, 200, 200), (x, 0), (x, SCREEN_HEIGHT))
|
||||
for y in range(0, SCREEN_HEIGHT, CELL_SIZE):
|
||||
pygame.draw.line(surface, (200, 200, 200), (0, y), (SCREEN_WIDTH, y))
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Update Items with Collision (Queueing) Behavior
|
||||
# ---------------------------
|
||||
def update_items(items, dt):
|
||||
for item in items:
|
||||
# Compute the proposed new position.
|
||||
new_x = item.x + item.dx * item.speed * dt
|
||||
new_y = item.y + item.dy * item.speed * dt
|
||||
can_move = True
|
||||
# Check all other items to see if one is ahead and too close.
|
||||
for other in items:
|
||||
if other is item:
|
||||
continue
|
||||
# Calculate vector from this item to the other.
|
||||
rel_x = other.x - item.x
|
||||
rel_y = other.y - item.y
|
||||
# Dot product to see if other is ahead along the item's movement direction.
|
||||
if (rel_x * item.dx + rel_y * item.dy) > 0:
|
||||
dist = math.hypot(rel_x, rel_y)
|
||||
if dist < MIN_DISTANCE:
|
||||
can_move = False
|
||||
break
|
||||
if can_move:
|
||||
item.x = new_x
|
||||
item.y = new_y
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Main Simulation Loop
|
||||
# ---------------------------
|
||||
def main():
|
||||
pygame.init()
|
||||
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
||||
pygame.display.set_caption("Factorio-like Simulation with Queuing & Mechanical Arm")
|
||||
clock = pygame.time.Clock()
|
||||
debug_mode = True
|
||||
|
||||
# Lists to hold simulation objects
|
||||
conveyors = []
|
||||
items = []
|
||||
arms = []
|
||||
factories = []
|
||||
|
||||
# -- Layout Setup --
|
||||
# Horizontal conveyor belt from (2,5) to (10,5)
|
||||
for i in range(2, 11):
|
||||
conveyors.append(Conveyor(i, 5, "right"))
|
||||
# Vertical conveyor belt from (10,5) to (10,9)
|
||||
for j in range(5, 10):
|
||||
conveyors.append(Conveyor(10, j, "down"))
|
||||
# Factory at (1,5) outputs items to the right (onto the horizontal belt)
|
||||
factories.append(Factory(1, 5, output_direction="right"))
|
||||
# Arm at (10,4): picks from cell (10,5) and places to cell (10,6)
|
||||
arms.append(Arm(10, 4, (10, 5), (10, 6)))
|
||||
|
||||
running = True
|
||||
while running:
|
||||
dt = clock.tick(FPS) / 1000.0 # Delta time in seconds
|
||||
|
||||
# Event handling
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
# Toggle debug mode with the D key.
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_d:
|
||||
debug_mode = not debug_mode
|
||||
|
||||
# Update simulation objects.
|
||||
for factory in factories:
|
||||
factory.update(dt, items)
|
||||
for arm in arms:
|
||||
arm.update(dt, items)
|
||||
# Update items with queuing/collision behavior.
|
||||
update_items(items, dt)
|
||||
|
||||
# ---------------------------
|
||||
# Drawing Phase
|
||||
# ---------------------------
|
||||
screen.fill((50, 50, 50)) # Clear the screen with a dark background.
|
||||
|
||||
# Draw grid lines if debug mode is on.
|
||||
if debug_mode:
|
||||
draw_grid(screen)
|
||||
|
||||
# Draw conveyors.
|
||||
for conveyor in conveyors:
|
||||
conveyor.draw(screen, debug_mode)
|
||||
# Draw factories.
|
||||
for factory in factories:
|
||||
factory.draw(screen, debug_mode)
|
||||
# Draw arms.
|
||||
for arm in arms:
|
||||
arm.draw(screen, debug_mode)
|
||||
# Draw items.
|
||||
for item in items:
|
||||
item.draw(screen)
|
||||
|
||||
# Global debug info.
|
||||
if debug_mode:
|
||||
font = pygame.font.SysFont("monospace", 18)
|
||||
debug_text = font.render("DEBUG MODE ON - Press D to toggle", True, (255, 255, 255))
|
||||
screen.blit(debug_text, (10, SCREEN_HEIGHT - 30))
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
173
factorio-drones/main.py
Normal file
173
factorio-drones/main.py
Normal file
@ -0,0 +1,173 @@
|
||||
import pygame
|
||||
import random
|
||||
import math
|
||||
from collections import deque
|
||||
|
||||
# --- Simulation Classes ---
|
||||
|
||||
class Task:
|
||||
__slots__ = ('source', 'destination', 'item', 'amount')
|
||||
def __init__(self, source, destination, item, amount):
|
||||
self.source = source
|
||||
self.destination = destination
|
||||
self.item = item
|
||||
self.amount = amount
|
||||
|
||||
class Storage:
|
||||
def __init__(self, name, pos):
|
||||
self.name = name
|
||||
self.position = pos # (x, y) position on screen
|
||||
self.inventory = {"item": 1000} # starting inventory
|
||||
|
||||
def add_item(self, item, amount):
|
||||
self.inventory[item] = self.inventory.get(item, 0) + amount
|
||||
|
||||
def remove_item(self, item, amount):
|
||||
if self.inventory.get(item, 0) >= amount:
|
||||
self.inventory[item] -= amount
|
||||
return True
|
||||
return False
|
||||
|
||||
def request_transfer(self, destination, item, amount, task_queue):
|
||||
if self.inventory.get(item, 0) >= amount:
|
||||
# Remove items immediately, simulating reservation.
|
||||
self.remove_item(item, amount)
|
||||
task = Task(self, destination, item, amount)
|
||||
task_queue.append(task)
|
||||
|
||||
class Drone:
|
||||
__slots__ = ('id', 'x', 'y', 'task', 'speed')
|
||||
def __init__(self, id, pos):
|
||||
self.id = id
|
||||
self.x, self.y = pos
|
||||
self.task = None
|
||||
self.speed = 2.0 # pixels per frame
|
||||
|
||||
def assign_task(self, task):
|
||||
self.task = task
|
||||
# Set drone's position to the source storage position.
|
||||
self.x, self.y = task.source.position
|
||||
|
||||
def update(self):
|
||||
if self.task:
|
||||
dest_x, dest_y = self.task.destination.position
|
||||
dx = dest_x - self.x
|
||||
dy = dest_y - self.y
|
||||
dist = math.hypot(dx, dy)
|
||||
if dist < self.speed:
|
||||
# Arrived: set position exactly to destination and deliver item.
|
||||
self.x, self.y = dest_x, dest_y
|
||||
self.task.destination.add_item(self.task.item, self.task.amount)
|
||||
self.task = None
|
||||
else:
|
||||
# Move a step toward the destination.
|
||||
self.x += self.speed * dx / dist
|
||||
self.y += self.speed * dy / dist
|
||||
|
||||
# --- Pygame Simulation ---
|
||||
|
||||
def main():
|
||||
pygame.init()
|
||||
screen_width, screen_height = 800, 600
|
||||
screen = pygame.display.set_mode((screen_width, screen_height))
|
||||
pygame.display.set_caption("Drone Simulation")
|
||||
clock = pygame.time.Clock()
|
||||
font = pygame.font.SysFont("Arial", 16)
|
||||
|
||||
# Global simulation state
|
||||
task_queue = deque()
|
||||
storages = []
|
||||
drones = []
|
||||
|
||||
# Unique id counters
|
||||
drone_id_counter = 0
|
||||
storage_id_counter = 0
|
||||
|
||||
# Create some initial storages at random positions
|
||||
for _ in range(5):
|
||||
pos = (random.randint(50, screen_width - 50), random.randint(50, screen_height - 50))
|
||||
storages.append(Storage(f"Storage-{storage_id_counter}", pos))
|
||||
storage_id_counter += 1
|
||||
|
||||
# Create some initial drones at random positions
|
||||
for _ in range(20):
|
||||
pos = (random.randint(0, screen_width), random.randint(0, screen_height))
|
||||
drones.append(Drone(drone_id_counter, pos))
|
||||
drone_id_counter += 1
|
||||
|
||||
running = True
|
||||
while running:
|
||||
# --- Event Handling ---
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_t:
|
||||
# Request a random transfer if at least two storages exist.
|
||||
if len(storages) >= 2:
|
||||
src, dest = random.sample(storages, 2)
|
||||
amount = random.randint(1, 10)
|
||||
src.request_transfer(dest, "item", amount, task_queue)
|
||||
elif event.key == pygame.K_d:
|
||||
# Add a new drone at a random position.
|
||||
pos = (random.randint(0, screen_width), random.randint(0, screen_height))
|
||||
drones.append(Drone(drone_id_counter, pos))
|
||||
drone_id_counter += 1
|
||||
elif event.key == pygame.K_s:
|
||||
# Add a new storage at a random position.
|
||||
pos = (random.randint(50, screen_width - 50), random.randint(50, screen_height - 50))
|
||||
storages.append(Storage(f"Storage-{storage_id_counter}", pos))
|
||||
storage_id_counter += 1
|
||||
elif event.key == pygame.K_i:
|
||||
# Add 50 items to each storage.
|
||||
for s in storages:
|
||||
s.add_item("item", 50)
|
||||
|
||||
# --- Update Simulation ---
|
||||
for drone in drones:
|
||||
if drone.task is None and task_queue:
|
||||
# Assign a task from the queue to the idle drone.
|
||||
task = task_queue.popleft()
|
||||
drone.assign_task(task)
|
||||
drone.update()
|
||||
|
||||
# --- Drawing ---
|
||||
screen.fill((30, 30, 30)) # dark background
|
||||
|
||||
# Draw storages (blue circles) with name and inventory.
|
||||
for s in storages:
|
||||
pygame.draw.circle(screen, (0, 100, 255), s.position, 20)
|
||||
info = font.render(f"{s.name}: {s.inventory.get('item', 0)}", True, (255, 255, 255))
|
||||
screen.blit(info, (s.position[0] - 30, s.position[1] - 40))
|
||||
|
||||
# Draw drones.
|
||||
# Idle drones are drawn in green; drones with tasks (in transit) are red.
|
||||
for d in drones:
|
||||
color = (255, 0, 0) if d.task else (0, 255, 0)
|
||||
pygame.draw.circle(screen, color, (int(d.x), int(d.y)), 5)
|
||||
|
||||
# Debug info: show task queue length, number of drones, storages.
|
||||
debug_info = font.render(
|
||||
f"Task Queue: {len(task_queue)} | Drones: {len(drones)} | Storages: {len(storages)}",
|
||||
True, (255, 255, 0))
|
||||
screen.blit(debug_info, (10, 10))
|
||||
|
||||
# Display instructions.
|
||||
instructions = [
|
||||
"Controls:",
|
||||
"T - Request Transfer",
|
||||
"D - Add Drone",
|
||||
"S - Add Storage",
|
||||
"I - Add Items to all Storages"
|
||||
]
|
||||
for i, line in enumerate(instructions):
|
||||
instr_text = font.render(line, True, (200, 200, 200))
|
||||
screen.blit(instr_text, (10, 30 + i * 20))
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(60)
|
||||
|
||||
pygame.quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user