The Farmer Was Replaced

The Farmer Was Replaced

Nash Feb 12 @ 11:22am
Efficient Pumpkin Patch
Wanted to share my efficient pumpkin solution for growing max sized pumpkins!

### Private Members farm_ledger = {} world_size = get_world_size() ### Methods def move_to(x, y): while(get_pos_x() > x): move(West) while(get_pos_y() > y): move(South) while(get_pos_x() < x): move(East) while(get_pos_y() < y): move(North) def work(): if (get_ground_type() != Grounds.Soil): till() if (get_water() <= 0.7): use_item(Items.Water) if (get_entity_type() != Entities.Pumpkin): plant(Entities.Pumpkin) def check_row(row): ret = True move_to(0,row) for i in range(world_size): x, y = (i, row) farm_ledger[(x,y)] = get_entity_type() if farm_ledger[(x,y)] != Entities.Pumpkin: ret = False move(East) return ret def plant_row(row): move_to(0,row) for key in farm_ledger: if farm_ledger[key] != Entities.Pumpkin: x, y = key move_to(x, y) work() plant(Entities.Pumpkin) ### PROGRAM clear() while True: for i in range(world_size): while check_row(i) == False: plant_row(i) check_row(i) farm_ledger = {} harvest()
< >
Showing 1-12 of 12 comments
Yityar Feb 17 @ 3:21am 
2
here is how I did it
while True: c=0 y=0 d=get_world_size()**2 while y == 0: for a in range(get_world_size()): for b in range(get_world_size()): move(North) if can_harvest(): c = c+1 if c == d: harvest() y=1 else: plant(Entities.Pumpkin) c=0 move(East)
basically made it count out the pumpkins until the amount was equal to world size
Last edited by Yityar; Feb 17 @ 3:24am
I've just been replanting one time and keep on going. Gotta try these methods!
Fivizzz Feb 21 @ 5:55am 
I store coordinates to every square as tuples in a list.

Visit the entire list in sequence. If nothing there, plant; if growing pumpkin, skip; if fully grown pumpkin, remove coords from list.

When list empty, harvest()

Note:
sz = world size
grid.mv() moves the drone to (x,y) tuple using shortest path
>> x & y span 1 to world size, not 0 to world size -1; personal preference.
settings.wthr = 0.75 (watering threshold)

#build coord list coord = [] for nx in range(sz): for ny in range(sz): coord.append((nx+1,ny+1)) #while coord not empty while len(coord)>0: for j in range (len(coord)-1, -1, -1): #decrement to preserve indexes x, y = coord[j] grid.mv(x, y) if get_ground_type()!=Grounds.Soil: #till if needed till() if get_entity_type()!=Entities.Pumpkin: #plant if no pumpkin harvest() plant(Entities.Pumpkin) if get_water()<settings.wthr:#water if needed use_item(Items.Water) if can_harvest(): coord.remove((x, y))#remove square from list #next square #when all squares confirmed good harvest()
Last edited by Fivizzz; Feb 21 @ 6:21am
acters Mar 19 @ 5:53am 
This is how I made my pumpkin code, I simply made a list and popped and checked each one iteratively until all pumpkins were harvestable. To reduce costs, when there is only one location to check, it runs a compressed cycle without the movement code. Also, because fertilizer got massively nerfed with high upgrade costs and weird infection mechanic. I simply left it out in favor of having the increased speed from just watering the ground.(previously when fertilizer was gotten through trade function, I was able to make it extremely fast by just spamming it lol)

#clear() # optionally clear the board def move_to_location(current_pos,new_location): if current_pos[0] != new_location[0]: # move only when needed if current_pos[0]<new_location[0]: for i in range(new_location[0]-current_pos[0]): move(East) else: for i in range(current_pos[0]-new_location[0]): move(West) if current_pos[1] != new_location[1]: # move only when needed if current_pos[1]<new_location[1]: for i in range(new_location[1]-current_pos[1]): move(North) else: for i in range(current_pos[1]-new_location[1]): move(South) world_size = get_world_size() # infinitely harvest or check until "Power" item is out to switch over to harvest sunflower while num_items(Items.Carrot) > 1000: # move back to starting position for i in range(get_pos_x()): move(West) for j in range(get_pos_y()): move(South) planted_locations = [] # initialize the board for i in range(world_size): for j in range(world_size): if get_ground_type() != Grounds.Soil: till() while get_water() < 0.75: use_item(Items.Water) if get_entity_type() == None: plant(Entities.Pumpkin) planted_locations.append([i,j]) move(North) move(East) if len(planted_locations): # edge case when starting with full pumpkin # move back to the last position move(West) move(South) planted_location = planted_locations.pop() current_pos = planted_location while len(planted_locations): # loop until only one spot is left while get_entity_type() == None: # plant if no pumpkin while get_water() < 0.75: use_item(Items.Water) plant(Entities.Pumpkin) if not can_harvest(): # if it is not fully grown then recheck at a later time # since we don't use fertilizer for speed up, we rely on drone speed # especially when using Power # fertilizer is more efficient but runs out and not worth it planted_locations.insert(0,planted_location) planted_location = planted_locations.pop() move_to_location([get_pos_x(),get_pos_y()],planted_location) while not can_harvest(): # make sure last location is fully grown and harvestable if get_entity_type() == None: while get_water() < 0.75: use_item(Items.Water) plant(Entities.Pumpkin) harvest()
I handle a few edge cases when it comes to starting with a board already prefilled and drone not in the starting position
Last edited by acters; Mar 20 @ 1:16am
Please keep sharing your pumpkin patches!
I'm quite proud of what I've thrown together.

def smart_harvest(f): # Waits for plant to grow before harvesting. Fertilising can be enabled. if get_entity_type() != None: while not can_harvest(): if f == True: use_item(Items.Fertilizer) harvest() def smart_water(x): # Waters tile. Use x to set maximum hydration. while get_water() < x: use_item(Items.Water) def smart_till(x): # Switch ground type if get_ground_type() != x: till() def get_pumpkins(x, y): # grows and harvests pumpkins until y pumpkins in inventory while num_items(Items.Pumpkin) < y: a = get_world_size() for j in range(a): # Increment columns for k in range(a): # Increment rows if get_entity_type() != None: # Check for and harvest existing crops smart_harvest() smart_water(x) smart_till(Grounds.Soil) # Till if needed while not can_harvest(): # monitor growth and replace if necessary if get_entity_type() == None: plant(Entities.Pumpkin) move(North) move(East) harvest() # harvest megapumpkin


Probably room for improvement, but it gets the job done and plugs quite nicely into my main script.
owenz Apr 3 @ 2:08am 
Originally posted by Adolf Diddler:
I'm quite proud of what I've thrown together.

def smart_harvest(f): # Waits for plant to grow before harvesting. Fertilising can be enabled. if get_entity_type() != None: while not can_harvest(): if f == True: use_item(Items.Fertilizer) harvest() def smart_water(x): # Waters tile. Use x to set maximum hydration. while get_water() < x: use_item(Items.Water) def smart_till(x): # Switch ground type if get_ground_type() != x: till() def get_pumpkins(x, y): # grows and harvests pumpkins until y pumpkins in inventory while num_items(Items.Pumpkin) < y: a = get_world_size() for j in range(a): # Increment columns for k in range(a): # Increment rows if get_entity_type() != None: # Check for and harvest existing crops smart_harvest() smart_water(x) smart_till(Grounds.Soil) # Till if needed while not can_harvest(): # monitor growth and replace if necessary if get_entity_type() == None: plant(Entities.Pumpkin) move(North) move(East) harvest() # harvest megapumpkin


Probably room for improvement, but it gets the job done and plugs quite nicely into my main script.

Looks like you have redundancies in your code, not really needed but otherwise looks good.

Also, why do you have a conditional check in your smart_harvest which doesn't update each loop but is passed in? Should look more like

def smart_harvest(fertilize): # Waits for plant to grow before harvesting. Fertilizing can be enabled. if get_entity_type() != None: while fertilize and not can_harvest(): use_item(Items.Fertilizer) harvest()

As python goes left to right when checking conditionals. if you pass in false to the smart_harvest it will never attempt to even check the second condition and skipping straight to the harvest part, while also removing a not updating conditional from the looping structure.

Here is my take on this issue with me using your code to build on.

def smart_harvest(fertilize): # Waits for plant to grow before harvesting. Fertilising can be enabled. if get_entity_type() != None: while fertilize and not can_harvest(): use_item(Items.Fertilizer) harvest() def smart_water(req_level): # Waters tile. Use x to set maximum hydration. while get_water() < req_level: use_item(Items.Water) def smart_till(req_ground): # Switch ground type if get_ground_type() != req_ground: till() def move_self(tx, ty): ws = get_world_size() dx, dy = get_pos_x() - tx, get_pos_y() - ty ns = (None, South, North) we = (None, West, East) def inner_move(delta, move_dir): if abs(delta) > ws // 2: delta -= (delta / abs(delta)) * ws if delta == 0: return for i in range(0, delta, delta / abs(delta)): move(move_dir[delta / abs(delta)]) inner_move(dx, we) inner_move(dy, ns) return get_pos_x(), get_pos_y() def pos_to_xy(pos): world_size = get_world_size() return pos % world_size, pos // world_size def xy_to_pos(x, y): world_size = get_world_size() return x + y * world_size def generate_blank_pumpkin_patch(): world_size = get_world_size() blank_pumpkins = [] for i in range(0, world_size ** 2): blank_pumpkins.append(False) return blank_pumpkins def check_pumpkin(): return can_harvest() and get_entity_type() == Entities.Pumpkin def check_pumpkin_patch(patch): # Use is to restart pumpkin patch w/o replanting whole field pumpkins = patch for i_ in range(get_world_size() ** 2): if True != pumpkins[i_]: x, y = pos_to_xy(i_) move_self(x, y) pumpkins[i_] = check_pumpkin() return pumpkins def can_harvest_pumpkin_patch(patch): return patch[0] == True and len(set(patch)) == 1 def maintain_pumpkin_patch(req_water_level=0.75, req_fertilize=False): pumpkin_patch = check_pumpkin_patch(generate_blank_pumpkin_patch()) pos = get_pos_x() + get_pos_y() * get_world_size() dir = 1 while not can_harvest_pumpkin_patch(pumpkin_patch): if get_entity_type() != Entities.Pumpkin: smart_till(Grounds.Soil) plant(Entities.Pumpkin) elif not can_harvest(): smart_water(req_water_level) pumpkin_patch[pos] = check_pumpkin() pos += dir if pos >= get_world_size() ** 2 or pos < 0: dir *= -1 pos += dir if pumpkin_patch[pos] == True: continue x, y = pos_to_xy(pos) move_self(x, y) def get_pumpkins(req_water_level=0.75, req_fertilize=False): maintain_pumpkin_patch(req_water_level, req_fertilize) harvest() # harvest megapumpkin while num_items(Items.Pumpkin) < 100000: get_pumpkins(0.75)

Some more stuff going on but it is basically the same.

Improvments:

1. Initial state check as it might fail inside while attempting to gather pumpkins so we do this so we can resume.
2. Used a list of the positions as True/False to indicate if that position needs attention still and then only went to those positions
3. Added default values and renamed variables to improve readability
4. Changed the movement of the drone to scan back and forth to optimize the movement around the board
5. Implemented a move function which does the shortest route to the wanted move

There are still improvements to be done on this code, was just keyboard banging it out so it had some new ***FEATURES***!
Last edited by owenz; Apr 3 @ 2:09am
Originally posted by owenz:
Also, why do you have a conditional check in your smart_harvest which doesn't update each loop but is passed in? Should look more like
it's designed to either fertilise a growing crop or just wait (though come to think of it, adding a smart_water() to that part might not be the worst idea). It's probably a slight waste of its cycles to keep checking if f is False, but it's just waiting so I'm not too fussed.
def smart_harvest(fertilize): # Waits for plant to grow before harvesting. Fertilizing can be enabled. if get_entity_type() != None: while fertilize and not can_harvest(): use_item(Items.Fertilizer) harvest()
I think you broke it. If fertilize = False, then it skips straight to harvest() regardless of whether or not the plant is ready.

I do quite like your version. It works perfectly and seems quite a bit faster than babysitting each individual square, even if the latter is using fertiliser. I might have a crack at the plant, scan, fix, repeat approach at some point,
Last edited by Adolf Diddler; Apr 3 @ 7:14am
owenz Apr 3 @ 9:13pm 
Originally posted by Adolf Diddler:
Originally posted by owenz:
...
it's designed to either fertilise a growing crop or just wait (though come to think of it, adding a smart_water() to that part might not be the worst idea). It's probably a slight waste of its cycles to keep checking if f is False, but it's just waiting so I'm not too fussed.
...
I think you broke it. If fertilize = False, then it skips straight to harvest() regardless of whether or not the plant is ready.

I do quite like your version. It works perfectly and seems quite a bit faster than babysitting each individual square, even if the latter is using fertilizer. I might have a crack at the plant, scan, fix, repeat approach at some point,

Definitely an oversight on my part, but as the smart harvest is basically just a glorified fertilizer it should be adjusted as such.

def smart_fertilize(): # Fertilizing can be enabled. if get_entity_type() != None: # Only need to do 1 check per iteration now while not can_harvest(): use_item(Items.Fertilizer)

Harvesting should just be the generic game harvest as there is not really any use case for doing otherwise. So just refactor to this and away one can go! The only time one would really want to dynamically change crops and thus remove one is if there was a need for a different crop to support a current operation, and generally it is better to just wait for the already planted crop to finish so no special culling logic is needed thus still back to square one of using the basic harvest function.

Also, now that is just a fertilize function it can be added to the loop logic! Though I doubt it will really speed up the planting process that much. The reasoning behind this is that there are 100 spaces that must grow and by the time you get down to that last 3-5 unless you get VERY, VERY, VERY lucky they tend not to be local to one another. Therefore, your bot will have to travel a certain number of spaces just to check so it will barely eek out and time savings as you have to "waste" time moving from one location to another.

Notes:
Also, if you watched the script run a few times it should be apparent that the check that is done initially should only be done when starting up as after it starts running the initial state of the board is always going to be empty. So it is easy to make it faster by removing this redundancy, (Again, just bashed this together quick as a demo)

There are other things but it is always a fun challenge to see if one can improve on someone else's code as this requires rationalizing what they were thinking and how they solved that problem. Then to go above that to optimize the code or improve the chain of logic. (Generally why most programmers would rather rewrite the ENTIRE thing than do so as it is not easy to get into another's head.)


I also implore you to try a similar approach to mine as well. As it will be very helpful to rationalize how another thinks. I use a number of tricks on how python works to get it to implicitly do the thing that I want, though for a seasoned python person it is explicit.

The coolest bit is this
def can_harvest_pumpkin_patch(patch): return patch[0] == True and len(set(patch)) == 1

As the whole board is stored as a 1-D array/list as True/False values you can check the value of the first one for it's truthiness. Using that with an and against the length of the set of values in the list, this bit is VERY cool, you can get it down to a set with 1 OR 2 length, where 1 means ALL are False or ALL are True.

Thus, if the first bit is True then if also the length of the set is 1 then ALL entries MUST be True so you can harvest it at that point!

If that didn't make any sense, then look up the documentation for the set function in python. (If you want to do a deep dive google group theory and/or set theory!)
Originally posted by owenz:
Harvesting should just be the generic game harvest as there is not really any use case for doing otherwise. So just refactor to this and away one can go! The only time one would really want to dynamically change crops and thus remove one is if there was a need for a different crop to support a current operation, and generally it is better to just wait for the already planted crop to finish so no special culling logic is needed thus still back to square one of using the basic harvest function.

That's exactly what it's for. It's part of a larger script that switches regularly between crops to keep them all ticking up at more or less the same rate. If the drone switches from polyculture to pumpkins, it can harvest the previous crop as it plants and not waste all those x5 bonuses and without having to do separate passes for harvesting and planting. Not to mention, I'm more often than not going to check to make sure there's a mature crop before harvesting, so it's nice to have it as a function. It's not just for my pumpkin script, it's a general use function I use on almost everything.

I also implore you to try a similar approach to mine as well. As it will be very helpful to rationalize how another thinks. I use a number of tricks on how python works to get it to implicitly do the thing that I want, though for a seasoned python person it is explicit.

I definitely will. In fact, I have to thank you for your help here. Seems counter-intuitive (at least to my barely competent anus), but the planting the whole thing and rechecking for holes is a lot quicker than just babysitting each square. I'm definitely inspired to have a crack at writing my own version.

There are other things but it is always a fun challenge to see if one can improve on someone else's code as this requires rationalizing what they were thinking and how they solved that problem. Then to go above that to optimize the code or improve the chain of logic. (Generally why most programmers would rather rewrite the ENTIRE thing than do so as it is not easy to get into another's head.)

Ha, glad it's not just me. Honestly, I had to spend about 15 minutes reading over your version until it made sense, and the only reason it didn't take longer is because I finally had the genius idea to just test it. Probably should've been step one, but what can you do?

As the whole board is stored as a 1-D array/list as True/False values you can check the value of the first one for it's truthiness. Using that with an and against the length of the set of values in the list, this bit is VERY cool, you can get it down to a set with 1 OR 2 length, where 1 means ALL are False or ALL are True.

I have to be honest, that went way over my head. It seems a good approach for cacti (I think did something similar in the last version of the game), but for pumpkins, I feel like just using a list of coordinates would be quicker. Remove a coordinate when you find a mature pumpkin, then harvest when the list is empty. Save you needing to translate list position into coordinates.
owenz Apr 4 @ 12:19am 
Originally posted by Adolf Diddler:
Originally posted by owenz:
...
I have to be honest, that went way over my head. It seems a good approach for cacti (I think did something similar in the last version of the game), but for pumpkins, I feel like just using a list of coordinates would be quicker. Remove a coordinate when you find a mature pumpkin, then harvest when the list is empty. Save you needing to translate list position into coordinates.

It all depends on what your requirements are.

Either you spend CPU cycles or RAM(memory), cost vs. benefits. Also, you have to remember that using memory has the cost of accessing that memory and such.

In my case I just use the inherent conversion of a 1-D array to (x,y) from the formula of x + y * width to keep the coords of the board with the pertinent information stored in the value. Which means that I have a contiguous section (potentially) of 100 bits (True/False) values which store not only the (x,y) value of the specific data but the data I need as well. Compare this to using a 2-D array/list or a dict where for this use case I would have to store the data and the coord that it is associated with.

Thus I minimized the memory footprint from python and used CPU cycles instead to calculate it on the fly as it is basically free as the size of the board is only 100 and even the crappiest of google books has a 1.3 ghz processor so that is not even a drop in the bucket on CPU time. Same could be said for the memory footprint as well. If scaled though to let say a world size of 1,000 the memory footprint would still be just 1,000,000 bit vs using a 2-D array or dict where who even knows! (Can calculate but won't) Mind you as python's list can store any type we are not getting real savings from doing this minus the fact we are not storing additional data.


Example of data growth between implicit storage of (x,y) vs explicit.
import sys pumpkins = {(x, y):False for x in range(10) for y in range(10)} print(sys.getsizeof(pumpkins)) # Results in 4688 bytes pumpkins = [False for _ in range(100)] print(sys.getsizeof(pumpkins)) # Results in 920 bytes pumpkins = {(x, y):False for x in range(1000) for y in range(1000)} sys.getsizeof(pumpkins) # Results in 41,943,128 bytes pumpkins = [False for _ in range(1000*1000)] sys.getsizeof(pumpkins) # Results in 8,448,728 bytes

As a note, using numy bit arrays would make the data far less but no reason to as the scope is this game.


Originally posted by Adolf Diddler:
Originally posted by owenz:
...
Remove a coordinate when you find a mature pumpkin, then harvest when the list is empty.
...

The beauty of how I am doing it with set conversion is that python is, for python, REALLY REALLY fast at turning a list into a set! (Hash table magic)
Also I remove any potential bugs from the removal and insertion of data into a data structure by doing it this way which is great! Which is why I would recommend just keeping it in memory, UNLESS you are doing BIG data stuff where that would be impossible.

Less bugs means a happier programmer!
Last edited by owenz; Apr 4 @ 12:21am
Originally posted by owenz:
The beauty of how I am doing it with set conversion is that python is, for python, REALLY REALLY fast at turning a list into a set! (Hash table magic)
Also I remove any potential bugs from the removal and insertion of data into a data structure by doing it this way which is great! Which is why I would recommend just keeping it in memory, UNLESS you are doing BIG data stuff where that would be impossible.

Less bugs means a happier programmer!

Fair enough, that makes a great deal of sense. I guess I'll have to bear that in mind if I ever do any coding outside of a programming game.
< >
Showing 1-12 of 12 comments
Per page: 1530 50