Skip to content

feat: added parallel face solver#15

Merged
kevinthegreat1 merged 18 commits intokosmolot-mods:mainfrom
themorlock:feat/auto-solver
Mar 5, 2026
Merged

feat: added parallel face solver#15
kevinthegreat1 merged 18 commits intokosmolot-mods:mainfrom
themorlock:feat/auto-solver

Conversation

@themorlock
Copy link
Copy Markdown
Contributor

Add /geodesy solve command for automatic flying machine layout

Summary

This PR adds a new /geodesy solve command that automatically calculates optimal slime/honey block placement and mob head markers after running /geodesy project. This eliminates the tedious manual process of figuring out island layouts.

Before: Manual placement of sticky blocks and mob heads (Steps 5-6 in the old workflow)
After: Run /geodesy solve and it's done automatically

Usage

/geodesy solve                    # Default: 5s timeout, cost=1.0
/geodesy solve <timeout>          # Custom timeout (1-300 seconds)
/geodesy solve <timeout> <cost>   # Custom timeout and cost (1.0-12.0)

The cost parameter controls the coverage vs. machine count tradeoff:

  • cost=1.0 → More machines, higher coverage
  • cost=4.0 → Fewer machines, may skip some pumpkins

Algorithm Overview

The solver finds optimal "islands" (connected groups of 4-12 blocks) to cover harvest cells.

Constraints

┌─────────────────────────────────────────────────┐
│  1. Islands must be 4-12 blocks                 │
│  2. Each island must contain an L-shape         │
│  3. Adjacent islands must alternate materials   │
│  4. L-shapes of different islands can't touch   │
│  5. Islands can't cover blocked cells           │
└─────────────────────────────────────────────────┘

Valid L-Shape Examples

  ██░      ░██      ██░░
  ░░░      ░░░      ░░░░
  ░░░      ░░░      ░░░░

  "L"    "reverse"  "extended"

Two-Coloring for Adjacent Islands

   SSSSS          S = Slime
   S   HHHH       H = Honey
   S   H
   S   H          Adjacent islands must use
       H          different materials!

Algorithm Steps

  1. Precompute shapes: BFS from each harvest cell to find all valid island shapes (4-12 cells with L-shape)
  2. Sort by scarcity: Process harvest cells with fewest possible shapes first (better pruning)
  3. Backtrack: Try placing islands, checking constraints, track best solution
  4. Score: harvest_covered - (island_count × cost)

Optimization

  • Faces are solved in parallel using a thread pool
  • BitSet for O(1) overlap detection
  • Early pruning when remaining cells can't beat current best

Files Changed

File Change
solver/IslandFaceSolver.java New - Core algorithm
solver/FaceGrid.java New - Input representation
solver/SolverResult.java New - Output with islands
solver/SolverConfig.java New - Timeout/cost config
GeodesyCore.java Added geodesySolve() + helpers
GeodesyFabricMod.java Registered command
README.md Documentation

Example Output

Solving 3 face(s) in parallel...
  EAST: 85% coverage (17/20), 24 blocks, 1243ms
  SOUTH: 100% coverage (15/15), 20 blocks, 892ms
  UP: 92% coverage (11/12), 16 blocks, 456ms
Solve complete. Run /geodesy assemble when ready.

@kosma
Copy link
Copy Markdown
Collaborator

kosma commented Jan 12, 2026

I'm speechless, so here's a dog instead:

image

@kosma
Copy link
Copy Markdown
Collaborator

kosma commented Jan 12, 2026

Also @kevinthegreat1 care to have a look if everything looks in place? If so, we can merge.

@themorlock
Copy link
Copy Markdown
Contributor Author

Haha thanks. I started this like 6 months ago in Python but forgot all about it. Had to use AI to help bring it into Java.

Feel free to try it out.

@kevinthegreat1
Copy link
Copy Markdown
Collaborator

Thanks, I will review this soon.

Is the coverage optimal? I think we should guarantee maximum/optimal coverage first and then the number of flying machines. Of course having options for this is fine.

Also, I think we should hook this up with the game tests. I might improve the tests as well.

@themorlock
Copy link
Copy Markdown
Contributor Author

The coverage is not necessarily optimal but the algorithm employs some heuristics to try to find good candidates.

It starts by pre-computing all valid islands (between 4 and 12 blocks, containing an L-shape, not covering any obsidian, etc.), then for each pumpkin, it assigns a score that counts the number of possible islands that includes it.

Then it just uses backtracking with a heuristic (it tries to cover pumpkins that are part of the least possible islands first).

This can still take a long time to find an optimal solution so the algorithm uses a score for each valid solution it finds. The score is simply Score = (Pumpkins Covered) - (Cost * Number of Islands).

The score can be changed to optimize for efficiency or coverage. A lower score indicates that the user doesn't mind extra flying machines to cover just a few more pumpkins (more coverage), while a higher score indicates that the user wants to build fewer machines (more efficiency).

The algorithm also has a timeout to prevent running forever (it returns best solution it finds in the time).

@themorlock
Copy link
Copy Markdown
Contributor Author

I have noticed that sometimes it'll return a solution that could easily be improved by just adding one more slime/honey block. This is probably due to the timeout and the algorithm not having enough time to backtrack all the way to replacing that particular island with a larger one.

I could probably fix this by adding another pass at the end that takes the best solution and for each island in it, looks for a larger island valid island that encompasses it.

Copy link
Copy Markdown
Collaborator

@kevinthegreat1 kevinthegreat1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pr. Please let me know if you have questions for any of these suggestions. I am also happy to implement some of these if you are unfamiliar.

Also, primitive data structures can be replaced with fastutil. I will also work on tests for this.

@themorlock
Copy link
Copy Markdown
Contributor Author

Thanks, will take a look soon.

I don't have much fabric experience so this will be good chance to learn more :)

@themorlock
Copy link
Copy Markdown
Contributor Author

Went ahead and fixed all the issues you found. I went with your suggestions on most of them except the ones I commented on.

@kevinthegreat1
Copy link
Copy Markdown
Collaborator

I also tried sorting faces by descending clusters covered and then by the cells count, but that actually seemed to give me worse results.

ObjectSet<IntSet> seenShapesGlobal = new ObjectOpenHashSet<>();

for (int tIdx = 0; tIdx < targets.size(); tIdx++) {
possibleShapes.put(tIdx, new ObjectRBTreeSet<>(SHAPE_PRIORITY_COMPARATOR));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've found a bug. This could be overwriting existing shapes found for tIdx.

Copy link
Copy Markdown
Collaborator

@kevinthegreat1 kevinthegreat1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@kevinthegreat1 kevinthegreat1 merged commit baecb8e into kosmolot-mods:main Mar 5, 2026
1 check passed
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.

3 participants