in ,

Building a command line tool to design a farm layout in Stardew Valley, Hacker News

Return to Blog

By John Lekberg on February , .




This week’s post will cover a command line tool that helps you play the video game


Stardew Valley .

Stardew Valley is a farming game (like Harvest Moon . You can manually water your crops, or you can use sprinklers to automate the process.

I wrote a Python script, sprinkler-layout , that designs a layout of sprinklers for me, for a given number of sprinklers (eg sprinklers). The goals of the layout are:

water as much land as possible. have a reasonably small perimeter.

    sprinkler-layout

  #! / usr / bin / env python3  import itertools   class Layout:     “A layout of sprinklers on a grid.”      def __init __ (self):         self._sprinklers=set ()         self._watered_squares=set ()      @classmethod     def generate (cls, *, num_sprinklers, coordinates):         Generate a layout, given:          - how many sprinklers to place.         - which positions to attempt to place them at.          “”         layout=cls ()          while layout.count_sprinklers ()  -radius:             yield x, y             y -=1         while x> -radius:             yield x, y             x -=1         while y   
  $ sprinkler-layout --help    
  Usage: sprinkler-layout [-h] --sprinklers N  generate a layout of sprinklers for Stardew Valley.  optional arguments:   -h, --help show this help message and exit   --sprinklers N the number of sprinklers to place    

I'm starting a new farming season in Stardew Valley and I have sprinklers available. I use sprinkler-layout to design a layout:

  $ sprinkler-layout --sprinklers 32    
  sprinklers  watered squares  (x)  squares, including perimeter wall  square perimeter map of sprinklers (3 by 3 blocks)   ... ... ... ... ..  ... ... ... #.  .. # .. # ... ... ..   ... ... #. .. # ..  ... # .. ... # .. ..  #. .. # ... ... ..   ... ... .. # .. # ..  ... # .. # .. ... ..  #. ... ... #. ..   ... .. # .. # ... ..  ... # .. ... ... #.  ... ... # .. # .. ..   .. # ... ... .. # ..  .... #. ...  ... ... ... ... ..   

Then:

    I construct the required materials squares of walls for the perimeter).
        I clear out enough space for the layout (a grid of by (squares).
            I place sprinklers as shown in the layout ( #
              ).
                I place the walls around the perimeter.

                  I use a custom class, Layout , to represent a sprinkler layout. Layout manages the internal state of:

                  Where sprinklers have been placed.
                    Which positions are watered by the placed sprinklers.
                    • Layout (has a) (class method) , generate , that attempts to position sprinklers by choosing from given positions. generate uses a greedy strategy to place the sprinklers:

                      • If I can place a sprinkler at this position, do it.
                    • I check if I can place a sprinkler by using sets of coordinates and checking that these sets are disjoint :

                        the set of the new sprinkler and its watered squares.
                          the set of already placed sprinklers and their watered squares.
                      • I have a generator function, spiral_coordinates , the generates positions in a spiral that looks like this: (starting from the center)

                         ( ^ ^ ^ v v>>> ^ ^ v>>>>> ^> ...   

                        I use this technique because it designs good enough layouts for me. spiral_coordinates is simple to implement and keeps the overall perimeter of the layout small.

                        The report function, print_report , computes a bounding box that encloses:

                            the sprinklers that have been placed.
                              the squares that are watered by the placed sprinklers.

                                Then, I take into account a 1 square thick perimeter wall and report:

                                The dimensions of the bounding box.
                                  The perimeter of the bounding box.

                                    The report generates a map of the placed sprinklers and partitions it into chunks:

                                 ... ...  ... ... ... #.  .. # .. # ... ... ..   ... ... #. .. # ..  ... # .. ... # .. ..  #. .. # ... ... ..   ... ... .. # .. # ..  ... # .. # .. ... ..  #. ... ... #. ..   ... .. # .. # ... ..  ... # .. ... ... #.  ... ... # .. # .. ..   .. # ... ... .. # ..  .... #. ...  ... ... ... ... ..   

                                I find the map harder to read without the partitioning:

                                 ...... ............ #. .. # .. # ........ ....... # ... # .. ... # ..... # .... # ... # ........ ........ # .. # .. ... # .. # ....... # ........ # ... ..... # .. # ..... ... # ........ #. ...... # .. # .... .. # ........ # .. .... # .. # ...... ..............    

                                This week's post covered a Python script that assists people playing Stardew Valley by designing a layout of sprinklers. You learned about:

                                  Using Python classes to manage internal state.
                                    Using Python sets to check if two sets of positions are disjoint.
                                  Using a simple greedy strategy to make decisions (placing the sprinklers).

                                  My challenge to you:

                                  Create a different way to generate coordinates than spiral_coordinates .

                                  For example, here's what a placement of 8 sprinklers looks like with spiral_coordinates :

                                   (Layout.generate)     num_sprinklers=8,     coordinates=spiral_coordinates () ) .print_report ()    
                                   8 sprinklers 42 watered squares  (x)  squares, including perimeter wall 62 square perimeter map of sprinklers (3 by 3 blocks)   ...  #. ....  ... # .. ...   ... ... # ..  #. #. ...  ...   ... # .. # ..  ...   

                                  And here's a placement of 8 sprinklers that tries positions only in a horizontal line:

                                    from itertools import count  Layout.generate (     num_sprinklers=8,     coordinates=((i, 0) for i in count ()) ) .print_report ()    
                                   8 sprinklers 42 watered squares 32 x 5 squares, including perimeter wall 66 square perimeter map of sprinklers (3 by 3 blocks)   ... ... ... ... ... ... ...  #. #. #. #. #. #. #. #.  ... ... ... ... ... ... ...    

                                  If you enjoyed this week's post, share it with your friends and stay tuned for next week's post. See you then!


      Read More ,

      What do you think?

      Leave a Reply

      Your email address will not be published. Required fields are marked *

      GIPHY App Key not set. Please check settings

      Don’t try to sanitize input – escape output, Hacker News

      Khalifa Fund For Enterprise Development signs US$100 million agreement supporting SMEs