#๐ Bird Trading Code Review
787 messages ยท Page 1 of 1 (latest)
@regal adder
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
I'm trying to optimize my code here.
https://paste.pythondiscord.com/AQSQ
The duplication prevention doesn't work yet and I'd like to add in a real life time-based element for determining which birds spawn as well. Overall, I'd just like to try and simplify/optimize my code because it's getting to be a bit much and I don't know I'm doing things the best way they can be done yet.
for i in range(0, len(available_items)):
if i[i] == item_selection:
item_selection.pop(i)
Well, this has a few issues. i[i] is using the index instead of a list, and item_selection.pop(i) doesn't seem to be the one you want to remove things from, right?
A list comprehension like this would probably be better:
available_items = [item for item in available_items if item != item_selection]
That rebuilds the list with everything that isn't the current selection.
It should also probably be done inside the for loop where you're picking the items, not after it. Right now you do the selections and then remove the last item selected from the available list.
For optimizing, you're doing basically the same thing across three different bird lists, so it would make sense to create a single function and call it for each of them, passing the item lists and such as parameters.
Hm. So if I understand that right, it would be like this, yes?
if bird_species in bird_list_common:
for item in item_list_common:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, bird_items_offered_count):
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
available_items = [item for item in available_items if item != item_selection]
Right. That would first fill up the available_items list based on reputation, and then loop through them removing choices from that list as it goes
That appears to prevent duplication until it runs out of items in the list, in which case it resets to the original list.
Yeah, so how do I prevent it from resetting to the original list once the list with removals becomes empty or there's not enough of them to fill up the item_selections_list?
[True, True, True, False, False]
BIRD SPAWN # 0
BIRD SPECIES Courigon
------Courigon 100 4------
['Tumbled Agate', 'Shiny Pebble', 'Tumbled Agate', 'Shiny Pebble']
BIRD SPAWN # 1
BIRD SPECIES Courigon
------Courigon 100 4------
['Shiny Pebble', 'Tumbled Agate', 'Lost Mail', 'Shiny Pebble']
BIRD SPAWN # 2
BIRD SPECIES Trinketalon
------Trinketalon 100 4------
['Rations', 'Rations', 'Wrist - Wraps - Bloodied', 'Rations']
BIRD SPAWN # 3
BIRD SPAWN # 4
Or maybe it's just not preventing the duplications at all and I missed something?
Are you wanting to prevent duplications across any birds? Or just the same bird?
Not duplications of birds.
Duplications of items.
So if it selects a bird from the common list, I don't want it to offer 4 Shiny Pebbles for example.
If the list is too small for it to give the full amount of offers, it should only give the amount it can instead of duplicating too.
Ah, gotcha
Couple changes then. You'll want to check at the start of the loop to make sure there's still items in available_items and break if you've run out
Also missed that the items are tuples, so your list comprehension should be [item for item in available_items if item[0] != item_selection]. Because you need to compare the names
That's why it wasn't catching the duplicate, sorry about that
Oh, no worries at all. You're teaching me too so I appreciate all the help!
So would a good check for that be
if bird_items_offered_count < len(item_selection_list):
```?
It did not like that.
In the loop right now you run for whatever the bird_items_offered_count is, and the available_items list is what's going to eventually run out and cause issues.
So you can either check to see which is smaller, something like min(bird_items_offered_count,len(available_items)) and use that for your loop range, or you can check the length of available_items at the start of the loop and, if it's empty, break out.
Oh I think I put the < the wrong way.
Oh so instead of this.
if bird_species in bird_list_rare:
for item in item_list_rare:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, bird_items_offered_count):
print(f'COUNT {bird_items_offered_count} LIST {item_selection_list}')
if bird_items_offered_count > len(item_selection_list):
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
[item for item in available_items if item[0] != item_selection]
I'd do this.```py
if bird_species in bird_list_rare:
for item in item_list_rare:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, min(bird_items_offered_count,len(available_items))):
print(f'COUNT {bird_items_offered_count} LIST {item_selection_list}')
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
[item for item in available_items if item[0] != item_selection]
Am I understanding you correctly?
Looks right. That should run for either the offer count, or how many items there are to offer, whichever one is smaller.
Hm. It's close.
[True, False, False, False, True]
BIRD SPAWN # 0
------Courigon 100 4------
COUNT 4 LIST []
COUNT 4 LIST ['Lost Mail']
COUNT 4 LIST ['Lost Mail', 'Tumbled Agate']
['Lost Mail', 'Tumbled Agate', 'Shiny Pebble']
BIRD SPAWN # 1
BIRD SPAWN # 2
BIRD SPAWN # 3
BIRD SPAWN # 4
------Courigon 100 4------
COUNT 4 LIST []
COUNT 4 LIST ['Tumbled Agate']
COUNT 4 LIST ['Tumbled Agate', 'Shiny Pebble']
['Tumbled Agate', 'Shiny Pebble', 'Tumbled Agate']
It's adding an additional one on there though?
[item for item in available_items if item[0] != item_selection] this isn't being assigned to available_items
You're just building the new list and tossing it lol
Oh woops.
It works!
Thank you so much for your help!
So then to optimize my code, what would I call the current function and then the function that properly generates the item list?
Maybe get_bird_rarity and then generate_bird_item?
Yeah, ideally you'd take what you have there and make it into a function that you can then call for every bird list, instead of copying the changes over to repeat the instructions for each one
Makes it easier to read and debug, and if you change it in the future you only have to adjust in one place and not multiple
Yeah, let me see if I can manage that and then submit for your approval.
Hm. This is very confusing.
Okay I think I got that part right but now it's saying that it can't compare a tuple to an int for the ```py
if item[2] <= player_reputation:
Because item[2] is equal to ('Lost Mail', 10, 20).
The whole tuple.
But it was indexing into the tuple before so I'm not sure what changed.
Because now item is a list???
Here, this is what I have right now.
Not understanding what changed and why item is now a list.
You seeing any clue of what I did incorrectly?
OH.
Track back through the function calls. So here's our issue, item_list is no longer a list of items. So something is calling this function wrong:
generate_bird_items(item_list, bird_species)
That happens up in generate_bird_spawns, with this: generate_bird_items(get_bird_rarity(bird_spawns[i]), bird_spawns[i])
So the item list we're feeding is whatever get_bird_rarity() returns. Which is this:
if bird_species in bird_list_common:
return item_list_common, bird_species
if bird_species in bird_list_uncommon:
return item_list_uncommon, bird_species
if bird_species in bird_list_rare:
return item_list_rare, bird_species
Tuples
I see.
That is not what I intended.
How do I return the item_list and bird_species separately then?
You don't. You get one return call from a function, so they have to be returned as a tuple, a list, some way of packaging them together. You need to split them up after they're returned.
Hmmmm okay.
Assuming you need them both returned at all, are you even using the bird species from this?
I understand.
Yes, bird_species goes into the get_player_reputation.
Because reputation differs depending on the species.
Right, but you already do that
bird_spawns[i] = generate_bird_species()
generate_bird_items(get_bird_rarity(bird_spawns[i]), bird_spawns[i])
I'm not sure I follow.
Oh like I could call get_player_reputation there and then it would be passed into generate_bird_items as an argument?
You generate a bird species and assign it to bird_spawns[i]
You then get the bird rarity by passing it the species. That function returns the item list and the species, which you already have, because you gave it to the function in the first place
def get_bird_rarity(bird_species): #Bird species is given to this function
...
if bird_species in bird_list_common:
return item_list_common, bird_species #You return the item list, and also the bird species that was given to the function
So basically you don't need to return both, you don't use it, you just need the item list
I'm honestly very confused.
When you call generate_bird_items(get_bird_rarity(bird_spawns[i]), bird_spawns[i])
You're giving it two arguments:
(get_bird_rarity(bird_spawns[i]), bird_spawns[i]
The first one is your function, which returns an item list and a bird species.
The second one is a bird species
Yes.
get_bird_rarity() doesn't need to return a bird species
You already have it
That argument is just for the item list
๐
Need to add functionality for actually naming the locations based on the town like oh it's in Town X, so it's on Fence 1, and stuff like that but that shouldn't be too hard. Then I can add functionality for making it so some bird species are IRL time-locked so the owl ones only come out at night.
Thank you so much for all your help again. You are a godsend!
Sure thing, glad you got it working ๐
Do you have any interest in working on video games?
Not at the moment, but that is how I got started in Python lol
Ah fair enough. I'm trying to build out the basic functionality for this feature in Python now since I kind of know Python and don't know any C++ for Unreal.
Solid plan. The more you practice and work with it, the faster and better you'll be at it. Even if you decide to learn C++ down the road, a decent bit of the logic will carry over.
Hopefully! If you meet anyone who'd like to work on something like Pokemon but better, let me know! I'd always appreciate the help.
Sorry to bug you one more time, but how might I implement time-locked spawns here? I've got the time ranges I'd like them to be potentially active for but I'm not sure of the best way to implement it.
def generate_bird_species():
rarity_list = [("Common", 80), ("Uncommon", 15), ("Rare", 5)]
#Bird_list format is ("Species", Probability, Beginning Spawn Time, End Spawn Time)
bird_list_common = [("Courigon", 100, "4:00:00", "20:00:00")]
bird_list_uncommon = [("Trinketalon", 90, "4:00:00", "20:00:00"), ("Baublekaw", 10, "20:00:00", "8:00:00")]
bird_list_rare = [("Hautehoot", 90, "22:00:00", "4:00:00"), ("Vogueowl", 10, "22:00:00", "4:00:00")]
rarity_selection = random.choices(population=[i[0] for i in rarity_list], weights=[i[1] for i in rarity_list], k=1)[0]
if rarity_selection == "Common":
bird_species = random.choices(population=[i[0] for i in bird_list_common], weights=[i[1] for i in bird_list_common], k=1)[0]
elif rarity_selection == "Uncommon":
bird_species = random.choices(population=[i[0] for i in bird_list_uncommon], weights=[i[1] for i in bird_list_uncommon], k=1)[0]
elif rarity_selection == "Rare":
bird_species = random.choices(population=[i[0] for i in bird_list_rare], weights=[i[1] for i in bird_list_rare], k=1)[0]
return bird_species
A variable that tracks the time, and then compare that to the start/end of the spawn times. If it's valid then it can be included.
Also, not sure how familiar you are with classes and object oriented programming, but as your birds gain more properties unique to each of them (rarity, spawn times, location, etc.) it would likely be better to go that route in the long run
I've got the datetime module in there already yes. I just am not sure how I'd do the conditions for it.
I'm somewhat familiar with classes and you do have a point. Would everything then go into the bird class?
Yeah, you'd have it store properties like the species, rarity, when it spawns, item lists (or keep those separate and look them up based on rarity, either way). Will make it easier to reference and compare things as your code grows and logic gets more complicated.
You have your start and your end, so you'd use datetime to get the current time and see if it's after the start and before the end.
Yes, I understand the logic for it, I just don't know how to implement it in the code itself.
Working on this now. Maybe that would be a better way overall to do all the birds then? Remove the rarity groups then and just imply rarity through probability instead of a two-tiered probability?
You'll use the time objects in the module:
https://docs.python.org/3/library/datetime.html#time-objects
datetime.datetime.now() gives you the current date and time, and then you can call .time() on that to just get the time. Similarly, you can set variables to hold the start and end times by building them with datetime.time(hour,minute,second). The hour, minute, second would come from the start and end times you've saved.
Once you have your start, end, and current time you can compare them like you would regular integers. Later times will be greater than earlier times.
Definitely could. This is one of those fun areas where you get to pick how it's structured. Whatever makes the most sense to you. You might write a bunch of it one way only to realize it doesn't work or you actually hate it, and go back to redo it.
I did this to get the time but they probably both work. ```py
current_datetime = datetime.now()
current_time = current_datetime.strftime("%H:%M:%S")
print(f'DATETIME {current_datetime} TIME {current_time}')
I mean like I literally don't know what code you'd add for that because I'm not sure how I could add an if statement in there.
And here's what I have so far for the class.
class OfferingBird:
def __init__(self, name, probability, spawn_time_start, spawn_time_end, possible_locations):
self.name = name
self.probability = probability
self.spawn_time_start = spawn_time_start
self.spawn_time_end = spawn_time_end
self.possible_locations = possible_locations
#OfferingBird format: Name, Probability, Spawn Time Start, Spawn Time End, Possible Locations
OfferingBird1 = OfferingBird("Courigeon", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""])
OfferingBird2 = OfferingBird("Baublekaw", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""])
OfferingBird3 = OfferingBird("Trinketalon", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""])
OfferingBird4 = OfferingBird("Outflit", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""])
OfferingBird5 = OfferingBird("Hautehoot", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""])
OfferingBird6 = OfferingBird("Vogueowl", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""])
Well, first you need to get the time variables set up so that you can compare them. So you'll still need to get your start, current, and end times as .time() objects. In your code snippet current_time is now a string, which you can't check times with > and <.
Then once you have them, you can just add it to the list comprehension when you're building the bird species to return:
[i[1] for i in bird_list_common if start_time < current_time and current_time < end_time]
Good start. You probably don't need to set up individual instances in a variable for each one, just have a list of all the different options to pick from, but this should work.
Oh I see. I'll change it to yours then.
So then I'd rebuild the start and end times like this.
OfferingBird1 = OfferingBird("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", ""])
Please elaborate on why I don't need individual instances for each one?
So I have
current_datetime = datetime.now()
current_time = current_datetime.time()
...
#OfferingBird format: Name, Probability, Spawn Time Start, Spawn Time End, Possible Locations
OfferingBird1 = OfferingBird("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", ""])
OfferingBird2 = OfferingBird("Baublekaw", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", ""])
OfferingBird3 = OfferingBird("Trinketalon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", ""])
OfferingBird4 = OfferingBird("Outflit", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village", ""])
OfferingBird5 = OfferingBird("Hautehoot", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village", ""])
OfferingBird6 = OfferingBird("Vogueowl", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village", ""]
...
bird_species = random.choices(population=[i[0] for i in bird_list_common if start_time < current_time < end_time], weights=[i[1] for i in bird_list_common], k=1)[0]
and then I'm not sure how to adapt this to be used by classes and without individual variables for each bird.
Yeah, I'm not really understanding how I'd go about using classes in the flow of generating these. Could you provide an example of like how I'd integrate the generate_bird_spawns function into the class?
Don't know what sort of data type I should use for the possible locations either. Maybe dictionaries because each area will have a given number of spawns.
But could also do a list of tuples.
Or should that be it's own class? @clear thorn
Kind of spinning my wheels here.
OfferingBirdItems should be its own class for sure.
Basically you'd dynamically create the instances. You can do that by looping through the list of options if you just want one of each. The way you have it now you're creating a new variable every time you want to add a bird, and then you'd have to make sure that variable is included everywhere else those birds are referenced.
Doing something like this, all you have to do is add a new entry to the list of birds and it gets added automatically:
class OfferingBird:
def __init__(self, name, probability, spawn_time_start, spawn_time_end, possible_locations):
self.name = name
self.probability = probability
self.spawn_time_start = spawn_time_start
self.spawn_time_end = spawn_time_end
self.possible_locations = possible_locations
#A list of tuples for all the bird data, new birds just get a new one added to the list
bird_data = [("Courigeon", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""]),
("Baublekaw", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""]),
("Trinketalon", 80, "4:00:00", "20:00:00", ["Wellspring Village", ""]),
("Outflit", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""]),
("Hautehoot", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""]),
("Vogueowl", 80, "22:00:00", "4:00:00", ["Wellspring Village", ""])]
#A list for all the instances that you'll be sending to functions
all_birds = []
#Loop through all the options and make OfferingBird instances for each
for bird in bird_data:
name, probability, start_time, end_time, locations = bird
spawn_time_start = datetime.strptime(start_time, "%H:%M:%S").time()
spawn_time_end = datetime.strptime(end_time, "%H:%M:%S").time()
all_birds.append(OfferingBird(name, probability, spawn_time_start, spawn_time_end, locations))
So this way just reuses the bird variable each time.
You should make classes out of things that are going to have a lot of properties, and that you'll have a lot of copies of. Like the birds, you have a bunch of them and they all have the same type of properties but different values, so a class lets you more easily keep track of them.
If your locations aren't going to have properties, or only one or two, it might not be worth making a class for them.
Also, some people just like classes, so they make one for almost everything. It's personal choice.
Locations will be like Town X, but then they'll have 3-8 bird spawn locations.
And I might add more properties later perhaps.
If you think it makes sense to have it as a class, go for it. If nothing else, you can always change it later if you don't like how it's turned out.
If you do a lot of comparing and referencing, it can make it easier. Like for your birds in my example, if you loop through all_birds after making the instances, you can check for values just by calling the property. bird.name gives you the name, bird.probability gives the probability, etc. No more messing with tuples and indexes
Yeah, I can see the benefit. Just really struggling to understand how to implement them.
So we need to get_player_location so we know how many potential birds to spawn, and then for each one where it's True, we want to generate a bird and then generate its items_list.
Let me see if I can add comments so this makes more sense.
That comes with practice. It's difficult to plan out how you're going to use something when you're not terribly familiar with it. For the bird generation based on location, your basic logic could look like this:
player_location = get_player_location()
location_birds = [bird for bird in all_birds if player_location in bird.possible_locations]
Then you can do random.choice(location_birds) to get a random one, or something fancier with the probabilities
class Player:
def __init__(self, player_name, location_name):
self.player_name = player_name
self.location_name = location_name
def get_player_location():
#return self.location_name
return "Wellspring Village"
class OfferingBird:
def __init__(self, offering_bird_name, probability, spawn_time_range, locations_list, offering_list):
self.offering_bird_name = offering_bird_name #String
self.probability = probability #Integer
self.spawn_time_range = spawn_time_range #Tuple
self.locations_list = locations_list #List of Tuples
self.items_list = offering_list #Object
def generate_offering_bird_spawn_locations():
bird_spawn_list = []
player_location = get_player_location()
if player_location == "Wellspring Village":
#Get the locations_count number for that location from the associated OfferingBirdLocation object.
for i in range(0, #locations_count)
bird_spawn_list.append(random.choice([True, False]))
#for each True in the bird_spawn_list
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
class OfferingBirdLocations:
def __init__(self, location_name, locations_count):
self.location_name = location_name
self.locations_count = locations_count
def get_locations_count(location_name):
class OfferingBirdItems:
def __init__(self, offering_bird_item_name, probability, reputation_requirement):
self.offering_bird_item_name = offering_bird_item_name #String
self.probability = probability #Integer
self.reputation_requirement = reputation_requirement #Integer
This is what I've got built out. But like for example, how would I get the locations_count number for each player_location there?
Because order of operations, you enter an area and the list of True and False is generated based on the OfferingBirdLocation object's location_count variable. So if we enter Wellspring Village and the OfferBirdLocation object where self.name == "Wellspring Village" has a self.location_count = 5, we'd get something like [True, False, False, True, False].
And then for each True in that bird_spawn_list variable, we'd generate a bird to go there and then generate the associated OfferingBird object's offering list.
Right? Does that make sense?
It might be simpler to generate a random number between 0 and the location count, and then get that many random birds from the list. Either way should work though.
I figured doing it this way lets me use each False index as indicator of where the Trues are.
Otherwise how would you know which spots specifically were filled?
If it matters which ones are filled or not then yeah, that works just fine
Like if the individual locations in the location count of 5 have unique properties or considerations, then you want to keep track of them, so it makes sense. If it's just the number of total spawns and it doesn't matter where which bird goes, then you don't really need to keep track. At least as far as I see it with what you have
Yes, that's what I'm planning for. Possibly adding different things to each location like spawn rate modifiers so one type of bird appears more frequently there or something.
So what would be the syntax for getting the location_count integer for the player_location in the generate_offering_bird_spawn_locations function?
Obviously not hard-coded to individual strings but I'm not sure how you'd write it to be dynamic.
The first thing would be to keep in mind that any functions inside a class are going to be specific to each instance of that class. Your function to generate spawn locations should probably not be tied to that class. Otherwise you'll be awkwardly calling bird.generate_offering_bird_spawn_locations() when it has nothing to do with the bird.
Similarly, to get the player location you'd be calling player.get_player_location(). Or even just player.location if you keep it as a property that gets updated when the player moves.
Hm. So generate_offering_bird_spawn_locations should not be in a class at all?
That makes sense. Okay.
There will probably be multiple players so player.get_player_location for each Player object should be good I think.
To make it dynamic would just be something like this. Whenever your game logic has the player move to a new location, you call player.update_player_location('somewhere'). That updates the location of the player object. Then whenever some other piece of code gets the location, it's returning wherever the player currently is
class Player:
def __init__(self, player_name, location_name):
self.player_name = player_name
self.location_name = location_name
def get_player_location(self):
#return self.location_name
return self.location_name
def update_player_location(self,new_location):
self.location_name = new_location
Yeah, just like the birds, each instance of the class you make will be distinct. Setting properties of one won't affect the others.
Ah okay. This makes sense to me.
So then we need to call get_player_location on the created player object.
player1 = Player("Shay", "Wellspring Village")
But how do we do that dynamically here?
def generate_offering_bird_spawn_locations():
bird_spawn_list = []
player_location = get_player_location()
if player_location == "Wellspring Village":
#Get the locations_count number for that location from the associated OfferingBirdLocation object.
for i in range(0, #locations_count)
bird_spawn_list.append(random.choice([True, False]))
#for each True in the bird_spawn_list
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
So that I'm not hard coding which player we're talking about?
That's going to depend on your game loop
Whatever logic is handling how your players submit inputs, how those inputs are processed, etc. That's going to be what calls these functions.
That's also when it would pass along which player it's updating
Okay so we'd need to do something like player.player_location and just player will be set elsewhere.
And then I'd need to prepopulate the OfferingBirdLocations right?
Right. You know you'll be passing a player object and what properties it's going to have, which is all you need to write this logic for now.
if __name__ == "__main__":
player_1 = Player("Shay", "Wellspring Village")
player_2 = Player("Neoncamouflage", "Wellspring Village")
offering_bird_location_1 = OfferingBirdLocation("Wellspring Village", 5)
Okay so we've got this as prepopulated data.
And then in the class we should have something like this right?
class OfferingBirdLocation:
def __init__(self, location_name, location_count):
self.location_name = location_name
self.location_count = location_count
def get_location_name():
return self.location_name
def get_location_count():
return self.location_count
Yeah, that would work. Then you'd make all the locations just like the birds and do whatever you need with them.
Why does it say self is undefined for the bottom 2 functions?
You forgot to pass it as a parameter. The functions don't know what self is
def get_location_name(self)
Oh oops. Thank you.
Okay so then how do I get the location_count for the OfferingBirdLocation where the location_name matches the Player location_name?
I'll repaste it for you so it's easier to follow.
First thing, you'll want to pass the player as a parameter. Not hardcode it
Because you want the main loop to be able to call generate_offering_bird_spawn_locations(player)
You'll also want to pass in the list of locations so you can loop through it
And check which matches the player location
Oh dang, you're correct.
Okay one second.
def generate_offering_bird_spawn_locations(player, offering_bird_location_list):
bird_spawn_list = []
player_location = player.get_location()
for offering_bird_location in offering_bird_location_list:
if player.get_location() == offering_bird_location_1.get_location_name:
offering_bird_location_count = offering_bird_location_1.get_offering_bird_location_count
#Get the locations_count number for that location from the associated OfferingBirdLocation object.
for i in range(0, locations_count)
bird_spawn_list.append(random.choice([True, False]))
#for each True in the bird_spawn_list
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
And then the location list.
And that would be me hard-coding all the OfferingBirdLocation objects and then putting that into a list, correct?
Yeah, sorry, had to run through it again. Just like we have the list of bird objects for each one, you'll have a list of location objects
No worries at all. It's a lot.
Okay, I've got 3 basic locations.
if __name__ == "__main__":
player_1 = Player("Shay", "Wellspring Village")
player_2 = Player("Neoncamouflage", "Wellspring Village")
offering_bird_location_1 = OfferingBirdLocation("Wellspring Village", 5)
offering_bird_location_2 = OfferingBirdLocation("Location 2", 3)
offering_bird_location_3 = OfferingBirdLocation("Location 3", 8)
offering_bird_location_list = [offering_bird_location_1, offering_bird_location_2, offering_bird_location_3]
Definitely still recommend doing that dynamically, but it'll work lol
def generate_offering_bird_spawn_locations(player, offering_bird_location_list):
bird_spawn_list = []
player_location = player.get_location()
for offering_bird_location in offering_bird_location_list:
if player.get_location() == offering_bird_location_1.get_location_name:
offering_bird_location_count = offering_bird_location_1.get_offering_bird_location_count
for i in range(0, offering_bird_location_count):
bird_spawn_list.append(random.choice([True, False]))
for i in range(0, bird_spawn_list):
if bird_spawn_list[i] == True:
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
I still don't quite get how you'd do that though.
Can you elaborate a bit more on the dynamic object creation?
You just make one list of all the details. So for the current locations it'd look like this:
location_data = [
('Wellspring Village',5),
('Location 2',3),
('Location 3',8)
]
Then we use one loop and make all the objects at once, in one list:
all_locations = []
for location in location_data:
name, count = location #This breaks the tuple's contents up and assigns each to a variable
all_locations.append(OfferingBirdLocation(name,count)) #Create the location with the data and add it to the list
Then anytime you need to search through locations, you just pass the all_locations list. If you need to make new locations, you add another tuple of data to location_data and don't have to worry about updating and making new variables everywhere else
Hm.
So then I can still search through location_data to find a specific location_name for example?
You could, but at that point there's no reason to be making them into objects. I'm assuming the locations will have more properties, methods, data that changes in each that you'd need to keep track of.
If not, and if it's pretty much always going to just be the name and the count, nothing that changes or updates or whatever. Then just use the data list from the get go and skip making them objects.
If you go that route, you can even make it a dictionary for extra convenience. Something like:
location_data = {
"Wellspring Village":5,
"Location 2":3,
"Location 3":8
}
And you can just pull count with location_data.get(player.location_name)
I want to do the object way just so I can learn more I think.
They're all options, no real wrong way, just whatever makes sense to you
That'll work
Okay I understand now. One moment while I do that for all the objects.
#Dynamically creates a list of all Player objects.
player_data = [
("Shay", "Wellspring Village"),
("Neoncamouflage", "Wellspring Village")]
all_players = []
for player in player_data:
player_name, player_location = player #This breaks the tuple's contents up and assigns each to a variable
all_players.append(Player(player_name, player_location)) #Create the location with the data and add it to the list
#Dynamically creates a list of all OfferingBird objects.
offering_bird_list = [
("Courigeon", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village", "Location 2", "Location 3"])
("Baublekaw", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village"]),
("Outflit", 80, (datetime.time(22,0,0), datetime.time(4,0,0)), ["Wellspring Village"]),
("Hautehoot", 80, (datetime.time(22,0,0), datetime.time(4,0,0)), ["Wellspring Village"]),
("Vogueowl", 80, (datetime.time(22,0,0), datetime.time(4,0,0)), ["Wellspring Village"])
]
all_offering_birds = []
for offering_bird in offering_bird_list:
player_name, player_location = player #This breaks the tuple's contents up and assigns each to a variable
all_players.append(Player(player_name, player_location)) #Create the location with the data and add it to the list
#Dynamically creates a list of all OfferingBirdLocation objects.
offering_bird_location_data = [
("Wellspring Village", "5"),
("Location 2", "3"),
("Location 3", "8")]
all_offering_bird_locations = []
for offering_bird_location in offering_bird_location_data:
player_name, player_location = player #This breaks the tuple's contents up and assigns each to a variable
all_players.append(Player(player_name, player_location))
Much more difficult to keep track of 3 of these.
Have to fix my comments too.
Phew. Okay, that was hard.
So then we have to use these in the generate_offering_bird_spawn_locations function.
Probably want to change the loops to match:
for offering_bird_location in offering_bird_location_data:
player_name, player_location = player
Right now they're all player
But then yeah. For the spawn locations it'll just be generate_offering_bird_spawn_locations(player,all_offering_bird_locations). Then the function checks the locations for which name matches where the player is, and uses that location's count to make your True/False list
Hm.
I'm getting an error trying to initialize the birds.
PS C:\Users\Shay\Documents\Godot\3DActionRPGRemasteredTutorial\3DActionRPGRemasteredPractice> & C:/Users/Shay/AppData/Local/Programs/Python/Python311/python.exe c:/Users/Shay/Documents/MyCode/MyPythonCode/NotPokemonBirdTrades.py
c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py:157: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
("Courigeon", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village", "Location 2", "Location 3"])
DATETIME 2024-12-12 15:26:11.863426 TIME 15:26:11.863426
Traceback (most recent call last):
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 157, in <module>
("Courigeon", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village", "Location 2", "Location 3"])
^^^^^^^^^^^^^^^^^^^^
TypeError: descriptor 'time' for 'datetime.datetime' objects doesn't apply to a 'int' object
I'm only seeing examples for creating a datetime.time object where you have to put in year, month, and day too.
(class) datetime
datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
The year, month and day arguments are required. tzinfo may be None, or an instance of a tzinfo subclass. The remaining arguments may be ints.
But all I care about is time of day. ๐ค
The datetime module has different object types. The most common is datetime. There's also date and time. In this case, we only want the time.
(datetime.time(4,0,0), datetime.time(20,0,0))
These are tupled together
Yeah, that way I could keep them as one variable and just do [0] or [1] depending on if I want start or end time.
Aren't I already doing datetime.time. though?
from datetime import datetime
You want to import time, or just import datetime by itself without specifying the datetime object
I tried that before and it didn't work.
Hmmm. Let me try again.
So you're saying just do ```py
import datetime
Right, that will let you use any of the objects. datetime.time(), datetime.date(), or datetime.datetime()
from datetime import datetime specifically imports the datetime object type, and not the other two
AttributeError: 'datetime.time' object has no attribute 'time'
What's your full code lol
import datetime
import random
current_datetime = datetime.time()
current_time = current_datetime.time()
print(f'DATETIME {current_datetime} TIME {current_time}')
Ah, gotcha, this bit is specific to trying to get the current time
current_time = current_datetime.time()```
You're getting the time. Then trying to get the time from the time. For this, you'll do:
current_datetime = datetime.datetime.now()
current_time = current_datetime.time()
Just because time() by itself doesn't have anything like now() to get the current one. You have to go through datetime first
Sorry about the confusion
Ah okay. That seems really odd.
You'd think they'd have one that just gives you the time.
Some better way might exist and I just don't know about it, that's certainly possible lol
๐ How do we fix the ones in the objects then?
("Trinketalon", 80, (datetime.time(4,0,0), datetime.time(20,0,0)), ["Wellspring Village"]),
Those are fine, it's just mad that the two are tupled together
The bird class, last I saw, expects them separate
If you want them as one value then you can change the tuple to an array and it should chill out
class OfferingBird:
def __init__(self, offering_bird_name, probability, spawn_time_range, locations_list, offering_list):
self.offering_bird_name = offering_bird_name #String
self.probability = probability #Integer
self.spawn_time_range = spawn_time_range #Tuple
self.locations_list = locations_list #List of Tuples
self.items_list = offering_list #Object
What is the difference between a tuple and an array?
It's now just made that a tuple object is not callable.
Tuples are immutable, they can't be changed once they're created whereas lists can. That's why you can't pop or append things with a tuple. Some functions this will cause an issue with, that's why I jumped to that here.
Arrays are what every other language calls a list, lol
Oohhh. Gotcha! ๐
Okay so then back to this.
def generate_offering_bird_spawn_locations(player, offering_bird_location_list):
bird_spawn_list = []
player_location = player.get_location()
for offering_bird_location in offering_bird_location_list:
if player.get_location() == offering_bird_location.get_location_name:
offering_bird_location_count = offering_bird_location.get_offering_bird_location_count
for i in range(0, offering_bird_location_count):
bird_spawn_list.append(random.choice([True, False]))
print(f'THIS IS IT {bird_spawn_list}')
for i in range(0, bird_spawn_list):
if bird_spawn_list[i] == True:
bird_spawn_list[i] = True
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
Yep, now you should be all set for this.
Though you've got some bugs, like missing some parentheses on function calls: offering_bird_location.get_offering_bird_location_count. So you'll probably have a few syntax errors to work through
Alright, fixed that.
How can I call this so I can test it?
generate_offering_bird_spawn_locations(player, offering_bird_location_list)
So I need to dynamically get those two arguments.
Which means I need a get_player_name function.
You can make one if you want. Or if you're looping through the players, then just player.player_name. Like this:
for player in all_players:
generate_offering_bird_spawn_locations(player.player_name, offering_bird_location_list)
or wait, no
Why do you need the name? lol
Because isn't that how we tell whose location where's getting in player.get_location?
Wait no.
That takes no arguments.
Yep, because that function is specific to each player instance, since it's inside the class
That goes back to how your main game loop will work. The simplest way to test right now is just to loop through all your players and do a thing for each
Hm.
Well let's start with player_list[0] for now I suppose so I can test the bird generation.
That works too
generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list[0])
offering_bird_location_list[0] your function wants the whole list, I believe.
Because it's checking which one your player is at
Oh yes. You are correct.
One moment, I forgot to remove the second tier of rarity from the item list so I've got to fix that.
So if we have item_name, probability, and reputation_requirement as item variables, what would be the best way to say that "X item can only come from Y offering bird"?
Probably just have to add another variable for items and birds I guess.
Hm. Actually no. I could do one list variable for items and just have that be a list of all the birds that can drop that item.
And that's much easier to change.
If you want it that specific, X item from Y bird, then yeah you'll need probably a property on the birds that contains their items. If you want something that goes by probability or such, then you can just compare that with each of the birds
Yep, that works
Okay back to the important stuff.
def generate_offering_bird_spawn_locations(player_name, offering_bird_location_list):
bird_spawn_list = []
player_location = player_name.get_location()
for offering_bird_location in offering_bird_location_list:
if player_name.get_location() == offering_bird_location.get_location_name():
offering_bird_location_count = offering_bird_location.get_offering_bird_location_count
for i in range(0, offering_bird_location_count):
bird_spawn_list.append(random.choice([True, False]))
print(f'THIS IS IT {bird_spawn_list}')
for i in range(0, bird_spawn_list):
if bird_spawn_list[i] == True:
bird_spawn_list[i] = True
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
I should put the current time inside Player objects too.
Is that specific to each player?
Timezones, so yeah.
Hm, that's fair
So then if we have a True value, we need to generate a bird.
Though it'd probably be better to save their timezone instead. Because once you set the player's time to the current time and it ticks over a minute, that time property is no longer accurate. You have to get it agan.
Not every minute, just in those functions that need a current time.
Well that would be every minute.
Everything will require current time.
Going to have lots of time-based events and all that.
Ah. For the moment I recommend just grabbing the current time where you need it. Timezones make things...messy
So that's going to be its own can of worms to debug and get working lol
Save it for after you've got your structure figured out
๐ Gotcha! Will do. Let's get the bird functions working.
The time thing is weirding me out so we can finish that later.
for i in range(0, offering_bird_location_count):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'method' object cannot be interpreted as an integer
Yep. Easy enough to expand what you have later on to also include a check for the time
So that means it wants an integer but found a method instead, which is a function that belongs to a class
Oops I forgot the ().
You'll see that when you forget parentheses. Instead of calling a function, you directly assign it
Yep
It's saying it's a string though.
Oh because it is.
I'm dumb.
for i in range(0, bird_spawn_list):
^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'list' object cannot be interpreted as an integer
Length.
Yep lol
I'm learning, I swear.
Classic part of programming. "Ok, it should all work now" and then you have to run it 14 times as it yells about all the little things you forgot
You can see it in real time.
So true.
Okay so now.
How do I call the start and end times there?
def generate_offering_bird_spawn_locations(player_name, offering_bird_location_list):
bird_spawn_list = []
player_location = player_name.get_location()
for offering_bird_location in offering_bird_location_list:
if player_name.get_location() == offering_bird_location.get_location_name():
offering_bird_location_count = offering_bird_location.get_offering_bird_location_count()
print(f'AWAWAWA {offering_bird_location_count}')
for i in range(0, offering_bird_location_count):
bird_spawn_list.append(random.choice([True, False]))
print(f'THIS IS IT {bird_spawn_list}')
for i in range(0, len(bird_spawn_list)):
if bird_spawn_list[i] == True:
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if start_time < current_time < end_time], weights=[i[1] for i in offering_bird_list], k=1)[0]
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
You need offering_bird_list, this function doesn't have it
So you either need to return the list of bird spawn locations, if that's all this function is supposed to provide, and then call another function or run through the logic to assign those birds. Or you need to pass the list of birds into this function so you can use it
Probably do a generate_offering_bird_spawn function instead and it send the returned spawn locations to it I think.
Or would it make more sense as one function in your opinion?
Either can work. It's best to separate them if the two actions, generating a spawn list and getting birds based on a spawn list, might ever happen separately.
If they'll only ever happen at the same time, as you're doing it now, then it can be one. I would change the function name in that case though, so it's more accurate
Probably default to the first one so you're not finding yourself writing half this function over again if you run into a situation where you do need that logic separated out
They should only ever happen at the same time.
The whole bird system will only ever work together.
You say that now ๐
Yeah, that's a fair point.
Okay, so we'll return the bird_spawn_list.
def generate_offering_bird_spawn_locations(player_name, offering_bird_location_list):
bird_spawn_list = []
player_location = player_name.get_location()
for offering_bird_location in offering_bird_location_list:
if player_name.get_location() == offering_bird_location.get_location_name():
offering_bird_location_count = offering_bird_location.get_offering_bird_location_count()
for i in range(0, offering_bird_location_count):
bird_spawn_list.append(random.choice([True, False]))
return bird_spawn_list
Honestly if you keep this project going long term, especially as you learn more, you'll probably wind up rewriting a good portion of this. Maybe all of it lol
Just how it goes
Hopefully I learn how to do it too. ๐
Okay so then return bird_spawn list and then put the output into generate_offering_bird_spawns.
Yep
Wait, it says return's in the wrong spot?
Oh that's right. Need to remove more of it.
def generate_bird_spawns(bird_spawn_list, offering_bird_list):
for i in range(0, len(bird_spawn_list)):
if bird_spawn_list[i] == True:
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if start_time < current_time < end_time], weights=[i[1] for i in offering_bird_list], k=1)[0]
#generate an OfferingBird object
#retrieve that OfferingBird object's associated OfferingBirdItems object
Looks good, you'll need to get the current time if you want to do that check right now though
I don't think those need a whole function
Your list is full of bird objects, so you can just directly reference the property like this: i.spawn_time_range[0] < current_time < i.spawn_time_range[1]
Since you're looping through each bird with i
Ohhhh.
I already made the get functions but that is good to know!
def generate_bird_spawns(bird_spawn_list, offering_bird_list):
for i in range(0, len(bird_spawn_list)):
if bird_spawn_list[i] == True:
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
weights=[i[1] for i in offering_bird_list], k=1)[0]
That'll work too
Using getters and setters, functions specifically to set or get a variable, is generally the more robust way to go about it. If you wind up changing formats or structure later on, it can save a lot of time by not needing to go rewrite every single place you reference the property
Ah, okay.
I'll be robust then!
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
So this should run both functions.
That will, yes
Hm...
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'get_spawn_time_start'
hmm
i is 0?
def generate_bird_spawns(bird_spawn_list, offering_bird_list):
for i in range(0, len(bird_spawn_list)):
if bird_spawn_list[i] == True:
print(f'AAHAHA {i}')
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
offering_bird_list[i].get_spawn_time_start() < current_time < offering_bird_list[i].get_spawn_time_end()],
weights=[i[1] for i in offering_bird_list], k=1)[0]
print(f'BIRD SPAWNS {bird_spawn_list}')
I believe it should be this.
Indexing with i.
for i in offering_bird_list this loops through the elements, not indexes
i[0] this is taking the first element of that element, which should make it freak out if it's an object
Is offering_bird_list the list of bird data, or the list of birds
I think it's the list of bird data
List of objects.
#Dynamically creates a list of all OfferingBird objects.
offering_bird_data = [
("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2", "Location 3"]),
("Baublekaw", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village"]),
("Outflit", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Hautehoot", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Vogueowl", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"])
]
offering_bird_list = []
for offering_bird in offering_bird_list:
offering_bird_name, probability, spawn_time_range, locations_list, offering_list, player_location = offering_bird #This breaks the tuple's contents up and assigns each to a variable
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_range, locations_list, offering_list))
That doesn't look right
for offering_bird in offering_bird_list:
That should be offering_bird_data
But that means the list should be empty, odd
Ah, you are correct.
OKAY. Fixed that.
Now I need to build out the item lists for each of those birds too so I can build them into the bird objects.
#Dynamically creates a list of all OfferingBirdItem objects.
offering_bird_item_data = [
("Shiny Pebble", 50, 0, ["Courigeon"]),
("Tumbled Agate", 40, 5, ["Courigeon"]),
("Lost Mail", 10, 20, ["Courigeon"]),
("Rations", 50, 0, ["Courigeon"]),
("Wrist Wraps - Clean", 40, 5, ["Courigeon"]),
("Wrist - Wraps - Bloodied", 10, 20, ["Courigeon"]),
("Running Shoes", 85, 0, ["Courigeon"]),
("Fingerless Gloves - Black", 5, 10, ["Courigeon"]),
("Fur-collared Jacket", 5, 85, ["Courigeon"]),
("Wellspring Village Pendant", 5, 90, ["Courigeon"])]
offering_bird_item_list = []
for offering_bird_item in offering_bird_item_data:
item_name, probability, reputation_requirement, offering_bird_list = offering_bird_item #This breaks the tuple's contents up and assigns each to a variable
offering_bird_item_list.append(OfferingBirdItem(item_name, probability, reputation_requirement, offering_bird_list)) #Create the location with the data and add it to the list
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
hc
So how do I look at the list at the end there and make it so that based on the birds in that list, it builds out an item list for that bird of only ones where their name was in the associated birds list?
#Dynamically creates a list of all OfferingBird objects.
offering_bird_data = [
("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2", "Location 3"]),
("Baublekaw", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village"]),
("Outflit", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Hautehoot", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Vogueowl", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"])]
offering_bird_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_range, locations_list, offering_list, player_location = offering_bird #This breaks the tuple's contents up and assigns each to a variable
for offering_bird_item in offering_bird_item_list:
if offering_bird_name in offering_bird_item_list:
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_range, locations_list, offering_list)) #Create the location with the data and add it to the list
Are you wanting this item list generated after generate_bird_spawns?
Oh you want the items as a property on the bird. So like the Gourigeon bird object would have a list of its items
Correct!
#Dynamically creates a list of all OfferingBird objects.
offering_bird_data = [
("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2", "Location 3"]),
("Baublekaw", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village"]),
("Outflit", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Hautehoot", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Vogueowl", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"])]
offering_bird_list = []
offering_bird_item_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_range, locations_list, offering_list, player_location = offering_bird #This breaks the tuple's contents up and assigns each to a variable
for offering_bird_item in offering_bird_item_list:
if offering_bird_name in offering_bird_item_list[3]:
offering_bird_item_list.append(offering_bird_item)
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_range, locations_list, offering_list)) #Create the location with the data and add it to the list
I think I'm close here.
Pretty much there
You'll want to scoot the second for loop over so it's inside the first one. Because you want to loop over every item, and do it for every bird
So like this?
#Dynamically creates a list of all OfferingBird objects.
offering_bird_data = [
("Courigeon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2", "Location 3"]),
("Baublekaw", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, datetime.time(4,0,0), datetime.time(20,0,0), ["Wellspring Village"]),
("Outflit", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Hautehoot", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Vogueowl", 80, datetime.time(22,0,0), datetime.time(4,0,0), ["Wellspring Village"])]
offering_bird_list = []
offering_bird_item_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_range, locations_list, offering_list, player_location = offering_bird #This breaks the tuple's contents up and assigns each to a variable
for offering_bird_item in offering_bird_item_list:
if offering_bird_name in offering_bird_item_list[3]:
offering_bird_item_list.append(offering_bird_item)
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_range, locations_list, offering_list)) #Create the location with the data and add it to the list
Yep
Okay, gotcha.
offering_bird_list[i].get_spawn_time_start() < current_time < offering_bird_list[i].get_spawn_time_end()],
~~~~~~~~~~~~~~~~~~^^^
TypeError: list indices must be integers or slices, not str
My fix did not work here.
Print out offering_bird_list before that and see exactly what it is
This still doesn't make sense
oh wait
If it's still for i in then that isn't an index
i is the bird
or the data item, whichever that list is
OFFERING BIRD LIST ['Courigeon'] I 0
OFFERING BIRD LIST ['Courigeon'] I 1
Looks like bird name value.
Well that's not right
Nope, we need the OfferingBird object.
Go check where you're calling that function and see what you're passing it
At some point something got mixed up
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
odd
Mind dropping the full thing again lol
I'm not sure what's gone astray with just the snippets
Yup!
Think I messed up something with adding the item list to the birds.
Offering_bird_item_list is indeed the full list of item objects.
Definitely different errors on that
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, offering_list = offering_bird
This needs offering_list removed, since that isn't part of the bird data
Yeah I caught that.
offering_bird_list = []
offering_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list = offering_bird #This breaks the tuple's contents up and assigns each to a variable
for offering_bird_item in offering_bird_item_list:
print(f'OFFERING BIRD ITEM LIST 3 {offering_bird_item_list}')
if offering_bird_name in offering_bird_item_list[3]:
offering_bird_item_list.append(offering_bird_item)
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, offering_list)) #Create the location with the data and add it to the list
print(f'OFFERING BIRD LIST2 {offering_bird_list}')
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
I need to index into the items in offering_bird_item_list before getting the 3rd element.
Oh the last line, appending the bird to the offering bird list, needs to back up out of that second for loop
Gotcha.
if offering_bird_name in offering_bird_item_list[3]:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: argument of type 'OfferingBirdItem' is not iterable
OFFERING BIRD ITEM LIST 3 [<main.OfferingBirdItem object at 0x000001E951463CD0>, <main.OfferingBirdItem object at 0x000001E951463D10>, <main.OfferingBirdItem object
at 0x000001E951463D50>, <main.OfferingBirdItem object at 0x000001E951463D90>, <main.OfferingBirdItem object at 0x000001E951463DD0>, <main.OfferingBirdItem object at
0x000001E951463E50>, <main.OfferingBirdItem object at 0x000001E951463E90>, <main.OfferingBirdItem object at 0x000001E951463ED0>, <main.OfferingBirdItem object at 0x000001E951463F10>, <main.OfferingBirdItem object at 0x000001E951463E10>]
So for that it looks like you've already turned those items into objects
Meaning you need to access their properties, not indexes
for offering_bird_item in offering_bird_item_list:
This makes offering_bird_item the item object as it loops through the list
You need to call any functions on that, not the whole list
I am confused.
this part
That is what we have already.
You might have it twice, but for that line specifically, it should be offering_bird_item, not offering_bird_item_list
Because only the item has that function to call
Okay please show me what should be the current version.
I may have messed up somewhere.
bird_specific_item_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list = offering_bird
for offering_bird_item in offering_bird_item_list: #Loop through each item object in the list
if offering_bird_name in offering_bird_item.get_offering_bird_list(): #Call the get function on the item to get the list of birds, and see if our bird is in it
bird_specific_item_list.append(offering_bird_item) #If so, add it to our bird specific item list
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, bird_specific_item_list)) #Create the bird with all the data, and our item list
I made a new variable for our bird specific items, because they were getting reused in the wrong places
Now that will break up the data into variables, then for each item it will call the function to get the bird list from that item object. If the bird is in that list, it adds it to the bird specific item list. Which is then used at the end to make the bird object
Very good call on the new variable. I was very confused.
Yeah, that'll happen when there's too many similar ones
Okay so that I beleive should be fixed.
Then we have
line 93, in generate_bird_spawns
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 94, in <listcomp>
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'get_spawn_time_start'
For that one, i is still the bird name. It's getting passed the data for the function somehow I believe. Go to the start of that function, as the first step, and print out the list to see what it is
Gotcha.
def generate_bird_spawns(bird_spawn_list, offering_bird_list):
for i in range(0, len(bird_spawn_list)):
print(f'OFFERING BIRD LIST {offering_bird_list} I {i}')
if bird_spawn_list[i] == True:
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
weights=[i[1] for i in offering_bird_list], k=1)[0]
print(f'BIRD SPAWNS {bird_spawn_list}')
OFFERING BIRD LIST ['Courigeon', <__main__.OfferingBird object at 0x0000012C4B8E01D0>, <__main__.OfferingBird object at 0x0000012C4B8E0250>, <__main__.OfferingBird object at 0x0000012C4B8E0290>, <__main__.OfferingBird object at 0x0000012C4B8E02D0>, <__main__.OfferingBird object at 0x0000012C4B8E0310>, <__main__.OfferingBird object at 0x0000012C4B8E0350>] I 0
That's not quite right.
How did that get in there?
lol, nope
Try printing out the list right after it gets made, is it getting inserted there?
Or in another function
bird_specific_item_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list = offering_bird
print(f'WOWOWOWOW {bird_specific_item_list}\n')
for offering_bird_item in offering_bird_item_list: #Loop through each item object in the list
if offering_bird_name in offering_bird_item.get_offering_bird_list(): #Call the get function on the item to get the list of birds, and see if our bird is in it
bird_specific_item_list.append(offering_bird_item) #If so, add it to our bird specific item list
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, bird_specific_item_list)) #Create the bird with all the data, and our item list
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
It's getting inserted right at the end.
Or at least, between the end and where it gets called in that function. Interesting
Yeah.
How could that be?
The moment it hits generate_bird_spawns, that gets added.
But that doesn't make any sense.
offering_bird_item_data = [
("Shiny Pebble", 50, 0, ["Courigeon"]),
("Tumbled Agate", 40, 5, ["Courigeon"]),
("Lost Mail", 10, 20, ["Courigeon"]),
("Rations", 50, 0, ["Courigeon"]),
("Wrist Wraps - Clean", 40, 5, ["Courigeon"]),
("Wrist - Wraps - Bloodied", 10, 20, ["Courigeon"]),
("Running Shoes", 85, 0, ["Courigeon"]),
("Fingerless Gloves - Black", 5, 10, ["Courigeon"]),
("Fur-collared Jacket", 5, 85, ["Courigeon"]),
("Wellspring Village Pendant", 5, 90, ["Courigeon"])]
offering_bird_item_list = []
for offering_bird_item in offering_bird_item_data:
item_name, probability, reputation_requirement, offering_bird_list = offering_bird_item
No, it gets added before that
offering_bird_list gets reused as a variable name in the item data
And after that, contains the name of one bird
You can either do the good code practice thing and find a new variable to use for the item data, or just reset offering_bird_list = [] before you start looping through the bird data
The part where you make the item list:
offering_bird_item_list = []
for offering_bird_item in offering_bird_item_data:
item_name, probability, reputation_requirement, offering_bird_list = offering_bird_item #This breaks the tuple's contents up and assigns each to a variable
offering_bird_item_list.append(OfferingBirdItem(item_name, probability, reputation_requirement, offering_bird_list)) #Create the location with the data and add it to the list
You change offering_bird_list to something else in both lines, because we need that variable later
Ah okay.
Then down where you're making the bird list I think you'll still need to create the variable offering_bird_list = [] because I don't actually see it anywhere else
Nice, now to see how it else explodes
๐
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 93, in generate_bird_spawns
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 93, in <listcomp>
bird_spawn_list[i] = random.choices(population=[i[0] for i in offering_bird_list if
~^^^
TypeError: 'OfferingBird' object is not subscriptable
Bare minimum I am learning a lot about how to find and fix bugs.
So it should just be
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
weights=[i for i in offering_bird_list], k=1)
```?
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()]```
This part yes, because you're looping over the birds in the bird list and getting their spawn time starts and ends
weights=[i for i in offering_bird_list]
This part you'll need to get the weights
Hm. Okay that makes sense but then it's saying this.
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 217, in <module>
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 93, in generate_bird_spawns
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Shay\AppData\Local\Programs\Python\Python311\Lib\random.py", line 495, in choices
cum_weights = list(_accumulate(weights))
^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unsupported operand type(s) for +: 'OfferingBird' and 'OfferingBird'
I don't believe we're trying to add birds where it says we are.
It's mad about the weights
weights=[i for i in offering_bird_list] This part, where I said you need to get the weights ๐
i for i in offering_bird_list means you take each bird in the list and assign it to the first i. Since they're objects, now you need to get its probability property to use for the weight
Oh hm.
What's the syntax for that then?
i.get_probability() for i in offering_bird_list?
Exactly
I'm learning! ๐
Oh maybe not.
raise ValueError('The number of weights does not match the population')
ValueError: The number of weights does not match the population
XD
oh duh
if i.get_spawn_time_start() < current_time < i.get_spawn_time_end()
This bit. Is limiting the population
So it needs to limit the weights too
Oh man. How do I do that?
def generate_bird_spawns(bird_spawn_list, offering_bird_list):
for i in range(0, len(bird_spawn_list)):
if bird_spawn_list[i] == True:
print(f'BIRD SPAWN LIST {bird_spawn_list}')
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
weights=[i for i in offering_bird_list if
i.get_spawn_time_start() < current_time < i.get_spawn_time_end()], k=1)
print(f'BIRD SPAWNS {bird_spawn_list}')
Seems to run fine for me
Really?
I get this.
Traceback (most recent call last):
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 216, in <module>
generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list)
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 93, in generate_bird_spawns
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if i.get_spawn_time_start() < current_time < i.get_spawn_time_end()],
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Shay\AppData\Local\Programs\Python\Python311\Lib\random.py", line 495, in choices
cum_weights = list(_accumulate(weights))
^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unsupported operand type(s) for +: 'OfferingBird' and 'OfferingBird'
oh, what
weights=[i for i in offering_bird_list if
That part got changed back
It's missing the function to get the probability
Oh weird.
Woops.
Good catch!
BIRD SPAWNS [False, False, [<main.OfferingBird object at 0x000001CD08160310>], False, [<main.OfferingBird object at 0x000001CD08160310>]]
WOOOOOOOO!
It works! ๐
Okay, and then just the items.
But dinner is ready so I'll be back shortly! Thank you again so incredibly much for all your help!
Okay, I am back!
def generate_bird_items(item_list, player_reputation):
bird_items_offered_count = get_bird_items_offered_count(player_reputation)
available_items = []
item_selection_list = []
for item in item_list:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, min(bird_items_offered_count,len(available_items))):
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
available_items = [item for item in available_items if item[0] != item_selection]
return item_selection_list
def get_player_reputation(bird_species):
if bird_species == "Courigon":
player_reputation = 100
elif bird_species == "Trinketalon" or "Baublekaw":
player_reputation = 100
elif bird_species == "Hautehoot" or "Vogueowl":
player_reputation = 100
return player_reputation
def get_bird_items_offered_count(player_reputation):
if player_reputation == 100:
return 4
else:
return player_reputation // 33 + 1
Okay so from generate_bird_items we return the bird_spawn_list which looks like this.
[False, False, [<main.OfferingBird object at 0x000001CD08160310>], False, [<main.OfferingBird object at 0x000001CD08160310>]]
So then we want to get the available items based on the player's reputation and the bird type.
So reputation should probably be a Player object variable.
Should reputation be across bird species, across locations, both, etc.?
@clear thorn How would you write for bird in bird_spawn_list where bird != False?
def generate_bird_offers(player_name, bird_spawn_list):
for bird in bird_spawn_list if bird != False:
if bird.get_offering_bird_name in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
if bird.get_offering_bird_name in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
if bird.get_offering_bird_name in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
for item in bird_spawn_list[i]:
print(f'WOWOW {item}')
``` This is what I have so far.
Honestly no idea, that's more a gameplay question I think
Probably just make a guard clause at the start of the loop. If bird is false then we continue to the next one
def generate_bird_offers(player_name, bird_spawn_list):
for bird in bird_spawn_list:
if not bird:
continue
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
Fair.
Gotcha.
And then apparently bird is a list item.
if bird.get_offering_bird_name in ["Courigeon"]:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'get_offering_bird_name'
So I need to index.
Wait no.
They're both lists.
Wait no.
You're missing parentheses
But apparently that is a list lol
Oh hey yeah, they are lists
()
[False, [<__main__.OfferingBird object at 0x000001513C3C3370>], False, False, [<__main__.OfferingBird object at 0x000001513C3C3370>]]
Ah you found it first. ๐
Ah, random.choices() returns a list, even if it's only one item, right.
So you can just throw a [0] on the end of that, back in the generate_bird_spawns() function if you want
Yup!
def generate_bird_offers(player_name, bird_spawn_list):
for bird in bird_spawn_list:
if bird:
print(f'BIRD {bird.get_offering_bird_name()}')
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
print("Common")
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
print("Uncommon")
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
print("Rare")
get_bird_items_offered_count(player_reputation)
Okay so this is where I'm at.
Then we need to add the reputation.
Hm.
Okay my reputation math function was poor.
Need your thoughts on a good way to do this.
def get_bird_items_offered_count(player_reputation):
if player_reputation == 100:
return 4
else:
return player_reputation // 33 + 1
Maybe I should do it to a maximum of 5 actually.
Then each 25 reputation would unlock another trade window with 1 extra for 100.
Okay so how do I write this so that 0-24 returns 1, 25-50 returns 2, 51-75 returns 3, 76-99 returns 4, and 100 returns 5?
I feel like there's got to be a neat math way to do that instead of just comparisons and ranges.
player_reputation // 25 + 1 should do it
Ah okay.
So I was right.
But I'd still need that top part for 100 specifically right?
No way to math around that?
Wow I've been working on this too long. Good lord.
lmao
Goobye basic mathematical skills.
Okay so this works perfectly then.
def get_bird_items_offered_count(player_reputation):
return player_reputation // 25 + 1
Yep, that'll do it
So then we need to get the list of items that bird can carry and the ones that have reputation requirements less than or equal to the players reputation with that bird rarity.
def generate_bird_offers(player_name, bird_spawn_list):
for bird in bird_spawn_list:
if bird:
print(f'BIRD {bird.get_offering_bird_name()}')
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
print("Common")
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
print("Uncommon")
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
print("Rare")
print(f'ITEMS OFFERED COUNT {get_bird_items_offered_count(player_reputation)}')
Not really even sure how to start with that.
Well the birds already have their items as a property
So you just need to compare the reputation
Hm.
So to get the offering list I do bird.get_offering_list() but then can I do .get_reputation_requirement() on top of that so like bird.get_offering_list().get_reputation_requirement()?
WAIT.
I know.
lol, I believe in you
def generate_bird_offers(player_name, bird_spawn_list):
offerings_available = []
offerings_displayed = []
for bird in bird_spawn_list:
if bird:
print(f'BIRD {bird.get_offering_bird_name()}')
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
print("Common")
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
print("Uncommon")
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
print("Rare")
bird_offering_count = get_bird_offering_count(player_reputation)
print(f'BIRD OFFERING LIST {bird.get_offering_list()}')
for offering in bird.get_offering_list():
if offering.get_reputation_requirement() <= player_reputation:
offerings_available.append(offering)
for i in range(0, min(len(bird.get_offering_list), bird_offering_count)):
offerings_displayed = random.choices(population=[i for i in offerings_available], weights=[i for i in offerings_available], k=1)[0]
Okay this is what I've got right now.
You want to remove the [0] from the end of random.choices()
Because now you do actually want the list
Other than that, looks good at first glance
Ah that's right.
I missed ().
offerings_displayed = random.choices(population=[i for i in offerings_available], weights=[i for i in offerings_available], k=1)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Shay\AppData\Local\Programs\Python\Python311\Lib\random.py", line 495, in choices
cum_weights = list(_accumulate(weights))
^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unsupported operand type(s) for +: 'OfferingBirdItem' and 'OfferingBirdItem'
We had this earlier because we were filtering the list and reducing the amount of weights and the birds differently or something.
But we're not filtering it here so we do we hae that?
Oh, no, so this is mad about the weights. It's trying to add i for each one
But i is an item*
So basically you need to get a property from i to weight it by, like you did with probability on the birds
Or decide whether you actually need weights at all, because that's an optional argument
Oh I see.
Yes, we have weights for the items.
So weights=[i.get_probability() for i in offerings_available], k=1) right?
Yep. for i in offerings_available will loop through that list and assign each element to i, so you can call the function on it
Think I did something very wrong somewhere because the offerings lists are like 50 deep.
ope
def generate_bird_offers(player_name, bird_spawn_list):
offerings_available = []
offerings_displayed = []
for bird in bird_spawn_list:
if bird:
print(f'BIRD {bird.get_offering_bird_name()}')
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
print("Common")
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
print("Uncommon")
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
print("Rare")
bird_offering_count = get_bird_offering_count(player_reputation)
print(f'BIRD OFFERING LIST {bird.get_offering_list()}')
for offering in bird.get_offering_list():
if offering.get_reputation_requirement() <= player_reputation:
offerings_available.append(offering)
print(f'OFFERINGS AVAILABLE {offerings_available}')
for i in range(0, min(len(bird.get_offering_list()), bird_offering_count)):
offering = random.choices(population=[i for i in offerings_available], weights=[i.get_probability() for i in offerings_available], k=1)
offerings_displayed.append(offering)
offerings_available = [offering for offering in offerings_available if offering not in offerings_displayed]
print(f'AVAILABLE {offerings_available.get_item_name()} DISPLAYED {offerings_displayed}')
This is what I have.
That last for loop you probably don't need
Well that's how you determine how many items they get though.
Ah, I see
You make the offerings_available list of all the things that they could be given based on the bird and their reputation and then they get the lower of the two number of items available or their reputation item allowance.
offerings_available = [offering for offering in offerings_available if offering not in offerings_displayed]
This bit then, doesn't need to be executing every loop. Only once at the end, after the offerings_displayed are all gathered up.
You'll also want to stick the [0] back on the end of random.choices(), since you're not pulling the whole list at once like I thought you were
Though it's overwriting it, not appending, so it still shouldn't wind up with too many of them
Isn't that bit the duplication prevention though?
Because you wouldn't want all 5 of your offers to be Shiny Pebbles.
Should only allow 1 of each item.
Oh that's where that came from
Sorry, hard to keep track of this XD
Oh you posted the link again, let me go run it here
Just spent a very confused five minutes tracking down a totally different bug because my local time flipped over and all the birds went invalid ๐คฆ
๐
I spent a very confused 15 on the same bug.
I don't understand why it has so many objects.
OH
It's doing it for each bird.
bird_specific_item_list = []
offering_bird_list = []
for offering_bird in offering_bird_data:
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list = offering_bird
for offering_bird_item in offering_bird_item_list: #Loop through each item object in the list
if offering_bird_name in offering_bird_item.get_offering_bird_carrier_list(): #Call the get function on the item to get the list of birds, and see if our bird is in it
bird_specific_item_list.append(offering_bird_item) #If so, add it to our bird specific item list
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, bird_specific_item_list)) #Create the bird with all the data, and our item list
print(player_list)
bird_specific_item_list is never reset to empty for the next bird
So each one has the items of the last
AH.
Okay that makes sense.
Where would we reset it here?
Probably just inside the first for right?
Yep
Yeah, you'll need some logic in there to flip the check for the nocturnal birds.
How's that?
Right now it checks if i.get_spawn_time_start() < current_time < i.get_spawn_time_end(). Which works well if you want to see if your time is greater than say 6AM and less than 8PM. Doesn't work so well checking to see if your time is greater than 8PM and less than 6AM.
Remove nocturnal birds ๐
Nooooo, night birds are cool. Gives you something to stay up late for.
Probably just an if statement before that line to see if it's in the nocturnal time range
If not, run the line that's there. If it is, run different logic.
They won't all share the same ranges though.
Ah, welp
So how would we go about that?
Hm.
Okay well before that, I did this correct, yes?
offering_bird_list = []
for offering_bird in offering_bird_data:
bird_specific_item_list = []
offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list = offering_bird
for offering_bird_item in offering_bird_item_list: #Loop through each item object in the list
if offering_bird_name in offering_bird_item.get_offering_bird_carrier_list(): #Call the get function on the item to get the list of birds, and see if our bird is in it
bird_specific_item_list.append(offering_bird_item) #If so, add it to our bird specific item list
offering_bird_list.append(OfferingBird(offering_bird_name, probability, spawn_time_start, spawn_time_end, locations_list, bird_specific_item_list)) #Create the bird with all the data, and our item list
generate_bird_offers(player_list[0], generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list))
Looks like it, if the list is all you changed
Yeah because we don't want to reset offering_bird_list.
Okay, fully comprehended.
Hmmmmm. Would 26,0,0 as a time be considered 2AM but also greater than 7PM?
I'm not sure that would be valid
Hmmmmm.
You can try but I feel like datetime is gonna freak out about it
Yeah and that wouldn't be a proper solution.
Something with absolute value?
No... ๐ค
I am very confused.
If all the ranges were the same difference from start to end you could maybe do something with addition to the low one if it's in front.
def is_hour_between(start, end, now):
is_between = False
is_between |= start <= now <= end
is_between |= end < start and (start <= now or now <= end)
return is_between
And the choices line in generate_bird_spawns() turns to this:
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if is_hour_between(i.get_spawn_time_start().hour, i.get_spawn_time_end().hour,current_time.hour)],
weights=[i.get_probability() for i in offering_bird_list if is_hour_between(i.get_spawn_time_start().hour, i.get_spawn_time_end().hour,current_time.hour)], k=1)[0]
What is |=?
Thank you stackoverflow
https://stackoverflow.com/questions/20518122/python-working-out-if-time-now-is-between-two-times
๐
Inclusive or, I think. Basically it only assigns the value on the right if it evaluates to True
I pretty much never see it used
So in English, what is happening?
is_between is false, if now is between the start and end times, or if the end time is before the start time and now is greater than or equal to start, or less than or equal to end, then is_between becomes true
Basically it's just another way to do two if statements that would flip the return value to True
Ah okay. Could you show me how the two if statement function would work too so I can relate them in my head?
And does this mean we could only set whole hours for the bird spawn times?
For certain events and stuff I think it would be cool to have minutes and seconds involved too.
def is_hour_between(start, end, now):
is_between = False
if start <= now <= end:
is_between = True
elif end < start and (start <= now or now <= end):
is_between = True
return is_between
Correct
You can make the logic fancier when you want to add those ๐
What would be the method for adding them in?
Expanding the function to consider the minutes and seconds if the hours are equal should work
Then instead of passing it the hours when you call the function, you just pass it the whole time
I guess I'm confused as to how it wouldn't work already if you just pass it the whole time.
Hm. Maybe it would lol
Wouldn't just removing the .hour send the whole time?
Yep
The comparison doesn't mention it anywhere so I think that would work?
Yeah, makes sense to me
Hm.
Did you try it with the hours and you were able to get the nocturnal birds?
I'm still not getting them.
Oh I'm dumb.
Needed to adjust the bird start and end times.
Okay, yeah without the .hour works too!
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 241, in <module>
generate_bird_offers(player_list[0], generate_bird_spawns(generate_offering_bird_spawn_locations(player_list[0], offering_bird_location_list), offering_bird_list))
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 137, in generate_bird_offers
offering = random.choices(population=[i for i in offerings_available], weights=[i.get_probability() for i in offerings_available], k=1)[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Shay\AppData\Local\Programs\Python\Python311\Lib\random.py", line 507, in choices
total = cum_weights[-1] + 0.0 # convert to float
~~~~~~~~~~~^^^^
IndexError: list index out of range
Didn't we fix this one?
Did you change something for your population and not your weights
Remember any change to one has to be done to the other
Yeah I remember that but I don't think so?
Where you just changed the hours back to time
I'm guessing the other one in that statement still needs done
Nope. No mention of hours anywhere.
How can we print the population and weights to see what's going on?
def generate_bird_items(item_list, player_reputation):
bird_items_offered_count = get_bird_offering_count(player_reputation)
available_items = []
item_selection_list = []
for item in item_list:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, min(bird_items_offered_count,len(available_items))):
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
available_items = [item for item in available_items if item[0] != item_selection]
return item_selection_list
``` this is the old version if that helps at all.
Hm, yeah it's fine with that
Looked at it again and it says it's failing in generate_bird_offers()
Yeah but I don't believe we modified the population or weights here did we?
offering = random.choices(population=[i for i in offerings_available], weights=[i.get_probability() for i in offerings_available], k=1)[0]
If there are no items in offerings_available
It's randomly picking birds
If it randomly picks birds not allowed in this time
No items
I thought we made it so it could only pick birds allowed in this time?
I thought that's what this did. ```py
bird_spawn_list[i] = random.choices(population=[i for i in offering_bird_list if is_time_between(i.get_spawn_time_start(), i.get_spawn_time_end(),current_time)], weights=[i.get_probability() for i in offering_bird_list if is_time_between(i.get_spawn_time_start(), i.get_spawn_time_end(),current_time)], k=1)[0]
Isn't this saying that the random choices are limited by those that equate to true when their start and spawn times enter the is_time_between function?
But only sometimes... ๐ค
Oh, mine works again
I'm currently in hour 21. You have no birds that spawn in hour 21
Mine's this.
#Dynamically creates a list of all OfferingBird objects.
offering_bird_data = [
("Courigeon", 80, datetime.time(4,0,0), datetime.time(22,0,0), ["Wellspring Village", "Location 2", "Location 3"]),
("Baublekaw", 80, datetime.time(4,0,0), datetime.time(22,0,0), ["Wellspring Village", "Location 2"]),
("Trinketalon", 80, datetime.time(4,0,0), datetime.time(22,0,0), ["Wellspring Village"]),
("Outflit", 80, datetime.time(20,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Hautehoot", 80, datetime.time(20,0,0), datetime.time(4,0,0), ["Wellspring Village"]),
("Vogueowl", 80, datetime.time(20,0,0), datetime.time(4,0,0), ["Wellspring Village"])]
So it should still be working 100% of the time.
Always something that will be spawning.
Ah, yep, yours covers it
Strange then
Gonna have to track back and log outputs to see where and why the list comes up empty
On it!
And we're looking at offerings_available, correct?
Or are we 100% sure it's that the birds list is empty sometimes?
If this is still the error you're getting, yeah
File "c:\Users\Shay\Documents\MyCode\MyPythonCode\NotPokemonBirdTrades.py", line 137, in generate_bird_offers
offering = random.choices(population=[i for i in offerings_available], weights=[i.get_probability() for i in offerings_available], k=1)[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Shay\AppData\Local\Programs\Python\Python311\Lib\random.py", line 507, in choices
total = cum_weights[-1] + 0.0 # convert to float
Right now the issue is with the list index out of range on offerings_available
To be certain, you could always run these just before that line:
print("Population",[i for i in offerings_available])
print("Weights",[i.get_probability() for i in offerings_available])
That would show you the exact lists that are being generated for random.choices
Oh weird.
Population [<__main__.OfferingBirdItem object at 0x000001712D2B0510>, <__main__.OfferingBirdItem object at 0x000001712D2B0590>, <__main__.OfferingBirdItem object at 0x000001712D2B05D0>, <__main__.OfferingBirdItem object at 0x000001712D2B0610>, <__main__.OfferingBirdItem object at 0x000001712D2B0650>, <__main__.OfferingBirdItem object at 0x000001712D2B0550>]
Weights [40, 10, 85, 5, 5, 5]
Population [<__main__.OfferingBirdItem object at 0x000001712D2B0510>, <__main__.OfferingBirdItem object at 0x000001712D2B0590>, <__main__.OfferingBirdItem object at 0x000001712D2B0610>, <__main__.OfferingBirdItem object at 0x000001712D2B0650>, <__main__.OfferingBirdItem object at 0x000001712D2B0550>]
Weights [40, 10, 5, 5, 5]
Population [<__main__.OfferingBirdItem object at 0x000001712D2B0590>, <__main__.OfferingBirdItem object at 0x000001712D2B0610>, <__main__.OfferingBirdItem object at 0x000001712D2B0650>, <__main__.OfferingBirdItem object at 0x000001712D2B0550>]
Weights [10, 5, 5, 5]
Population [<__main__.OfferingBirdItem object at 0x000001712D2B0610>, <__main__.OfferingBirdItem object at 0x000001712D2B0650>, <__main__.OfferingBirdItem object at 0x000001712D2B0550>]
Weights [5, 5, 5]
Population [<__main__.OfferingBirdItem object at 0x000001712D2B0610>, <__main__.OfferingBirdItem object at 0x000001712D2B0650>]
Weights [5, 5]
BIRD Hautehoot
Rare
BIRD OFFERING LIST [<__main__.OfferingBirdItem object at 0x000001F859160590>, <__main__.OfferingBirdItem object at 0x000001F8591605D0>, <__main__.OfferingBirdItem object at 0x000001F859160610>, <__main__.OfferingBirdItem object at 0x000001F859160650>, <__main__.OfferingBirdItem object at 0x000001F859160550>]
ALL OFFERINGS Wrist - Wraps - Bloodied
ALL OFFERINGS Running Shoes
ALL OFFERINGS Fingerless Gloves - Black
ALL OFFERINGS Fur-collared Jacket
ALL OFFERINGS Wellspring Village Pendant
OFFERINGS AVAILABLE [<__main__.OfferingBirdItem object at 0x000001F859160590>, <__main__.OfferingBirdItem object at 0x000001F8591605D0>, <__main__.OfferingBirdItem object at 0x000001F859160610>, <__main__.OfferingBirdItem object at 0x000001F859160650>, <__main__.OfferingBirdItem object at 0x000001F859160550>]
Population [<__main__.OfferingBirdItem object at 0x000001F859160590>, <__main__.OfferingBirdItem object at 0x000001F8591605D0>, <__main__.OfferingBirdItem object at 0x000001F859160610>, <__main__.OfferingBirdItem object at 0x000001F859160650>, <__main__.OfferingBirdItem object at 0x000001F859160550>]
Weights [10, 85, 5, 5, 5]
Population []
Weights []
This one was the last before it errored.
Ope, that one's not
So yeah, something is making that list empty
offerings_available
It just instantly drops everything.
def generate_bird_offers(player_name, bird_spawn_list):
offerings_available = []
offerings_displayed = []
for bird in bird_spawn_list:
if bird:
print(f'BIRD {bird.get_offering_bird_name()}')
if bird.get_offering_bird_name() in ["Courigeon"]:
player_reputation = player_name.get_bird_reputation_common()
print("Common")
if bird.get_offering_bird_name() in ["Baublekaw", "Trinketalon"]:
player_reputation = player_name.get_bird_reputation_uncommon()
print("Uncommon")
if bird.get_offering_bird_name() in ["Outflit", "Hautehoot", "Vogueowl"]:
player_reputation = player_name.get_bird_reputation_rare()
print("Rare")
bird_offering_count = get_bird_offering_count(player_reputation)
print(f'BIRD OFFERING LIST {bird.get_offering_list()}')
for offering in bird.get_offering_list():
print(f'ALL OFFERINGS {offering.get_item_name()}')
if offering.get_reputation_requirement() <= player_reputation:
offerings_available.append(offering)
print(f'OFFERINGS AVAILABLE {offerings_available}')
for i in range(0, min(len(bird.get_offering_list()), bird_offering_count)):
print("Population",[i for i in offerings_available])
print("Weights",[i.get_probability() for i in offerings_available])
offering = random.choices(population=[i for i in offerings_available], weights=[i.get_probability() for i in offerings_available], k=1)[0]
offerings_displayed.append(offering)
offerings_available = [offering for offering in offerings_available if offering not in offerings_displayed]
print(f'AVAILABLE {offerings_available} DISPLAYED {offerings_displayed}')
Did I miss a change?
Hm, I don't have the [0] at the end of random.choices()
And I dunno if that's right or not lol
You said to add it back earlier I believe.
Oh I remember.
Without it, it was duplicating the list or something.
Me neither. I don't see any patterns to what's happening before those are empty.
Just not sure why, it's gotta be a bug in the deduplication logic
Well the old one looked like this and it worked.
def generate_bird_items(item_list, player_reputation):
bird_items_offered_count = get_bird_offering_count(player_reputation)
available_items = []
item_selection_list = []
for item in item_list:
if item[2] <= player_reputation:
available_items.append(item)
for i in range(0, min(bird_items_offered_count,len(available_items))):
item_selection = random.choices(population=[i[0] for i in available_items], weights=[i[1] for i in available_items], k=1)[0]
item_selection_list.append(item_selection)
available_items = [item for item in available_items if item[0] != item_selection]
return item_selection_list
Maybe I didn't change something over properly?
I have no idea
for i in range(0, min(len(bird.get_offering_list()), bird_offering_count)):
if len(offerings_available) == 0:
break
This fixed it though lol
That's the best I've got for the moment
I mean, I don't know if that's a fix. ๐
Because that list should never be empty.
Might be the best for today though.
We call that a "band aid" XD
For today at least. I'm going to watch some YouTube and get some sleep. Thanks for all of your help. You've been fantastic!
Have a good one!
You as well!
This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.