entelect-challenge
Entelect Challenge — Photospheria

Project Overview
The Entelect Challenge — Photospheria project is a Python-based optimisation system designed to solve a simulated ecosystem problem. The objective is to generate a planting schedule that produces the highest possible score after the simulated world has run for a fixed number of ticks. Each schedule specifies when and where different plant species should be placed on a grid. The organisers then execute the schedule using a private simulator and evaluate only the final state of the ecosystem.
The project was developed as part of the Entelect Challenge and focuses on a problem that combines simulation, optimisation, reverse engineering, and experimental analysis. Rather than simply writing a program that places plants on a grid, the project required reconstructing the behaviour of a hidden cellular-automaton simulator and then optimising planting decisions against that model.
The Level 1 solution achieved a recorded score of 274,709,453. The repository was subsequently extended to support the more complex Level 2 environment, which introduced animals, world events, species unlocks, and a much larger search space.
The Challenge
The Photospheria problem is an example of an open-loop control problem. The solver submits a complete schedule in advance and does not receive feedback about the world during execution. The final score depends on the number of occupied cells, the diversity of the plant species that survive, and how long the plants remain alive.
The ecosystem is represented as a grid containing different terrain and soil types. Each plant species has its own characteristics, including maturity time, spread rate, spread geometry, preferred soil, invasiveness rank, and special rules. Some plants spread through neighbouring cells, while others are affected by shade, seasons, nutrient depletion, or competition from other species.
This meant that an apparently sensible schedule could perform badly. Planting equal numbers of each species might maximise diversity at the time of placement, but natural spreading could cause one species to overtake the others before the final score was calculated. Similarly, planting too early could cause plants to die from nutrient depletion before the final tick.
Reconstructing the Hidden Simulator
The most significant technical challenge was that the organisers’ simulator was not available. The project therefore implemented an independent reconstruction in src/sim.py. This simulator models the ecosystem as a sequence of grid states, updating plant placement, seasonal behaviour, spread, maturity, nutrient depletion, death, shade, competition, animals, events, and unlock conditions.
The simulator was designed around a configurable Rules object. This allowed uncertain interpretations of the challenge specification to be tested independently. For example, the implementation supports multiple interpretations of competition, spread timing, shade behaviour, nutrient drain, and species unlocks. Instead of hard-coding one interpretation, these mechanics could be switched on or off during calibration experiments.
This design was important because several parts of the challenge documentation were ambiguous. The project could compare possible interpretations of the rules and measure how much each one affected the final result. It also made the simulator easier to extend when moving from Level 1 to Level 2.
Determinism was treated as a core requirement. The challenge required the solver to produce the same output whenever it received the same input. To support this, the project avoided unseeded randomness, wall-clock-based search limits, and unsorted set iteration. Tie-breaking rules were explicitly defined using plant indices and invasiveness ranks so that results would not vary between machines or Python processes.
Independent Validation and Scoring
The project separates schedule generation, simulation, scoring, and validation into different components. This was done to prevent one incorrect assumption from being repeated throughout the entire system.
The verify.py script independently checks whether a generated schedule is valid. It does not import the solver or simulator. Instead, it parses the level file and reimplements the relevant constraints, including coordinate bounds, terrain compatibility, soil compatibility, plant unlocks, duplicate placements, tick limits, and the maximum of 20 placements per tick.
This independent validation layer was especially important because the organiser’s engine silently ignores some invalid placements. For example, locked plants may be ignored, and only the first 20 placements in a tick may be applied. The validator reports these problems before submission, reducing the risk of a schedule appearing valid locally while producing a different result on the platform.
The scoring implementation is stored in src/score.py. It accepts a plain representation of the final grid and calculates the diversity, coverage, longevity, and final score components independently of the simulator. It also supports parameter sweeps across different possible values of the scoring exponents. This allowed candidate schedules to be compared based on their average or worst-case performance rather than only their best possible result.
The project’s tests reinforce this separation. They include hand-calculated scoring examples, empty-grid cases, single-species entropy tests, NumPy and pure-Python comparisons, and architectural checks confirming that the scorer and validator do not import the simulator.
The Level 1 Strategy
Level 1 used a 50-by-50 grid with 500 ticks. Although the complete plant catalogue contained 31 species, analysis of the unlock conditions showed that only five species were available in this level: Grass, Rose Bush, Dwarf Sunflower, Lavender, and Oak Tree.
The world contained 1,800 cells that could be used by these starting species. The remaining cells were clay, water, or stone and could not be planted. This created a maximum coverage of 72 percent of the total grid.
At first, equal planting appeared to be the logical solution because the scoring function rewards balanced species proportions. However, experiments showed that equal starting populations did not remain equal. The species had different maturity times, spread patterns, and competitive strengths, causing the final ecosystem to become unbalanced.
To investigate this, the project tested all 120 possible planting orders. The results showed that exposure time was more important than the initial invasiveness ranking. Grass matured quickly and could spread for a long period if planted too early. The final strategy therefore placed Oak Tree, Rose Bush, Lavender, Dwarf Sunflower, and Grass in that order, ensuring that Grass had the least time to expand before the final scoring tick.
The solver also used unequal seed weights. Aggressive species such as Oak Tree and Dwarf Sunflower were given smaller starting territories, while other species received more cells. This helped reduce the size of their spread frontiers and made the final distribution more stable.
Spatial Planning
The solver did not treat every cell as an independent decision. Instead, it divided the usable area into compact connected regions and assigned each region to a plant species.
This approach reduced the number of exposed boundaries between competing species. Compact regions were more predictable because a species had fewer opportunities to spread into neighbouring territories. The partitioning algorithm used deterministic breadth-first searches, sorted candidate cells, and explicit fallback behaviour for disconnected areas.
The final schedule also used carefully selected planting windows. Plants were placed near the end of the simulation so they would still be alive during scoring. At the same time, the schedule avoided leaving a long gap between the last placement and the final tick, because mature plants could spread into neighbouring cells during that period and damage the balance between species.
The experiments showed that a short final “tail” was important. One schedule with a ten-tick gap before scoring experienced significant redistribution, while a schedule with a much shorter gap preserved a better balance of species.
Calibration Through Experimentation
The repository contains a number of experiments used to evaluate uncertain mechanics. The Level 1 world was particularly useful as a calibration environment because its stone walls, soil regions, and water areas made it possible to isolate specific behaviours.
The test suite checks that Grass spreads in a Manhattan diamond, that Rose Bush waits until maturity before spreading, that plants die after nutrient exhaustion, and that dead-matter cells regenerate nutrients at a different rate. It also tests Oak Tree’s shade radius, seasonal restrictions on Lavender, competition between Grass and Rose Bush, and the 20-placement-per-tick limit.
The project also used real platform submissions as experimental evidence. The first successful Level 1 submission achieved a score of 274,709,453. Two later attempts appeared theoretically attractive because they used more equal seed distributions, but both scored worse. These results demonstrated that the real simulator behaved differently from some early assumptions.
This led to an important change in methodology. The local simulator was not treated as a perfect predictor until it could reproduce known platform results. Instead, it was used as a ranking tool for comparing strategies. A proposed change was not considered trustworthy merely because it improved the local simulation.
Level 2 Planning
Level 2 introduced a larger 70-by-100 grid, animals, a Rain event, and a much more complex species unlock system. The number of potentially reachable species increased from five to approximately 28.
This changed the optimisation problem significantly. In Level 1, the solver could manually place enough plants to fill most of the usable grid. In Level 2, the 20-placement-per-tick limit meant that manual placement alone could not provide sufficient coverage. Natural spreading became essential.
The Level 2 strategy uses two phases. The first phase is a scaffolding phase, where the solver grows prerequisite species in order to satisfy unlock conditions. These plants may eventually die and contribute little to the final score, but they are necessary to unlock additional species.
The second phase is the final scoring garden. Near the end of the simulation, the solver places as many unlocked species as possible while respecting terrain, soil, shade, and dependency constraints. The goal is to finish with a diverse and balanced ecosystem that remains alive at the final tick.
The planning logic in src/plan.py converts nested unlock conditions into ordered stages. It supports logical conditions such as AND, OR, and NOT, as well as coverage thresholds, count requirements, species presence, world events, dead matter, and burnt soil. This allows the planner to generate a strategy from the data files rather than relying on a manually hard-coded list of species.
Submission and Reproducibility
The project includes a submission packaging tool called make_submit.py. It creates the required answer JSON file and source archive for each level.
Rather than compressing the current working directory, the script uses git archive. This ensures that only committed files are included in the submission. The script also warns about uncommitted changes and checks that the README is present before creating the archive.
The project runbook documents the complete workflow from a clean clone to a validated submission. A typical workflow is to install the dependencies, run the benchmark suite, generate a schedule, validate it independently, and package the committed source and answer files.
This process reduces the chance of submitting code that was not tested or of accidentally excluding important changes from the final archive.
Technical Lessons
The project demonstrated that model validation should come before large-scale optimisation. A search algorithm can make a flawed simulator look convincing by finding solutions that exploit its inaccuracies. For this reason, the project prioritised controlled experiments and real submission results before trusting local optimisation results.
Another important lesson was the value of treating ambiguous rules as parameters. By placing uncertain behaviour behind configuration flags, the project could evaluate different interpretations without rewriting the simulator.
The project also showed the importance of optimising structure rather than individual actions. Instead of searching every possible plant placement, the solver searched over planting order, seed weights, connected regions, timing windows, and unlock stages. This reduced the search space while preserving the most important strategic choices.
Finally, the project used failed results constructively. Lower-scoring submissions were not simply discarded; they were treated as evidence about the hidden simulator. This made it possible to identify the importance of competition, species territory size, and the timing of the final planting sequence.
Conclusion
The Entelect Challenge — Photospheria project is a practical example of optimisation engineering under uncertainty. It combines a deterministic cellular-automaton simulator, an independent validator, a standalone scoring system, graph-based unlock planning, spatial partitioning, numerical experimentation, and reproducible submission tooling.
The project’s strongest achievement was not only the Level 1 score of 274,709,453. It was the development of a disciplined workflow for solving problems where the real objective function is hidden and local models must be validated through experimentation.
By separating simulation from validation, treating ambiguous rules as configurable hypotheses, and using real evaluation results to refine the model, the project created a robust foundation for solving increasingly complex ecosystem levels. It demonstrates how reverse engineering, experimental design, and algorithmic optimisation can be combined to solve a challenging black-box simulation problem.
Got a system that outgrew its first design?
Let's talk
