• Content count

  • Joined

  • Last visited

Community Reputation

332 Neutral

About EvincarOfAutumn

  • Rank
  1. the public syndrome

    It makes sense for the monster to check the player’s shield when it attacks, because that’s what the monster is doing in the simulation. It makes sense for the player object to have a shield object, because that’s exactly the situation you intend to represent. You could have a public member function on the player for getting a read-only view of their equipment—then monsters could use those methods to answer questions like “Does the player have a shield, and is it wooden?” but wouldn’t be able to alter that information in unstructured ways.   An interaction like this can be modelled in many ways. It’s up to you to start with something plausible, and alter it when you see the need to. And you can’t really predict the choices you will make in the future, so it doesn’t make sense to write much more than just what gets the job done well in the present and is easy enough to change later.   OOP is a class of architectural philosophies, not a consistent theory with specific or consistent rules, so there is no “right answer” here. You just have to develop a sense of design aesthetics over time. One good way to do that is to have other developers review your code and point out areas for improvement. But I would say correctness by construction is a very strong guideline.
  2. the public syndrome

    An object should be correct by construction, and its public API should only produce valid states as well.   If you have a Point class with two public fields X and Y, that’s fine as can be—there are no invalid Points, so there are no invariants to preserve, and the fields can be exposed directly. Whereas if you have a Heroine object with Health and Shield fields, then it does not make sense to expose these directly, because code could place the heroine in an invalid state by killing her even though she has a shield. That’s why people recommend methods like “heroine.takeDamage(monster.strength)”.   The other thing to consider is that if you use immutable objects—that is, treat them as values representing the state of an object, not as the state itself—then as long as objects are correct by construction, they cannot ever be in an invalid state because they will never change after construction. That can make it significantly easier to reason about what your code is doing and ensure its correctness.
  3. Game Engine, How do I make one

      A game engine is a system for making games—an application, or more commonly a library, providing services such as audio & video output, controller input, timing, physics, and so on. How do you make one? You don’t. If you want to make a game, you should make a game. You will not accomplish that goal, nor really improve as a software developer, by creating middleware without specific goals and requirements in mind.   Just pick an engine (or less—a basic graphics+audio+input library such as SDL is fine) that’s compatible with your style of thinking and/or your preferred programming language. Use it to make something. If you encounter limitations, take the time to understand and work with them. Working under constraints will help you think more creatively. Keep a diary of these situations and maybe next time try a different engine.   You can learn a lot by making an engine—about systems programming, graphics, audio, input, API design, and infrastructure in general—but it’s best to fight on a single front. Want to make a game? Take advantage of the work that others have done. Interested in engine design? Do it as a learning exercise. And anyway, you can learn all of those skills in other ways, incidentally, in the process of creating useful software.
  4. unique_ptr

    [tt]unique_ptr[/tt] owns the object that it points to. [tt]move()[/tt] can be used to transfer that ownership to another [tt]unique_ptr[/tt]. You will find that after your call to [tt]move()[/tt], [tt]vFoo.front()[/tt] will be a null [tt]unique_ptr[/tt]; ownership of the object has been transferred to [tt]p[/tt] using its move-assignment operator.
  5. An enum made of structs

    Why not just declare objects?   // Move.h extern const Move TACKLE, GROWL, HYPER_BEAM, ...; // Move.cpp const Move TACKLE(...); const Move GROWL(...); const Move HYPER_BEAM(...);   If you need these in a table (e.g., to refer to them by integer ID), then you can build that separately.
  6. Hap: a simple concurrent programming language.

    The current implementation runs a periodic interrupt and polls events, but it’s a relatively minor change to do it push-based, and I mean to. Hap is deliberately not general at the moment, for the sake of getting something out the door—it’s an iterative process. The original idea included “repeat when” and “repeat whenever” for running loops in response to events, but these proved challenging to implement correctly with the current model, so I held off. “repeat when” would begin evaluating when a condition became true, and repeatedly evaluate until that condition became false. “repeat whenever” would do the same thing, but also resume execution when the condition became true again.
  7. Hap: a simple concurrent programming language.

      Hah, looking like a mistake isn’t good. Then again, “fn” has precedent in Clojure and Rust, so perhaps “fun” isn’t too far off the mark. The “function” keyword in JavaScript must have looked perfectly fine when the language was designed, but it’s a pain now that the use of the language has evolved. I’ll think about it and take input from others. Syntax is unimportant and then it’s important, you know?     I appreciate it! I have toyed with LLVM before and will most certainly move to it in the future. (ApochPiQ took a similar route with his Epoch language, for example.) What’s done is done, and the existing interpreter certainly isn’t bad—just not ideal. Now, the things that concern me most are the design of a standard library and support for embedding. I would be interested to hear your opinion on those. As far as a standard library goes, what I’m working on at the moment is quite simply exporting native functions through a global object. For example:   var keys = hap.map.keys; var map = { this: "this", that: "that" }; # ["this", "that"] trace keys(map);   The “hap” object would give you a few broad namespaces such as “map”, “list”, and “io”. When embedding Hap, you would use the same mechanism to export native functions to your Hap code. You could also make foreign function declarations by leaving off the body of a function definition. In both cases, marshalling would be done by the Hap library. I would also like to support immutable names by way of a “val” keyword (à la Scala). In the future I will add support for type annotations, and move to static typing with global type inference—I have an appropriate inference algorithm. This may seem radical, but it offers distinct advantages. For one thing, I don’t think it’s possible to manage the complexity of Hap-style concurrency and dynamic typing together. These small concurrency examples are simple to understand, but I fear they won’t scale without some help.
  8. Hap: a simple concurrent programming language.

      Thank you!     That’s a valid point. I don’t want to bikeshed over syntax, as it’s easy to change, but here’s my rationale for using short keywords:You use them frequently, so they ought to be succinct.They’re unlikely to conflict with good, useful identifiers. None of this “klass” or “function_” business.They’re just abbreviations, not abstract mnemonics or operators.   Also fair. I’ve written a number of compilers and interpreters and this is just what felt right for a prototype. Whenever a statement is evaluated outside an atomic section, the interpreter runs an interrupt during which the active asynchronous conditions are evaluated. It was very easy to write a simple VM with this kind of custom behaviour built in—easier than implementing it atop an existing model. In the early stages of a language, there is more value in flexibility. Only as the language matures can you know what the “right” way is.
  9. Well, I’ve gone and done it again—written a language when nobody asked me to. This one might be interesting to game developers, and I’m interested to hear feedback from anyone with an opinion about what ought to be done with it. The language is called Hap and is designed as a readable dynamic language for game prototyping. It uses single-threaded cooperative multitasking to support simple event-based concurrency. The example from the README sums it up nicely:   var health = 100; when (health <= 0) { print("Goodbye, cruel world!"); exit; } print("Hello, sweet world!"); while (true) health = health - 1;   This code prints:   Hello, sweet world! Goodbye, cruel world!   Hopefully that’s enough to get your attention. Another example shows how you can use the asynchronous control flow statements to write surprisingly readable code:   fun make_cycler(start, end) { var current = start; whenever (current > end) current = start; ret lam() { var previous = current; current = current + 1; ret previous; }; }   This function returns a function generating a cycle of values in the given range. For instance:   make_cycler(0, 2)   Will produce the sequence:   0, 1, 2, 0, 1, 2, …   Hap can be used on its own, but the idea is to support embedding in larger systems, much like Lua or JavaScript. Being only a few weeks old, the language naturally has some issues that would need to be addressed before it could be used in production. In particular:   There is no standard library, and thus no way to perform a number of common math and container operations. Embedding the language is not as simple as it should be. Asynchronous control flow is not first-class, i.e., there is no way to refer to or halt a “whenever” statement. The prototype interpreter is not designed with performance in mind.   I will gladly address these and any other issues if there is interest in the general concept. Tell me what you think!
  10. Protodata—a language for binary data

      What do you mean exactly? First-class support for format versions?
  11. Protodata—a language for binary data

      Yeah, I’m well aware of the limitations. Just trying to decide where to go with it.   I think what I’d really like for Protodata to become is the killer DSL for two things: Writing a legible, executable file format spec; and Doing simple reporting and transforming of data in existing files. Think “binary sed with external modules”.
  12. Protodata—a language for binary data

      Yeah, there definitely needs to be some way to avoid repetition. Both your examples would be covered by a macro system. For now the C preprocessor works:   #define int s32 int 1 2 3 #define something(foo, bar, text) \ u16 foo \ u32 bar \ utf8 text \ u8 0 something(0, 1, "x")   But I intend to add something more concise and typesafe. In particular, something that feels more native to the language.
  13. Protodata—a language for binary data

      I hadn’t really, but that’s a good idea. Macros and expressions are in the works—the main challenge there is just designing something nice, not so much the actual implementation. I’ll be sure to include a way to make absolute and relative references.
  14. I recently rewrote and improved some old software of mine, and figured the next step is to put it in the hands of people who might use it.   Protodata lets you write binary data using a textual markup language. I’ve found this really useful in game development when I want a custom format, would rather not use plain text or XML, and don’t want to invest the time to make a good custom editor. Protodata supports signed and unsigned integers, floating-point numbers, and Unicode strings, all with a choice of bit width and endianness.   Edit: here’s an example document describing a cube mesh.   # File endianness and magic number big u8 "MESH" # Mesh name, null-terminated utf8 "Cube" u8 0 # Using an arbitrary-width integer for zero padding u24 0 # Vertex count u32 8 # Vertex data (x, y, z) f32 +1.0 +1.0 -1.0 +1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 +1.0 -1.0 +1.0 +1.0 +1.0 -1.0 +1.0 +1.0 -1.0 -1.0 +1.0 +1.0 -1.0 +1.0 # Number of faces u32 6 # Face data (vertex count, vertex indices) u32 4 { u16 0 1 2 3 } # Back 4 { u16 4 5 6 7 } # Front 4 { u16 0 4 7 1 } # Right 4 { u16 1 7 6 2 } # Bottom 4 { u16 2 6 5 3 } # Left 4 { u16 4 0 3 5 } # Top   Please tell me what you think and offer suggestions for improvement.
  15. Become a Good Programmer in Six Really Hard Steps

    Excellent article, and I agree 100%. Most people aren't going to get nearly as far as is possible, or even realise how far it's indeed possible to go. This ent up on Hacker News, by the way.