|
I'm trying to improve testing in my code base, and we basically have the following workflow:
There's a pretty clear delineation between the steps 1-3 and 4, which basically just requires the output of steps 1-3, so I'm working to generate a way to build a test composite data structure. It is itself a dataclass, but it has a lot of heavily nested objects, some inherited from a java interface, and none with clear factories/etc. If I can construct a test version of this, I can plug it directly into the rest of the business logic in Step 4, so that's my current plan, but I'm a little bit of a loss as to how to approach this. Is it worth looking into Pydantic/etc? If you had to do something like this, how would you go about approaching it. Falcon2001 fucked around with this message at 23:36 on Sep 29, 2022 |
# ? Sep 29, 2022 23:30 |
|
|
# ? Jun 5, 2024 04:47 |
|
FredMSloniker posted:Good advice should I take this to Linux, but I'm running Windows. As for the database connection... would this work? SQLite will lock the db for writing while it handles writes within a transaction, which can cause other writes fail to commit during this time, if you try to write from two threads at the (relatively) exact time. You can enable WAL mode to speed up writes (https://sqlite.org/wal.html) and you could add a handler to retry your transactions after a delay if they cause an exception on write. You are probably going to want to use a context with the cursor or db connection objects to either destroy that at the end of your method or another method to ensure the cursor is closed promptly as that will cause the implicit transaction to commit. You can also manually call con.commit() to ensure this happens in your control. Wallet posted:The simple way to do multithreaded processing here would be to basically pass a list of jobs that will create those table entries (along with whatever data they need to do that) and then have your main thread deal with writing what they calculate when they return it. If your bottleneck is literally processor time threading it will help performance, but it's also going to introduce a lot of complexity. Unless you already have a working example of what you want to do that isn't performant enough when single threaded I wouldn't bother at this point. Also this. SQLite3 is mp safe, but wierdness can happen.
|
# ? Sep 30, 2022 00:13 |
|
FredMSloniker posted:As for the database connection Example: Python code:
code:
If I am reading your stuff correctly, your work function: - Accepts a state as a parameter - Returns a score + a set of new states and then you: 1) Store a (that state, its score) tuple into the database 2) Call the work function on any of the new states that aren't already in the database Is that right? Assuming that and setting aside whether this top-down thing is a good approach to solve the problem: multiprocessing.Pool is mostly set up to take a known list of things and run a function on each one, farming the computation across processes. A job can't schedule a new job itself, only the main process can do that. So the new states to explore need to get returned to the main process (either directly as returns from the work function, or via the database file). Using return values would look something like this: code:
|
# ? Sep 30, 2022 01:58 |
|
Wallet posted:The simple way to do multithreaded processing here would be to basically pass a list of jobs that will create those table entries (along with whatever data they need to do that) and then have your main thread deal with writing what they calculate when they return it. If your bottleneck is literally processor time threading it will help performance, but it's also going to introduce a lot of complexity. Unless you already have a working example of what you want to do that isn't performant enough when single threaded I wouldn't bother at this point. Oh, my current program is definitely bottlenecking on processor time. Anyway, here's the current code, which is definitely running faster: code:
|
# ? Sep 30, 2022 04:03 |
|
Install flake8 and run it on your code, it'll give you a ton of formatting tips A lot of stuff is happening in main(). Try to write functions that do just 1 thing really well. It's okay if this drifts toward 2 or 3 things but it shouldn't be 10 things. Try to avoid magic numbers, including magic indices. The gently caress is state_complex[0] or state_complex[2]? It's better if you assign variable names to the components of this object and then operate with those variables; that'll make your code closer to self-documenting and it'll be easier to work on. Instead of `if type(entry[0]) == bool:`, use `if isinstance(entry[0], bool)`. Better still, try not to have code that's forking in different directions based on the type of a variable. Try to make entry[0] always be a specific type QuarkJets fucked around with this message at 04:49 on Sep 30, 2022 |
# ? Sep 30, 2022 04:23 |
|
QuarkJets posted:Install flake8 and run it on your code, it'll give you a ton of formatting tips I was today years old when I found out pip was a default part of Python. themoreyouknow.jpg And yeah. I'm gonna work some more on other parts of the code, then come back to this bit.
|
# ? Sep 30, 2022 06:50 |
|
All right, it's become clear that I need to sit down and properly learn Python and not just Google how to solve the problem of the minute. What's a good resource that's not going to make me feel like a kindergartener? The official documentation, or something else?
|
# ? Sep 30, 2022 19:08 |
|
A lot of the stuff QuarkJets mentioned is less Python and more "OOP in general", I think, so you're not likely to find a python resource that does a very good job of it. The best thing I'm aware of is honestly just, posting about it online, and reading about it online (especially what makes "good code" and how its different from bad code). I think a good first step would be trying to refactor your loop into discrete problems that need to be solved and then writing a function that solves exactly one of those problems, a good example here is probably this one: Python code:
You could change this to "if already_in_table(state_string)", but I think you were right not to, because it doesn't increase the at-a-glance readability of the code just to have the sqlite query live somewhere else. That's a sign that this code is doing too much, and that the level of abstraction here is too low --> not enough information is being hidden/is encapsulated by a logical construct with a name and a signature. The more this happens, as you keep layering on new code paths and conditions, the harder it becomes over time to unwind the code and give it a name. We're basically at a point where, in order to suggest a potential refactor, we have to understand the entire codebase and the full context of the problem you're solving. Being good at OOP design is one way to combat this problem. I gave it a read-through and honestly I can say that I just can't understand it enough to offer any specific advice, except that -- Python code:
|
# ? Sep 30, 2022 19:52 |
|
A piece of the standard library that I personally reach for all the time when I have "collections of stuff to do" is the collections.deque, which you might find to be more ergonomic for your problem. Specifically, that you can create them, put things in them, and then get things out of them later. If you want to just handle things one at a time, the workflow is to your_deque.append() your stuff -> you can your_deque.pop() to get the "most recently appended" or you can your_deque.popleft() to get the "first appended". I suspect that you can clean up a lot of your "list with index counter that gets incremented" and "dictionary that you check the length of and del items from" with a deque, since it can be looped on, and it mutates itself as you use it: Python code:
12 rats tied together fucked around with this message at 20:11 on Sep 30, 2022 |
# ? Sep 30, 2022 20:06 |
|
12 rats tied together posted:I gave it a read-through and honestly I can say that I just can't understand it enough to offer any specific advice, except that -- Well, I can tell you what that specific bit was doing, at least. The outer condition was 'have I processed everything?' I then went through to_process and checked which ones I'd processed already; if I had, I marked them for deletion in to_drop, and if I hadn't, I stored an AsyncResult in that key (previously, it held True.) Then I dropped the already-processed keys. The inner loop was 'have all of the apply_async calls returned their data?' I then went through to_process again. If a call had returned its data, I processed that data, which included putting marking the new keys in new_to_process (values True) and adding the call's key to to_drop. At the end of the inner loop, all the calls that I was done with were dropped from the dictionary, and I repeated until all calls had returned their data, then did to_process = new_to_process. Eventually, the outer loop would wind up dropping everything (all of the states the inner loop returned were processed already), the inner loop would be skipped, and the outer loop would exit. None of that really matters right now, though, because I'm going to be scrapping much of what I've already done and starting over. Would anyone be interested in hopping over to the thread I've been doing this all for and kinda looking over my shoulder as I re-implement the code, saying 'wait, why are you/you shouldn't be doing that'? I don't want to flood this thread with 'okay, I did this, is this okay?'
|
# ? Sep 30, 2022 20:30 |
|
On a related note, it looks like the Python style guide discourages having lines > 79 characters, and some of the strings in my code are going to be very long indeed. The problem I'm encountering is how to re-wrap a string in the code if I make some changes to it. Of the options I'm aware of, only triple-quoting a string doesn't add non-white-space characters to the beginning or end of a line, preventing me from automatically re-wrapping the line in Notepad++ or something. However, it does that by including the newlines and indentation in the string, which is a problem - especially since sometimes I want newlines in the string, so I can't just strip them out automatically! Should I just suck up flake8 making frowny faces at my long strings, or is there a more Pythonic way to handle a block of text without re-rolling it by hand every time I want to correct a typo?
|
# ? Sep 30, 2022 21:35 |
|
Python code:
|
# ? Sep 30, 2022 22:02 |
|
FredMSloniker posted:On a related note, it looks like the Python style guide discourages having lines > 79 characters, and some of the strings in my code are going to be very long indeed. The problem I'm encountering is how to re-wrap a string in the code if I make some changes to it. Of the options I'm aware of, only triple-quoting a string doesn't add non-white-space characters to the beginning or end of a line, preventing me from automatically re-wrapping the line in Notepad++ or something. However, it does that by including the newlines and indentation in the string, which is a problem - especially since sometimes I want newlines in the string, so I can't just strip them out automatically! Should I just suck up flake8 making frowny faces at my long strings, or is there a more Pythonic way to handle a block of text without re-rolling it by hand every time I want to correct a typo? I bump any linter line length checks to 120. The 79 thing is insane imo.
|
# ? Sep 30, 2022 22:02 |
Luv too code on a 386 in VGA text mode
|
|
# ? Sep 30, 2022 22:10 |
|
Foxfire_ posted:
The 3rd one is what I use exclusively, for whatever that's worth.
|
# ? Sep 30, 2022 22:37 |
|
Python code:
|
# ? Sep 30, 2022 23:03 |
|
black with --preview will reflow long strings, but it will also change a bunch of other stuff. It is actually hard to do string splitting automagically and be correct in all situations (f-strings, raw strings, ...) and the conversion black does will potentially change the meaning of the code in some edge cases (it should also yell if that happens, it compares the syntax tree before and after reformatting). I typically just do it by hand if I'm messing with a long string (and not worry very much about occasional short lines when reflowing it), but big string constants don't come up that often for me.
|
# ? Sep 30, 2022 23:42 |
|
necrotic posted:I bump any linter line length checks to 120. The 79 thing is insane imo. My team does 100, but yeah 79 is...antiquated. Just keep it reasonable so you're not cosplaying as a java dev and you'll be fine.
|
# ? Oct 1, 2022 00:07 |
Google enforces 79 come hell or high water
|
|
# ? Oct 1, 2022 02:05 |
|
Well, as I'm not a trillion-dollar corporation, I'm gonna indulge myself and go to 120 characters, because really. Probably the big boys would stuff all this text in a data file or something. And since nobody seemed interest in hopping threads, which, fair, I'm gonna keep asking for help here until someone tells me to stop. Going right back to the beginning of my goal - finding the optimal path through the game book "Impudent Peasant!", and generalizing that solution to other game books - I need to first explain to the computer how to play. That starts with peasant.py, which I've pared way back to this for the moment: Python code:
|
# ? Oct 1, 2022 02:53 |
|
Python code:
Python code:
Python code:
Python code:
----------------------------------------- Python code:
1. Turn this big block of documentation into a module docstring 2. Update your functions to use proper docstrings (triple-quotes) 3. Include function arguments in your function docstrings ----------------------------------------- Python code:
----------------------------------------- Python code:
Python code:
Python code:
I think that you should extract all of this code to its own function. You could call it "roll_stamina. If you did that for each of these "roll" types then you'd have a "get_state_complex" function that's kind of self-documenting and easy to follow. In fact, roll_stamina and roll_luck look like they'd be exactly the same function but with 1 word changed, so just define the function a little more generically and make that word an input argument (roll_skill("STAMINA") vs roll_skill("LUCK"))
|
# ? Oct 1, 2022 03:42 |
|
I also want to point out that you could use Enum to make your code a little more programmatic, it'll let you avoid having to do so many string comparisons. Here's an example:Python code:
|
# ? Oct 1, 2022 04:35 |
|
Let's look at this for loop:Python code:
First, I'll recommend again to use f-strings. Second, I'd say you should refer to the luck variable rather than putting 'b("%d." % (i + 7))' at the end of your string. You stored i+7 earlier. What happens if you need to change this to i+8? Would you rather make that change 3 times or 1 time? I'm also not a fan of multiple-assignment, except in the case where you're literally extracting values from some kind of container. What I mean is that I don't like lines that look like "a, b = 1, 2". Maybe it's just personal preference, but it feels like that kind of code is harder to read and parse than separate lines. If I'm trying to figure out where "b" was assigned, then it'd be great if I could just scan the left-side of the indented code instead of having to scan the entire line because there are multiple assignments per line. For function calls I like to either have all of the arguments in a single line or all of the arguments spread out across multiple lines. No mixings, e.g. 2 arguments on one line then 1 on the next. The indentation of each argument should be the same for all arguments. Instead of iterating over range(6) and incrementing all of your integers by 1, you could iterate over range(1, 7). But this is making use of what's commonly referred to as "magic numbers". You and I know that this is a d6 roll, so the valid values are 1 through 6. This might not always be obvious. It could be better to assign this range of values to some descriptive variables, like "min_luck" and "max_luck". The 6 being added to the luck roll is also a magic number. Perhaps we can call it luck_modifier I've noticed that you have a function named "i" and you're using loop variables named "i". This is why single-character function names are horrible; flake8 is probably complaining because you're overwriting a function definition in the global scope with. If you want to keep using single-character function names then you'll need to remember to not use i as an iteration variable. Normally I'd say rename the function and keep using "i" as your counter variable, but Putting it all together: Python code:
Python code:
QuarkJets fucked around with this message at 06:03 on Oct 1, 2022 |
# ? Oct 1, 2022 05:51 |
|
FredMSloniker posted:
This is very minor, but what program are you using to write your code? If it's VSCode, you can add a vertical ruler by modifying your preferences: https://stackoverflow.com/questions/29968499/vertical-rulers-in-visual-studio-code which is very handy since it's just a nice visual cue.
|
# ? Oct 1, 2022 06:03 |
|
OP mentioned Notepad++. Strong recommend for switching over to VS Code here, too
|
# ? Oct 1, 2022 06:05 |
|
QuarkJets posted:Do this instead: Python code:
Python code:
Python code:
I went through and gave the markup functions full-word names. Actually, I created a markup function for the general case of 'bracket this with BBcode' and made functions to call that for convenience. As for flake8's complaint... it's not doing it now. I'll remember that, next time, if that error appears, something else is wrong. QuarkJets posted:So what's happening here? I thought the current_section was "Roll STAMINA", why does it appear to be setting the section label to "Roll LUCK"? It's difficult to follow what's happening. When this function returns, it will have six different state strings, each with a different value for (Initial) STAMINA, but all of them will have the section as "Roll LUCK" - because when we proceed from that state string, that's the section we'll be on. QuarkJets posted:I think that you should extract all of this code to its own function. Python code:
That said, I have tried to do this before in Lua. In Lua, I could do this: Lua code:
If you know a more Pythonic way to do what I'm doing, feel free to suggest it! QuarkJets posted:I also want to point out that you could use Enum to make your code a little more programmatic, it'll let you avoid having to do so many string comparisons. Here's an example: Falcon2001 posted:This is very minor, but what program are you using to write your code? You'll have to convince me to learn Visual Studio Code. I'm not saying I can't be convinced, just that the screenshots aren't selling me. In any event, here's my modified code: Python code:
|
# ? Oct 1, 2022 06:16 |
|
QuarkJets, one of your posts came in while I was composing that one, and I missed it. As it's getting late, I'll address it in the morning.
|
# ? Oct 1, 2022 06:24 |
|
FredMSloniker posted:Breaking every section out into its own function won't make get_state_complex look any nicer, and I'd have to define a function name for each possible section. I will be using functions for things sections have in common - for instance, 'offer three choices that only differ in what section we turn to next' or 'damage the character and turn to one section if they survive and another if they don't' - but, especially in "Impudent Peasant!", there's a lot of stuff that's unique to a single section that happens. There's two main patterns I reach for when doing this, one of them you already touched on: put the functions in a dictionary. Just for completeness, that would look something like this: Python code:
Second pattern is basically the same except instead of manually pairing a string key to a function value, you can use structural pattern matching. This is useful because it gives you a clean, ergonomic way to interpret an input object (like your game state) and dispatch to an arbitrary code path while also performing setup for that code path. The example in the link is actually input handling for a text adventure game, you might find it somewhat relevant.
|
# ? Oct 1, 2022 06:38 |
|
FredMSloniker posted:The reason I have... Sure, you could do this: Python code:
quote:game_state is a deserialized version of the state string we took as input. I'm changing its section to "Roll LUCK", then (in the first trip through the i loop) setting its STAMINA and Initial STAMINA to 7. Then I call add_choice, which serializes the game state and stores that state string and the description of the choice leading to it, in this case "You rolled a 1. Your Initial STAMINA score is 7.", in o["choice_keys"]. Next time through the loop, the STAMINA and Initial STAMINA are 8, and the state string and choice description are modified accordingly. Are you setting the section to "Roll LUCK" because that's the section that comes after rolling stamina? That's unclear. I think that it'd be clearer if you were just incrementing an index or fetching the next value for an iterable instead of manually setting the next section label like this Here's an idea: define an enum of your section labels. Python code:
quote:Right now, my vision is that the code (eventually) will look something like this: I think that def sections["1"](state_string): is equivalent to def roll_luck(state_string): for tidiness, but the latter is a lot more descriptive. Am I missing something? quote:That example is giving me a FATAL error. That said, I'll come back to that thought if we don't wind up making it moot. lol nice
|
# ? Oct 1, 2022 07:01 |
|
Python code:
This is way cleaner IMO and way faster too: Python code:
|
# ? Oct 1, 2022 07:18 |
|
I started writing a reply before midnight, and it's now after midnight, and I need to go to bed, so you'll see that reply in the morning. Before you do, though, I have a question:https://docs.python.org/3/tutorial/classes.html posted:A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (containing functions such as abs(), and built-in exception names); the global names in a module quote:the global names in a module Is there a way I can access the namespace of, say, usefulfunctions.py from another program? Does it take the form of a dictionary whose keys include, say, the names of the functions usefulfunctions.py defines? Would the values of those keys happen to be the functions in question? Because if even one of those is true, I think it would be very useful.
|
# ? Oct 1, 2022 08:13 |
|
Are you creating a program to play the gamebook, or are you creating one to solve the best path through it? Because while the structure you're going for seems adequate for allowing a human to play through the book, it's going to make things very difficult as far as efficiently solving it goes. To solve the book efficiently, you're going to want to manipulate and simplify the section graph. You can't do that if the links between sections are just opaque functions - you want your code to be able to inspect those connections and merge them together. (For example, combining a chain of free choices between two-to-three options into a single free choice between 20 different outcomes; or combining a series of free and random choices into a single weighted random choice). Instead of expressing each section as an opaque function, I'd aim to express it as a structured data element. Something like this: code:
|
# ? Oct 1, 2022 08:53 |
|
Right. Now that I've slept and am more or less fresh, let's do a reply. Note: I have some ideas later in the post that would lead to modifying code earlier in the post, so read through the whole thing before commenting, please!QuarkJets posted:Are you setting the section to "Roll LUCK" because that's the section that comes after rolling stamina? That's unclear. I've renamed game_state to next_game_state, since it may start as the current game state but is really a scratch pad for creating new game states to turn into state strings. Hopefully this will deal with your concerns re: changing the section name. In that vein, to the previous code, I've added: Python code:
And changed the relevant sections of get_state_complex to: Python code:
I've also made this change: Python code:
QuarkJets posted:I think that it'd be clearer if you were just incrementing an index or fetching the next value for an iterable instead of manually setting the next section label like this 99 times out of 100, I'm not going to be incrementing the section. On Section 9, for instance, I have the option to guard a warehouse (Section 47), enter a cave (Section 23, but only if I have at least one torch), or go fishing (Section 25, but only if I have fishing gear). That said, would the code look like this? Python code:
Actually, I did a bit of research on the post I made last night. Could I do something like this (leaving section a string)? Python code:
12 rats tied together posted:Second pattern is basically the same except instead of manually pairing a string key to a function value, you can use structural pattern matching. This is useful because it gives you a clean, ergonomic way to interpret an input object (like your game state) and dispatch to an arbitrary code path while also performing setup for that code path. The example in the link is actually input handling for a text adventure game, you might find it somewhat relevant. Hm. I can see replacing the 'if - elif - elif - else' with 'match case - case - case - case other', but I'm not sure what I gain by doing so. Performance? I'm not passing get_state_complex a command; I'm passing it a representation of my current situation in the game and asking what commands are valid. I could just as well be sending it a tic-tac-toe board and whose turn it is to move and having it send back what the current player's valid moves are and what the board will look like for each. Can you elaborate? QuarkJets posted:You're copying a dictionary by serializing it and then deserializing it? If I said I was unaware of a better way, that would imply I looked. Does that create a deep copy? Or should I be importing deepcopy from copy (which I literally learned was a thing right now from Googling 'python deep copy dictionary')? (Please don't despair as your flickering flashlight tries futilely to illuminate the depths of my ignorance.) Jabor posted:Are you creating a program to play the gamebook, or are you creating one to solve the best path through it? Because while the structure you're going for seems adequate for allowing a human to play through the book, it's going to make things very difficult as far as efficiently solving it goes. I'm doing both, ultimately. I want to have output that looks like this: Section 34 posted:The tangled wood lies on a small piece of land to the east of Bitterford, near the Red River. Farmer Corran accompanies you most of the way, but stops some distance from the wood and refuses to walk any closer. His two remaining dogs shiver and whimper alongside him, their tails tightly curled downwards in abject fear. So I'm limited in how much I can simplify the section graph. That said, if there's only one choice you can make in a given section, I can simplify things. If my previous plan for get_state_complex would work, I can use this instead: Python code:
So it would elide sections until it reached an actual choice. This will be especially useful when I get into combat and have a lot of situations where I'm like 'okay, what weapon are you using this round?' and 95% of the time the choice is obvious. As my professors used to say, questions? Comments? Difficulties?
|
# ? Oct 1, 2022 16:56 |
|
FredMSloniker posted:99 times out of 100, I'm not going to be incrementing the section. On Section 9, for instance, I have the option to guard a warehouse (Section 47), enter a cave (Section 23, but only if I have at least one torch), or go fishing (Section 25, but only if I have fishing gear). That said, would the code look like this? At the end of the day, every section has its own logic for determining what happens. Some sections might have the same or similar logic; for instance, rolling starting LUCK is very similar to rolling starting STAMINA. If you designate a function for every section then you can simplify your code and improve its readability. Instead of setting the state of a variable and then using that state to determine what to do next, what if you just did the next thing by calling the next function in the chain? code:
quote:If I said I was unaware of a better way, that would imply I looked. Does that create a deep copy? Or should I be importing deepcopy from copy (which I literally learned was a thing right now from Googling 'python deep copy dictionary')? (Please don't despair as your flickering flashlight tries futilely to illuminate the depths of my ignorance.) Deepcopy will create a complete and exact copy of what you provide it. It's likely that you don't need to make deep copies of anything but I'm not sure. But it sounds like you want sorted keys. Creating a new dict from the sorted dict will give you a sorted deep copy. dict(sorted(x.items())) is the pattern you'd want for a sorted copy; if you just wanted a copy with no sorting then deepcopy() would be fine.
|
# ? Oct 1, 2022 19:39 |
|
QuarkJets posted:At the end of the day, every section has its own logic for determining what happens. Some sections might have the same or similar logic; for instance, rolling starting LUCK is very similar to rolling starting STAMINA. If you designate a function for every section then you can simplify your code and improve its readability. Instead of setting the state of a variable and then using that state to determine what to do next, what if you just did the next thing by calling the next function in the chain? That would work just fine if my goal were simply to play the game book, but my aim is to solve it. I don't want to get into my ideas on how I plan to implement this yet, but my goal is to make a program that takes peasant.py and generates a corresponding scoring table (I plan on using sqlite3) that gives the expected score for every possible state string. (Which is to say, every state string that can come of following choices from the beginning of the game book; you're not going to have a state string where your STAMINA is, say, 24 because that can't happen in this game book.) To get those state strings, it's going to have to be able to walk through the game book section by section; it can then score those state strings by working backwards from the winning and losing sections. Once I have that table, I can then ask 'okay, if I'm on this section, and I have these stats and these things, what are my choices, and what's my expected score for each one?' And I can have a third program take peasant.py and that scoring table (or possibly embed all of peasant.py's output in the scoring table, though that would make it much larger) and kick out neatly-formatted BBcode for the given situation. So after I've finished showing off all of the content of the book in my LP, I can then go through and show the optimal path and the expected score at each step, so they can see just how much a good roll makes things better and a bad roll makes things worse. Again, that's the end goal. (And I did say as much earlier, but maybe I wasn't clear enough?) For now, I just want to implement getting from one state string to another without making Baby Guido cry. QuarkJets posted:Deepcopy will create a complete and exact copy of what you provide it. It's likely that you don't need to make deep copies of anything but I'm not sure. I'm not 100% sure whether I'll need to make deep copies yet. Heck, my code so far doesn't need to make shallow copies. But it's good to know whether a copy is deep or not so I know what I can safely do with it. QuarkJets posted:But it sounds like you want sorted keys. Creating a new dict from the sorted dict will give you a sorted deep copy. dict(sorted(x.items())) is the pattern you'd want for a sorted copy; if you just wanted a copy with no sorting then deepcopy() would be fine. The keys don't need to be sorted in the game state. They need to be sorted in the state string because, as my current version of dumps says: Python code:
The functions get_state_complex calls may modify the game state in arbitrary ways, but if I can get to section 9 in the same condition three different ways, it doesn't matter in exactly what order I got the equipment on each path or when those two special flags got set. I want the same state_string for the same situation. That means dict(x.items()) would work for copying my dictionaries. Though I assume they'd be shallow copies. So would the thing with return vars().get(section, lambda a: None)(state_string) work as a way to call a function by the name in the section string and return its output, or return None if there's no function by that name?
|
# ? Oct 1, 2022 20:21 |
|
FredMSloniker posted:That would work just fine if my goal were simply to play the game book, but my aim is to solve it. I don't want to get into my ideas on how I plan to implement this yet, but my goal is to make a program that takes peasant.py and generates a corresponding scoring table (I plan on using sqlite3) that gives the expected score for every possible state string. (Which is to say, every state string that can come of following choices from the beginning of the game book; you're not going to have a state string where your STAMINA is, say, 24 because that can't happen in this game book.) To get those state strings, it's going to have to be able to walk through the game book section by section; it can then score those state strings by working backwards from the winning and losing sections. This also works for solving the book, assuming you're passing around a game state. To play the game, you take random rolls. To solve it, you iterate over all possible roll values. The overall code structure is the same: functions calling other functions depending on the values of local variables. For instance this: Python code:
quote:So would the thing with return vars().get(section, lambda a: None)(state_string) work as a way to call a function by the name in the section string and return its output, or return None if there's no function by that name? I don't think that this does what you want it to do, and it'd be bad code even if it did work. Don't do this. If you absolutely need to be able to map function string names to functions, create a dict that contains those mappings and then refer to that. It'd be better still if you didn't need to refer to a lookup table of function names at all and instead just called the functions directly. QuarkJets fucked around with this message at 22:09 on Oct 1, 2022 |
# ? Oct 1, 2022 22:04 |
|
If a function ever reaches an end-state for the game, call a function that writes off the state to your sqlite database or whatever. Eventually the database will be populated with all of those end states and the paths you took through them.
|
# ? Oct 1, 2022 23:10 |
|
On review, I see what you're doing there, QuarkJets. That'd do a depth-first search of the state space, if I'm reading it correctly? There'd be a win() and a lose() to write their scores into the score table, and every other state function would be 'first, call the state functions after this (which will record their scores); then use those scores to calculate the score of this state and record that', right? ...I find myself in something of an awkward situation. On the one hand, I have what I consider to be perfectly valid reasons to not want to do it that way. On the other hand, you're being helpful, and you know more about Python than I do, and I don't want to be all 'well actually I think I should do it differently because' and coming off sounding ungrateful or, worse, like I think I know more than you do. So I'm going to think about this carefully, explain why I think your approach isn't the one I want, and hope you'll understand my intent and respond in kind. It's entirely possible, in a Fighting Fantasy book, to wind up right back where you were before - not just the same numbered section, but the exact same situation. The most obvious way this can happen is in combat, if all parties involved miss, but there are other ways to get into a loop, some of which aren't escapable (or are only escapable by death). So you wind up in the situation where a state's score x is 1/6 * 1 + 3/6 * 0 + 2/6 * x. A depth-first search is gonna get stuck. There are a couple of ways I can solve this. I can very carefully code the game so you can't actually loop. I have to be careful I don't screw up the relative probabilities of wins and losses by making draws impossible, of course, and I have to make sure I catch every possible way to loop. I can have some form of loop detection back it out of loops, but it needs to be able to distinguish between loops it can't escape (which should be scored as losses) and loops it can escape (which should be treated as if they don't even exist). I'll definitely want to do the first as much as I can so the solver doesn't waste its time, and the second is a good fallback for if I miss something. But there's a third option. If I do a breadth-first search, then I can repeatedly iterate over the scores, staring them as a minimum and maximum possible value. In the example case, x would start at [0, 1]. After one iteration, it'd be [1/6, 3/6]. After two, it'd be [8/36, 12/36]. Eventually it'd converge on [1/4, 1/4] - or the closest representations of those values a float can hold - at which point it won't iterate on that score any more. When it can't make any changes, every state should have converged; if not, there's a bug to gish somewhere. In order to do a breadth-first search, though, I need the state functions to return the results of a single step. Unless I'm missing something. And I need some way to look at the state string and say 'okay, for this section I need to use this function'. ...maybe, instead of having a section string, I could store the next section function in the state itself? Be like Python code:
Would that work?
|
# ? Oct 2, 2022 05:30 |
|
FredMSloniker posted:Notepad++. Really, I don't need the ruler now, since I adjusted the window width, but I haven't had a reason to take it out. Just to be clear: There's no right answer for how you want to write your code. If you want to use notepad, go for it; I know professional software devs who exclusively use vim which is basically a hyperpowered commandline program, and I know others who swear by highly specialized heavy IDEs like Visual Studio (not-Code) (Minor side note: VS Code and Visual Studio are entirely different programs and only really share 'you can write code in here!' as a feature. The list of differences is probably bigger than the similarities.) The biggest argument for VS Code over Notepad++ is that VS Code is highly extensible to do a lot of things, and is purpose built for being a code-aware text editor. It's not a full blown IDE like Visual Studio, it's basically a halfway point between Notepad++ and VS. (Also the extensions platform is much nicer to use than Notepad++ addons.) Because of that, it bakes in some ideas that are quite handy for programming - here's a few basic ones:
Basically it's a lot of little improvements because it was built to write code first and foremost, instead of just being a great notepad replacement. (FWIW: I love Notepad++ and use it a ton.)
|
# ? Oct 4, 2022 06:52 |
|
|
# ? Jun 5, 2024 04:47 |
We're trying to use remotezip to access some files via an authenticated session. For various reasons, we've subclassed Session to support some extra things, such as dealing with how requests handles redirects & headers these days, dealing with session headers when accessing files through cloudfront vs. in-region directly through s3, etc. All of those behaviors are non-negotiable, so we're stuck with 'em. The proof of concept for doing this just creates the RemoteZip object by copying the headers and cookies from the authenticated session, which works fine in the base case, but that loses all the extra features of our custom session. It looks like remotezip just calls requests.get() for most of its actions. What might be the most sensible way to get remotezip to perform all its requests-based actions through our Session object? I probably explained that horribly... Here's how we got it working in the most basic sense, which only works in some cases: Python code:
Python code:
What I think I want is for remotezip to behave as: Python code:
Bad Munki fucked around with this message at 21:57 on Oct 4, 2022 |
|
# ? Oct 4, 2022 21:55 |