Skip to content

Autoresolve campaign battles by simulation - #56

Merged
Drefvelin merged 1 commit into
mainfrom
feat/war-autoresolve-simulation
Sep 26, 2026
Merged

Drefvelin merged 1 commit into
mainfrom
feat/war-autoresolve-simulation

Conversation

@Drefvelin

@Drefvelin Drefvelin commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #54.

What changes

  • Autoresolve (both leaders agree) now simulates the battle instead of launching a live one: committed regiments become lives, each side gets a luck factor in [1 - luck, 1 + luck], and Lanchester's square law decides the winner and the winner's losses. The loser loses loser_loss_fraction of its lives. Ties go to the defender.
  • Deaths go through the normal outcome path (casualties to regiments at 1 per 5 deaths, fuel, occupation, schedule advance), with the offensive coalition pinned. A prepared but unstarted battle is deleted. A started battle cannot be autoresolved.
  • A stalled vote postpones war.battle_voting.max_postponements times (default 1), then autoresolves. The counter resets when a battle is scheduled or resolved.
  • Wars left in the legacy AUTORESOLVE_PENDING phase are resolved on the next tick.
  • Battle start hours are now 21, 22 and 23; window_end_hour closes the window and is no longer a start hour.
  • Mercenaries are left out of the simulation, since there is no roster.

Config (add to live war.yml)

war:
  battle_voting:
    max_postponements: 1
  autoresolve:
    luck: 0.15
    loser_loss_fraction: 0.5

Tests

mvn test: 2141 passing. New BattleAutoresolveServiceTest covers the prediction; schedule, tick, window and config tests are updated.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Battles now autoresolve when leaders agree to simulation or voting reaches its postponement limit. Results account for each side’s strength and configured luck, apply losses to the losing side, and notify players.
    • Added configuration for postponement limits and autoresolve settings.
  • Bug Fixes
    • The battle window’s end hour can no longer be selected as a start time.
    • Battle postponement counts reset after a battle is scheduled or resolved.

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d3340348-9078-497f-a43a-dce292e97056

📥 Commits

Reviewing files that changed from the base of the PR and between f23feb9 and da05dc6.

📒 Files selected for processing (21)
  • src/main/java/net/tfminecraft/simplefactions/Cache.java
  • src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java
  • src/main/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleLaunchService.java
  • src/main/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleOutcomeService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleAdminService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleFeedbackFormatter.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleAutoresolveService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleTickService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleWindowService.java
  • src/main/java/net/tfminecraft/simplefactions/war/campaign/vote/VoteResults.java
  • src/main/resources/war.yml
  • src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderBattleScheduleTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleLaunchServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleOutcomeServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleFeedbackFormatterTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleAutoresolveServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleTickServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleWindowServiceTest.java
  • src/test/java/net/tfminecraft/simplefactions/war/campaign/vote/BattleVoteServiceTest.java

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

War battle voting now limits postponements before attempting autoresolution. The resolver predicts battle losses and applies campaign outcomes directly. The change also updates autoresolution feedback, resets postponement counts, and excludes the configured battle-window end hour.

Changes

Campaign battle flow

Layer / File(s) Summary
Scheduling policy and configuration
src/main/java/net/tfminecraft/simplefactions/Cache.java, src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java, src/main/resources/war.yml, src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleService.java, src/main/java/net/tfminecraft/simplefactions/war/campaign/vote/VoteResults.java, src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleWindowService.java, src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderBattleScheduleTest.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleServiceTest.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleWindowServiceTest.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/vote/BattleVoteServiceTest.java
Configuration adds defaults for postponements, autoresolve luck, and loser losses. Vote closure attempts autoresolution after the configured postponement limit, and successful scheduling resets the cycle count. The configured end hour is no longer a valid start hour.
Autoresolve execution
src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleAutoresolveService.java, src/main/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleLaunchService.java, src/main/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleOutcomeService.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleAutoresolveServiceTest.java, src/test/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleLaunchServiceTest.java, src/test/java/net/tfminecraft/simplefactions/war/battle/campaign/CampaignBattleOutcomeServiceTest.java
The resolver predicts battle outcomes from committed regiments and configured luck, applies casualties and campaign state, clears proposal flags, and broadcasts results. Autoresolve entry points delegate to it, and applying an outcome resets postponements.
Resolution state and admin feedback
src/main/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleTickService.java, src/main/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleAdminService.java, src/main/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleFeedbackFormatter.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/runtime/BattleScheduleTickServiceTest.java, src/test/java/net/tfminecraft/simplefactions/war/campaign/admin/WarScheduleFeedbackFormatterTest.java
Tick processing handles pending autoresolution and marks autoresolved votes as changed. Admin results and feedback report autoresolution separately from postponement.

Priority: ⬇️ Low

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant BattleScheduleService
  participant BattleAutoresolveService
  participant CampaignBattleOutcomeService
  participant BattleCasualtyService
  BattleScheduleService->>BattleAutoresolveService: resolve(war) after postponement limit
  BattleAutoresolveService->>BattleAutoresolveService: predict battle result and losses
  BattleAutoresolveService->>CampaignBattleOutcomeService: apply campaign outcome
  CampaignBattleOutcomeService->>BattleCasualtyService: apply casualties
  BattleAutoresolveService-->>BattleScheduleService: return resolution result
Loading

Suggested reviewers: ryanbarlow97

Merge Risk: ⚪ Minimal · up to da05d

The identified feedback concern does not affect reachable close-vote responses, and naval auto-losses complete their campaign outcome. No actionable merge-blocking risk remains after normal checks.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to da05d

Autoresolve is now able to change regiments and territory without a live battle. The normal path has safeguards, but an interrupted or overlapping resolution could leave a war in an inconsistent state or apply an outcome more than once.

Retained concerns

  • Medium · security · inferred: The new multi-step autoresolve path has no visible one-time claim or atomic boundary around casualties, campaign progression, and prepared-battle cleanup. Interruption, retry, or overlapping calls could apply a war outcome twice or leave persisted losses without a completed transition.
Security review details

Security Blast Radius

  • inferred — A repeated or partial outcome would affect the participating war, its factions' regiment commitments, and potentially its occupation or progression state. The inspected path does not establish exposure to other tenants, credentials, or infrastructure.

Security Findings and Attack Paths

  • inferred — Participants can influence vote state, but no direct player-controlled retry or concurrency exploit is established. The supported integrity risk is that eligible resolution calls can reach persistent effects without a visible one-time transition guard; interruption or overlap could repeat those effects.

Trust Boundaries and Controls

  • observed — Normal vote close requires an active war in VOTING and a due close unless forced. The resolver additionally rejects inactive wars, started prepared battles, and missing provinces; it does not itself enforce a phase or quorum requirement.

Resilience and Maintainability Implications

  • observed — Faction casualty persistence occurs inside outcome application, before prepared-battle deletion and the tick's subsequent war persistence. These writes are not visibly coupled by a transaction in the inspected flow.

Hardening Proposals

  • proposed — Make resolution a per-war, per-battle one-time transition and define recovery for failure between casualty persistence, campaign advancement, war persistence, and prepared-battle deletion.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 85 functions across 20 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: campaign battles now use simulation-based autoresolution.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 4.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 85 functions across 20 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

I’m a rabbit with a battle plan,
One more vote, then resolve we can.
Luck rolls softly, losses land,
Campaigns turn by paw and hand.
The closing hour stays out of sight,
I nibble clover through the night.

Comment @coderabbitai help to get the list of available commands.

@Drefvelin
Drefvelin force-pushed the fix/war-offensive-battle-sides branch from 07c13bc to e41f35d Compare September 26, 2026 12:37
@Drefvelin
Drefvelin force-pushed the feat/war-autoresolve-simulation branch from 0634fcd to d57480f Compare September 26, 2026 12:37
@Drefvelin
Drefvelin force-pushed the fix/war-offensive-battle-sides branch from e41f35d to 670bdb9 Compare September 26, 2026 13:16
@Drefvelin
Drefvelin force-pushed the feat/war-autoresolve-simulation branch 2 times, most recently from 0c47c30 to 206d40a Compare September 26, 2026 13:28
Base automatically changed from fix/war-offensive-battle-sides to main September 26, 2026 13:36
Autoresolve no longer starts a live battle. Each side's committed
regiments become lives, a luck roll scales them, and Lanchester's
square law picks the winner and the winner's losses. The loser loses a
set share of its lives. Losses go through the normal casualty and
outcome path, with the offensive pinned so fuel is charged correctly.

A stalled battle vote postpones once (war.battle_voting.max_postponements)
and then autoresolves. Legacy AUTORESOLVE_PENDING wars are resolved on
the next tick. Battle start hours are now 21 to 23; the window end hour
is not a start time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Drefvelin
Drefvelin force-pushed the feat/war-autoresolve-simulation branch from 206d40a to da05dc6 Compare September 26, 2026 13:36
@Drefvelin
Drefvelin merged commit 3d267ec into main Sep 26, 2026
2 checks passed
@Drefvelin
Drefvelin deleted the feat/war-autoresolve-simulation branch September 26, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants