extends Control signal on_win() ## Number of cocentric discs @export var disc_count : int = 16 ## Number of slots per discs @export var disc_slots : int = 16 ## Maximum shift induced by a disc move @export var disc_max_shift : int = 5 ## Minimum number of influenced discs @export var disc_min_influence : int = 0 ## Maximum number of influenced discs @export var disc_max_influence : int = 4 class Influence: ## Relative move of the discs var moves : Array[int] func _init(m : Array[int]) -> void: moves = m ## Effect of turning on particular disc upon the others var influence : Array[Influence] var state : Array[int] @onready var rng : RandomNumberGenerator = RandomNumberGenerator.new() @onready var area : CanvasItem = %BoardGame @onready var transition_states : Array[float] # MOVE TO VIEW @onready var accumulated_states : Array[int] # MOVE TO VIEW func _ready() -> void: area.draw.connect(_area_draw) # MOVE TO VIEW rng.seed = hash(Time.get_unix_time_from_system()) # Compute influences influence = [] var mn : int = min(disc_min_influence, disc_max_influence) var mx : int = clamp(max(disc_min_influence, disc_max_influence), mn, disc_count - 1) var sh : int = absi(disc_max_shift) for i : int in disc_count: var influenced_discs_count : int = rng.randi_range(mn, mx) var target_discs : Array[int] = [] var remaining_discs : Array[int] = [] for j : int in disc_count: if j == i: continue remaining_discs.append(j) for j : int in influenced_discs_count: var t : int = rng.randi() % remaining_discs.size() target_discs.append(remaining_discs[t]) remaining_discs.remove_at(t) var shifts : Array[int] = [] for j : int in disc_count: if (j == i) or (not j in target_discs): shifts.append(0) else: var s : int = rng.randi_range(-sh + 1, sh) if s <= 0: s -= 1 shifts.append(s) var inf : Influence = Influence.new(shifts) influence.append(inf) # Initial puzzle state state = []; state.resize(disc_count); state.fill(0) # Shuffle for i : int in 16: var d : int = rng.randi() % disc_count _move_disc(d, 1 if rng.randi() % 2 == 0 else -1) #region Move To View transition_states = []; transition_states.resize(disc_count) accumulated_states = []; accumulated_states.resize(disc_count) for i : int in disc_count: transition_states[i] = state[i] accumulated_states[i] = state[i] area.queue_redraw.call_deferred() #endregion # Input @onready var current_disc : int = 0 func _input(event: InputEvent) -> void: var move_by : int = 0 if event.is_action_pressed("ui_up"): current_disc = (current_disc + 1) % disc_count elif event.is_action_pressed("ui_down"): current_disc -= 1 if current_disc < 0: current_disc = disc_count - 1 elif event.is_action_pressed("ui_left"): move_by = -1 elif event.is_action_pressed("ui_right"): move_by = 1 if move_by != 0: _move_disc(current_disc, move_by) _move_disc_drawing(current_disc, move_by) # MOVE TO VIEW - use signal var inf : Array[int] = influence[current_disc].moves for i : int in disc_count: var j : int = inf[i] * move_by _move_disc(i, j) _move_disc_drawing(i, j) # MOVE TO VIEW - use signal _check_win() area.queue_redraw.call_deferred() func _check_win() -> void: for i : int in disc_count: if state[i] != 0: return on_win.emit() func _move_disc(disc_id : int, move_by : int) -> void: if move_by != 0: state[disc_id] += move_by if state[disc_id] > disc_slots: state[disc_id] -= disc_slots elif state[disc_id] < 0: state[disc_id] = disc_count + state[disc_id] #region Move To View @export var transition_speed : float = 0.1 @export var transition_ease : Tween.EaseType = Tween.EASE_IN_OUT @export var transition_trs : Tween.TransitionType = Tween.TRANS_BOUNCE func _move_disc_drawing(disc_id : int, move_by : int) -> void: if move_by != 0: accumulated_states[disc_id] += move_by var tween : Tween = create_tween() # One tween per disc ?? ... what a waste tween.tween_method(_set_disc_temporary_position.bind(disc_id), transition_states[disc_id], accumulated_states[disc_id], transition_speed).set_ease(transition_ease).set_trans(transition_trs) func _set_disc_temporary_position(r : float, id : int) -> void: transition_states[id] = r area.queue_redraw.call_deferred() @export var disc_thickness : float = 16. @export var disc_distance : float = 4. @export var disc_slot_margin : float = 0.98 @export var segment_points : int = 16 func _area_draw() -> void: var center : Vector2 = area.size / 2. var start_radius : float = 0 var end_radius : float = disc_thickness var angle_inc : float = (2. * PI) / disc_slots var h_angle : float = (angle_inc / 2.) var slot_h : float = h_angle * disc_slot_margin for i : int in disc_count: var c_radius : float = (start_radius + end_radius) / 2. for j : int in disc_slots: var slot_angle : float = j * angle_inc area.draw_arc(center, c_radius, slot_angle - slot_h, slot_angle + slot_h, segment_points, Color.CORNFLOWER_BLUE if current_disc == i else Color(0.3, 0.3, 0.8, 0.7), disc_thickness, true) start_radius = end_radius + disc_distance end_radius = start_radius + disc_thickness var angle : float = (transition_states[i] * angle_inc) area.draw_arc(center, c_radius, angle - slot_h, angle + slot_h, segment_points, Color.GREEN_YELLOW, disc_thickness, true) #endregion
or share this direct link: