Emerald_Knight's recent activity

  1. Comment on I made a video to showcase / explain my free space pathfinding algorithm. in ~comp

    Emerald_Knight Link Parent
    My reply to this is a bit late. I apologize for that, as I've been a bit busy. I seem to have misunderstood the way your algorithm works, probably because of how late it was when I wrote the...

    My reply to this is a bit late. I apologize for that, as I've been a bit busy.

    I seem to have misunderstood the way your algorithm works, probably because of how late it was when I wrote the original comment. It sounds like instead you perform either boundary-based or edge-based traversing of your map, possibly in the form of a depth-first search. During this search, your boundary intersections effectively act as a sort of "pseudo-node", and it's along these pseudo-nodes that you perform the actual movement. The exiting of these boundaries is what allows you to calculate these intersections.

    Does that sound accurate? I don't actually have any code to look at, so I only have the high-level overview to work with. The actual underlying details I can only speculate at. I'd be interested in a more detailed explanation if I'm still off about the details! :)

    This is like saying everything is a 1 or a 0 but it's not particularly interesting. Here I'm talking about free space in the "mathematical" sense.

    I completely understand what you meant. That being said, the real world can only be expressed digitally to some defined resolution. For instance, the quantity of real numbers between 1 and 2 is infinite, yet digitally we can only express an infinitely small subset of the real numbers between 1 and 2. You can certainly redefine the resolution at any given point, and we can even make the assumption that we utilize your algorithm in a space with infinite resolution. But ultimately we have to define some resolution, which makes the space you're operating on a grid system.

    Because the space you're operating on is always a grid system, there are some important implications. For instance, imagine if you tried to apply these rectangular boundaries to a series of interconnected circular areas--there could be potentially infinitely many boundaries in order to obtain full map coverage, because a rectangle that doesn't spill out of a circle can never cover the entire area of the circle and vice versa. Because of this, the number of boundaries required could end up being proportional to the resolution of your space, depending on how that space is shaped. The fact that your boundaries work as well as they do could very well be because your maps are shaped as interconnected rectangles and don't contain any high-resolution curves. This is clearly a point that you're already aware of, since you've explicitly qualified your explanation with "I can have rectangle obstacles", but I just want to emphasize why it is that I'm focusing so much on the underlying notion of "free space" when contrasting mathematical free space and digital free space.

    This doesn't, in any way, detract from the idea of utilizing the algorithm in mathematical free space. In fact, it suggests that your algorithm could be incredibly effective for determining shortest paths within rectangular-shaped maps because of the usefulness of the clustering and event-driven processing. It's only a re-framing of the problem to allow us to point to similar problem spaces and to better consider the strengths and potential limitations of the algorithm.

    I feel like we're both on the same page, but what we're emphasizing is different. I apologize for being so unclear previously.

    2 votes
  2. Comment on I made a video to showcase / explain my free space pathfinding algorithm. in ~comp

    Emerald_Knight Link
    I could be off the mark, but this sounds to me like a version of hierarchical path finding. If we consider that even "free space", when represented digitally, has a finite amount of granularity,...

    I could be off the mark, but this sounds to me like a version of hierarchical path finding.

    If we consider that even "free space", when represented digitally, has a finite amount of granularity, then even a "free space" system is really just a grid system with sufficiently small unit size. So we're definitely still working with a grid here. The sheer quantity of grid nodes in such a system would be far too numerous to reasonably process, however, so in order to more efficiently process them it's better to group them into distinct clusters (in your case, the concept of "node boundaries"). The initial path is planned out for travel between clusters, then the final path is laid out by planning out the paths within the individual clusters. Fewer nodes to process from the very beginning, then each of the nodes involved in the final solution represent much smaller, easier to solve problems that can be handled very quickly.

    You appear to be subdividing the space into rectangular boundaries, plotting the top-level path between those boundaries, then plotting the path within the individual boundaries by considering details like boundary intersection and whether the start and end points are both contained within the same boundary space. This definitely seems to qualify as hierarchical path finding to me.

    I think one of the most important considerations for your approach, however, is that your maps are very grid-like. Not having tons of tiny, densely-packed, irregularly-placed obstacles, or many smooth-curve paths--more generally, regions that can't be easily subdivided into rectangular sub-regions--makes the boundary-intersection strategy effective. I'd be curious to see how your algorithm benchmarks when stress-tested.

    Cool to see that you independently developed your own hierarchical path finding strategy, in any case. Nice work!

    4 votes
  3. Comment on Tildes Docker Image in ~tildes

    Emerald_Knight Link Parent
    I can see how my phrasing could cause confusion. It's definitely ambiguous. Sorry about that!

    I can see how my phrasing could cause confusion. It's definitely ambiguous. Sorry about that!

    4 votes
  4. Comment on Codecademy vs The BBC Micro in ~comp

    Emerald_Knight Link Parent
    You're right. This is a pretty significant problem.

    You're right. This is a pretty significant problem.

  5. Comment on Tildes Docker Image in ~tildes

    Emerald_Knight Link Parent
    Please note the installation requirements for their Docker Desktop application. You're still installing a hypervisor, which indicates an underlying VM instance being utilized. They certainly...

    Please note the installation requirements for their Docker Desktop application. You're still installing a hypervisor, which indicates an underlying VM instance being utilized. They certainly abstract it away, which is great, but a spade is still a spade.

    5 votes
  6. Comment on Tildes Docker Image in ~tildes

    Emerald_Knight Link Parent
    To clarify, it's possible to run a compatible Linux VM and use containers within said VM. But at that point you're basically just doing what Vagrant does with extra steps. Docker is better for...

    To clarify, it's possible to run a compatible Linux VM and use containers within said VM. But at that point you're basically just doing what Vagrant does with extra steps. Docker is better for handling multiple microservices rather than a single service setup like Tildes. Both tools have their own problem spaces, and Docker just isn't in the Tildes problem space at this point in time.

    4 votes
  7. Comment on Tildes Docker Image in ~tildes

    Emerald_Knight Link
    Docker and Vagrant are incredibly different. Docker is basically a software package. It needs to be compatible with the underlying operating system. Vagrant is a virtual machine provisioner and is...

    Docker and Vagrant are incredibly different. Docker is basically a software package. It needs to be compatible with the underlying operating system. Vagrant is a virtual machine provisioner and is thus compatible with systems that Docker isn't.

    For example, you can run a Linux Docker image on top of a Linux distro with containerization available, but on distros without it or on other operating systems like Windows, you can't run it at all. A Vagrant configuration doesn't have this problem.

    4 votes
  8. Comment on Codecademy vs The BBC Micro in ~comp

    Emerald_Knight Link Parent
    My stance on platforms like codecademy is that they're not particularly good for learning about programming fundamentals, but they're great for learning about the basics of a specific programming...

    My stance on platforms like codecademy is that they're not particularly good for learning about programming fundamentals, but they're great for learning about the basics of a specific programming language. I wouldn't discount the value of the latter just because of the lack of the former, but I'll agree that it drives me nuts that they still sell themselves as the former.

    1 vote
  9. Comment on My country decided that animal sacrifice in the name of religion is constitutional. in ~talk

    Emerald_Knight Link Parent
    I just want to address a small point here: there's actually a very big disconnect between the physical reflex associated with pain and the actual cognitive processing of it. If you burn yourself,...

    if you bring an open flame toward them, they back away from it, just like I would.

    I just want to address a small point here: there's actually a very big disconnect between the physical reflex associated with pain and the actual cognitive processing of it. If you burn yourself, for example, you'll react physically before your brain has actually had time to elicit the sensation of pain. Thus, a reflex isn't necessarily indicative of pain sensation.

    With that out of the way, I'd like to point to good old Occam's razor: If an animal experiences damaging stimulus and then hesitates around or actively avoids that damaging stimulus in the future, then odds are far better than not that they're experiencing the actual sensation of pain. Avoidance is a learned behavior, and one that you don't learn from every reflexive twitch of your body--it's one you learn from the twitches that are accompanied by an incredibly intense and uncomfortable neurological sensation that you desperately want to never experience again (unless, you know, you're into that kind of thing).

    3 votes
  10. Comment on My country decided that animal sacrifice in the name of religion is constitutional. in ~talk

    Emerald_Knight Link Parent
    Extending from this, humans can cognitively reason about pain. Dogs and other animals can't. They don't know why they're in pain, just that they are. They end up being not only hurt, but confused...

    Extending from this, humans can cognitively reason about pain. Dogs and other animals can't. They don't know why they're in pain, just that they are. They end up being not only hurt, but confused and afraid, making their experience of pain arguably worse than our own. Animals are very much like perpetual children, and one wouldn't consider a child somehow lesser than an adult in terms of priority of protection--indeed, we typically prioritize children above ourselves.

    14 votes
  11. Comment on My country decided that animal sacrifice in the name of religion is constitutional. in ~talk

    Emerald_Knight Link
    Some of the points I want to make are touched on elsewhere, but I'll collect them here in a top-level comment: There is definitely a difference in ethics with regards to animals vs. humans. I...

    Some of the points I want to make are touched on elsewhere, but I'll collect them here in a top-level comment:

    There is definitely a difference in ethics with regards to animals vs. humans. I believe this difference to be small, however. Killing an animal for the sake of eating it is fine, but killing an animal for the sake of killing it is wrong. Killing an animal humanely, where it doesn't experience undue suffering, is fine, but causing pain and suffering is wrong. And even before the end of an animal's life, its treatment while it's still alive matters quite a bit--we shouldn't mistreat an animal just so we can keep it around to eat later, but should instead raise it under suitable living conditions.

    In the case of religious sacrifice, the questions to be asked are:

    1. Was the animal treated well in life?
    2. Was the animal killed humanely?
    3. Was the animal eaten afterward rather than being allowed to rot and go to waste?

    If the answer to those 3 questions are all "yes", then I see nothing wrong with allowing religious sacrifice of an animal. It's when any of those questions are answered "no" that I believe there to be an ethical problem.

    Also, I've noticed here and elsewhere that there is often the argument that animals don't feel pain the way that humans do. This argument is absurd. Even barring all evidence to the contrary, with animals exhibiting clear experiences of pain and suffering when injured, we can go the other route and ask this question: would it be okay to kill another human being just because they were born with a genetic defect that causes them not to feel any pain at all (a condition which very much does exist)? Obviously not. The ethics of killing transcend the concept of pain and depend primarily on the belief that deprivation of life is wrong.

    We shouldn't be relying on such myopic arguments. Ethics are not based purely on pragmatism, so let's not pretend that they are.

    8 votes
  12. Comment on Banning of users and the discussion of controversial subjects in ~tildes

  13. Comment on Banning of users and the discussion of controversial subjects in ~tildes

    Emerald_Knight Link Parent
    My understanding is that it was challenged via the Flag Protection Act, but ultimately upheld via United States v. Eichman. Multiple attempts have been made since then to allow the prohibition of...

    My understanding is that it was challenged via the Flag Protection Act, but ultimately upheld via United States v. Eichman. Multiple attempts have been made since then to allow the prohibition of flag desecration, but to my knowledge none have been passed.

    13 votes
  14. Comment on Banning of users and the discussion of controversial subjects in ~tildes

    Emerald_Knight Link Parent
    Uh... I could be wrong about this, but I'm pretty sure it's protected speech via Texas v. Johnson.

    Since the burning of the American flag is not considered to be free speech.

    Uh... I could be wrong about this, but I'm pretty sure it's protected speech via Texas v. Johnson.

    10 votes
  15. Comment on Conceptualizing Data: Simplifying the way we think about complex data structures. in ~comp

    Emerald_Knight Link Parent
    Thank you, I really appreciate that!

    Thank you, I really appreciate that!

    2 votes
  16. Comment on Conceptualizing Data: Simplifying the way we think about complex data structures. in ~comp

    Emerald_Knight Link Parent
    Regarding recommendations, this is tricky. Right now you're just starting out, so I wouldn't recommend concerning yourself too much with writing good code yet. Instead, continue working on nailing...

    Regarding recommendations, this is tricky.

    Right now you're just starting out, so I wouldn't recommend concerning yourself too much with writing good code yet. Instead, continue working on nailing down your problem solving process and your understanding of programming problems as a whole. For example, if you're the kind of person who currently solves problems by process of trial and error, e.g. copy-pasting from StackOverflow and making changes until things work, then you need to stop what you're doing right now and change the way you approach solving problems. These are the fundamentals from which everything else you do will build on, so it's important to build on those fundamentals as much as you can. Solve lots of problems and lots of different kinds of problems. Keep challenging yourself with harder and harder problems over time.

    Quite frankly, no programmer is going to write efficient, non-hacky code when they're just starting out. Learn to accept that right now. You're a novice. You're new to programming. Find me one novice who writes good code and I'll find you a thousand that don't. Writing good code isn't your goal right now. Learning to internalize basic programming constructs is. Writing shitty code is part of the process. It's a rite of passage. Embrace it.

    That being said, it doesn't hurt to start thinking about how to fix your code to make it better. My personal recommendation would be for you to revisit old projects after you've been away from them from several months and update them. It's hard to beat the experience of having to modify an existing project that doesn't adhere to good code practices. If you really want to study refactoring code in depth, though, you might look into getting your hands on the book Refactoring: Improving the Design of Existing Code, by Martin Fowler. It could prove to be a good source of reading material for you.


    Regarding what inspires me to write about these subjects in general, there are a few things that come to mind.

    First, I've had to maintain legacy code. As a result, I've encountered some absolutely horrible design anti-patterns. I've also written some pretty terrible code myself. In both cases, I've had to go in and make changes to the existing code, and it's a painful, frustrating experience every time. I often found myself having to stop myself from procrastinating because I just didn't want to have to touch those parts of the code base. This is what we call "technical debt", and it's measured specifically by the amount of time and effort it takes to make changes to code. I've become very accustomed with technical debt, so it's very often on my mind and being considered whenever I write code.

    Second, there are a lot of things I wish I'd known about prior to getting into professional work, things there were never taught during my CS studies. I like to take the opportunity to help other programmers--both new programmers and programmers who just haven't been fortunate enough to be exposed to these ideas--by sharing them whenever I think to do so.

    Third, I've just always enjoyed teaching others.

    Regarding what inspired me to write about this this subject in particular, however, one thing stands out. I had some code that I'd written last year for my current work. It worked perfectly fine for a while, but as time went on I realized that I kept copy-pasting the same exact code and kept adding more and more edge case checking. As my data structures became more complicated and needed to be extended, it also became more and more of a headache to make changes. It was awful. But then, I'd made a critical realization that I'd taken for granted for so long: everything I was doing could be done in the form of a recursive tree traversal, because these data structures were really just a bunch of nodes and leaves in a tree. That lead to a major overhaul of that particular part of the system and has made it trivial to further extend these structures. What was once a nightmare to maintain is now so ridiculously simple that I could get hit by a bus today and whatever programmer replaced me would have no trouble whatsoever making use of it.

    In short, though, it all boils down to this: I love programming and I love taking the opportunity to teach others about it :)

    5 votes
  17. Comment on How much testing do you guys do? in ~comp

    Emerald_Knight Link
    This depends largely on the complexity of the project I'm working on and how frequently I make potentially breaking changes. Tests take time to write, and the time it takes to write them affects...

    This depends largely on the complexity of the project I'm working on and how frequently I make potentially breaking changes. Tests take time to write, and the time it takes to write them affects feature velocity. When you work for a startup, feature velocity is essential. That being said, sometimes you find yourself making changes that require you to constantly go back and thoroughly test multiple parts of your system. Manually.

    When you find yourself doing a lot of manual testing like this, it's important to bite the proverbial bullet and write yourself some unit tests, otherwise the amount of time spent manually testing your changes will far outweigh the amount of time it would have taken you to write the automated tests. Additionally, you avoid being worn down by the prospect of having to perform even more manual testing, as well as avoiding the very real possibility of forgetting a test case that results in a bug making its way into production.

    Manually testing your code can thus be considered a form of technical debt. You can't avoid technical debt and some amount of technical debt is a good thing, but eventually you have to pay it down as it eventually becomes too expensive to leave alone.

    So, to answer your questions: I only do as much automated testing as is needed to free up development bottlenecks. I fucking hate having to write unit tests, but I hate having to do the same, monotonous manual testing over and over again and not being able to get any real work done, so I do my best to strike a balance between writing the tests I need and deferring other tests until they're actually needed. In between, I just do as much manual testing as is necessary to be confident that my code isn't suddenly broken. I will say, though, that once the unit tests are actually in place, I absolutely love them. It's nice being able to do a small refactor and have all of my unit tests tell me either "no problem, you're good to go" or "lol yeah, you broke something, moron". It makes my life a hell of a lot easier.

  18. Preface Conceptual models in programming are essential for being able to reason about problems. We see this through code all the time, with implementation details hidden away behind abstractions...

    Preface

    Conceptual models in programming are essential for being able to reason about problems. We see this through code all the time, with implementation details hidden away behind abstractions like functions and objects so that we can ignore the cumbersome details and focus only on the details that matter. Without these abstractions and conceptual models, we might find ourselves overwhelmed by the size and complexity of the problem we’re facing. Of these conceptual models, one of the most easily neglected is that of data and object structure.


    Data Types Galore

    Possibly one of the most overwhelming aspects of conceptualizing data and object structure is the sheer breadth of data types available. Depending on the programming language you’re working with, you may find that you have more than several dozens of object classes already defined as part of the language’s core; primitives like booleans, ints, unsigned ints, floats, doubles, longs, strings, chars, and possibly others; arrays that can contain any of the objects or primitives, and even other arrays; and several other data structures like queues, vectors, and mixed-type collections, among others.

    With so many types of data, it’s incredibly easy to lose track in a sea of type declarations and find yourself confused and unsure of where to go.


    Tree’s Company

    Let’s start by trying to make these data types a little less overwhelming. Rather than thinking strictly of types, let’s classify them. We can group all data types into one of three basic classifications:

    1. Objects, which contain key/value pairs. For example, an object property that stores a string.
    2. Arrays, which contain some arbitrary number of values.
    3. Primitives, which contain nothing. They’re simply a “flat” data value.

    We can also make a couple of additional notes. First, arrays and objects are very similar; both contain references to internal data, but the way that data is referenced differs. In particular, objects have named keys while arrays have numeric, zero-indexed keys. In a sense, arrays are a special case of objects where the keys are more strictly typed. From this, we can condense the classifications of objects and arrays into the more general “container” classification.

    With that in mind, we now have the following classifications:

    1. Containers.
    2. Primitives.

    We can now generally state that containers may contain other containers and primitives, and primitives may not contain anything. In other words, all data structures are a composition of containers and/or primitives, where containers may accept containers and/or primitives and primitives may not accept anything. More experienced programmers should notice something very familiar about this description--we’re basically describing a tree structure! Primitive types and empty containers act as the leaves in a tree, whereas objects and arrays act as the nodes.


    Trees Help You Breathe

    Okay, great. So what’s the big deal, anyway? We’ve now traded a bunch of concrete data types that we can actually think about and abstracted them away into this nebulous mess of containers and primitives. What do we get out of this?

    A common mistake many programmers make is planning their data types out from the very beginning. Rather than planning out an abstraction for their data and object architecture, it’s easy to immediately find yourself focusing too much on the concrete implementation details.

    Imagine, for example, modeling a user account for an online payment system. A common feature to include is the ability to store payment information for auto-pay, and payment methods typically take the form of some combination of credit/debit cards and bank accounts. If we focus on implementation details from the beginning, then we may find ourselves with something like this in a first iteration:

    UserAccount: {
        username: String,
        password: String,
        payment_methods: PaymentMethod[]
    }
    
    PaymentMethod: {
        account_name: String,
        account_type: Enum,
        account_holder: String,
        number: String,
        routing_number: String?,
        cvv: String?,
        expiration_date: DateString?
    }
    

    We then find ourselves realizing that PaymentMethod is an unnecessary mess of optional values and needing to refactor it. Odds are we would break it off immediately into separate account types and make a note that they both implement some interface. We may also find that, as a result, remodeling the PaymentMethod could result in the need to remodel the UserAccount. For more deeply nested data structures, a single change deeper within the structure could result in those changes cascading all the way to the top-level object. If we have multiple objects, then these changes could propagate to them as well. And what if we decide a type needs to be changed, like deciding that our expiration date needs to be some sort of date object? Or what if we decide that we want to modify our property names? We’re then stuck having to update these definitions as we go along. What if we decide that we don't want an interface for different payment method types after all and instead want separate collections for each type? Then including the interface consideration will have proven to be a waste of time. The end result is that before we’ve even touched a single line of code, we’ve already found ourselves stuck with a bunch of technical debt, and we’re only in our initial planning stages!

    To alleviate these kinds of problems, it’s far better to just ignore the implementation details. By doing so, we may find ourselves with something like this:

    UserAccount: {
        Username,
        Password,
        PaymentMethods
    }
    
    PaymentMethods: // TODO: Decide on this container’s structure.
    
    CardAccount: {
        AccountName,
        CardHolder,
        CardNumber,
        CVV,
        ExpirationDate,
        CardType
    }
    
    BankAccount: {
        AccountName,
        AccountNumber,
        RoutingNumber,
        AccountType
    }
    

    A few important notes about what we’ve just done here:

    1. We don’t specify any concrete data types.
    2. All fields within our models have the capacity to be either containers or primitives.
    3. We’re able to defer a model’s structural definition without affecting the pace of our planning.
    4. Any changes to a particular field type will automatically propagate in our structural definitions, making it trivial to create a definition like ExpirationDate: String and later change it to ExpirationDate: DateObject.
    5. The amount of information we need to think about is reduced down to the very bare minimum.
    6. By deferring the definition of the PaymentMethods structure, we find ourselves more inclined to focus on the more concrete payment method definitions from the very beginning, rather than trying to force them to be compatible through an interface.
    7. We focused only on data representation, ensuring that representation and implementation are both separate and can be handled differently if needed.

    SOLIDifying Our Conceptual Model

    In object-oriented programming (OOP), there’s a generally recommended set of principles to follow, represented by the acronym “SOLID”:

    • Single responsibility.
    • Open/closed.
    • Liskov substitution.
    • Interface segregation.
    • Dependency inversion.

    These “SOLID” principles were defined to help resolve common, recurring design problems and anti-patterns in OOP.

    Of particular note for us is the last one, the “dependency inversion” principle. The idea behind this principle is that implementation details should depend on abstractions, not the other way around. Our new conceptual model obeys the dependency inversion principle by prioritizing a focus on abstractions while leaving implementation details to the future class definitions that are based on our abstractions. By doing so, we limit the elements involved in our planning and problem-solving stages to only what is necessary.


    Final Thoughts

    The consequences of such a conceptual model extend well beyond simply planning out data and object structures. For example, if implemented as an actual programming or language construct, you could make the parsing of your data fairly simple. By implementing an object parser that performs reflection on some passed object, you can extract all of the publicly accessible object properties of the target object and the data contained therein. Thus, if your language doesn’t have a built-in JSON encoding function and no library yet exists, you could recursively traverse your data structure to generate the appropriate JSON with very little effort.

    Many of the most fundamental programming concepts, like data structures ultimately being nothing more than trees at their most abstract representation, are things we tend to take for granted and think very little about. By making ourselves conscious of these fundamental concepts, however, we can more effectively take advantage of them.

    Additionally, successful programmers typically solve a programming problem before they’ve ever written a single line of code. Whether or not they’re conscious of it, the tools they use to solve these problems effectively consist largely of the myriad conceptual models they’ve collected and developed over time, and the experience they’ve accumulated to determine which conceptual models need to be utilized to solve a particular problem.

    Even when you have a solid grasp of your programming fundamentals, you should always revisit them every now and then. Sometimes there are details that you may have missed or just couldn’t fully appreciate when you learned about them. This is something that I’m continually reminded of as I continue on in my own career growth, and I hope that I can continue passing these lessons on to others.

    As always, I'm absolutely open to feedback and questions!

    14 votes
  19. Comment on Does anyone on Tildes not speak—intentionally or not—to one or both of your parents? If so, why? in ~talk

    Emerald_Knight Link
    Time to dump a preview of a couple of decades worth of emotional baggage here, I suppose :) My biological father is just a piece of garbage. He cared more about his own image and ego than my...

    Time to dump a preview of a couple of decades worth of emotional baggage here, I suppose :)

    My biological father is just a piece of garbage. He cared more about his own image and ego than my future. Even though he hardly had any presence in my life, he had the audacity to suggest that he would "disown" me if I switched high schools to an alternative school due to my failing grades, as though he had any claim to me in the first place. Hell, he treated me like shit before I was even born, trying to force an abortion by punching my pregnant mother in the stomach. This same egotistical fuckwit actually ran for mayor in one of the larger cities in my state almost two decades ago (and barely made a blip in that race, mind you). Overall just not someone worth wasting my energy on.

    My stepdad, the man I actually think of as my dad, I'm on okay terms with, but he definitely rubs me the wrong way. He had a tendency to undermine my mom's parenting efforts and allowed my younger brother in particular to get away with a lot of things. In fact, there were a lot of clear points of displaying favoritism that I didn't realize consciously as a kid, but ended up becoming aware of more recently as I've been out of college and had the downtime and the opportunity to think about my upbringing more consciously. It also turned out that he's done some pretty shitty things, especially to my mom, but she shielded me from that information when I was young so I could develop my own opinion of him. He raised me, so I do love him, but I've definitely become pretty disillusioned and it's caused me to distance myself.

    My mom was going through a lot of shit when she was raising me. She definitely screwed up in her parenting. A lot. And honestly, part of me subconsciously still resents her for not properly preparing me for adulthood, not giving me the freedom I needed to grow, and for having the kind of temper that made me fear doing just about anything if there was even a remote chance that she might be upset with me (even small things like asking for a ride somewhere), among other things. But she did try her hardest, and put herself through a lot for the sake of my brother and I. It's just unfortunate that the damage is still done, so once again I kind of distance myself.

    I do talk to my mom and dad whenever either gets in touch. I don't intentionally block them out. But there's enough emotional distance that I don't feel inclined to make the effort to initiate, and I can't afford the much-needed therapy to help bridge that distance.

    In short: My biological father can go fade into obscurity where he belongs, but as for my parents, I just don't take initiative with contacting them because my upbringing makes me a walking sack of job security for whatever therapist I end up seeing in the future.

    9 votes
  20. Comment on Some small updates over the past week in ~tildes.official

    Emerald_Knight Link Parent
    No problem! And yeah, the humor in those websites is definitely a little dry and esoteric. As for your username, it still gets a bit of a chuckle out of me now and then :)

    No problem! And yeah, the humor in those websites is definitely a little dry and esoteric.

    As for your username, it still gets a bit of a chuckle out of me now and then :)

    2 votes