Thank you for your donations!   Download the book!   Public source code repository   Join the group!
Showing posts with label todo. Show all posts
Showing posts with label todo. Show all posts

New variants!

Quite some time ago I wrote that I'd like to add some enlarged, but otherwise Classical Chess variants, but dismiss them as not belonging to the book, and requiring their own, separate book which would also discuss mobility. In the meantime, I kinda warmed-up to the idea that enlarged Classical Chess variants do belong to the book presenting new variants, especially since their objective is to make playing on large chessboards more approachable to average, casual player.

Currently, I plan on adding enlarged Classical Chess and Croatian Ties variants as remarks at the very end of the book; those variants won't be fully featured, but will include images. I'm not entirely happy with this design choice, since it will disrupt the flow of the book; but it should be worth it, given that this new book on mobility and such is still very far away, if it will be written at all.

I plan on adding 3 new Classical Chess variants, those will feature only pieces found in Classical Chess, with exactly the same rules, except longer rushes and castlings. I'll also add 3 new Croatian Ties variants, those would be nearly identical to their Classical Chess counterparts, but all Knights would be upgraded to Pegasuses (Pegasi?), and all Pawns will be of side-ways variety; this is to compensate somewhat for decreased mobility of Pawns, Knights on a larger chessboards. New variants will be played on 14 x 14, 20 x 20 and finally 26 x 26 chessboards.

New variants won't bring neither any new pieces, nor any new interactions. Still, I'll have to expand Python application which generates images for the book to support all new variants; later, library will have to be expanded as well. This will take some time, given my usual tempo of writing (which is very slowly) I hope I'll have the book finished by the end of this year.

Tectonic shift

It's unusual to have very base data model change this late in a project; any yet, here we are. Currently, I was implementing generators of all legal paths every piece can make in a ply. Since this change will disrupt development for quite some time, let me try to explain it.

The issue I'm talking about is that currently piece and tag enumerations are separated; which would be fine in almost all designs, since most of the time one data entity is indeed independent from any other, e.g. one person's year of birth is very separated from their phone number. This is not the case here, Bishop can never get "can rush" tag, nor Knight can ever castle.
Edit: Tags and pieces have very few, and very intimate relationships; usually tag can be attached to a few pieces (e.g. rush and en passant tags for privates). Technically, tag is a link between a piece and a field at which that piece is located. Once that link is broken (e.g. a Pawn has been activated and moved away), there is no more tag present (e.g. activated Pawn has lost its promotion tag) even if both a piece and its originating field are still present in a game. Most of the time the best data model is the one designed after real-life; this is why original design featured tags separated from pieces. 

Another issue with separated piece and tag enumerations is that, well, they are separated. So, every function working with pieces (and in a chess game that means all of them) has to have two separate parameters. Yes, you can combine then in a neat struct, together with some other bits (e.g. a position) to bring number of parameters down; but, they both are now present in every struct you have to pass around.

Plus, they still take too much space, in fact, twice as much as needed; and that's after a change has been made quite some time ago, so that only 1 byte (char) is stored per enumeration instead of default 4 (enums are just fancy ints). You might think that we have moved past storage issues since like forever, but that is true only for local apps. For libraries, one has to consider possibility that it might serve many users at once, also it might need to do it on a restricted hardware (e.g. micro-controllers).

There is another issue with enumeration storage, and that's space it takes in a chessboard struct; again, currently it's double the amount it actually needs. Parsing user notation and applying it to a current chessboard is not an easy task; if undo is supported then one has to apply all moves performed until that point. To speed things up (and avoid doing the same job twice) position after performed move can be stored, and later retrieved. Only problem is, the largest variant has 676 fields, 190 pieces and 14 different interactions (losing tags are not counted, as they're result of interactions), which means it can last much longer than classical chess game. The longest recorded classical chess match in tournament was 269 moves, but legal match can stretch up to 5898.5 moves with 50-moves draw rule, or up to 8848.5 moves if 75-move draw rule is used instead.

For the record, move to me always means all actions performed by one player in one turn; this is different from FIDEs definition used above which define a move as white player action followed by black player action; for FIDE definition I use term cycle. Reason why I refer to move as such is because each player moves their pieces independently to each other (current chessboard position notwithstanding), there is a meaningful choice to be made; in fact, making a choice is the whole point of a game.

So, how long can chess game go in the latest and greatest variant of them all, the One? Honest answer, I don't know. I tried to guess-timate the thing, but it's probably wildly inaccurate. My guess is that tournament games won't last much longer than 18.000 moves, and technically legal games could probably go for 10.000.000+ moves; both with 50-cycle draw rule. This post is already too long, if you're interested to hear my reasoning, let me know in comments below.

As you can see, if undo is supported by storing chessboards, just having variant enumeration as an int in a chessboard struct can easily eat up to 40+ MB of space, just for that one enum. Undo chessboards could currently use 13+ GB of RAM in total; with changes proposed here implemented, that could drop down to 6.5+ GB. This is why I'll merge piece and tag enums, and also change variant enum storage to byte. Such a change will affect everything in the project; for the time, there will be development without (much) progress.

En passant finally described! And checking opponent's King! More news at 11! Also, the book has been updated ...

I'm updating the book after only two months, since there has been some significant changes, including en passant being thoroughly described, for real. More detailed list of changes follows:

  • fixed side-effects table, added footnote
  • clarified displacement
  • described checking opponent's King
  • clarified static Serpent
  • clarified castling blocked
  • clarified piece actions
  • clarified Scout can be rerouted around any non-empty field
  • described en passant blocked, denied, turned into capture, divergence, ...
  • described activation after en passant
  • described multiple rushes, en passants in a cascade
  • described en passant in close quarters
  • described en passant affected by a Star, Starchild
  • switched to non-shifted tilde
  • and many more tiny edits, fixes, ...

While I was reviewing changes I made to the book since the last update, it also occurred to me that I gave too much leverage to pieces attacking opponent's King. Most pieces grew significantly more powerful, especially in late variants, while King remained the same it ever was, in effect making it much weaker against any opposing force. This is fine, and as it should be; the tricky part here is finding the right balance. This is why I already started changes to limit checks only to pieces directly "in line of sight" of opponent's King, meaning that King is in check only if all capture-fields between an attacker and a King are empty. In hindsight, what happened here is just me always leaning towards giving a player more choices, a piece more mobility; in short, more power for all; sometimes more power is just too much.

I also lean towards doing the same with Wave. Currently, due to its transparency, Wave cannot be hard-pinned to its King. With upcoming changes, Wave on a capture-field would nullify any check to a King behind it. The reason is the same one used to make Pyramid incapable of checking opponent's King, while at the same time that very same Pyramid can capture any other opponent's piece; it is to ensure all checks, checkmates are direct, simple to see, and reason about. It also helps that in doing so, it makes rules (and code implementing them) less fiddly.

It could be argued that transparency of Wave is hard principle, i.e. it should always work like that, without exceptions (which is true), and also that King would be sufficiently protected just by aforementioned changes, without changing Waves (also true). Counter-argument might be that directness of checks and checkmates is also hard design principle, i.e. no pieces between checking piece and checked King. Good game design promotes, and sticks to its own design principle hierarchy. To me, directness of checks, checkmates have precedence over any other rule; which means, transparency of a Wave will have to have an addendum.

I plan finishing changes mentioned here, then continue working on the code; book update will follow its own (give, or take) quarterly schedule. In the meantime, I'm also starting to think about revising One variant, namely Starchild could be reverted to its initial design, where any piece could activate it. Still, this is very low priority, and in very early stage; so, it most likely won't make it in time for next book update.

The book was compiled on March 9, 2025, version is 20250309.071052, and can be found in usual places.

Too much coffee

Since the very last post, I reverted most of the changes to the repository. Too little sleep, and too much coffee will do that to you. You see, I overlooked that out of 8 divergent pieces in the latest variant, 6 are available to any one player, and 4 of those are also transparent. Which means that every single piece in a move (and especially the ones starting a cascade) has to recursively build its path along chessboard, to find out its longest path.

Except, that is not completely correct; generated path for the first piece has to maximize momentum, not its length; and obviously enough, those two things are not necessarily the same. Also, I still need a way to filter out all generated paths according to parsed user notation, since it might contain specific fields visited, and side-effects exerted. And so, I do need a parser; hence revert.

Not all is lost, most of development on now archived branch was also merged back into mainline development; including newly assigned reposition symbol. I also renamed modules storing parsed notation, because those were all too easily confused with modules parsing user notation. Storage for parsed notation (at very least, steps and side-effects) should also be part of generated paths, since paths also has to contain everything encountered on a current chessboard.

Before I delve more deeply into generating paths, I plan to clean-up losing tags, try to resolve TODOs in parse modules, and also have parsing user notation covered with tests. As for tests, one can easily get drown in those, and still fail to test some peculiar interaction. I think, I should give up on testing all of interactions since there are simply too many of those, and cover only the simplest forms.

For instance, I should have one battery of tests just for step separators, the other one just for side-effects, and another still for ply separators, and one for move statuses; each of those should test only one separator, side-effect, status. It might be possible to generate tests based just on a chessboard set-up, and test all interactions that way. However, without a proper way to generate all legal moves for a given situation on a chessboard, it's too early to  think about it.

Early spring clean-up time it is, I guess. And after all of that, on to generating and filtering paths.

Conveyor-belt no more

You might have noticed in the repository that I already scrubbed clean C source code of all kinda working, but not all that useful modules. The problem is that design consists of all special cases, where every special case has its own super-special case; special cases all the way down. Trying to implement this in a super-uniform, context-agnostic code based on a conveyor-belt design is nigh on impossible.

So, this time I'll try different approach, and handle each piece and each interaction separately. Also, instead of trying to implement all of pieces and side-effects all at once, I'll start with one simple case (say, Bishop), and then do everything from parsing notation to applying that parsed ply to chessboard, just for that one piece. In short, finish vertical implementation, before going sideways (except for new Pawns ;). This will also allow for testing full stack before adding more pieces, interactions.

Downside to this approach is that adding each new piece, interaction will also require changes to already existing, and tested, API. Unfortunately, there isn't much I can do about that. Still, slow progress is much better than no progress at all.

Conveyor-belt, you ask? Well, in what seems like eternity, when dinosaurs roamed the Earth Flash-based games on the web were popular, there was one fun and engaging lil' puzzler, even if it featured sometimes wonky physics. So, when objective is to move balls to the other side of a valley, and gain some height in the process, what the most obvious solution immediately comes to mind? Conveyor-belt, of course! It works for sure, it's easy to understand, a bit grindy to implement; but hey, upsides surely outweighs its downsides, right?

If you'd like to try it yourself, here it is. Spoiler alert, conveyor-belt might not be the best solution, and by far.

Finished once more

Oki, so the book is updated once more, and there are no more planned additions to it, so I'll call it done. That is, if I don't stumble upon a special interaction which was somehow left undocumented. For instance, I only recently stumbled upon rushing Pawn starting a cascade (nothing special here), which blocks capture-field of opponent's Pawn, so that en passant is not possible anymore (whoops!). Missing details like this are always a possibility, the best I can do is update the book as needed.

What I really wanted to write about is what didn't made into the book. First thing is a (semi-)transparency of Unicorn and Shaman. This isn't really a problem, but a nuisance; it adds so much in complexity, without enhancing game-play really.
It also does not make sense. I always tried to have all pieces congruent with their role, their story. And having physical entities suddenly becoming transparent to physical is not it.

Other things that didn't make it into the book are resized and expanded Classical Chess variants. I started doing it as casual remarks at the end of the book, but then realized that (if described in sufficient detail) these variants would significantly overgrow its casual remark status.
Worse, those variants actually do not belong to the book. Just as each piece has its story, so does the book as a whole. You most likely don't want to be reading synopsis for 1984, while finishing Alice's Adventures in Wonderland.

I was thinking for quite some time about a new book, where I'd  discuss mobility and calculated values of pieces in different variants. There is some chance that new book will be written, and  then alternative Classical Chess variants would be described in it. While I'd like to do it, right now is not the right time for it. That new book would be a major undertaking, and there are more important things I have to do.

My current plan is to finish game engine library and console application first. After that, I'll add enhanced PGN and other chess file formats support, and obviously document all enhancements done to adapt them for all Croatian Chess variants. And in a distant future, bot and networking support, then GUI application.

Simplifying

Changes described in the previous post are mostly done. I'll continue working on the book, hence no update just yet. There would be no changes to rules, pieces, designs; I'll be just clarifying descriptions, most notably, movement of Shaman really needs to be simplified. Another issue is that I put a few examples in parallel into just one image. Obviously not ideal, but also not sure if it would really benefit from splitting it up.

As for changes described in the previous post, Starchild has been made transparent to all pieces, Unicorn is now transparent to Shaman, and Shaman is now transparent to all own pieces (i.e. in the same color), and to opponent's Shaman. Sometimes you just have to take the plunge; I'm not really happy with this kind of a blunt design, lumping all pieces into just three categories. On the other hand it does make sense, and can't be really more selective, while also keeping its simplicity.

One planned change that didn't make it, is that Starchild can be activated only on trance- and miracle-fields. While that can be done, both Waves and Starchilds are kinda life-line; if player ever looses all of Waves and Starchilds, (s)he cannot cascade anymore, and cannot resurrect any captured pieces, which is a major setback. To leave opportunity to save at least some of those pieces, I decided to keep activation of Starchilds and Waves on its step-fields.

While castling is special, one-off move, in reality is not that special compared to e.g. resurrection, and so it can stand to a reason that it too should participate in a mundane cascades. So, Wave is now set as not blocking when castling, and Starchild is blocking only destination fields of castling pieces. As a result, castling can now also start a cascade.

With all of those changes made, I do plan on revisiting movement of Starchild, its activation rules, just to check if all of those are sound. In fact, the book as a whole could receive a little reading from me. Yes, really. 

While I did wrote the book, changes are always made to one page, one example, one paragraph at a time. So, keeping track of all rules (and exceptions to those rules) is nigh on impossible. And yet, it's necessary if one is to check that all things are inherently congruent, to themselves, and between each other. So, I'm going to re-read the book to see if rules (and their exceptions) could be simplified while keeping their original spirit intact.

Streamlining

Usually, I would preface talk about the book with an (also, usually overdue) book update, then write something about plans for the future. Not this time. Changes done so far were minimal, and so it didn't make sense to update the book this time around, given that I'm thinking about reversing some of the changes made. Right now, I'm kinda stuck in the middle of a coding session. As soon as I put together minimal implementation for paths, routes, I'll do changes to the book, since it has priority over any code.

For example, one change committed to repository, but not yet in an updated book is clarification that all step-fields must to be empty when castling. This however is one of the carry-overs from classical chess, which doesn't have transparent pieces, and can be reverted back. After the change, Wave could be placed in-between castling Rook and King, and it still wouldn't prevent said castling.

Things do get dicey from here on, since then Wave could also be activated by either King or Rook. Sure, it could be done, and momentum could be calculated by simply counting step-fields traveled over by e.g. Rook. But, I don't like activating Wave while castling, since Rook is sort-of stepping over King, and castling is meant as an one-time super-special move, not a regular one. Maybe solution is to simply state that castling pieces cannot activate Wave, but are not obstructed by it, either; that seems the most reasonable design so far, will see.

Speaking of transparency, I'm also thinking about making Starchild transparent. You see, it kinda makes sense, but not all of the time. One design was to make Starchild transparent to pieces travelling over step-fields, but not over capture-fields. This would mostly just complicate things, but wouldn't do much for gameplay, since almost all pieces have the two kinds of fields lumped together, anyway. I tried to reconcile the two opposing design choices, but so far couldn't really make it work.

A solution (although, kinda cheesing it) could be to have Starchild transparent to some, but not all of pieces. For instance, Starchild could be transparent to e.g. Unicorn, Shaman, Starchild and naturally Wave, but not other pieces. Again, not really happy with this design, so I'll have to take a look into it. Although, past experience thought me not to relay too much on first impressions, sometimes necessary evil is necessary.

I have to admit I made a blunder: currently Starchild cannot be activated, and it's just by a simple decree ("you can't do that"). This is not good. What I should have done is describe interactions, everything else should be derived from that. In this instance, I should have written that Starchild can only be activated on miracle-fields (by other Starchilds), or on trance-fields (by Shamans). While net result is the same, one does not impose arbitrary limitations (and you already know how I feel about those), it just enumerates interactions.

Together with bad no-activation rule will go rule that material pieces gain one momentum from Starchild, by simply diverging from it. I don't know what kind of a brain-fart I had that day, but oh boy, it's still smelly. Solution is simple, if a piece has enough momentum, and Starchild is transparent to it, great; if not, it'll be blocked from moving any further, just like any other piece.

Another design that I'd like to simplify is set of potential destination fields for resurrecting syzygy. Other things that desperately needs simplifying are rules regarding multiple syzygies, be it a piece moving from one into another, or standing on a shared field, or something else.

In short, while the book is finished, some things are still settling, and it will take its time, just like everything else. On the other hand, if you look at classical chess, which is much simpler design, it took roughly a millennia to have rules more-or-less in its current form. As an additional bonus, I'm very, very slow; both in writing, and in coding. If you ask me, I'd say don't prepare your popcorn just yet ...

A look back, and forward

Well, that last update took way longer than I was expecting; there were no book updates for half a year. In my defense, life happens, and time flies, ever so faster. When you're young, it might seem you're invincible, nothing really bad can happen to you, and you'll live forever. Guess what, I'm in my fifties now, and this is no longer so for me. Sooner, or later, you'll too start noticing little annoyances, you'll slow down, you'll be vulnerable just like anyone else, ... if you still have parents, be nice to them.

Speaking of life, it seems it'll be happening more, and more in the future, too. Among other things, the skyscraper I live in is scheduled to have a major renovation, with works spanning a good year, or so. Or, maybe not, I'm not sure. You see, people involved in the project already tried to take advantage of naive owners at multiple levels, all the while it's mostly paid by the EU. I'm so tired of all the scummy local sheriffs; I wish EU would put a stop to it, but I'm pissimistic. Anyways, if project still goes forward, I'm not sure how much of work I'll be able to do, I assume I'll be all night-shifting for at least a year.

With the latest update to the book, I'll be so bold, and announce the book to be finished. Yes, you know, I know, it's finished, again. But unlike two-and-half years ago, this time I really don't see anything that needs to be added to the book. Sure, the book desperately needs a few finishing touches, and after that a good spanking polish, and before that I do need to finish grammar, and the last variant most likely needs rewrite. Still, all of this is non-essential in my view, and will be done at more leisurely pace, in parallel to the very next stuff I'll be doing.

What now needs to be done, is to start implementing all that new features I added in recent updates. That means, cleaning up existing code, and while it's not starting from scratch, it's not that far off, either. Just divergence added a major plot in the twist, so to speak, by adding a new layer of complexity when analyzing movement notation, and trying to reconstruct what actually was meant to happen.

On the other hand, starting almost from scratch does have liberating effect on some of code designs that are sub-par. For instance, when parsing notation, I also tried to validate some, if not most, of rules applicable to notation at hand. Prior to that, I was also going back-and-forth on validating things as soon as possible, and wasn't happy either way. Now it's clear to me, validating too early just complicated parser, while also made my job a lot harder. 

So, in the future, parser has to be completely separated from validating code. But more importantly, this little lesson also makes me rethink some of design choices I follow because they're good engineering practice. Good practice is a form of a generic design pattern, not necessary applicable to all situations, so maybe I should give up on them, just  like I did on trying to catch errors in input as early as possible. Sometimes, one has to relax, have another coffee, and accept that all errors will be caught when their (validation) time comes.

Putting some sense into a journey

Month has already past since the last post, and it doesn't seem much to be done. Anyway, more, bigger, and better plans to follow ;)

Sometimes, it seems when I put forward a plan, the opposite find its way to prove to be superior choice. For instance, I was ranting how I don't like arbitrary designs when discussing limits on Shaman's movement. Well, it turns out, it's way better to have arbitrary limitation in place, then deal with yet another massively OP piece, which isn't all that tricky to move, and so fails to reward skilled gameplay. This also fits nicely into general design of variants, where almost every possible rule has an exception to it. If Shaman's movement has to be that exception to the "no arbitrary limitations" rule, so be it.

I changed the way Shamans get entranced into trance-journey, and in doing so, I also wanted the same for Starchilds. Then I realized, it's a good time as ever to have two journeys divorced, at least in name, preferably also in named functions of pieces involved. So, trance-journey will stay in its original form, involving entrancing and entranced Shamans. Newly named sense-journey would involve at least one Starchild; starting with initiating piece (Shaman, or Starchild), then uplifting Starchild, and then uplifted arbitrary piece. Not sure about function names of pieces in sense-journey, maybe I should find different terms? Anyway, the two journeys would stay related to each other; the way entranced Shamans and uplifted pieces move would stay the same, as it would notation using @ sign. This is less then ideal, I'd prefer to have different symbol for encoding sense-journey. The problem is, I'm kinda out of ASCII symbols, and it would make no sense to use anything other than  ASCII for English-based notation.

Another change that I plan to do, is to finally add Scouts and Grenadiers into the mix. I was thinking about it a while ago, but wasn't so sure about designs. Even though I put it on a slow burner, and never took time to think about it thoroughly, it bothered me for quite a bit, and primary reason is because current design adds large swaths of plain, old Pawns[*] which drown out all the other pieces, just with sheer numbers. Now I have made up my mind, even though not everything is clear; I still have to have movement rules and designs of pieces sorted out, before they can appear on a chessboard. Still, Scouts would be much more mobile version of a Pawn, while Grenadiers would be more dangerous, and more tactical ones. Collectively, Scouts and Grenadiers would be called Troopers, while Privates would be any of Pawns, Scouts and Grenadiers. I'll also leave promotion/demotion limited to just Pawns; so Troopers cannot be promoted outright, and has to be demoted to Pawns before they can be promoted. Movement limit of Monolith would also stay based on count of Pawns alone. In showing their Pawn descendancy, Troopers would be able to rush, and could be subjected to en passant.
[*] sideways movement notwithstanding

My immediate plan is to deal with some fixes to notation, namely divergence, displacements of Pawns by Serpent, then review/fix as needed trance-journey. After that, I'll do sense-journey, hopefully covering its notation right after it's done. And after that, I'll do Scouts and Grenadiers. And after that still, I'll update the book; it's been quite some time, and changes, both done and planned, are significant.

No title, just plans

Since my last post I changed Serpent to displace only Pawns, not all of pieces. I deem change necessary, otherwise Serpent would be able to break any formation, not just Pawn fortresses, which would be a little too OP. Breaking Pawn formations is good though, it opens up gameplay, and effectively limits static play.

This goes in as a preparation for another planned change, movement of Serpent should be unrestricted. Currently, there is some arbitrary cap in play, and you already know how I dislike arbitrary designs. So, plan is to remove cap, but also forbid loops. So, Serpent could visit any field, but only once in a ply.

Before you ask, Wave activated by Serpent will stay limited to two initially chosen directions. If I'd allow for such a Wave to move as unrestricted Serpent does, it would have half of a chessboard at its disposal, because Wave is transparent, and does not spend momentum while moving. Which all means, Wave activated by Serpent would be way too OP. So, that restriction has to stay where it is.

As for immediate to-do, I'll change way Shamans get into trance-journey. Currently, I sort-of abused Waves to activate the other Shaman, which can then optionally go into trance-journey. I'll simply relieve Waves from that duty, and have one Shaman directly activate the other one. This all will be done on neighboring fields, i.e the ones which are not step-, not capture-fields of neither light nor dark Shaman, so it'll be very specific, and very special movement.

Reasons for the change, you ask? For one, it provides build-up to trance-journey, and a warning to the other side what a player is up to, and also provides (a little) room to counteract. This is also similar how Shaman captures opponent's pieces; first it has to close a distance to its prey, then in next move it can go capturing. Second, trance-journey is now deliberate move, not optional, mixed with other side-effects. And this also makes it so much easier to figure out in parser, analyzer code; all good in my book B).

One thing that still hasn't clicked the right way is how Starchild is  activated, namely separation between activation on step- and miracle-fields, combined with all the other Starchild rules, and exceptions to them. Sure, it all have logical explanations why they are that way, that doesn't make them right. So, I'll probably have to revisit those, before deciding what to do. One option is to forbid Starchild to be activated at all, that would simplify designs, and since Starchild still has a lot of other own stuff to do, that might not be too much of a loss.

Another thing that I stumbled-upon is a realization that complex mechanisms are not explained properly, i.e. one simple mechanism at a time, and only once. I figured out, I have a tendency to stuff all of mechanics into one example, and then repeat that poorly explained mechanics over-and-over again. No good. I mean, that was main reason why I rewrote Miranda's Veil. This is, of course, completely my fault; such is a price of learning things by doing them.

But now, it left me wondering, what if I revisit all of examples from the very beginning, and rewrite all that needs to be redone? This can be done, and I think I should, since it would make the book so much better. The only problem I see is that it'll take months to finish, quite possibly to the end of a year. Again, this is something that I won't be rushing into, I'll take my time to decide if, and when I want to take the plunge.

Just to clarify, complex should not be confused with complicated; complex things are comprised of a few other, smaller things, all of which are -in, and off itself- simple. Complicated things are just spaghetti-designs, many things arbitrarily bolted on top of each other, without any regard for consequences.

Divergence on the run

I made two relatively small changes to Serpent, namely now it can displace pieces in its path. This is similar to Shaman, but without the need to be bothered with super-special, secondary movement. Given that Serpent's meandering movement lends itself nicely against opponent's Pawn structures, this might also help break those fortifications, even without capturing anything.

Another change is that I finally extended the range of a Serpent, it can now do up to 16 steps, up from 9 steps. I'm still not completely satisfied with Serpent's design. Issue I have is with arbitrary limit which has to be put onto Serpent, otherwise it would be able to loop forever, and build infinite momentum. Either I'll make loops illegal, or straighten Serpent's path in some capacity. Although, having a piece preloaded with Bongcloud-level of pure, unadultered fun is not below me. 

I won't be updating the book just yet, these changes are too small to warrant that, especially because I plan to do larger changes in the immediate future. One planned change is to move divergence from Wave onto Shaman, Wave would stay transparent. Obviously, this means moving all divergence sections into Conquest of Tlalocan variant, and redo all the examples, and reword all the texts. Not difficult, just physically large change.

One other change I was thinking about was to change how Monolith moves. Currently, it has uninspired, fidgety, but still unrewarding pathing. I was thinking about changing it into accelerating Centaur, with each jump longer than previous one, preferably without an arbitrary limit to its movement. Obviously, each step has to be chosen independently from any previous choice; problem is that might turn Monolith into super-duper OP Centaur, which can reach any single field from any position on a chessboard. This is not good, even if Monolith cannot interact with any piece, so I'll try to find something more reasonable. 

One crazy possibility is to limit Monolith to the number of Pawns a player posses. For instance, if light player has 5 Pawns, Monolith moved by light player can do only up to 5 steps. This also fits nicely into gameplay; at the beginning Monoliths can do a lot of steps, but are cramped on board, as game progresses Monoliths are not so confined by sheer number of pieces, but are limited by number of Pawns still on board.

And last (but not least), there is a common, recurring task; that is to sit down, and reread last two variants, and simplify (or, at least, clarify descriptions of) syzygy, and other rules regarding Monolith, and Starchild. I don't like it, but those needs to be done, to be able to hold all rules in ones head at the same time.

New book update!

I'm updating the book, there are quite a few interesting changes to hold it back. Most notably, Wave is now transparent, and also Wave and Starchild are now divergent. Static move is now officially illegal. And so on, ... you get the idea.

Changes since the last update:

  • Wave is transparent
  • Wave and Starchild are divergent
  • added static move, piece, loop rules
  • added symbol for resurrecting opponent's pieces
  • clarified self-checkmate
  • updated move, and compatibility grammar
  • clarified promotion notation
  • small grammar fixes (e.g. promotion is mandatory)
  • and much more!

The latest update to the book was made on January 24, 2023, version is 20230124.083845, and can be found in usual places.

After this, I'll continue working on library; there are quite some changes to be retrofitted. Also, a lot of guess-work has to be done to reconstruct proper path taken by any piece, because transparency and divergence notation is optional.

Reason why it is optional is because "o, this is too hard" is not a valid reason; and convenience for a user is more important. Only if there are some data that are truly missing (like, not specifying a piece for e.g. a promotion), then I'd say side-effect is mandatory.

Anyway, I think there should be no more major changes to the rules, and the book. After all, I'm running low on ASCII symbols usable for algebraic notation.  :)

Wave to be divergent

A while ago I made Wave transparent, which was logical, and generally, good thing to do. Since then it occurred to me that Wave could be made divergent, i.e. any piece instead of activating Wave could choose to continue moving, but optionally change its course.

This opens quite a few new tactical possibilities, and makes Wave way more useful, since only two pieces are enough for a meaningful diversion. For a cascade to be immediately meaningful, usually three pieces are needed: activator, Wave, and activated piece. In a cascade where only Wave has been moved along with initiating piece, beside repositioning and maybe some latent threat, Wave isn't immediately useful, and momentum gathered by activator is lost. Only downside that I could see with divergent Wave is that it would be even more valuable, as it wasn't already too much so.

While I do like the idea, there are a few open questions, and, at the moment, I can't really make a reasonable case for one choice over the other. A few issues that comes to mind: should initiating piece after diverging be limited by momentum; should piece upon encountering Wave be always diverging, even if it does not change its direction (thus forfeiting transparency for diverging); should piece be allowed to diverge from opponent's Wave, or only own; what about own Wave diverging from opponent's Wave; should piece be allowed more than one diversion in the same ply, (move?); should piece also diverge from own Starchild, (opponent's?); and so on.

There are quite a few choices with this seemingly simple change to gameplay. Since I can't decide right now which set of options is the best, I made second best thing, wrote blog entry about it. I hope to resolve those issues soon, and then it'll make it into the book. 

This is The Avocado!

With capital T. And capital A. Compared to which Sparta is a very mild chamomile.

No matter how you try to slice it, you'll end up in an unbreakable kernel. And, if you try to force it, you'll end up with just a few annoying, inedible bits, and kernel still barely scratched. That is an avocado. 

Good engineering practice, and really the only one to enable reasonable rate of progress, is to divide any problem into smaller and smaller bits, until you can build from solutions to the smallest problems, up to the whole. And, of course, you'll want least possible amount of special cases, since those ruin your division into smaller and smaller bits.

Enter The Avocado Croatian Chess. Its algebraic notation features mild context-sensitive grammar (I was reasonable about it). It also has external data (game status, and chessboard, more precisely positions and tags of all pieces on it) not just as a dependency, but as a context; because to be able to parse any ply in a cascading move, one has to apply all previously parsed plies to chessboard, since those are changing positions and tags of pieces. To top it off, it also features self-evident, reasonable requirement for a convenient notation, i.e. almost all movement can be noted with just a piece + destination field (+ side-effects, if present).

For trance-journey, the same applying to context chessboard thing has to be done for steps, since those are also changing positions, tags. Congratulations, this is a very first special case, and to handle it via unified interface, now every step of every ply has to be applied to a context chessboard. In fact, there is barely any rule that doesn't have a special case, making any unified interface a nightmare to implement. While it might look like a self-inflicted pain (which it is), the matter of fact is that special cases are the meat of the game, without them it would be just a scaling game. I have to confess, I was thinking about ditching the whole Croatian Chess shebang, and regress to scaled-up Classical Chess, just by copying 3 side figures, and accompanying Pawns a few times over, resulting in 14 x 14, 20 x 20, and finally 26 x 26 chessboard, with no special cases pain. While that might do for a while, it's ultimately boring, and brings nothing new to the table.

Now you see why you can't just take notation alone and try to parse it, you'll need live chessboard (positions, tags), and game status (who is on the move). You can't even say if move notation taken alone is valid, some plies aren't legal, depending if e.g. activation of pieces are made on step- versus capture-fields. I could've opted to do most of validity tests, and forego those which can't be done without chessboard, but that would pose at least three problems. First, most tests would be done, but some, seemingly arbitrarily, would be missing. As a consequence, validity would be restricted, and non-intuitive compared to what most users (me including) would expect. And there is a problem that validity checks are done on notation, and would be repeated again during parsing, now with greater scope since chessboard is available, and most likely again while actually applying movement of pieces, side-effects to chessboard.

So, it seems to me that whole game engine has to be put upside-down, like a pyramid with a pointy-bit at the very bottom. That means, most of work done so far is actually useless, and it should be deleted. Better to start from scratch, then try to salvage garbage; this includes almost all of tests application. Next, movement data has to be considered correct, so there would be no wasting time trying to validate it. This is probably the hardest part of implementation, as it too goes against well ingrained engineering practices, where all of user inputs are considered invalid, until proven otherwise. Both major components (parsing, applying move) would have to produce user-readable error messages. Actually, there would be only one major component, as the only difference between parsing and applying notation would be on the last leg, when the latest context chessboard is returned from parser, or it's set as a new live chessboard, and game status updated accordingly, if applying a move.

On top of all that, a few days ago I found out that activation rules are not thought through, or at very least, not formulated exactly. Problem is that Pyramid can't be activated on a step-fields directly, but currently it can be done in a cascade indirectly, via Wave, and with no restrictions. Since activation is a keystone of the whole lot, I'll have to do it before any coding can take place. Needless to say, I'll also have to revisit every single example in the book which includes activations, and change those accordingly.

In short, I have done very little so far, a lot of book rewriting has to be done, before any real coding work could commence.

const considered (mostly) harmful

I started salvaging algebraic notation (AN) parser from top down (as posted by the end of August), when I stumbled upon a serious issue with iterators. My fault for sure, but what was peculiar about it is that I was somewhat subconsciously trying to write Python in C, and longing for more expressive language. In fixing this issue, I also had to unlearn quite a few things, and re-learn some I mostly forgotten. After a few iterations, I ditched static variables inside function, for input/output parameters. While, at first, this looks like a eyebrow-rising design, it's thread safe, clear how to use it, without need for an awkward initialize_iterator parameter.

While I was fixing iterator in string utils, I stumbled upon another issue, variable and parameter names weren't consistent. So, I started fixing those, when in the middle of the changes I stepped into a turd of a const-correctness, especially involving pointer-to-pointer parameters. Looks like I'm not the only one; what is actually surprising is how long code peppered with consts lasted. I was searching for answers, and I found out it's by (bolted-on) design, from the very onset.

Some pondering later, I figured out that const is not meant to communicate constraints of underlying function algorithm, but to designate initializer data (strings, arrays, tables, ...) which won't change as such, and so do not slap const carelessly onto every thing in sight.

So, rules for applying const I now use are simple:
1. const is mostly for parameters
2. char * parameters, and any pointer to simple* struct, union
3. module or global variables, if used for initialization, and won't change
4. anything else should be const free

* simple in this context means it does not contain pointer

This change has already being made, and it's refreshing to see code free-ed from all that const noise. Now, I'm finishing renaming of variables, parameters, while I also occasionally double as a plumber (no, not by saving Princess Peach, but by fixing some (memory) leaks). After that I have to do a couple of  relatively simple TODOs, then re-implement a few iterators. After all that, I'll get back to my original task, re-doing parser. I won't try to predict how long this all will take, I'm notoriously really bad at reading from crystal ball.

Topsy-turvy

I almost finished notation parser, meaning application is almost usable. Almost, as in most cases, is as good as near miss, it just won't cut it. Idea was to implement base library from bottom-up, but also to keep modules as separate as possible. So, development started with just essential functionality first, those are now almost ready to be integrated to have two humans play game against each other. Later, of course, features would be added as they're finished.

Issue is that without all the specifics of a game in progress (current position on a chessboard, who's on the move, list of previous moves, etc.) it is not possible to parse all of algebraic notation. I didn't forgot about short notation relaying on a context, but I also didn't think too much about dependencies it inevitably creates.

So, development needs to be turned into top-down affair, and a lot of stuff needs to be added (game status, list of moves, rules checkers, steps generators, path finders, ...), before short notation can be reliably parsed. For a user, this means for quite some time there won't be any visible progress, only commits to repository.

Project status

In a last few weeks, after the latest book update, I have redo move+ply+step implementation, added outputting (relatively reasonable) notation from data, simplified tests which now prints formatted notation, bug fixes, and more.

Very next things to do: add tests based on book notation examples, start writing code documentation, parser for user input notation. Normally, I would continue writing parser, since that would bring the most value to the table. 

There are already tests which do most of transformations to chessboard, and most of notations, so it's not the most pressing thing to do. However, tests based on book examples would provide value in that it's already in the book how application should behave. Sometimes covering all the features also reveals shortcomings in implementation, and it shouldn't be all that difficult to write all book tests, only tedious.

There are also some 6k+ sloc in C already, and I kinda see things coming up together, so it might be prudent to start documenting library. Things are easily forgotten, especially in a slowly, but surely growing code-base. I have already tested doxygen, worked out-of-box, and I didn't exactly liked it. Might try Sphinx,  liked it using it for Python sources, will see.

Spelling correctly

Since last update I started implementation in C. Due to C demanding to be exact with one's thoughts and code, I also started testing thoroughly the most basic facilities, namely data model and performing transformations to chessboard positions.

One thing leads to another, and so I ended examining notational grammar. Even though it was documented where it was not correct, it started to bother me more than I could bear. So, I embarked on a side-quest to fix it, which I did, even though it's still not 100% exact.

This is currently where project stands. I still have some leftover TODOs in the book, after which I'll update book preview. After that C implementation will resume. I plan to revisit data model and try to reasonably simplify it, which will also cascade into changing accompanying allocators, other functions, and tests. 

So, almost everything implemented so far could change; which could be regarded as development without a (meaningful) progress. This is still Good Thing, since it's much cheaper to change designs early in project life, than later. Change early, change often.

I'll be also looking into options to write more error-resilient code, even if that means introducing more of boiler-plates. Contrary to popular belief, boiler-plates are good, especially if true-and-tested, hardened against (most) errors, and if there aren't too many of those.

Also, I'll be in a need of a better naming scheme; current one is covering only some items, in some scopes; so function, struct names in library are all over the place.

Versioning

Inherent problem with versioning is that no text or code naturally have any version number attached to it. It's just a construct to keep track of changes, and be capable to compare own version with what is available in a repository. Additionally, I have to keep track of changes between the book and application; ideally, without having to do it by-hand, which is very error-prone, and time-consuming.

Currently, before each commit of book sources README and the book root files are updated with incremented commit number and current UTC time. Problem is, it is used exclusively for the book, versioning is not compatible with anything used for application versioning, and usage of commit number to keep track of changes. Commit number greatly depends on history, which one can easily mess with; by changing master branch (git branch master --onto my_branch), by squishing or deleting commits, by merging other branch, by starting a new (or relocating existing) repository at some other git hosting company.

So, in a following few days I'll be re-doing versioning, starting from what has been done to keep track of book related commits, changing commit number to independent counter, and expanding to include application sources. Independent counter needs to be saved, I'll use JSON in the root of the project so that it is shared between the book and the application; JSON is widely used, is human readable, has great support, and it's easily extensible, too.

In short, for application sources I'll adopt Semantic versioning 2.0 (https://semver.org/); seems reasonable, a lot of people is using it, has great support. I'll use 4th item, build number, so that for my project version would have <major>.<minor>.<patch>+<build>.<squished-utc-commit-time> format. For book full-blown versioning does not make any sense; so, only build number will be used, instead of currently used commit number; UTC commit time is already used. For instance, based on current application version and last commit to the book newly formatted version would be  0.1.0+962.20210305164027, in the book and README new version would be written just as 962.20210305164027 to simplify comparison.

Build number then would be incremented after each meaningful commit, i.e. either to the book or to the sources. If application sources has been changed, then in addition to the build number, at least a patch number has to be incremented as well, or, depending on a scope of commit, minor or even major number. Build number would not be incremented for administrative commits, e.g. if readme file is updated. Build number will never be reset, this is crucial to enable comparing the book and the application versions.