Predator-Prey Ecosystem Simulation in Code: Modeling Grass, Rabbits, and Wolves

    This article presents a practical agent-based modeling workflow for a grass–rabbit–wolf predator-prey ecosystem. It covers world and entity design, energy and reproduction rules, time-step updates, a pseudocode implementation sketch, parameter experiments and typical outcomes, visualization options, engineering practices such as randomness and performance management, and how to use language models as coding assistants while validating results.

    1. Why Use Simulation to Understand Ecosystems

    The relationship between predator and prey populations is a classic problem in ecology. On a grassland, more rabbits give wolves more food, and the wolf population expands. As wolves expand, rabbits decline, food becomes scarce for wolves, and the wolf population begins to fall again. Textbooks often describe this cycle with differential equations, such as the Lotka-Volterra model. The equations are clear, but they make it difficult to show intuitively how individual behavior, spatial distribution, and random fluctuations affect the outcome.

    Agent-based simulation offers another perspective. Instead of solving global equations directly, it assigns simple rules to each individual and lets the system evolve over time. The overall patterns an observer sees emerge from many local interactions. This approach is well suited to answering questions such as “What happens to the whole system if a certain parameter changes?” and it is convenient for repeated experiments in teaching and research.

    The goal of this article is to provide a practical modeling workflow. Readers can follow the steps to build their own simulator and draw conclusions through parameter experiments.

    2. Modeling Approach: From Rules to State

    2.1 Define the World and Entities

    The first step in a simulation is to define space and entities. This example uses a fixed-size grid, such as 100 × 100 cells. The boundary can be treated as “wrap-around,” meaning an individual that leaves from the right side enters from the left. This reduces boundary effects.

    The world contains three types of objects:

    • Grass: occupies cells, can grow and be eaten, and does not move actively.
    • Rabbits: positioned by cell, move, eat, reproduce, and die.
    • Wolves: also move, eat, reproduce, and die, with rabbits as prey.

    Each entity type needs to record position, energy, and alive status. Energy is the key variable linking “eating” and “survival”: eating provides energy, activity consumes energy, and an individual dies when energy is exhausted.

    2.2 Design Energy and Reproduction Mechanisms

    The energy model determines the basic dynamics of the system. A reasonable design usually includes the following elements:

    1. Energy gain: a rabbit gains a fixed amount of energy by eating grass, and a wolf gains more energy by eating a rabbit.
    2. Energy cost: each individual consumes a certain amount of energy at every time step, and moving costs more than staying still.
    3. Reproduction condition: energy is above a threshold, and an offspring is produced with a certain probability. After birth, part of the parent’s energy is allocated to the offspring.
    4. Death condition: energy is less than or equal to zero, and the individual is removed from the world.

    Note that these values do not have a single correct answer. They are assumptions of the model and should match the research question. For example, if the goal is to study how wolf energy expenditure affects population persistence, the per-step cost for wolves should be kept as a tunable parameter.

    2.3 Advance Through Time Steps

    The simulation advances in fixed time steps. The update order at each step affects the results. A common approach is as follows:

    1. Grass grows randomly: empty cells grow grass with a certain probability.
    2. Rabbits act: move one random step, eat grass if the current cell has grass, deduct energy, and check death and reproduction.
    3. Wolves act: preferentially move toward the nearest rabbit within their field of view, prey on it if adjacent, and then similarly check death and reproduction.
    4. Record statistics: record the number of each entity type for later analysis and plotting.

    A fixed update order ensures reproducibility. If fairness needs to be introduced, the action order of individuals can be randomly shuffled at each step, and the results can then be observed to see whether they remain stable.

    3. Core Implementation Sketch

    The following code gives the skeleton of the main loop in pseudocode style to make the logic easier to understand. In an actual implementation, data structures and exception handling can be added according to the language being used.

    1. # Global parameters
    2. GRASS_GROW_PROB = 0.05 # Probability that an empty cell grows grass each step
    3. RABBIT_GAIN = 4 # Energy a rabbit gains from eating grass once
    4. WOLF_GAIN = 10 # Energy a wolf gains from eating one rabbit
    5. RABBIT_COST = 1 # Energy a rabbit consumes per step
    6. WOLF_COST = 2 # Energy a wolf consumes per step
    7. REPRO_THRESHOLD = 12 # Energy threshold required for reproduction
    8. REPRO_PROB = 0.3 # Reproduction probability when the threshold is met
    9. def step(world):
    10. # 1. Grass growth
    11. for cell in world.empty_cells():
    12. if random() < GRASS_GROW_PROB:
    13. cell.grass = True
    14. # 2. Rabbit actions
    15. for rabbit in shuffle(world.rabbits):
    16. rabbit.move_random(world)
    17. if world.cell_at(rabbit).grass:
    18. world.cell_at(rabbit).grass = False
    19. rabbit.energy += RABBIT_GAIN
    20. rabbit.energy -= RABBIT_COST
    21. if rabbit.energy <= 0:
    22. world.remove(rabbit)
    23. elif rabbit.energy >= REPRO_THRESHOLD and random() < REPRO_PROB:
    24. world.spawn_child(rabbit)
    25. # 3. Wolf actions
    26. for wolf in shuffle(world.wolves):
    27. prey = world.nearest_rabbit(wolf, radius=3)
    28. if prey:
    29. wolf.move_toward(prey)
    30. else:
    31. wolf.move_random(world)
    32. if world.rabbit_at(wolf.position):
    33. world.remove(world.rabbit_at(wolf.position))
    34. wolf.energy += WOLF_GAIN
    35. wolf.energy -= WOLF_COST
    36. if wolf.energy <= 0:
    37. world.remove(wolf)
    38. elif wolf.energy >= REPRO_THRESHOLD and random() < REPRO_PROB:
    39. world.spawn_child(wolf)
    40. # 4. Record statistics
    41. world.record(len(world.grass), len(world.rabbits), len(world.wolves))

    Several points in the code deserve attention. First, the action order of individuals is shuffled to prevent early actors from gaining a long-term advantage. Second, predation occurs after movement, which keeps the logic simple and easy to check. Third, statistics are recorded at the end of each step, providing data for later plotting.

    4. Parameter Experiments: Three Typical Outcomes

    After adjusting parameters, the system may show different trajectories. The following analysis is based on reasoning about the model structure; readers should verify it with their own run results.

    4.1 Ecological Balance

    When rabbit reproduction speed, grass growth speed, and wolf energy consumption are in appropriate proportion, the three population sizes fluctuate within a certain range. As the wolf population increases, rabbits decrease. Wolves then lose individuals because of insufficient food, and rabbits recover as pressure declines. This back-and-forth relationship keeps the system in a dynamic equilibrium over the long term.

    In practice, whether the system is balanced should be judged from statistical features over a longer time scale, such as the mean and fluctuation amplitude of population sizes, rather than from the value at a single moment.

    4.2 Wolf Extinction

    If the per-step energy cost for wolves is too high, or if the rabbit population is too small for wolves to obtain enough energy, wolves will decline before the other species. After the wolf population falls, rabbits lose their predator and may briefly increase, but grass will then come under pressure. The total biomass of the world may decline, and the system is not necessarily stable.

    This result suggests an important modeling point: predator suppression of prey is an important factor in maintaining system stability. If this link is removed, the model loses its ecological meaning.

    4.3 Rabbit Overpopulation

    If the rabbit reproduction rate is too high and grass growth is fast enough, the rabbit population will increase rapidly. The wolf population will also rise because of abundant food, but reproduction requires more energy, so growth usually lags. At some stage, the rabbit population exceeds the carrying capacity of grass, grass is heavily consumed, and rabbits then die from starvation and decline.

    Therefore, “overpopulation” is often only a periodic peak. If the model lacks a regeneration constraint for grass, rabbit overpopulation will continue indefinitely, which often reflects a model design problem rather than an ecological law.

    4.4 Recommendations for Experimental Design

    To draw reliable conclusions, it is recommended to organize experiments as follows:

    • Fix other parameters and change only one variable at a time, such as rabbit reproduction probability.
    • Run each parameter set with multiple random seeds and analyze the distribution of outcomes rather than relying on a single result.
    • Record time series, calculate average population sizes, peaks, and cycle lengths, and support judgments with data.
    • Keep the parameter table and random seeds so that experiments can be reproduced.

    5. Dynamic Visualization Output

    Simulation results need to be presented as images so that the evolutionary process can be observed. There are two common approaches.

    The first is grid animation. Draw one frame per time step, using different colors for grass, rabbits, and wolves. The number of frames can be set to several hundred or several thousand, and then the frames can be combined into an animated image or short video. This approach is intuitive and suitable for showing spatial distribution and individual clustering.

    The second is a line chart. During the simulation, draw three population curves synchronously, with time steps on the horizontal axis and individual counts on the vertical axis. The curves clearly show cycles, amplitudes, and decay trends, making them suitable for quantitative analysis.

    The two approaches can be used together: grid animation helps understand the process, while line charts help compare parameters. When plotting, pay attention to color contrast so that readers with color-vision difficulties can still distinguish species.

    6. Engineering Considerations

    6.1 Randomness Management

    Simulations rely on many random decisions. Without a fixed random seed, the same parameters may produce very different results, making it difficult to determine the effect of a parameter. It is recommended to record the seed for each run and note it in reports.

    6.2 Performance Optimization

    As the number of individuals increases, the cost of iterating over each one rises. Common optimizations include using a two-dimensional array to store cell states, using a spatial index to speed up “find the nearest rabbit,” and avoiding repeated object creation inside loops. When the number of individuals reaches tens of thousands, these optimizations can significantly affect runtime.

    6.3 Parameter Validation

    It is necessary to check parameter ranges before running. For example, reproduction probability should be between zero and one, and energy cost should be non-negative. If parameters are set outside a reasonable range, a clear error message should be given rather than silently running.

    6.4 Result Validation

    Whether a model is reasonable needs to be confirmed through validation. You can check conservation, such as whether energy flow at each step is consistent with the rules; you can also check extreme cases, such as whether the rabbit population declines as expected when grass growth speed is zero.

    7. Using Language Models to Assist Simulation Coding

    In recent years, large language models have been able to generate first drafts of simulation code from natural-language descriptions. When using such tools, treat them as efficiency tools rather than the final source of answers.

    Tasks suitable for handing to a model include: generating basic data structures, supplementing plotting code, translating pseudocode into a target language, and adding comments to functions.

    Tasks that require human review include: confirming that the rule description matches the generated code, checking boundary conditions and random logic, running tests and verifying statistics, and determining whether conclusions are determined by model design rather than code errors.

    A practical workflow is: first have a human write clear rule descriptions and a parameter table, then let the model generate code, then write several unit tests, such as “rabbits do not grow when grass is zero,” and finally run the simulation and compare results. This division of labor takes advantage of the tool’s speed while ensuring the reliability of conclusions.

    8. Conclusion

    The core of predator-prey simulation lies in rule design. The energy, reproduction, and death rules of the three entity types—grass, rabbits, and wolves—determine the direction of the system. By adjusting parameters, you can observe different outcomes such as the system approaching balance, predator extinction, or prey overpopulation, and each outcome corresponds to a set of mechanisms that can be explained.

    Readers are encouraged to follow the steps in this article to implement a minimal version, first getting the system running, and then gradually adding visualization, parameter experiments, and statistical analysis. This process from simple to complex can both deepen understanding of ecosystems and build general experience in modeling complex systems.