My Python-Automated Pumpkins are Now Efficient...
Вставка
- Опубліковано 3 січ 2025
- Program and optimize a drone to automate a farm and watch it do the work for you. Collect resources to unlock better technology and become the most efficient farmer in the world. Improve your problem solving and coding skills.
Check out The Farmer was Replaced here:
-- store.steampow...
Check out my Python tutorials:
• VARIABLES, TYPES, AND ...
#games #olexa #strategy #farming #automation #programming
Thanks for watching!
-- Extra Olexa Content - / @olexa2
-- Discord: / discord
-- Twitch: / olexastream
-- Twitter: / olexayt
-- Reddit: / olexa
-- My Nexus Gamestore: www.nexus.gg/o...
-- UA-cam: / olexayt
-- Business Email: olexakid@gmail.com
-- Join this channel to get access to perks:
/ @olexayt
Music I Use:
-- www.bensound.c...
-- Spring by Ikson
-- Harris Heller Streambeats Lofi Spring by Ikson | ikson.com/tell...
Music promoted by www.free-stock...
Liking just to keep olenky dreaming about his pythonscoded farm
29:36 The fact that you were, in fact, pressing the right button before too, and didn't change anything in the code, but this time it worked (as it should've before) is PEAK coding experience. Change nothing, different results.
This is now probably one my favorite certifefied Olexa Moments™
^feel that too well me after 4h trying to figure out how to solve the easy Maze (i am a programmer but never worked with this kind of things) ok lets check the hints. implements it drone just spins in circels. continues the next 4h or so to try to figure out the problem creating a more and more complex code to end up with the same code as in the beginning but now it works. best wasted 8h of my life(it was 4am when i finally solved it sob)
@@befana666_vods Sometimes there is an explanation for this. Every once in a while the eletromagnetic efect of the sun impacting the earth can change a few bits from your HD from 1 to 0 or vice-versa. So you can tell to yourself that god is mocking your code and changing thing around.
@@adrianocs4 ok will do tomorrow when i have to earn my food trying to understand why one time it works and the other time it doesnt
You think nothing changed, but in this video, the drone was in a different starting position.
As to why it didn't work, the problem was, that there was greater, or less than, but not equal zero in his code, that he fixed with else, later on. So when the drone was equally far away to the left or right, there was no case for that.
@@adrianocs4 gonna use this as my reason for failing CS.
can only speak for myself, but watching you explain this stuff (most of which isn't new to me so far, but is still a bit tricky) is surprisingly entertaining. and especially with it being a bit broken when you first run it and having to scramble and figure out what's wrong was probably even more fun to watch than if it had worked perfectly the first time
Refactor your code to only use a queue of coordinates and in the initialization you add every possible coordinate into the queue (also, you can use tuples and destructuring syntax for the coordinates if you prefer) and you can make the algorithm work this way:
1. pop first item of the queue
2. go to the coordinates of the item
3. if it cannot be harvested, plant a pumpkin then add current coordinates to queue
4. repeat until the queue is empty (goto line 1)
5. harvest the giga pumpkin
this might not be the most efficient as the first pass will use your goto function instead of simple moves, but it will be a generalized, more readable and easily modifiable algorithm. If you wish to do an efficient first pass, I suggest you extract the first pass into its own function as it has no interaction with the extra passes and will help you not mess up your code. The second pass with the efficient goto may also be a separate function.
also, I'll add that the mazes unlock is quite interesting, they involve graph search algorithms (like bfs and dfs) and if you'd like you can also eventually use path finding algorithms (Djikstra or A*)
This is definitely cool and the correct approach but I’m not gonna do it haha
I wrote a "hold left-hand to wall" algorithm to solve the mazes, super simple algorithm, solves really fast.
the first solution I made was to move in a random direction until it found the treasure... while inefficient, it would eventually solve any maze
@@doubledigitIQ Random direction go was my first attempt too... Until I saw the power consumption of sunflowers.
@@doubledigitIQ I downloaded this game yesterday and my first solution was a random maze “solver” too 😂
@@OlexaYT might be easiest to just make it check in order of closest if you're planning to optimize pumpkins further, otherwise it's about as good as you can get on a 6x6 farm
44:10 you can highlight multiple lines and shift+tab to unindent them all by 1.
If playing on Steam the overlay normally comes up with shift+tab. This annoyed me so much I had to change it to crtl+shift+tab.
@@sirusmajwhy did I not think of changing the key bindings
Can't wait for the final episode (based on the tech tree) is something like "5k likes and I mod the game to add content"
[spoilers]
there's a fully automated option, where you can make a program that goes from the start of the game to buying all the upgrades.
@@satibel so maybe like... 15-20k likes? Yeah we can do that
@@travis1347 Just to automate the game, not even 1k. If you want to get competitive on leaderboard (top 30), you're likely going to be hardcoding approaches for each "section" of the game which will bloat the amount of code needed... maybe reach 1-2k.
Thank you so much for the "goto" code. While I now understand how it works after you explaining it, and see what the code is doing, it was far far above my rookie coding's head to even conceive of. I had ideas for something like it but it would have been really jank, so I'm glad I didn't try that.
I did realize one could just combine some lines with an OR operator so it looks cleaner.
I also didn't know you could call functions from other windows and keep codeblocks seperate, that's really nice!
this video is probably fueling the teacher olexa fanart (unless that already exists)
Was going to comment, that goto function is pretty inefficient, but realised, that the gamefield is basically a toroid which really would need thouse four clauses. :)
As for strange behavior, i think you are trying to harvest not mature crop in some cases, which breaks it, and your code resets.
e.g. You have only one item in temp_coords, drone goes to this one spot, plants pumpkin, then iterates once again on the same spot, checks that it is a pumpkin, but not that it is mature, harvests it, which breaks not mature pumpkin, leaving all other fields populated. Then breaks from the loop and iterates from the very begining.
That is indeed the case. There should be a wait after planting the last pumpkin to let it mature and fuse with all the other pumpkins.
It could be solved with just two if/else's if he used xor
I think he can add in "if canharvest()" the line above "harvest()" - else: do a flip() for a wait moment.
@@sirusmaj that's whether you want to waste time or waste loop processing - then again, I have no idea what the equivalent (total actions per second) will be
This is by far the only series of this game that explains it like you do. And you do so in a way that keeps it interesting. You gotta keep these coming!
Your not waiting for the last pumpkin to grow before you harvest. The pumpkins dont combine if they are not fully grown
I'm 9 hours late, but still proud of myself for figuring it out, lol
yeah I think he just needs can_harvest() instead of is_entity == pumpkin
You implemented it as promised, awesome! Loved your explanation of the goto.
Also another fun tip, if you want nice docstrings that appear when you hover over one of your own functions, you can get that!
Just add a comment line immediately above your def statement with whatever your docstring will be :)
These episodes have taught me more about coding than a course at uni did
Your problem with the code is that by the end of the code there might be only one or two empty spots in the grid, but you evaluate it again before they finished growing, then it says there is a pumpkin there when it had not matured yet. It harvests and tries to plant again on a filled grid. If you add the check about not 'can harvest' to also be added to the list (Or temp list) it should solve the issue. If it is not mature it will consider it as empty for the cycle and try again until it is either dead and can plant again or is mature and the whole grid is fully fused for your harvest.
Also, thank you very much for the video. I love your content!
Just wanted to let you know, that you can use breakpoints in your code here. On the left side of a window where there is a vertical like, press on the line you want a break at. Now if you run the code normally, it will stop there and you can step through with the second button.
Totally forgot we had the built in debugger here hahahaha
To answer your question at the end, if there are only one or spots in coord_list it is too fast checking them to account for their growth time. Your if statement checking to append to the list should use the `get_entity() == None` as its check instead of checking if a pumpkin is there because a partially grown pumpkin will still resolve as true. Maybe add a `while get_entity() == Entities.Pumpkin and not can_harvest(): do_a_flip()` above the check to give the partially grown ones to either complete or die.
You can also do 'while not can_harvest(): pass' to wait.
It is more effective to plant other crops in the gaps when planting trees (also more complex)
Depending on the needs we can always be planting trees with other crops keeping the spacing
I’d rather just plant bushes so we can treat it as a wood function.
@@OlexaYT
Yes, totally valid, I understand the urge to modularize and isolate the functionality, I just let the urge to be fully efficient speak for a moment.
Definitely worth to just isolate it.
I've been watchin your vids for more than a month now.
Just today I found out that I'm not subscribed, this should be a crime and I should go to jail for that lol
Thanks for the content, as a software developer, this game hits just right for me :)
@@OlexaYT I do trees + carrots for standard use; these work well together, and it's more time-efficient overall to get all wood from trees than from a mix of trees & bushes, since trees have higher output. Eventually, the primary use for wood is upgrading carrot output anyway.
You can select your code and tab to indent the selection and shift+tab to remove the indent. massive time saver :)
That’s a godsend, thanks. You’ll see me not do that tomorrow, it’s because I already recorded haha
Also i think my farm is so dang fast with the sunflower power-ups it's literally not getting enough time to find missing pumpkins now with this algorithm and movement... i end up with empty lists with pumpkins missing
The sunflowers are crazy lol
@@OlexaYT If playing on Steam the overlay normally comes up with shift+tab. This annoyed me so much I had to change it to crtl+shift+tab in the Steam options.
"Aaaaand we're in hell" bahaha cracking me up!!
I loved that you left the currx/y debacle in the video I caught it immediately and it gave me flashbacks to college because me an my friends always made stupid mistakes like that and just couldn't figure it out for the life of us. But as soon as a another of us saw the code fresh they'd figure it out immediately. "Damn, I'm stupid" was the catchphrase for the group
Thank you for posting this video, I just bought this game and made my pumpkin farm and much more(trade management with stock refilling by switching farms before returning to the previous farm, capable of recursively switching farms, and starting from any farm such as pumpkin, sunflower or any I create separately).
Instead of scanning for failed pumpkins(empty spots), I just make a first pass to plant, and then, on a second pass, I plant and force grow with fertilizer every empty non-harvestable spot. It will also fertilize if my drone is too fast and reaches a pumpkin before it grew, I just spam fertilizer until it is mature, then check if it is harvestable(empty spots are not harvestable!)
This strikes a balance of speed and fertilizer usage when creating the big pumpkin. making only one pass and using fertilizer for every spot and checking for harvestable pumpkin is takes longer than the two passes I make with the second pass utilizing the fertilizer. This also means I do not use watering mechanics as it does not offer time benefits since I need to make a second pass either way.
Interesting. I did this:
wdist = abs(y - ytarget - size) % size
edist = abs(y - ytarget - size) % size
if (wdist < edist):
return West
else:
return East
And the same for North/South. I got there by thinking of the spaces on the other sides like they were on extra squares off the edges (with coordinates = coords + size one one side, and coords - size on the other) and simplifying from there.
Also, the modulus operator (%) is so that it also works for distances that *don't* wrap
Brilliant. I love it. I love trying to catch the problem before you do (I never have)
After 4 videos about this game, i learned more than in my entire school time 😂 good job
It's very helpful to have a custom upgraded harvest function, which waits over its square until the object can be harvested before actually harvesting (and checking first that something actually exists, so it can't get stuck). I call that on every crop, from grass to sunflowers.
For pumpkins, my solution doesn't require a temp list. Instead, I store the length of the "holes" list in a variable in the outer loop, and in the inner loop iterate through from the back end of the list to the beginning, popping out entries as needed.
I like doing all trades at the start of the program in bulk, to simplify and to account for the later multi-trade unlock. In this case, I buy until I have size * size * 2 seeds, to have a buffer for those that will need redoing.
You can significantly reduce the number of conditions needed to calculate a direction for your goto() function. Notice that you have opposite signs (like > and ) when the direction is East.
This means we can use multiplication property to unify pairs of conditions for each direction. And you will get something like this:
currx = get_pos_x()
size = get_world_size()
dir = East
if (abs(x - currx) - size / 2) * (currx - x) < 0:
dir = West
while get_pos_x() != x:
move(dir)
LESSGOOO I Love this game. Helping with the 1000 again!
So I’m kinda happy with myself, as I was watching and finally understood the code for “goto “ I realized I did something a lot like it except I had it set to always go to 0,0 so I didn’t need to mess with absolutes but I did the same operations where taking half the world size and go east or west to be efficient. Makes me happy that I ended up somewhat like you
Not only did I get the game after watching your videos but you sir have earned my like, comment, and subscription. Great content man!
Got the game to give this a shot here is what I came up with, I first divide the movement function into a "linear_movement" function, this allows me to isolate the movement into one direction and then evaluate them as it was just a row, removing some duplicated logic there. Now the next part is the hard part, determining which direction to move, for that I isolated it to just getting the direction using similar logic you had then broken out in a function to make it so I was able to debug when needed easier. and then finally just having the movement while loop checking if we are there yet.
I am sure there is an easier way with some more time, but in my testing, I didn't have any weird movement.
def move_to(x, y):
wrap_linear_move(get_pos_x, x, East, West)
wrap_linear_move(get_pos_y, y, North, South)
return
def wrap_linear_move(position_func, destination, PositiveDirection, NegativeDirection):
direction = get_direction(position_func, destination, PositiveDirection, NegativeDirection)
while position_func() != destination:
move(direction)
def get_direction(position_func, destination, PositiveDirection, NegativeDirection):
halfDist = (get_world_size()/2)
DeltaMovement = (position_func() - destination)
if halfDist < DeltaMovement:
if DeltaMovement < 0:
direction = NegativeDirection
else:
direction = PositiveDirection
else:
if DeltaMovement > 0:
direction = PositiveDirection
else:
direction = NegativeDirection
return direction
ok i like how you think, but here are a fix and an optimization path for you, for the pumpkins
1. move the "coord_list = []" above the first set of for loops
2. remove the if/else structure in the first set of for loops keeping the while loop for seeds intact (remember to correct the indentation),
and of course keeping the "water_me()" and "plant()" functions as well ^^
3. before the move(south) in the first set of for loops, add the "coord_list.append()" from the second loop
4. remove the entire second set of for loops
5. the fix: i beleve the "break" at the end only exits the if statement and not the while loop like it's supposed to, but simply indenting it back one would break the while loop before it is done looping through the missing pumpkin patches. so i would change the while loop, so that a variable set to true is used, and insted of a break the ending if statement ends with setting that variable to false
hope this helps ^^, and have a nice day :)
Okay the problems you had:
You at the end of the big pumpkin check you dont check if the last pumkin is fully grown
That means that you harverst it befor it is an adult and can form the mega pumpkin, out of the view of the code compliting your harvest what means that the code trys to replant the field
I also think that you could improve your goto() by having one statement be =< so that you don't need the last else
But ❤ the videos and ty for showing me the came
For years, I had a 12h python tutorial in my "watch later" folder on UA-cam and did never had the willpower to get into it, but thanks to you and the game I finally have no reason to procrastinate getting into coding❤
I am sorry incase you can't read sth or want me to clarify. English is not my first language so if you need any clarification hit me up and I will gladly try my best to explain myself better 😅
An easier way to remember coordinates is to have all your farm in a list where index 0 is 0,0, index 1 is 0,1 etc.
From there, you can find the x and y using: x = index // world_size and y = index%world size. this is what I did for sunflowers
You can have the list be 0 for non pumpkins and 1 for pumpkins.
I think I’m… not a fan of that lol. I’ll show y’all how I do sunflowers tomorrow
@@OlexaYT Fair enough. I'm excited to see a different approach from the one I did.
Can't wait to see your solution for sunflowers, it should be fun!
Not wanting to toot my own horn, but sunflowers is our best and most successful one yet. Can’t wait to show yall
@@OlexaYTSo you already recorded the next episode without this one reaching 1k likes? You spoil us!
You guys are animals, you’ll reach it
@@OlexaYT turns out this was a series after all 😈
I’m really just Python automated farming likes at this point. It’d be really cool to just have y’all like all my videos :) it helps me out a lot, so this is a good incentive
I really hope everyone keeps liking these videos, i love this series (not series) so much
When you have your list of coords you only take the first coord in the list, but you take them in the order of where you plant them, when you use your goto(x,y) it takes the next spot in the order of plots but not the closer to the spot you are in wich is not using your goto as efficiently as you made it. It will only go to the next plot wich could be just under the plot that you are on but it could be on the other side of the whole field and you could have an empty plot just before the plot you are on wich will be the closest but with the list of coords it will goto the plot on the other side of the field and not to the closest wich is not the most efficient. Don’t know if i’m explaining it good but i think you can check the coord list and goto the empty plot wich will be the closest and not just the next in the list
56:27 it goes back once the pumpkin is fully grown because the drone doesn’t know that it has been grown. It has to scan that there is a pumpkin at least once, and that is the once
Juuuust finished the last video, hell yeah keep making these plzzzz
Always happy to see more of this series!!
With all this automation and power we have lost something great. We lost the backflips.
liking and commenting for the inevitable mental breakdown over digital pumpkins and optimization nightmares.
I don’t even know why I find this so fascinating. I have no clue how to code and no desire to learn how.
But take my like and make the next one
My first liked video on this platform from a creator, really good content, love your stuff
You could refactor your goto by re-ordering the if statement order and then collapsing two of the if else into the else statement as it runs identical while statements.
Maaan, I found this game yesterday, right before going to bed, woke up this morning, and began playing it throughout the day (mostly cuz I was getting a bit tilted from work), ended up spending like 30 mins to make something similar to the motion system U did, similar idea, looking completely different, bit shorter, not that it made it easier to read haha.
Then after that, I just leaned into the motion system and made a self-balancing system that harvests everything by using the companion bonuses and does a full set of pumpkins occasionally.
Currently, the balancing is 1:1 for everything, but tomorrow 100% gonna add a balancer based on proportions between a set of variables I can adjust, proportions are great to just leave the app running while doing something else.
Btw, I work with data integration, so 99% SQL automation, but Python is soooo great to work with, it even had me staying late tonight to mess around with it a bit more, a breath of fresh air coming from cursed 15+ years old procedures that no one knows how they still work.
For the goto function, have you considered modular arthmetic? That seems to be how moving x and y works. With a world size of 6, your distance is (current x - target x) mod worldsize - worldsize/2 is how much you move left/right
That’s almost correct, but that means a distance of 4 would become moving 1 instead of moving -2. To counterbalance, you’ll want to add worldsize/2 before the modulus. It’s like moving 3 to the right, then not moving (cause moving 6 is the same as 0), and then moving 3 back to the left. Position doesn’t change.
@@nuggetoftruth865 ah, circled back to the video, I see. Thank you, stranger!
I tried it out, and modular arithmetic does dramatically simplify the code:
a = (currx - x) % size
b = (x - currx) % size
if a
Your goto(...) could be way more efficient. Something like so:
def goto(x, y):
# We're left of the target location
while get_pos_x() < x:
move(East)
# We're right of the target location
while get_pos_x() > x:
move(West)
# We're below the target location
while get_pos_y() < y:
move(North)
# We're above the target location
while get_pos_y() > y:
move(South)
No need to be checking world side and doing weird abs(...) checks just to figure out which direction to move. The difficult part of this is that we don't have a confirm for what the directions mean (North/South/East/West). So it's not like we can say "move((-1, 0))" and that'll move us West 1 tile. If we could somehow correlate the different in position with directions, it'd be even easier to write since we could just calculate the different and then tell the drone to follow that path.
If you want to move an indentation back, you can mark what you want to move and press shift tab.
Took me wayy to long to figure out when i started programming
You can cut the number of if's in half by testing (abs(currx - x) > (size / 2)) == (currx > x), rather than having and. When they are the same you move east, when they are different you move west, and because you only have one if, you tie-break being equidistant, rather than fail to move or have the final else
So for my GoTo function, I went with calculating how many moves in each direction need to be made, then just plugged the positive or negative resulting value into a helper function that runs a loop to move that many times depending on if the original value was positive or negative to determine direction. I ended up with this:
def move_drone(amount, negative_dir, positive_dir):
if amount == 0:
return
for i in range(abs(amount)):
if amount < 0:
move(negative_dir)
else:
move(positive_dir)
def move_x(amount):
move_drone(amount, West, East)
def move_y(amount):
move_drone(amount, South, North)
def calc_move_amount(half_size, delta, abs_delta, sign):
if abs_delta > half_size:
return (get_world_size() - abs_delta) * sign
else:
return delta
def goto(dest_x, dest_y):
delta_x = dest_x - get_pos_x()
delta_y = dest_y - get_pos_y()
abs_delta_x = abs(delta_x)
abs_delta_y = abs(delta_y)
flipped_sign_x = (delta_x / abs_delta_x) * -1
flipped_sign_y = (delta_y / abs_delta_y) * -1
half_world_size = get_world_size() // 2
move_x(calc_move_amount(half_world_size, delta_x, abs_delta_x, flipped_sign_x))
move_y(calc_move_amount(half_world_size, delta_y, abs_delta_y, flipped_sign_y))
I think it turned out fairly compact, though there might be some Python specific tricks that could combine a few things. This is my first time writing Python code, I'm usually more familiar with C style languages, C#, Javascript, etc.
You could calculate delta x and y using '(dest_x - get_pos_x() + half_world_size) % get_world_size() - half_world_size' I used that in a function that calculates the wrapping offset between two coordinate tuples for my goto.
I don't own the game, so my code won't work directly, however I saw that many of the different starting conditions result in the same outcome.
def goto(coord: list[int]):
directionList = [["North", "South"], ["East", "West"]]
for coordId, dest in enumerate(coord):
curr = currCoords[coordId]
direction = directionList[coordId][((dest-curr >= 0) ^ (abs(dest-curr) >= mapSize[coordId]/2))]
while currCoords[coordId] != dest: move(direction)
This uses an xor to filter out those extra steps and make it more compact.
This doesn't automatically update the new positions, so replacing "currCoords[coordId]" with "[get_pos_y, get_pos_x][coordId]()" might be usefull.
This code was made for an asymmetrical n-dimensional map, so "mapSize[coordId]" can be replaced with "get_world_size()".
edit: The ingame version would look something like this.
def goto(coord):
directionList = [["South", "North"], ["East", "West"]]
for coordId, dest in enumerate(coord):
curr = [get_pos_y, get_pos_x][coordId]()
direction = directionList[coordId][((dest-curr >= 0) ^ (abs(dest-curr) >= get_world_size()/2))]
while [get_pos_y, get_pos_x][coordId]() != dest: move(direction)
edit2: switched "South" and "North" to compensate for flipped board in game.
i like watching the your debugging process. i think it's helpful to see how to debug code. it's a large part of programming. remember "code does what it's told to do, seldom what you want it to do".
have you thought about debugging with breakpoints? also when the code is paused, just hover the mouse over a variable to see it's value.
great videos, keep 'em coming.
I kinda forgot there was a built in debugger in this haha
I started to learn coding (mainly in C++). And I am not that far into it, but seeing a list with different types of data was deeply unsettling to me. Love the videos!
In most oop langs you can cast to object and store an object list, but please just don't
@@russianyoutube Is it generally not good to use objects that way?
@@davez5201 depends on your usecase
I was waiting until I played the game first to come look at your videos of it and I'm so pleased that I've never touched Python before this game and I'm much happier with my pumpkin harvesting than you seem to be lol
This is my code for pumpkins:
def pumpkin_cycle():
pumpkin_size = 0
reset_position()
while True:
for i in range(get_world_size()):
for j in range(get_world_size()):
if get_entity_type() != None:
pumpkin_size += 1
else:
plant(harvest_crop)
move(direction)
move(East)
if pumpkin_size == get_world_size() ** 2:
do_a_flip()
harvest()
else:
reset_position()
pumpkin_size = 0
Wow, you came up with pretty much the same solution I did, which I guess makes sense since it seems somewhat obvious that moving over and checking pumpkins that have already fully grown is wasting time when you only need to check the pumpkins you've planted recently.
There'a a quality of life to organize the various functions (files I guess), If u drag one on top of the name of another one they get stacked.
The way I solved "goto" is basically just calculating if I need to go further than half the map, then go the opposide way.
I also added safe offsets, because going outside the map goes to the opposide side, so negative values calculates from opposide border, eg -1 is either max X or Y cord. Useful if you plant something and just want to go to last tile and wait for it to be fully grown like sunflowers.
Just for comparison, my "goto(x,y)" function is this:
```
def XY():
return get_pos_x(), get_pos_y()
def goto(x=None,y=None):
size = get_world_size()
if x < 0:
x = size + (x % -size)
x = x % size
if y < 0:
y = size + (y % -size)
y = y % size
atX, atY = XY()
goX, goY = East, North
if x != None and x != atX:
if x < atX and atX - x < size / 2 or x > atX and x - atX > size / 2:
goX = West
while get_pos_x() != x:
move(goX)
if y != None and y != atY:
if y < atY and atY - y < size / 2 or y > atY and y - atY > size / 2:
goY = South
while get_pos_y() != y:
move(goY)
```
Watching you try to figure out why your drone keeps going when you didn't update your current x variable was great.
Saying "is about to work perfectly", then it failing, failing some more, and failing a bit extra, then working is such a programming moment.
This game is so fun
Love your vids They always give me new ideas
I really want to see how you will handle the sunflowers. I'm just glad I came up with a working solution for them.
Culture shock cause we learnt and used absolute value since 5th grade. Love this series
Love this series
26:13 I was yelling to my screen "You are not updating the value!!" I've been there too 😢😂
I thought of a simple algorithm for goto with fewer "if" conditions that I thought you might enjoy (apologize if it already appears in the comments - I didn't see it). I'll just do the X movement.
You have ww (the world width, 6), sx (starting x position) and ex (ending x position). You'd calculate (ex - sx) and (ex - sx) - ww and take the one where the absolute value is smaller. If that answer was negative, move that many spaces West, and if it was positive, move that many spaces East. So for ww=6, sx=1, ex=5 you'd get (ex -sx) = 4 and (ex-sx)-ww = -2. |-2| is the smaller, so you'd move 2 spaces West. For ww=6, sx=5, ex=1 you'd get (ex-sx) = -4 and (ex-sx)-ww = 2. |2| is the smaller, so you'd move two spaces East.
The final pumpkin harvest reveal was so incredible
amazing coverage!
Here is an algorithm that does this in linear time (O(n)) if you want. (Don't read this if you want to try that yourself first).
If someone has a more efficient algorithm for that I'd love to hear it!
You can separate the problem into two stages.
First during the initial planting stage you plant on every tile that needs planting and build up a stack of positions that don't have harvestable pumpkins.
Next is the filling stage where you pop an item from the stack and check the position for a harvestable pumpkin, if there is no pumpkin, plant one and add the position to the bottom of your stack. If there is a pumpkin that is harvestable continue.
Once your stack is empty it means that all positions have a harvestable pumpkin and you can harvest, and restart.
I’m doing my part!
Have you considered just doing incremental steps in the goto function?
The drone just moves while its not at the target, und just does one step to the target each iteration, it looks if its, above, below, left or right of the target, and then moves in the corresponding direction.
def goto(t_x,t_y):
x = get_pos_x()
y = get_pos_y()
while x != t_x and y != t_y:
x = get_pos_x()
y = get_pos_y()
if x < t_x: #drone left of target
move(East)
elif x > t_x: #drone right of target
move(West)
elif y < t_y: #drone below target
move(North)
elif y > t_y: #droen above
move(South)
To further increase efficiency you could calculate dx, and dy and then move multiple times in one direction instead of repeating the process.
a bit too late for commenting, but I think the goto function could get way easier if you work using the vector from origin to destiny to choose direction, and if modulus is greater than half the diagonal of the total field, reverse the direction based on the angle of the vector is facing: towards north or south, only Y axis, and towards east or west, only X axis. If facing a diagonal (NE, SE, NW or SW) both of them
Me seeing olexa pull up paint and drawing a data table: Leans forward and pays Extra attention
concerning the goto function.
I'd make variables for both cases.
wrap_around = abs(curry-y) < size/2
move_up = ((curry-y) < 0
If they are both equal move(North) else move(South)
Same for x but different.
Gotta say, with the codeing shown in the first videos, i am impressed with the go to function :p Guess you are better at it when you get to have some time to plan and not on the spot xD
Crazy that somebody who does something for a living is pretty okay it
Can’t watch yet, but popped in to drop a like. Gotta get that next episode out asap ❤
I got this to work with the position thing you got me inspired to try it myself and it looks awesome:
def goto(x,y):
start_x = get_pos_x()
start_y = get_pos_y()
world_size = get_world_size()
if abs(start_x-x) < world_size/2:
for i in range(abs(start_x-x)):
if start_x-x < 0:
move(East)
else:
move(West)
else:
while (x != get_pos_x()):
if start_x-x < 0:
move(West)
else:
move(East)
if abs(start_y-y) < world_size/2:
for j in range(abs(start_y-y)):
if start_y-y < 0:
move(North)
else:
move(South)
else:
while (y != get_pos_y()):
if start_y-y < 0:
move(South)
else:
move(North)
i never thought i'd get programming lessons on an automation game
i require more
There is another way to solve problem of "way" == "half of world size". You should use >= and and < and get rid of "else". Than you can tune it so drone will prefer to stay inside farm. This looks more beautiful in situations like you have to go from 0,0 to 3,3 and back to 0,0 (on farm size 6).
Also, I believe, ((currx - x) < 0) is slower than (currx < x) and it is doing same thing.
my approach for goto function was entirely different.
i based mine on the understanding that every point on the board is equal that same point + world_size.
for a world size 6: (0,0) === (6,6) === (0,6) === (6,0)
using this assumption we can calculate a simple diff from point A to B, from point A to B+world_size and from point A+world_size to B
the absolute min between those three is the shortest path and the positivity of the difference determines if we must invert our direction laterally\horizontally
code:
def short_direction(target, position):
direction = position - target
inverted_p = position + get_world_size() - target
inverted_t = position - (target + get_world_size())
min = direction
if abs(min) > abs(inverted_p):
min = inverted_p
if abs(min) > abs(inverted_t):
min = inverted_t
return min
def move_or_invert_move(direction, inverted):
if direction == East:
if inverted:
direction = West
elif direction == West:
if inverted:
direction = East
if direction == North:
if inverted:
direction = South
elif direction == South:
if inverted:
direction = North
move(direction)
def goto(target_x, target_y):
short_x = short_direction(target_x, get_pos_x())
short_y = short_direction(target_y, get_pos_y())
for i in range(0, abs(short_x)):
move_or_invert_move(West, short_x < 0)
for i in range(0, abs(short_y)):
move_or_invert_move(South, short_y < 0)
@45:40 selection then tab or shift + tab is a life changer
but Shift+Tab = Steam overview xD
Thanks for your video. I yoinked your goto code.. still going to write my own nightmare fuel code for pumpkins using the updated goto and a list. Probably will play with tuples also. Funny thing is after I saw the code for goto I went AAAH yea I understand now. Funny thing is it is 200 cycles (whatever that means in real world time) per move of the drone. So saving a few moves saves time, but really would we even notice?
Oh and thank you for doing these videos. Been fun game to practice/relearn what I forgot over the years for coding.
Peace
it does a full scan because you are too fast. you only check for pumpkin, but not if its fully grown -> the drone tries to harvest the last pumpkin that is still growing. The scanning is planting for the next round
27:00
I'm dying inside.
bright side. I'm now going to buy this. been programming for 20 years. kinda hated python when I gave it a whirl more than a decade ago. this has maybe changed my mind
I think I would've approached the move something like:
while x != curr_x:
if abs(curr_x - x) > (curr_x+world_size) - x:
move(West)
else:
move(East)
No idea if that's correct though... Wrapping math hurts my head...
I've just tried and it only moves east
@@andreabiaggi8643 I had a proper go at solving it, this was my final solution:
def MoveTo(x, y):
def domove(c, posFun, posMove, negMove):
cur = posFun()
if c == cur:
return
d2 = c - cur # no wrap
d1 = d2 - get_world_size() #left/lower wrap
d3 = d2 + get_world_size() #right/upper wrap
moves = 0
if abs(d1) < abs(d2):
moves = d1
elif abs(d2) < abs(d3):
moves = d2
else:
moves = d3
dir = posMove
if moves < 0:
dir = negMove
moves = -moves
while moves:
moves -= 1
if not move(dir):
return
domove(x % get_world_size(), get_pos_x, East, West)
domove(y % get_world_size(), get_pos_y, North, South)
Whenever I have to do these sorts of cases I use something binary addition with the boolean statements:
while currx != x:
switch_x = (abs(currx - x) > size/2) + 2*((currx - x) > 0)
if switch_x in [0,3]:
move(East)
elif switch_x in [1,2]:
move(West)
currx = get_pos_x()
That’s one of the places I tend to go as well. Personally though, I’m a big math guy, so I first try to see if I can transform the input space into the output space via pure math. Removing if statements is satisfying haha
dir = [East, West, South, North]
dist_x = (target_x - current_x + gridsize/2) % gridsize - gridsize/2
dist_y = (target_y - current_y + gridsize/2) % gridsize - gridsize/2
for i in range(abs(dist_x):
move(dir[min(max(dist_x, 0), 1)])
for i in range(abs(dist_y):
move(dir[min(max(dist_y, 0), 1) + 2])
Do be enjoying these
25:42 It's pretty good
26:05 it immediately Implodes
if you have xor, you could do this use one of the statements in the video, if it is true with xor, move the opposite way as the statement said in the video and if the xor is false, move the same way.
Just finished implementing this in my game yesterday
Your goto() function can take advantage of the min() function you unlocked to cut down on all the if-elif statements.
def goto(x, y):
CURRX = get_pos_x()
CURRY = get_pos_y()
SIZE = get_world_size()
delta_x = abs(CURRX - x)
x_steps = min(delta_x, SIZE - delta_x)
x_direction = "EAST" # Assume you will move to the right
if (delta_x >= SIZE / 2):
x_direction = "WEST" # Move to the left instead
delta_y = abs(CURRY - y)
y_steps = min(delta_y, SIZE - delta_y)
y_direction = "NORTH" # Assume you will move up
if (delta_y >= SIZE / 2):
y_direction = "SOUTH" # Move down instead
# Actually move the drone
for x in range(x_steps):
if x_direction = "EAST":
move(East)
else:
move(West)
for y in range(y_steps):
if y_direction = "NORTH":
move(North)
else:
move(South)
Love this series btw, just wanted to give a slight suggestion ❤
I look forward to mazes. :)
took me over an hour to do something similar, ended up just using 2 while loops for east and north and call it a day.
Totally fair!
This is where I start to get confused.
Edit: the live debugging helped me understand it
Really liking this little game
I know this is terrible programming, but you don't really need temp_coord. If you append to coord_list directly, the for loop continues, since you keep adding stuff to it, until you don't add it anymore.
For the move function, you can calculate how many times you have to move east or west, and use that instead of the while condition.
Loving the series
I’ve been burned mannnnnny times in the past by removing data from a list while iterating through it so as a safe measure I like to make sure I create a temp list that I can use
Classic Heisenbug that cannot be observed when debugging, because of timings :D
Man refuses to unlock Fertilizer and makes spaghetti code as a result: The Movie
this feels like one of my teachers classes and i love it lol
I wanted to try making the wrapping goto function myself. Had fun and made it so abs() wasn't needed.
# # don't have dict yet
# swap_dir = {
# West: East,
# East: West,
# North: South,
# South: North
# }
# # are the directions just numbers? 0-3
# swap_dir = [0,0,0,0]
# swap_dir[West] = East
# swap_dir[East] = West
# swap_dir[North] = South
# swap_dir[South] = North
def swap_dir(direction):
if direction == West:
return(East)
elif direction == East:
return(West)
elif direction == North:
return(South)
elif direction == South:
return(North)
def move_dir(direction, amount=1):
# check for negative amount
if amount < 0:
d = swap_dir(direction)
a = amount * -1
else:
d = direction
a = amount
# if faster to loop around
if a > get_world_size()/2:
d = swap_dir(d)
a -= get_world_size()/2
else:
d = direction
a = amount
# actualy move now
for x in range(a):
move(d)
def goto(x, y):
move_dir(West, get_pos_x() - x)
move_dir(South, get_pos_y() - y) # y=0 is at the bottom
Olexa keeping at 1000 because he wants to continue just as much as we do lmao. The issue is your drone is too fast and detects a growing pumpkin which triggers the harvest and reset.
Instead of rescanning the entire field over and over:
1. plant the entire field once
2. move on to next code block or call a function to fill in the missing pumpkins
3 to fill in, scan until you encounter a blank square
4. add water, plant new pumpkin, and do a flip (1 second delay) or print something (1 second delay) or both (2 second delay); pumpkin will grow in 1 second if square is watered.
5. rescan the immediate square. If still empty, repeat step 4
The drone, because of the delay, will not move on until the square has a fully grown pumpkin. This will fill in all the empty squares, one at a time, and returns (or ends the loop) when the last square of the grid scans as occupied or harvestable. This procedure only navigates the grid once, bypassing the final redundant full grid scan.
6 when the function returns or the fill code ends, perform a harvest and loop back to step 1
You could fertilize the square to grow the pumpkin immediately so you don't need to delay, but fertilizing does not affect the odds of death. If the grid has many missing pumpkins, the cost of fertilizer may not justify using it on pumpkins.