Author Topic: Simon blogs  (Read 12464 times)

0 Members and 1 Guest are viewing this topic.

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #105 on: January 08, 2018, 09:33:14 am »
How to prevent bugs by design

When you write a Lemmings-like game and you need replay functionality, do not let assignment clicks issue both a replay action and affect the lemmings directly. Instead, issue only replay actions. Then, for the next physics update, fetch these new assignments from the replay and apply them. If you design like this, you will kill an entire class of replay-desyncing bugs outright. This is good design.

The replay collection on github is not only a check that I ship 100 % solvable levels. It has massive value as an integration test. When the replays pass and not fail, I know that the physics, the tile-loading code, the replay code, etc.,  all behave well together.

D has built-in coverage analysis via optional compiler switch, and I gave that a go: Lix's unittests and the replay verification together cover about 40 % of the entire codebase. Compared with professional software development, 40 % is lousy coverage, you would rather aim at 90 % to 100 %. But most of the uncovered code is editor, GUI, networking, and similar areas that were not written with unittesting in mind. Considering that I haven't written those areas with tests in mind at all, 40 % coverage is surprisingly high.

For the networking, I wrote a separate small command-line tool that connects to the server and can chat, which was useful during the network development. But this is not automated testing.

When you have a complex program that depends both on some abstract model and on outside dependencies like images or networking, don't let the model depend on the outside dependencies. Ideally, make the model self-contained, therefore easy to test, and merely feed the outside dependency into the model as a very last step. Example: In Lix, multiplayer replays can play from a replay file, you don't need networking to watch that. The replay can play back locally with all of the physics and different player colors. The networking would merely supply inputs for the physics during a match.

-- Simon

Offline Forestidia86

  • Posts: 354
    • View Profile
Re: Simon blogs
« Reply #106 on: January 08, 2018, 01:39:53 pm »
I'm not sure if that's off to what you've said but there is a possibility that a replay desynches but nevertheless passes. But that's more a "problem" for single replays than for the huge replay base you have since for the latter it's unlikely that most of them are such corner cases.

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #107 on: January 08, 2018, 11:54:52 pm »
Yes, it's always possible that tests pass despite a bug. But that's in the nature of such tests.

To mitigate, we can make more tests, or more precise tests that examine more corner cases.

-- Simon

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #108 on: January 22, 2018, 02:12:06 pm »
D class references may be null

D still is the langauge that produces the fewest Simon rants. Many common problems have great, short solutions in D. But, as with anything worthwhile, it has its corners to point and and rant. What everybody loves most!

Class objects live on the garbage-collected heap. The basics of OO of D are similar to OO in Java and work great. Your class variables are references (pointers, names). If you want polymorphism in any language, you need reference semantics, therefore all this is consistent, good design.

The initial value of any class reference is null. That is also a good choice.



class Color { /* ... */ }
class Porcupine {
    Color color;
// implicitly null
}

But D has no way to statically enforce that a reference cannot be null in a given context. You may assert a reference's non-null-ness, but that happens at runtime. I want nice check at compiletime. Also, it feels slightly dumb to assert for non-null because your operating system already asserts this for you on every usage: The OS will print "segmentation fault" and you run your debugger. :P

I like to write several short methods, and naturally, some of them take class arguments. I really don't want to assert that my parameters aren't null every time over and over.

Structs in D are value types, they have no such issues. If I define Point to be a struct with two ints, x and y, then Point will occupy as much memory as two ints would, and its default value is x = 0, y = 0, because 0 happens to be the default value for int. (Everything in D is default-initialized, which kills a massive class of bugs from C++. If you're absolutely sure that the default initialization kills your performance, you can explicitly write Point p = void;)

Problem statement: I wish for a class reference that is guaranteed to be non-null. Assigning null to this reference is a compile-time error, no matter how indirect the assignment was. It's an acceptable hack to have it null by default, as long as nobody can ever read/dereference it before it's overwritten with non-null for the first time.

Let's attempt a solution: I could wrap all my nullable D class references in a templated struct, such as the following, where C is the template parameter.

NotNull!C (click to show/hide)

Then I can ask for a NotNull!C everywhere I'd normally ask for a C. This has 3 downsides:
  • It's a nonstandard hack. This problem is really really common in OO and it's worth a language feature, or at least a library solution. The D standard library has Nullable!S for structs, but no NotNull!C for classes.
  • It doesn't statically prevent me from instantiating the thing with null, e.g., auto x = NotNull!C(null);. It still passes compilation and only asserts at runtime. The benefit over normal class references is that it explodes already when we create the null reference, not merely when we dereference later, as a normal segfault would.
  • It's an abstraction inversion. The non-nullable type is type with simpler behavior, I can call all methods without segfault. The nullable type is the more complex type, I can either call methods on it or must check first for non-nullness. My NotNull implements a simple type in terms of a more complex type. This is bad design.
Who does it better? In Zig, your types cannot be null, not even pointers, unless you add ?, the nullable type modifier. You may not use a nullable type freely unless you specify what happens in the null case. The language has special, short constructs for exactly this problem.

In general, Zig is designed around easy passing of error conditions because it avoids exceptions and garbage collection. % is another type modifier, it means "either your type or an error code".

C# gets a mention here for its null-conditional dereferencing operator ?. that calls a method only if the reference is not null. This cures the symptom of having many ugly null checks throughout the codebase, but the original issue still stands -- it's not a compile-time prevention of assigning null to class variables.

-- Simon
« Last Edit: January 22, 2018, 04:17:03 pm by Simon »

Offline ccexplore

  • Administrator
  • Posts: 4736
    • View Profile
Re: Simon blogs
« Reply #109 on: January 23, 2018, 08:32:36 am »
I have to imagine that this is probably a relatively well-studied problem in computer science, and the literature probably has much more in-depth information on this topic.  Very generally speaking, static verifications by the compiler can often either mean many false positives (so you end up not really paying attention to them), or else only work in limited cases (so you still have much code where the compiler can't help you, especially the "hard" cases where it would've mattered the most).  I wonder if maybe this is difficult enough to do well (in one or both of the ways enumerated above) that for most languages (or even compiler writers, since it's probably possible to embed custom static asserts into existing language syntax like comments), it had been deemed not worth the effort?

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #110 on: January 23, 2018, 01:26:32 pm »
I'm not well-versed in the academics of software engineering, but yeah, I assume there should be something. And I expect other modern languages (newer than both C++ or D) to have something in this direction.

C++ would have initializer list, that would be the obvious place to assign to the non-null-ref from a guaranteed-non-null-source like new expression, other non-null-ref, STL pointer with similar guarantee.

D has no initializer lists, but the D compiler analyzes program flow in constructors, it will already error if you assign a value to const/immutable field more than once. I believe the infrastructure for non-null is similar to such const-enforcing infrastructure.

I'd be okay if you could forcefully cast from nullable reference to non-null-reference. You can already cast away D's immutable and thereby assume responsibility for any undefined behavior. Memory-safe example: Allocate memory, do something complex in it, then assign that chunk to a pointer-to-immutable. This promises that the chunk will never change, not even through other references to the same chunk. If no mutable references to this chunk escape the scope, there will be no undefined behavior, everything is perfectly fine. For this hack, there is even a standard library function, assumeUnique, which merely wraps the cast to immutable under a descriptive name. I guess offering similar hacks for subverting null would be fine.

From the linked page:
The downside of using assumeUnique's convention-based usage is that at this time there is no formal checking of the correctness of the assumption; on the upside, the idiomatic use of assumeUnique is simple and rare enough to be tolerable.

I believe D offers only nullable references out of tradition, and it would be a massive breaking change. As I said before, the non-nullable class reference to a C should be C, whereas the nullable reference is the oddity that deserves the stigmatic name C? or Nullable!C or similar.

I would love to see such a massively breaking change, yes yes, I have no qualms smashing porcelain for a small extra gain. But I'm not everybody. >_>;; And when your language is used in the industry, this might well be infeasible.

-- Simon
« Last Edit: January 23, 2018, 02:12:11 pm by Simon »

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #111 on: February 12, 2018, 05:55:55 pm »
Lix 1v1

I want to play Lix 1v1 on highly swingy maps, where a few mistakes lose a game in one shot and then you immediately play the same map again. The games are so rapid that even though you care about winning, the actual outcome never sticks in mind for long afterwards.

geoo has always loved to play in this style, but anybody is welcome as a 1v1 opponent. I merely know that geoo shares this taste in maps. I've shied away from heavyweight multitasking maps like Chasing Upwards, but I miss that also.

Maybe the nuke-exploders should be instant for an even more rapid series of games?

Discord

Closed-source, invasive privacy policy, bloated, and slow. Use IRC for chat, public logs and a forum for archiving, and Mumble for voicechat. There is vector.im as an open-source Discord alternative but I haven't ever heard about it outside of very narrow search queries for Discord alternatives.

The massive downside of forums is that you must register for every single one separately. Sadly, github issues is not a good forum. :lix-evil:

Type popcorn

Rant from 2012, before Python got optional type annotations: Abscissa's Why I Hate Python (Or Any Dynamic Language, Really) -- A stab at type errors and missing-variable errors that a compiler would catch. The rant generated 74 comments, it's a phenomenal popcorn thread (= you can read it like you would watch an exciting action movie, munching popcorn along the way).

I still miss the non-null class refs in D. Good thing I occasionally re-read my own posts here, very instructive because the reasoning and implementation pitfalls aren't easy to keep in mind completely. >_>

-- Simon

Offline Colorful Arty

  • Posts: 584
  • You are loved!
    • View Profile
    • Colorful Arty's Youtube Page
Re: Simon blogs
« Reply #112 on: February 12, 2018, 07:01:18 pm »
I'd take Python Type Errors over C/C++ Segmentation faults ANY DAY. At least the Type Error tells you WHERE it happened for a quick and easy fix.
My Youtube channel where I let's play games with family-friendly commentary:
https://www.youtube.com/channel/UCiRPZ5j87ft_clSRLFCESQA

My levelpack: SubLems
For NeoLemmix: http://www.lemmingsforums.net/index.php?topic=2787.0
For SuperLemmini: http://www.lemmingsforums.net/index.php?topic=2704.0

Upcoming levelpack: ArtLems
Development Topic: http://www.lemmingsforums.net/index.php?topic=3166.0

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #113 on: February 19, 2018, 11:29:17 am »
Python Type Errors over C/C++ Segmentation faults

In a good system, the segfaulting/type-mismatching code doesn't even compile in the first place. See earlier posts. :lix-glare:



Order

To compose a piece of email,

1. attach file.
2. Write body.
3. Write subject. If subject is hard to write, prune the body and it will become clearer.
4. Pull in the recipients and send.

To reply to email,

1. attach file.
2. Prune prune prune the other person's bloat.
3. Reply to pruned quotation.

The human mind is exceptionally good at detecting patterns, but has a terribly hard time formulating rules to explain patterns.

E.g., email composed in a different order than I presented. You type the prose that nobody cares about, then fail to attach the file because it's 100 % clear what you have to do and it's boring to do it. The human mind skips over this step because there is a glaring pattern here: We always attach the file that we made earlier.

E.g., copypasta of code, leading to long functions. The function has conceptual segments that aren't abstracted away. Even if it's clear to the writer because they know about the segments, it's not clear to the first-time reader; the first-time reader must assume that everything depends on everything. The cure here is to forcefully refactor anything that gets too long.

E.g., procrastination of dull work. If thesis is roughly finished and only debugging, writing introduction, proofreading etc. is to be done, the work is no fun. We don't want to do it. The pattern here is: Do random things until you have enough for the thesis, then add chrome and turn it in. The chrome is considered standard, but this time, there is no shortcut/catch-all solution, unlike with the earlier examples. Thus, procrastinate.

-- Simon

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #114 on: February 19, 2018, 12:46:52 pm »
Mouse cable









-- Simon

Offline Colorful Arty

  • Posts: 584
  • You are loved!
    • View Profile
    • Colorful Arty's Youtube Page
Re: Simon blogs
« Reply #115 on: February 19, 2018, 03:24:54 pm »
Or alternatively, you could just get a wireless mouse. ;)
My Youtube channel where I let's play games with family-friendly commentary:
https://www.youtube.com/channel/UCiRPZ5j87ft_clSRLFCESQA

My levelpack: SubLems
For NeoLemmix: http://www.lemmingsforums.net/index.php?topic=2787.0
For SuperLemmini: http://www.lemmingsforums.net/index.php?topic=2704.0

Upcoming levelpack: ArtLems
Development Topic: http://www.lemmingsforums.net/index.php?topic=3166.0

Online Simon

  • Administrator
  • Posts: 2273
    • View Profile
    • Lix
Re: Simon blogs
« Reply #116 on: February 19, 2018, 04:04:02 pm »
I use the mouse only occasionally, every 10 minutes for a couple seconds. Wireless mice love to sleep whenever I'm not mousing and then take time (0.2 seconds or longer?) on the first input because they must reconnect. This means that most of my wireless mouse inputs have massive lag.

I switched back to cable mice to end this madness. Maybe wireless mice for over 100 euros do better, I haven't tried any of those.

-- Simon

Offline Colorful Arty

  • Posts: 584
  • You are loved!
    • View Profile
    • Colorful Arty's Youtube Page
Re: Simon blogs
« Reply #117 on: February 19, 2018, 07:04:55 pm »
That's fair. My problem is that I lost the USB connector for my wireless mouse, but since I use a laptop, wired mice are quite nice for me.
My Youtube channel where I let's play games with family-friendly commentary:
https://www.youtube.com/channel/UCiRPZ5j87ft_clSRLFCESQA

My levelpack: SubLems
For NeoLemmix: http://www.lemmingsforums.net/index.php?topic=2787.0
For SuperLemmini: http://www.lemmingsforums.net/index.php?topic=2704.0

Upcoming levelpack: ArtLems
Development Topic: http://www.lemmingsforums.net/index.php?topic=3166.0

Offline mobius

  • Posts: 2096
  • semi-inactive. PM me with important questions.
    • View Profile
Re: Simon blogs
« Reply #118 on: February 19, 2018, 07:25:23 pm »
I've been using pic #3 for a long time now and haven't had any problems with the extra cord pushing; maybe it depends on type of cord or mouse. Also I wrap my cord around the legs of the keyboard. I guess that's more like a loop.

I used to use wireless mouse but occasionally I'd knock it off the table. Now if it falls the cord keeps it from hitting the floor and breaking. :D
« Last Edit: February 19, 2018, 07:56:56 pm by mobius »
"I choose the danger" -Dr. McCoy



soundcloud: https://soundcloud.com/a-tax-980147606/raindrops
deviant art: http://rhubarb338.deviantart.com/

Offline Raymanni

  • Posts: 246
  • Indeed.
    • View Profile
Re: Simon blogs
« Reply #119 on: February 20, 2018, 09:19:06 am »
Finally someone mentions this problem, I've been struggling with this forever. I used to work with pic 3, but the cable kept pushing my mouse forward, so I ended up with pic 4. It's the most acceptable solution for now, but still feels a bit hack-ish. ???