So, I moved to new online Git repository, but since then there was no update on project status. Well, this is it; somewhat overdue, I admit, but with a (good?) reason. You see, most of my followers are chess enthusiasts, not programmers; so, I postponed my initial post since it's not that interesting, then got side-tracked, again (as one does).
In the meantime, there has been some progress on generating paths a piece can make, then comparing those generated paths with user notation, and finally executing a move if a unique match has been found. Due to a sheer scale and count of cases, tests has been separated into different executable:
All tests run One variant, since it's the most complex one, and features one of the largest chessboards in the book. One of tests (out of a few dozens!) is this one: "ta 9" command simulates user who typed "Bg3*N", and given the situation on a chessboard bellow, test then tries to establish if there is a full path (step-by-step) and side-effect (capturing), that matches user notation:
The first thing to do is to find moving piece and its starting position. This isn't as straight-forward as one might think: there might be multiple pieces of the same kind, some with or without a tag, and sometimes opponent's pieces qualify as well, e.g. if activated in a cascade.
After finding a moving piece, all possible paths were generated; in the printout those are indented since they're all connected to each other, in one big, branching tree. The first line shows starting position ("d6"), then piece found at that position ("B", that is light Bishop) and its tag ("!", that is no tag found)., then activator (empty, since this is the first piece in a move), then momentum ("0"), which is still being accumulated ("+"). Lines starting with "<" are forking paths, i.e. all paths connected to it would inherit previous path segment(s), while lines starting with "^" are alternative paths, i.e. would replace other previous segments. Forking paths are used after the starting position, and after divergence. Alternative paths are used if there are multiple possible side-effects (interactions) between moving and stationary pieces; or, as an alternative path segment to a fork in a path. Note, alternative paths do not include displacements since there are too many possible destinations; those are handled differently (as a list of tentative (i.e. all possible) displacements, for each step).
All possible steps a piece can make are enumerated in a list, separated for each piece; enumeration starts from x-axis (3 o'clock) and then goes anti-clockwise. This is why here the first segment after the starting position goes all the way to "x26", then to "a9", then "a3", and finally to "g3". At that last path segment, application found dark Knight ("n"), with no tag ("!"), and established it can be captured ("*N" as a side-effect to the last step of a generated path segment). Momentum of a light Bishop at this moment is "3+", meaning that Bishop had already made 3 steps, and is still accumulating momentum.
Now all paths has to be generated by traversing path tree, and stitching together path segments from starting position to the very last step; each leaf node in a tree ends one path. Those generated paths are then compared to user notation, to see if there is a unique match. Except, that's not entirely correct; due to the need to maximize accumulated and minimize spent momentum, paths are searched for the shortest possible path, and then for the longest, if different. So, the longest path is preferred for the first piece in a cascade, otherwise the shortest path is used. Here, there is only one path possible, so it's saved as the shortest one, e.g. "d6.e5.f4.g3*N".
After unique path has been found, it's time to apply it to the chessboard:
As one might imagine, light Bishop left its starting position, and replaced dark Knight on a chessboard. Except, that is how it would have been done in a physical world. In virtual, digital world light Bishop on its starting position was replaced with None piece, i.e. the one used when there is no piece on a particular position; and then dark Knight on a destination field has been replaced by light Bishop after it has been stripped of its tag, even if it didn't had any. After movement all pieces lose their tag; but, tag stripping is such a simple operation that I didn't bother to check if a piece doesn't have any.
As you can see from a very cursory glance into game engine, it's not that simple to explain it, and it's even worse to implement. In the meantime, I was also trying to think of a simpler, more elegant solution to the problem, and it seems that I have found one. This is why I'll be ditching all this generating of a path tree, extracting each full path from that tree, then comparing it to user notation; and replacing all of this with functions for each piece specifically. That way, a lot of guesswork (but, what if a piece can diverge? is it transparent? to what?) can be eliminated, because in a specific function one has to handle only one piece (e.g. a Bishop) and can safely ignore interactions that are not relevant.
Why I didn't made such a transition earlier? Well, it took me quite some time to figure out that in a cascade I could use intermediary chessboards to communicate all the changes made in a previous ply, and then accumulate all those changes into the final chessboard for a move. The one thing I don't like about piece-specific functions is that can easily lead to a lot of code duplication, since similar code can be embedded in many piece-specific blocks; usually a lot of variables are shared between the two, making it difficult to refactor similar code into a function of its own. The other thing I don't like is that algorithm replaces data, and so no intermediary data is generated, and so the only way to check correctness of such a function is to debug. Oh, well ... nothing is perfect.



