I recently picked up a copy of my favorite game Stardew Valley again.If you don’t know the game, I can highly recommend it! You inherit a pixel farm andyou are in charge of everything. Crops, animals, fishing, mining and never forget to socialize.My plan was to shut off work for at least a few hours while playing. But at one pointyou inevitably start optimizing your farm. In most cases, the layout of crops.Aaaaaaaand that’s how you turn a farming game into an optimization problem you try to solve with R.
1 |
|
If you start a standard farm, this is what you can work with.
The first step is to get the layout into R. Luckily, I found areddit post,where someone posted the layout as an excel file. I converted the file into a matrix anda long data frame. The data frame is for plotting and the matrix for the actual optimization.
1 |
|
1 |
|
1 |
|
1 |
|
The green tiles are the ones we can use for farming. In theory, we could plant3414 crops there. But the tiles are totally unprotected against the nastycrows that would eat all your precious crops. We need scarecrows to protect them.
When you plant your crops, you should make sure that your crops fall into the range ofa placed scarecrow. No crow will ever touch anything there.
A single scarecrow can protect 248 tiles. And now we have our first optimization problem:Where should we place scarecrows onthe farm to maximize the protected area?
Now of course we could just blindly put them on the map until everything is covered.But this would a) reduce the number of tiles we can use for growing crops and b)result in a lot of unnecessary overlap between scarecrow ranges. So the goal is to maximize thecovered area and minimize the overlap of scarecrows. This is not an easy task though.If you want to place one crow somewhere on the farm, you have 3414 possibilitiesto do so. For two crows 5,825,991 and for three 6,626,093,764. So it is definitely not a viable option to try out allpossible placements, especially when the number of crows increases. We will not be ableto get the optimal solution, but we can try to come as close as possible.
Simulated annealing
There are certainly several heuristics we could usebut I decided to apply simulated annealing.The general idea is very simple. In each iteration, try out a new solution. If it isbetter then the old one, keep it. If it is worse, accept it with a certain probability.This second step is very important, since it allows us to get out of local maxima.The probability to accept worse solution decreases over time, since we assume that wecome closer to the “true” maximum. New solutions are of course not chosen randomly, but constructed fromprevious solutions. In our case, I decided to implement four potential alterations of the current solution:
-
add a scarecrow on a random unoccupied tile
-
delete a random scarecrow
-
teleport a scarecrow to a random unoccupied tile
-
move a scarecrow to random neighboring tile
This is the theory, now to the code.
R code
The code of the helper functions (add/delete/move/teleport scarecrows) can be found at the end of this post.
We can use the init_farm()
function to get an initial scarecrow layout
1 |
|
This initial solution covers 820 tiles. Now onto the optimization.
1 |
|
Note that this will run for a while (R and loops, you know). If you want to optimize the runtime,implementing it in C++ with Rcpp
might do the trick.
Below is the best layout found during the optimization.
1 |
|
The number of placed scarecrows is 18. 3119 tilesare uniquely protected and 88 are protected by more than one scarecrow.That leaves 207 tiles outside of the crow ranges.So we can now safely plant 3207 crops without crows attacking them.But wait, there is more! We also need to water them…
I am sure nobody wants to water those crops manually, so we need to place sprinkler.To optimize their placement, we do exactly the same as for scarecrows. Our optimizationgoal is to maximize the number of protected and watered tiles.
1 |
|
This is the final layout. The dark blue tiles are the ones that are watered and protected by a scarecrow.light blue tiles are only watered, dark green tiles only protected and light green ones are neither.
1 |
|
This layout gives us 3144 watered tiles with 180 sprinkler and2998 of those are protected by a scarecrow.
If you run this code by yourself, you will notice that you might get different (even better!)results. Simulated annealing is not deterministic, hence it produces different nearlyoptimal solutions in each run.
1 |
|
1 |
|
Related