idle_core 0.1.0
idle_core: ^0.1.0 copied to clipboard
Deterministic idle engine core with tick simulation and offline progress.
idle_core #
Pure-Dart idle engine core with deterministic ticks and offline progress.
Quick start #
Add the dependency:
dependencies:
idle_core: ^0.1.0
Minimal usage:
import 'dart:io';
import 'package:idle_core/idle_core.dart';
class GameState extends IdleState {
final int gold;
final int rate;
const GameState({required this.gold, required this.rate});
GameState copyWith({int? gold, int? rate}) {
return GameState(
gold: gold ?? this.gold,
rate: rate ?? this.rate,
);
}
@override
Map<String, dynamic> toJson() => {'gold': gold, 'rate': rate};
}
class UpgradeRate extends IdleAction {
final int delta;
const UpgradeRate(this.delta);
}
GameState reducer(GameState state, IdleAction action) {
if (action is IdleTickAction) {
return state.copyWith(gold: state.gold + state.rate);
}
if (action is UpgradeRate) {
return state.copyWith(rate: state.rate + action.delta);
}
return state;
}
void main() {
final engine = IdleEngine<GameState>(
config: const IdleConfig<GameState>(dtMs: 1000),
reducer: reducer,
state: const GameState(gold: 0, rate: 1),
);
engine.tick(count: 5);
engine.dispatch(const UpgradeRate(2));
engine.tick(count: 3);
final offline = engine.applyOffline(0, 10 * 1000);
stdout.writeln('Final: ${offline.state.toJson()}');
}
API overview #
IdleEngineorchestrates ticks and offline application.IdleConfigdefines time step and safety caps.IdleStateis immutable and JSON-serializable.IdleReduceris a pure function(state, action) -> state.TickClockis injectable for tests.TickResultandOfflineResultsummarize progress.EventBusoptionally collects events for result snapshots.
Core concepts #
State and reducer:
- State is immutable and JSON-serializable.
- Reducer is pure and deterministic.
- Tick logic is driven by
IdleTickAction(dtMs).
Determinism rules:
- No
DateTime.now()inside the reducer. - No randomness without a seeded PRNG passed through state.
Offline progress #
final result = engine.applyOffline(lastSeenMs, nowMs);
Offline processing:
delta = clamp(now - lastSeen, 0, maxOfflineMs)ticks = floor(delta / dtMs)then capped bymaxTicksTotal- Work is chunked by
maxTicksPerChunk
Configuration #
IdleConfig fields:
dtMs: fixed tick size in milliseconds (default 1000)maxOfflineMs: maximum offline windowmaxTicksTotal: hard cap on total ticks appliedmaxTicksPerChunk: chunk size for offline workresourceDelta: optional(before, after) -> Map<String, num>eventBus: optionalEventBusto collect events
Results #
TickResult and OfflineResult include:
ticksAppliedresourcesDelta(if provided)events(ifEventBusis used)
OfflineResult also includes:
requestedDeltaMs,clampedDeltaMs,appliedDeltaMsticksRequested,ticksCapped,chunks