• 0 Posts
  • 20 Comments
Joined 2 years ago
cake
Cake day: June 22nd, 2024

help-circle



  • TL;DR: You’re getting different results for negative or otherwise invalid/unexpected input arguments.

    A non-equivalence appears when passing a negative value as arg0 or arg1, which will always result in averageBets == 0 in the example, but >=0 with your code. The reason for this happening basically hides in your conditional incrementing of games, which allows for unintended “extra loops” if your conditions desiredGain <= 0 or stake < 1 are not met.

    Also, the condition desiredGain = goal - stake; (desiredGain <= 0)(goal - stake <= 0)(goal <= stake) in your “win branch” is not equivalent to cash < goal, returning different results for all arg0 >= arg1.

    The simpler example implementation handles these edge cases implicitly with cash > 0 and cash < goal, skipping the main work, but still running exactly all tries. It’d be fine, and actually preferable, IMHO, to add explicit input validation to your code to basically abort early, if the input values are out of bounds, but there’s always a case to be made for keeping loops and conditions as simple, local, and immutable as possible, to avoid creating sneaky edge cases.

    Unfortunately, it is very common for many examples to lack error checking and/or input validation altogether, just like here.


  • Consider a key pair, consisting of two brutally large numbers, but otherwise pretty much identical. Magical math exists that makes it so that if you math your data with one of these brutally large numbers, you get the original data back only if you math it with the other large number. That’s basically it.

    Now we slap convention onto this, and keep one of the paired, large numbers a secret, and call it our private key, the other number is disseminated and called the public key for that reason.

    Now everyone can math data with your public key, so that only the paired private key, which only you know, can de-math it. This is encryption/decryption.

    Signing is very similar, but now you use your private key, which only you know, to math a digest of your data, and all the world can de-math this correctly only with your public key, thus proving it was indeed your private key used to math the data in the first place, and by extension attribute the signature to your public key identity. Your private key is never known to anyone but you, which is an essential difference to “classical” symmetric encryption with a shared secret.

    You may realize how easily a code signature can become a liability, if you fail to keep your private key secret for any reason. You can be trivially impersonated, with basically no chance of recourse or deniability with an SSH key, while you can at least invalidate a GPG key publicly and mark it as “stolen” that way. This is potentially very important, if there’s any legal meaning attached to your signature, and if not, why bother with code signing in the first place, if “trust me bro” is well enough.












  • Generally speaking, there is a race condition lurking where the OS may do whatever to your file you just checked, rendering the check strictly obsolete the moment you get the result. This isn’t typical, but possible, and a lovely old-school security vulnerability class. :)

    A more practical argument is that you’re going to handle any errors your open() may throw, anyway, and therefore it’s simply redundant to check for file existence explicitly beforehand.

    Under specific circumstances, you may want to do explicit, very specific tests for more detailed error reporting than “error opening file!”, for example “save file is corrupted” if it’s too short or zero-length, or “save file directory is gone. What the hell, dude? Recreating it, but stop fiddling with my files!”

    This is easy to overengineer. Best is to get into the very sensible habit of always checking for errors or exceptions returned by your calls, and this will become a non-issue.

    In this particular use-case of save file loading, you might implement displaying a listing of save files in a directory with opendir/readdir or FindFirstFile/FindNextFile and its ilk, to offer a list of files to load, which doubles as a crude existence test already. Many ways lead away from Rome. If you’re considering loading an autosave and offer a “Continue” button or something, a cheap existence test would work very well to decide if that button needs to be displayed in the first place, but doesn’t free you from handling any open() errors later. You could also open() and validate an autosave directly, and when/if the user decides to “Continue”, use the already reserved file descriptor and maybe even the preloaded save data to quickly jump into the game.

    If you want a simple answer: Do not introduce race conditions. Always acquire a lock for a shared resource before doing anything with it.


  • Gyroplast@pawb.socialtoMemes@lemmy.mlAn alternate timeline
    link
    fedilink
    arrow-up
    33
    arrow-down
    2
    ·
    2 years ago

    What kind of soulless psychopath leaves a window of this size unmaximized is the real question here. Also, a horizontal scrollbar in the main section, able to scroll maybe 8 pixels total to see some more of the glorious empty padding, could have been a nice touch as a consequence of the “unintended” window size.


  • I would not differentiate needlessly between the terms function and [“dunder”, “instance”, “module”, “class”] method, this isn’t practical at the current point in time, and arguably not pythonic. They are all callables and behave very similar for all practical purposes, all quacking like a duck.

    If you want to deep-dive into the intricacies, there’s technically a bunch of alternative “method types” with slightly differing calling and argument passing conventions. There are class methods, static methods, class properties, and module-scope functions off the top of my head, all very similar, but particularly suitable under specific circumstances.

    If I wanted a String utility class, for example, I could have that:

    class String:
      @staticmethod
      def len(string: str) -> int:
        if not isinstance(string, str):
          raise TypeError("Cannot determine length of non-string object")
        return len(string)
    
    s = "foobar"
    assert String.len(s) == 6
    

    In Python I generally find static methods hardly as relevant as in other languages, and tend to create utility modules instead, with plain functions and maybe dataclasses for complex arguments or return values, which can then be imported and called pretty much the same way as a static method, but without the extra organisational class layer. I’d argue that’s very much a matter of taste, and how much of a purebred OOP jockey you are, for better or worse.

    Class properties are cool, however, you might want to look into that. It’s getters/setters on crack, masquerading as their respective property, so that something like this “just works”:

    s = MyString("foobar")
    assert s.len == 6
    

  • Are there any other ways I would find the length? Or are methods and functions the only options?

    You could get creative and find several inferior, silly, and utterly insane ways of achieving the same result, for example by treating a string as an interable (read: “list” or “array”) of its constituent characters, and count the number of characters. This feels very “example (but not exemplary) code on the first pages of a crappy C++ textbook”, but hey, it’s a way:

    length = 0
    string = "foobar"
    for char in string:
      length = length + 1
    print(length)
    

    Mind you, this is not programming. This is toying around, and perfectly valid in that way, but no-one in a halfway sane state of mind would dare suggest doing it this way with Python if you actually care about the result. :)