Feature/fable facelift + Working - #3
Open
JdMasuta wants to merge 87 commits into
Open
Conversation
Documents the Arcane Engineering (D&D 5e homebrew) domain background, audits the current components.py prototype for concrete bugs, and lays out a prioritized build list (resistors, concentration components, logic gates, junction-to-junction links, manim converter, tests, docs) so Fable 5 can pick it up and complete the project.
…step() edge cases - raise CircuitException in Junction.__init__ instead of discarding it, and require only one side to have arity 1 (and, not or) as the original comment intended - fix UnboundLocalError in connect() when the next component does not require input (wire_obj was appended without being created) - fix next_component -> next_comp typo that silently dropped the link into an opening junction - guard division by zero when spreading remainder energy in Junction.step() - gate connect() debug prints behind a verbose flag Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Per-step transferable energy is 1/resistance. A Resistor is the sole actor on both of its links (same convention as Junction) and wires now skip Junction-or-Resistor neighbours, since otherwise a downstream wire would pull at its own unthrottled rate and defeat the resistance. Wire gains an explicit energy_out_rate (inf, preserving its dump-all behaviour) so subclasses can throttle output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
…onvention Concentration is the capacitor/crystal analogue of D&D concentration: it charges at charge_rate per step while releasing nothing, dumps its held energy downstream once it reaches capacity, and break_concentration() dissipates whatever is held (a failed save). Replaces the growing isinstance checks with an explicit self_managed_links marker on the base component: self-managed components (Junction, Resistor, Concentration) are the sole actors on their links, and when both ends of a link are self-managed the downstream side pulls. Junction outputs now skip self-managed neighbours accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Rewrites connect() as a build-nodes-then-link pass that keeps the exact output structure plot() expects. Adjacent branch lists now link their closing junction straight into the next opening junction (downstream junction pulls; upstream skips self-managed neighbours, so energy moves exactly once per link). Branch sublists no longer grow a vestigial self-wire, closing junctions wrap around only at depth 0, and junction name counters actually increment so names stay unique. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
LogicGate extends Junction: n inputs merge into 1 output and energy only moves while the gate's boolean rule over its active inputs holds (an input is active when it holds >= threshold energy). Multi-input gates placed directly after a branch list are adopted by connect() as that list's closing junction. NotGate is an inline inverter that emits from an internal reserve while its input is quiet and consumes the control signal that holds it shut. Junctions now carry an explicit junction_role stamped by connect(), so plot() no longer relies solely on the '-' name convention (which user-named gates would break); the name fallback remains for hand-built circuits. Switch now honours the self-managed-link convention so it cannot double-move energy against a junction or gate neighbour. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
flatten/step_all/total_energy/simulate/plot_history reproduce what the components.py __main__ block does inline, so tests, demos, and the manim converter can share them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
plot() gains record/show parameters: with a record dict it captures the exact xy segments each component symbol draws (and now passes ax to every symbol call instead of relying on plt.gca()). manim_scene builds on that: trace_layout() replays the plot walk silently and make_circuit_scene() turns the segments plus a simulate() energy history into a Scene where each component's stroke brightens and thickens with its energy, driven by one ValueTracker sweep. manim is imported lazily so everything else works without it; a DemoScene is exposed for manim -pql when it is installed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
build_circuit()/load_circuit() map a small JSON format onto the nested lists connect() consumes: objects with a 'type' key become components (remaining keys go to the constructor), arrays nest exactly like connect() branch lists. Returns a name -> component registry so callers can reach switches and gates after loading. Includes an example spec and a CLI (python circuit_spec.py spec.json --steps N) that draws the circuit and its energy history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
32 tests: per-component step behaviour (wire, resistor, concentration, switch, caster, AND/OR/NOT gates), connect() topology including junction-to-junction links and gate adoption, energy conservation with cast() consumption accounted exactly, JSON spec loading and its error paths, layout tracing, and regressions for the five fixed bugs. Adds pyproject.toml/requirements.txt (manim optional) and stops tracking __pycache__. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
README now credits GorillaOfDestiny's Arcane Engineering supplement as the conceptual basis, documents every component and its circuit analogue, and shows how to build, simulate, JSON-load, and animate circuits. TODO marks the original four items done and lists the real remaining gaps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Moves the five modules under arcane/, switches sibling imports to absolute arcane.* imports, and adds a public API in arcane/__init__.py (manim-backed helpers exposed lazily so importing arcane never needs manim). Extracts trace_layout into arcane.layout and a shared palette + energy gradient into arcane.theme, so the GUI preview and manim export will draw from one source of truth. Adds arcane.render.render_circuit() for programmatic manim rendering with explicit fps/quality, console entry points (arcane-gui, arcane-sim), and a setuptools build config. Tests updated to import from arcane; all 32 still pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
A dark-themed desktop app (arcane.gui) that shows the circuit schematic and the energy-over-time graph side by side. A transport bar scrubs through the simulated timeline: the schematic recolours each component by its energy at the selected step (using the shared theme gradient, so it matches an exported video) while a gold cursor tracks the same instant on the energy graph. Playback advances so a full sweep lasts the configured render duration. Controls cover circuit source (demo or JSON spec), step count, log-scale and plumbing-visibility toggles, per-switch toggle scheduling, and manim frame-rate/resolution/duration. Rendering runs on a background QThread so the UI stays responsive, and the Render button is disabled with a hint when manim is absent. Simulation state lives in a Qt-free SimulationSession for testability, and repeated runs reset to initial energies so they are reproducible. Also brings the original modules up to PEP8 (autopep8 + manual line wrapping, behaviour-preserving); the whole package now passes pycodestyle and the 32 existing tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Render tests inject a fake manim module to verify render_circuit translates fps/quality/duration into the right config keys (quality before frame_rate so the explicit fps wins), copies the produced file to the requested path, and guards the no-manim and bad-quality paths. GUI tests run under the offscreen platform: session reproducibility across reruns, switch-event scheduling changing the outcome, the load->run-> scrub flow, and the plumbing-visibility filter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
README now leads with the desktop studio (circuit + energy views, scrubbing, manim export), documents the install extras, entry points, scripting API, JSON specs, and package layout, with a hero screenshot. requirements.txt gains PySide6; TODO records the studio as done and lists the real next steps. Also tidies test import wrapping to keep the whole project pycodestyle-clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
SpellWave realizes the Theory of Magic formalization: an exact spreading Gaussian packet times a stationary max-range absorber gives a non-unitary |Psi|^2 whose raw norm decays, and A(t) = 1/int(|Psi|^2 dx) renormalizes it so P = A|Psi|^2 stays a valid probability density until the spell fizzles. Includes the classical-model comparison series that exhibits the ultra-magic catastrophe, per-step arrays aligned to the circuit timeline, and waves_from_cast_log() to spawn waves from simulate()'s cast log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
simulate(..., cast_log=[]) wraps each Caster's cast for the duration of
the run and appends {step, caster, energy} on every firing, restoring
the method afterwards. Non-breaking; callers use it to spawn spell
waves without instrumenting components themselves.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
make_spellwave_scene() sweeps a SpellWave's precomputed P(x,t) as a glowing curve toward a dashed max-range marker, with a DecimalNumber counter showing A(t) climb as the absorber eats the raw norm and the curve collapsing at the fizzle. Same ValueTracker/updater pattern and guarded manim import as manim_scene.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
The tempconfig/copy plumbing moves into _render_scene so render_circuit and the new render_wave share one path; both keep the same fps/quality/run_time semantics and guarded error handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
SpellWave and waves_from_cast_log join the eager public API; make_spellwave_scene and render_wave are exposed lazily like the other manim-backed helpers. Version bumped to 1.1.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
_style_axes gains an optional ax parameter (defaulting to self.ax) so multi-axes subclasses like the spell wave view can reuse the theme styling; single-axes callers are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
SpellWaveView stacks two themed axes: the renormalized energy density E*P(x) of every wave at the scrubbed step (absorber region shaded, max range marked, live A(t) and raw-norm readout) above the log-scale timeline where the classical model blows up toward the ultra-magic catastrophe while the renormalized total stays finite, with the shared gold time cursor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
run() records casts via simulate(cast_log=...) and builds one SpellWave per firing using the session's wave settings (max range, speed, packet width), keeping waves aligned to the run's timeline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
New 'Spell wave' group (max range, speed, packet width, show-classical toggle emitting waveSettingsChanged) and a 'Render spell wave...' button alongside the renamed 'Render circuit...' button, sharing the fps/resolution/duration options via waveRenderRequested. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
The worker takes a zero-argument callable returning the output path instead of hard-coding render_circuit, so circuit and spell-wave renders share one background-thread path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
The lower half becomes a QTabWidget (Energy over time | Spell propagation), both driven by the transport scrubber. Runs feed the wave view from the session's waves, the status bar reports cast counts, and circuit/wave renders go through the generalized worker with buttons that re-enable appropriately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
QTabWidget/QTabBar styling to match the arcane palette: rounded surface tabs with an accent border on the selected tab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Covers the normalization invariant (int P dx == 1 every alive step), A(t) drifting well past 1 as the absorber bites, the classical model exceeding 1e5x cast energy while the renormalized total stays finite, fizzle behaviour, parameter validation, simulate() cast_log recording and method restoration, and cast-log-to-wave alignment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Extracts the fake manim environment into a helper and verifies render_wave translates fps/quality and delivers the produced file, plus the guarded no-manim path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(util): move circuit_spec into the util sub-library Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(manim): manim_wave.py becomes arcane/manim/wave.py refactor(manim): extract build_wave_mobjects for scene composition make_spellwave_scene() now dispatches on the wave's type: 1D waves stay a glowing curve, SpellWave2D becomes an ImageMobject whose pixel_array is remapped through the shared theme gradient each frame. build_wave_mobjects() exposes the mobject-building half at a caller- chosen offset/scale so the combined circuit+wave scene can reuse it on a shared ValueTracker. Untested against a real manim install (cannot be installed in this environment); exercised via the fake-manim harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
feat(manim): carry gate labels and the NOT-gate circle into scenes refactor(manim): manim_scene.py becomes arcane/manim/circuit.py refactor(manim): extract _circuit_mobjects shared by both scene builders make_combined_scene() stacks the circuit schematic above the wave it cast, one ValueTracker driving both halves on the circuit-step timeline. Circuit scenes now include static Text/Circle mobjects from trace_layout_with_decor(). Untested against a real manim install; exercised via the fake-manim harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(manim): render.py becomes arcane/manim/render.py Same fps/quality/tempconfig plumbing as render_circuit/render_wave. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Documents that 'from manim import ...' inside this sub-package is an absolute import and always resolves to the third-party package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(api): point the public API at the new sub-library paths XorGate/NandGate/SpellWave2D join the eager exports; make_combined_scene/render_combined join the lazy manim-backed ones. Version bumped to 1.2.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(gui): session imports move to arcane.util caster_wave_overrides() collects each Caster's wave_range/speed/width (e.g. from a JSON spec) for waves_from_cast_log; the wave_2d flag picks SpellWave2D per run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
feat(gui): combined-render and builder buttons, render busy indicator refactor(gui): render imports move to arcane.manim The progress bar is indeterminate by design: manim's Python API exposes no per-frame callback, so an honest busy indicator beats a fake percentage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
When the session produced SpellWave2D waves the spatial panel switches from per-wave curves to an imshow of the most recently cast living wave's energy density, on the shared theme gradient, with the same A(t) readout and catastrophe timeline below. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(gui): manim imports move to arcane.manim Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Structured tree editor: components (fields generated from constructor signatures), parallel groups, nested branches, reorder/delete; Build validates through the same build_circuit()+connect() pipeline as the JSON loader and hands the studio a ready spec. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
…e.util Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(tests): imports follow the sub-library split XOR passes with exactly one hot input and closes when both go hot; NAND passes until all inputs are hot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(tests): imports follow the sub-library split A low-capacity output fills exactly to max_energy with the excess going to outputs that still have room; unplaceable energy stays in the junction instead of vanishing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
2D normalization (double integral == 1 every alive step), A(t) drift, the classical catastrophe vs the finite renormalized total, fizzle, parameter validation, wave_cls selection, and override fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Regression for the check_level crash plus end-to-end coverage: walker names, trace_layout segments, plot(), and energy conservation through the doubly nested topology. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
… bar refactor(tests): manim availability import follows the split Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
refactor(tests): render module imports follow the sub-library split Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
The fake provides enough of manim's API to run construct() and drive updaters across several tracker values like the real frame loop would, catching wrong signatures/indices/names that import guards cannot — while saying nothing about actual pixels (only a real manim install can). Covers circuit, 1D wave, 2D wave, both combined variants, and gate-label/NOT-circle decorations, restoring the guarded no-manim state afterwards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Px2UwirEWHX8WWf1158cM5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I used Claude Fable 5 to do a finish-write of the original repository, then created a branch from there to implement some quality of life features (GUI, installable python package format, etc.) If you are interested in the finish-write (adheres to your original project), check out the other branch on this fork. I'd love to work further on this with you, but more direction would be a great help, such as using the issues section of github to request features and report bugs.
I hope this is in the vein of what you were looking to build! Thank you for being the coolest kind of nerd.
To run a quick test, clone this branch of my fork, navigate to the root directory of the clone, create a venv, activate, and install requirements.txt. Lastly, to run the gui, run "python -m arcane.gui"