The Farmer Was Replaced

The Farmer Was Replaced

Kosmisch Jun 7, 2024 @ 7:57am
Sunflower code help
I need help with my sunflower code :(

################

defs for the code:
def RETURNHOME():
while get_pos_x() > 0:
move(West)
while get_pos_y() > 0:
move(South)

def harvest_grid():
for x in range(get_world_size()):
for y in range(get_world_size()):
if get_ground_type() != Grounds.Soil:
till()
if can_harvest():
harvest()
move(South)

move(East)

##################

This is it right now:

RETURNHOME()
harvest_grid()

pup = 0
Sonmin = 15
pos_x = 0
pos_y = 0
Sun = 1
step = 0

while True:

while (get_world_size()*get_world_size()) != pup and Sun == 1:
if (get_pos_x()+get_pos_y()) == 0:
pup = 0
move(North)
if get_entity_type() == Entities.Grass:
till()
if get_entity_type() == Entities.Sunflower and can_harvest() == True:
pup += 1
plant(Entities.Sunflower)
if get_pos_y() == (get_world_size()-1):
move(East)

move(North)
quick_print(Sonmin)

if get_pos_y() == (get_world_size()-1):
move(East)
if measure() >= Sonmin and can_harvest() == True:
harvest()
plant(Entities.Sunflower)
if measure() > Sonmin:
Sonmin = measure()
pup = 1
Sun = 2

else:
step = step + 1
if step >= 101:
Sonmin = Sonmin -1
step = 1
RETURNHOME()
< >
Showing 1-6 of 6 comments
owenz Jun 7, 2024 @ 10:02pm 
Greetings,



Originally posted by Kosmisch:
I need help with my sunflower code :(

################
defs for the code:
def RETURNHOME(): while get_pos_x() > 0: move(West) while get_pos_y() > 0: move(South) def harvest_grid(): for x in range(get_world_size()): for y in range(get_world_size()): if get_ground_type() != Grounds.Soil: till() if can_harvest(): harvest() move(South) move(East) ##################
This is it right now:
RETURNHOME() harvest_grid() pup = 0 Sonmin = 15 pos_x = 0 pos_y = 0 Sun = 1 step = 0 while True: while (get_world_size()*get_world_size()) != pup and Sun == 1: if (get_pos_x()+get_pos_y()) == 0: pup = 0 move(North) if get_entity_type() == Entities.Grass: till() if get_entity_type() == Entities.Sunflower and can_harvest() == True: pup += 1 plant(Entities.Sunflower) if get_pos_y() == (get_world_size()-1): move(East) move(North) quick_print(Sonmin) if get_pos_y() == (get_world_size()-1): move(East) if measure() >= Sonmin and can_harvest() == True: harvest() plant(Entities.Sunflower) if measure() > Sonmin: Sonmin = measure() pup = 1 Sun = 2 else: step = step + 1 if step >= 101: Sonmin = Sonmin -1 step = 1 RETURNHOME()

There we go, that is your code with the code blocks around them, when posting just click the formatting help button under the post to see the different formatting codes.
owenz Jun 7, 2024 @ 11:00pm 
TLDR: None of the code is perfect, I leave it you the viewer if they are looking for that.

So first things first
while (get_world_size()*get_world_size()) != pup and Sun == 1: if (get_pos_x()+get_pos_y()) == 0: pup = 0 move(North) if get_entity_type() == Entities.Grass: till() if get_entity_type() == Entities.Sunflower and can_harvest() == True: pup += 1 plant(Entities.Sunflower) if get_pos_y() == get_world_size(): move(East)

This is totally not doing what I suspect you want it to do. Let us got with for loops instead
Let us get rid of the code that isn't doing anything as well, and add in the missing parts.

As a note, we should measure the sunflowers as we plant them as the petals is determined at planting.

# Let us store the sunflowers as we go, it is needed ws = get_world_size() sunflowers = [] for i in range(ws ** 2): sunflowers.append(0) for i in range(ws): for j in range(ws): if get_ground_type() == Grounds.Turf: till() if num_items(Items.Sunflower_Seed) > 0 or trade(Items.Sunflower_Seed): plant(Entities.Sunflower) if get_entity_type() == Entities.Sunflower: sunflowers = measure()
move(East)
move(North)[/code]

Now it will move along in a nice manner, and as a nice bonus we now have the values of the sunflowers!

OK, time to get serious on these sunflowers. Let us locate the highest value of petals, harvest it and replant it while also updating the list of values.

while True: x, y = (0, 0) max_val = 0 for i in range(len(sunflowers)): if sunflowers > max_val:
max_val = sunflowers
x, y = i % ws, i // ws # This is a common formula to get x,y from a 1-D repesentation

delta_we = x - get_pos_x()
for i in range(abs(delta_we)):
move(dir_we[delta_we / abs(delta_we)])

delta_ns = y - get_pos_y()
for i in range(abs(delta_ns)):
move(dir_ns[delta_ns / abs(delta_ns)])

if measure() == max_val:
if can_harvest():
harvest()
else:
do_a_flip()
# Replant and change sunflowers for new value
if get_ground_type() == Grounds.Turf:
till()
if num_items(Items.Sunflower_Seed) > 0 or trade(Items.Sunflower_Seed):
plant(Entities.Sunflower)
if get_entity_type() == Entities.Sunflower:
sunflowers[x + y * ws] = measure()
else:
break # Break out is something goes wrong[/code]

OK, Now all that is done.

There are some tricks involved so feel free to ask away about them.

Here is the complete code, it runs but is not the best it can be.

def RETURNHOME(): while get_pos_x() > 0: move(West) while get_pos_y() > 0: move(South) def harvest_grid(): for x in range(get_world_size()): for y in range(get_world_size()): if get_ground_type() != Grounds.Soil: till() if can_harvest(): harvest() move(South) move(East) RETURNHOME() harvest_grid() ws = get_world_size() sunflowers = [] for i in range(ws ** 2): sunflowers.append(0) for i in range(ws): for j in range(ws): if get_ground_type() == Grounds.Turf: till() if num_items(Items.Sunflower_Seed) > 0 or trade(Items.Sunflower_Seed): plant(Entities.Sunflower) if get_entity_type() == Entities.Sunflower: sunflowers[get_pos_x() + get_pos_y() * ws] = measure() move(East) move(North) dir_ns = (None, North, South) dir_we = (None, East, West) while True: x, y = (0, 0) max_val = 0 for i in range(len(sunflowers)): if sunflowers > max_val:
max_val = sunflowers
x, y = i % ws, i // ws # This is a common formula to get x,y from a 1-D repesentation

delta_we = x - get_pos_x()
for i in range(abs(delta_we)):
move(dir_we[delta_we / abs(delta_we)])

delta_ns = y - get_pos_y()
for i in range(abs(delta_ns)):
move(dir_ns[delta_ns / abs(delta_ns)])

if measure() == max_val:
if can_harvest():
harvest()
else:
do_a_flip()
# Replant and change sunflowers for new value
if get_ground_type() == Grounds.Turf:
till()
if num_items(Items.Sunflower_Seed) > 0 or trade(Items.Sunflower_Seed):
plant(Entities.Sunflower)
if get_entity_type() == Entities.Sunflower:
sunflowers[x + y * ws] = measure()
else:
break[/code]

Last edited by owenz; Jun 7, 2024 @ 11:07pm
PadainFain Jun 9, 2024 @ 5:11am 
Here's my Sunflower function. Note that I decided to plant a whole field and then harvest the entire field. Since the yield scales with the square-root of the number of sunflowers the loss is minimal and it's probably more efficient than replanting. Rougly speaking for harvesting the entire field you get about 70% of the yield for a bit over 50% of the work of harvesting and replanting.


size = get_world_size() do_sunflowers(False) def do_sunflowers(use_fertilizer): # We will store the locations with each score in a dict # The key will be the score # the Value will be a list of lists, the inner lists being the locations scores = {1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[], 10:[], 11:[], 12:[], 13:[], 14:[], 15:[]} for x in range(size): while get_pos_x() != x: move(East) for y in range(size): while get_pos_y() != y: move(North) planting(Entities.Sunflower) if use_fertilizer == True: use_item(Items.Fertilizer) petals=measure() scores[petals].append([get_pos_x(), get_pos_y()]) # We wait on the final square to ensure all of them are grown. while can_harvest() == False: do_a_flip() # Now process the dict from max score down i = 15 while i > 0: while len(scores) > 0:
x = scores[0][0]
y = scores[0][1]
move_to(x,y)
harvest()
scores.pop(0)
i -= 1

def move_to(x,y):
distance_east = x - get_pos_x()
distance_north = y - get_pos_y()
if distance_east < size / 2 or (distance_east < 0 and abs(distance_east) > size / 2):
move_direction = East
else:
move_direction = West
while get_pos_x() != x:
move(move_direction)
if distance_north < size / 2 or (distance_north < 0 and abs(distance_north) > size / 2):
move_direction = North
else:
move_direction = South
while get_pos_y() != y:
move(move_direction)[/code]
Last edited by PadainFain; Jun 9, 2024 @ 5:15am
owenz Jun 9, 2024 @ 2:21pm 
Originally posted by PadainFain:
Here's my Sunflower function. Note that I decided to plant a whole field and then harvest the entire field. Since the yield scales with the square-root of the number of sunflowers the loss is minimal and it's probably more efficient than replanting. Rougly speaking for harvesting the entire field you get about 70% of the yield for a bit over 50% of the work of harvesting and replanting.

...

TLDR: Partial Harvest provide a boost of up to 8% over a full harvest and a 377% difference from pruning and replanting only the best.

OH, now I am thinking. The sunflowers if you always prune for the max will naturally go towards min, which follows the half-life decay equation or
N(t) = N_0 (1/2) ^ (t / (t_(1/2)) )
Where t is time and (t_(1/2) is the half life of the material

This also means that the sunflowers will produce less and less power as you approach t=infinity and eventually reach the min. The reason for this is that if you are able to move from high to low but cannot harvest anything but the max you will end up in a situation where the newly planted sunflower is always greater than or equal to the max. So no movement but as a result you can only harvest 1 sunflower per harvest cycle.

So a full field of sunflowers harvested from start to finish would look like

f(x) = floor(sqrt(x))
sfs = ws ** 2
total_yield = Sum(f(sfs), as sfs goes to 0)

Or ~671 power w/o costs

Now the real question is, is there a point at which we can harvest the field to and maximize the harvest?

Such as say 70% of the field and then replant. We then have to time, or get op cost, for each replanting cycle and harvest cycle as we will use that to optimize the percentage required to maximize the function. We want to maximize power per sec.

Looks like
total_time = harvest_cycle + planting_cycle
total_yield_per_sec = total_yield / total_time

In your use case both the harvesting cycle and planting cycle would be 100% of what ever cost it takes. Let us just use a timed run to get what ever that is.

For a basis, doing a complete plant then harvest is getting me the following after 10 runs
Averaging 19.2 seconds per cycle(harvest+plant)
Averaging 1961.97 power per cycle(harvest+plant) -> have x3 for power from upgrades

The above power matches the 671 power per cycle with the x3 multi being 2013 so the cost of power is 51.03.

With only replanting the sunflowers at the max positions I get an average of 12.9 power per sec over 30 mins, no watering.


Now let's try to see if a partial harvest is better than a complete harvest, for this metric we will go back to timing as a whole instead of per harvest.

Also as a note the formula would now turn into
total_yield = Sum(f(sfs), as sfs goes to min_number_of_sunflowers_on_field)
Also as a result of how the sunflowers grow, the min number will always be slightly lower than what is set or 0 as I harvest the chain of the same crop
So each yield will be 432(unlock multi) or 1296 per harvest

As I feel like brute forcing it, it's fun, let us just write some code to walk from 70%(sunflowers_max) - 30%(sunflowers_max) to see what is the best rate with water. Step is 10% as well.
All test were 5 mins as well. Also more code optimizations were adds here.

All in power per sec
i: 0 = 27
i: 30 = 102
i: 40 = 101
i: 50 = 99
i: 60 = 98
i: 70 = 93
i: 100 = 94

It is shown that partial and whole field harvests are superior to a single harvest to maximize f(x) vs maximizing total_yield = Sum(f(sfs), as sfs goes to 0)
It is also shown that a partial harvest is more effective than a full harvest.


Overall there is a way to mathematically "prove" what is the most optimal solution but given the random nature of how the sunflowers grow I personally do not have the maths know how to approach the definite solution and can only go with a brute force method. As such given all the above information the relationship between the drone movement and the time it takes to plant/harvest and all the other operations start to play out with the length of time the sunflower takes to grow in a very dynamic way.

Overall the approach where a partial field is harvested shows a good improvement over a full harvest and a night and day difference to harvesting only the best sunflowers and then replanting.

Also, watering reduces the impact the time the sunflower takes to grow thus allowing for the drone to whip over to the ones it needs to harvest. So if a lower drone speed is used it would drastically change what percentage of the field a person would want to harvest to maximize the power production, along with watering having the same level of effect but only if the drone can handle the faster sunflower growth.

In addition to the above point, as the number of times the field has been replanted approaches infinity the values of the sun flowers will naturally converge to the min number meaning more time spent harvesting and as an effect longer planting cycles as well. This means the dynamic between the two is very hard to control unless a person went for a strict, as soon as you hit this value switch mode, then there is going to be some fluctuations in the output over a short period with it converging to a stable output over time.

In conclusion, this approach to this problem is interesting and fun to monkey around with.

Here is the code I used to perform the above analysis.

##### DEFS AND HELPERS def coord_int(x, y, world_size): return x + y * world_size def int_coord(pos, world_size): return (pos % world_size, pos // world_size) def move_oe(x, y, tx, ty, move_fn): ws = get_world_size() dx, dy = x - tx, y - ty # Will always eval to 1 or -1 so None will not be selected ns = (None, South, North) # Will always eval to 1 or -1 so None will not be selected we = (None, West, East) if dy == 0 and dx == 0: return get_pos_x(), get_pos_y() def inner_move(delta, move_dir): if delta != 0: if abs(delta) > ws // 2: delta -= (delta / abs(delta)) * ws move_fn(move_dir[delta / abs(delta)]) inner_move(dx, we) inner_move(dy, ns) return move_oe(get_pos_x(), get_pos_y(), tx, ty, move_fn) def plant_sunflower(entity, ground): if entity != None: return False if ground != Grounds.Soil: till() if num_items(Items.Sunflower_Seed) > 1 or trade( Items.Sunflower_Seed, to_buy_at_once ): return plant(Entities.Sunflower) return False def maintain_water_level(perc): if get_water() < perc: uses = (perc - get_water()) // 0.25 return use_water(uses) return False # Given the wanted water per sec, calculates the required amount of water tanks def water_maintenance_needed(req_water_per_sec): return req_water_per_sec * (200) def water_maintenance(water_per_sec): needed = water_maintenance_needed(water_per_sec) - num_items(Items.Empty_Tank) if needed > 0: return buy_water(needed) return False ##### START OF ANALYSIS PART to_buy_at_once = get_world_size() retries = 3 sunflowers = [] for i in range(get_world_size() ** 2): sunflowers.append(0) harvest_cycle_avg = 0 planting_cycle_avg = 0 def plant_cycle(): p_start = get_time() min_sunflower = [] for i in range(len(sunflowers)): if sunflowers == 0:
min_sunflower.append(int_coord(i, get_world_size()))

while min_sunflower:
# goto the location
go_x, go_y = min_sunflower.pop()
move_oe(get_pos_x(), get_pos_y(), go_x, go_y, move)


if get_entity_type() != None:
if get_entity_type() != Entities.Sunflower:
harvest()
if get_ground_type() != Grounds.Soil:
till()
plant_sunflower(get_entity_type(), get_ground_type())
maintain_water_level(0.5)
sunflowers[coord_int(go_x, go_y, get_world_size())] = measure()
# if wanted, water here

if planting_cycle_avg == 0:
planting_cycle_avg = get_time() - p_start
else:
planting_cycle_avg = (planting_cycle_avg + (get_time() - p_start)) / 2

def harvest_cycle():
h_start = get_time()
max_sunflower = []
max_val = 0

for i in range(len(sunflowers)):
if sunflowers > max_val:
max_val = sunflowers
max_sunflower = [int_coord(i, get_world_size())]
elif sunflowers == max_val:
max_sunflower.append(int_coord(i, get_world_size()))

max_sunflower = max_sunflower[::-1]

# goto the max sunflowers and harvest them
iter_count = 0
while max_sunflower:
go_x, go_y = max_sunflower.pop()

move_oe(get_pos_x(), get_pos_y(), go_x, go_y, move)
if not can_harvest():
water_maintenance(0.5)
max_sunflower.insert(0, (go_x, go_y))
else:
harvest()
sunflowers[coord_int(go_x, go_y, get_world_size())] = 0
iter_count += 1
if iter_count * retries > len(max_sunflower):
break

if harvest_cycle_avg == 0:
harvest_cycle_avg = get_time() - h_start
else:
harvest_cycle_avg = (harvest_cycle_avg + (get_time() - h_Start)) / 2


def basic_test():
# Basic Test, full plant and full harvest
clear()
start_time = get_time()
start_power = num_items(Items.Power)

plant_cycle()
while max(sunflowers) > 0:
harvest_cycle()

t_time = get_time() - start_time
p_total = num_items(Items.Power) - start_power
quick_print("Time Taken", t_time)
quick_print("Power Gained: ", p_total)

return t_time, p_total


def basic_test_run():
t_avg, p_avg = basic_test()
for i in range(9):
t, p = basic_test()
t_avg = (t_avg + t) / 2
p_avg = (p_avg + p) / 2

quick_print("")
quick_print("Time Avg: ", t_avg)
quick_print("Pwer Avg: ", p_avg)

def count_sunflowers():
sum_sun = 0
for i in range(len(sunflowers)):
if sunflowers > 0:
sum_sun += 1

return sum_sun

def adv_test(perc, test_time):
# Basic Test, full plant and full harvest
clear()
start_time = get_time()
start_power = num_items(Items.Power)

if perc < 0.05:
sf_total = 0
elif perc > 0.95:
sf_total = get_world_size() ** 2
else:
sf_total = get_world_size() ** 2 * perc
while get_time() - start_time < test_time:
plant_cycle()
while count_sunflowers() > (sf_total):
harvest_cycle()

t_time = get_time() - start_time
p_total = num_items(Items.Power) - start_power
quick_print("Time Taken", t_time)
quick_print("Power Gained: ", p_total)

return t_time, p_total

# basic_test_run(True)
num_mins = 5
time_total, power_total = adv_test(0, 60 * num_mins)
avg_pph = power_total // time_total
quick_print("i:", 1, " Avg Power Per Sec: ", avg_pph)
for test_run in range(30, 80, 10):
clear()
sunflowers = []
for i in range(get_world_size() ** 2):
sunflowers.append(0)
time_total, power_total = adv_test(test_run / 100, 60 * num_mins)
avg_pph = power_total // time_total

quick_print("i:", test_run, " Avg Power Per Sec: ", avg_pph)
time_total, power_total = adv_test(1, 60 * num_mins)
avg_pph = power_total // time_total
quick_print("i:", 1, " Avg Power Per Sec: ", avg_pph)

quick_print("")
quick_print("Planting Avg: ", planting_cycle_avg)
quick_print("Harvesting Avg: ", harvest_cycle_avg)
[/code]
Last edited by owenz; Jun 9, 2024 @ 2:22pm
PadainFain Jun 11, 2024 @ 11:49am 
Since the power scaling is logarithmic I wouldn't be surprised if the ideal point was 1/e of the field, or (1-1/e) of it. However it's probably then offset by the distribution of petals at any given time. I gave no consideration at all to number of petals beyond ordering them correctly.


The nice thing about just clearing the entire field is a) you don't end up with a field of weak flowers (which I didn't consider) and b) the field is cleared - something you would want to write a function for anyway so as not to waste those sunflowers when you decide to go plant dinosaurs or something else.
owenz Jun 11, 2024 @ 6:19pm 
Originally posted by PadainFain:
Since the power scaling is logarithmic I wouldn't be surprised if the ideal point was 1/e of the field, or (1-1/e) of it. However it's probably then offset by the distribution of petals at any given time. I gave no consideration at all to number of petals beyond ordering them correctly.


The nice thing about just clearing the entire field is a) you don't end up with a field of weak flowers (which I didn't consider) and b) the field is cleared - something you would want to write a function for anyway so as not to waste those sunflowers when you decide to go plant dinosaurs or something else.


I basically got that 1/e is the optimal solution but it is of course skewed by all the associated costs.

Also, it wouldn't take much of a stretch to just change my current code to include a switch to allow for complete harvest then exit the function naturally and switch states. So it is possible to allow it to farm for power until some state is reached then switch to the next cycle without much change.

In addition you could start farming in sub-optimal(no unlocks) conditions and adjust accordingly as at lower speed levels the benefit of keeping more flowers should show itself.

If you were tracking how much resources you were generating it would be possible to allot a specific amount of time for each cycle and as a result you could predict how much you would need. This would be especially valuable for the speed run as you would not waste too much time with phase changes.
< >
Showing 1-6 of 6 comments
Per page: 1530 50