Power Cable/Minecraft "Redstone" Power?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Corruptinator
:warning: Old Version Published before Godot 3 was released.

Hello!

I was wondering if anybody knew how to make a node act and simulate like an actual power cable. I was working on a simulator of being in charge of a electric power plant company and I was able to make wires read from the power plant, using Boolean to tell whether receiving power is true or false. The problem though is that if i remove one of the wires that still receives power from the plant, but connected to other wire nodes, the power is still true to other wires.

I now ran into a bit of a dead end realizing I have no Idea on how power cables work in programming, I wanted to achieve a similar effect on how Minecraft handles Redstone power when one of them can easily disconnect between two other redstones and cause one of them to lose redstone energy.

Here is the code in case anybody is interested:

extends Area2D

# class member variables go here, for example:
# var a = 2
# var b = "textvar"
var mouse_hover = false
var tracking = false
var detect = false
var power = false
export (bool) var debug = false

func _ready():
	# Called every time the node is added to the scene.
	# Initialization here
	self.connect("area_enter",self,"_on_area_enter")
	self.connect("area_exit",self,"_on_area_exit")
	self.connect("mouse_enter",self,"_on_mouse_enter")
	self.connect("mouse_exit",self,"_on_mouse_exit")
	set_fixed_process(true)
	set_process_input(true)
	pass

func _fixed_process(delta):
	
	if debug == true:
		print(power)
	else:
		pass
	
	if detect == true:
		var obj = get_overlapping_areas()
		for i in obj:
			if i.is_in_group("Power_Plant"):
				power = true
			elif i.is_in_group("Wire"):
				if i.power == true:
					power = true
			else:
				power = false
				pass
	else:
		power = false
		pass
	
	#When the object is clicked, the Fixed Process tells the object to follow the mouse position, Globally.
	if tracking == true:
		self.set_global_pos(get_global_mouse_pos())
		pass
	else:
		pass
	
	pass

func _input(event):
	
	
	#Click on the object to allow the Fixed Process to pick up the object and move, works when "hovered" over
	if (event.is_action_pressed("ui_left_click")):
		if mouse_hover == true:
			tracking = true
			pass
		else:
			pass
		pass
	
	#When Click is released, it causes the object to stop following.
	elif (event.is_action_released("ui_left_click")):
		tracking = false
		pass
	else:
		pass
	
	pass

func _on_area_enter(area):
	detect = true
	pass

func _on_area_exit(area):
	detect = false
	pass

#These two functions allows mouse hovering, to allow specific object to click on rather than let all "AREA" objects get
#clicked on.
func _on_mouse_enter():
	mouse_hover = true
	pass

func _on_mouse_exit():
	mouse_hover = false
	pass

Your question is quite general, there are tons of ways of making a wire system, or something that acts like it. Details count as well.
Your script also includes input management but I can’t tell what you really want by just reading it.

The only advice I can give is, generally, cables conveying power can be represented as a graph (general or grid-based), because it’s all about connecting stuff to other stuff.
Then you must choose how it behaves when you switch power: does it traverses the network instantly? Does it have some propagation logic instead? Do you need execution order to prevent concurrent access or endless loops?

In Minecraft, redstone is updated on a grid as a mix of the two:
When a block changes state, it updates its neighbors instantly, which update too in one go, until it reaches some special blocks (repeaters, pistons, whatever) or runs out of power over the distance. These are put in a queue and are dequeued on the next tick, which starts again one more iteration etc until the queue is empty.

In another prototype I made, I handled power differently by representing it with a token that traverses a graph (nodes, grid, whatever). When power is 1, an “on” token is emitted and runs through the graph at each iteration (1 per frame or until it gets to the end). Same goes when power is 0, I send an “off” token. Then the token can change state during its course, like loosing power, increasing power, pick some information, split in two…

Lots of ways to do cables. One thing to avoid though: stack overflows and infinite loops :stuck_out_tongue:

Zylann | 2017-02-17 13:46

Keep track of what things are connected is called the dynamic graph connectivity problem. A naive solution is to traverse the graph on every update, but this is linear in the number of edges and might be slow for large nets. There are more efficient algorithms that run in log or log^2 (I can’t remember) time in the number of edges, but they are significantly more complicated to implement.

raymoo | 2017-02-17 20:43

:bust_in_silhouette: Reply From: eons

I think your problem may not be programming but design.

This is just an idea of how to approach the situation:

  • You have a power plant that generates power, and that may be the end of the basic power plant.

  • Now the fun part, wires, wires just define a connection, from A to B, what i understand of your description, A and B could be a power plant or a set of wires, but to the wire it does not matters, it is connected to things that give or receive power.

Then if a wire is removed, all the connected things to A and B should get notified of the change (does not matters if are wires or a power plant) and trigger a cascade of notifications to their connections to update the power supply data.

The same may apply if a connection consume power from the grid, all the grid will have to be updated.

The way you notify changes on the grid will depend on the design, could be signals, notifications, group calls or directly call methods on the connections, the order of the update may be important too (you may have loops and end on a lock).


Regardless of the method you use, do it on paper first, solve the most simple situations, then try more complex ones, attacking all at once will just pile up bugs.


ps: Having a underlying graph representation of the power grid could be better for more complex situations but you will have to study a bit about graph data structures.

:bust_in_silhouette: Reply From: avencherus

It’s a broad question that deals with data structures and algorithms. You’ll want to study things like directed graphs.

A simplistic approach to your problem, assuming you have a one way flow might look something like this:

extends Node2D

class PowerNode:
	var power_on = false
	var outputs = []
	
	func add_output(node):
		outputs.append(node)
		
	func set_state(state):
		power_on = state
		for connection in outputs:
			connection.set_state(state)
			
	func disconnect():
		set_state(false)

func _ready():
	var a = PowerNode.new()
	var b = PowerNode.new()
	var c = PowerNode.new()
	var d = PowerNode.new()
	
	a.add_output(b)
	b.add_output(c)
	b.add_output(d)
	
	printt("Is power on at D?", d.power_on == true, "Is power on at A?", a.power_on == true)
	
	a.set_state(true)
	
	printt("Is power on at D?", d.power_on == true, "Is power on at A?", a.power_on == true)
	
	b.disconnect()
	
	printt("Is power on at D?", d.power_on == true, "Is power on at A?", a.power_on == true)