Fairy Chess Side Project - PendulumProject2020/PendulumProject GitHub Wiki
This article is about the Tangible layer.
The Fairy Chess side-project serves as a test to the Epifyte model and demonstrates its range of uses.
The reason why I chose to test the model using a game is that games tend to both allow and inspire a large variety of mechanisms, hence serving as a good means to test the flexibility of the model. I chose fairy chess in particular because of my interest in designing board games, which originated from the time when I was enthusiastic about gaming and game-making but still did not know programming - which is required for games with automated mechanisms.
A typical chess game setup modeled using epifytes is illustrated below. It should be read from bottom to top. A light blue arrow from A to B represents that A binds to B.
In the below explanation, in the case where the board is a square board, we denote the side length of the board (in terms of the number of cells) as N.
There are several main things to understand and take note of:
- The first thing that we have to decide is the relations between the board and the cells. There are three most intuitive approaches, and we can only choose one of them: i) The board is an object, but the cells are not. That is, cells are represented solely by coordinates. ii) The cells are objects, but the board is not. That is, the board is nothing more than the collection of cells. iii) The board is an object, and the cells are also objects. The approaches (i) and (ii) each has its flaws. For (i), it is difficult if you want to change the board structure in the middle of the game. For example, if you have a "Space destroyer" piece that destroys cells instead of pieces, erasing cells from the board, it will be difficult to enforce the idea that a cell is gone and cannot be used anymore. Consider another situation where you want to put a teleportation portal. In (iii), we can simply implement a quotient map by having two coordinate pairs map to the same cell. In (i), since the cells are indistinguishable from the coordinates, it is difficult to convey to the program the fact that two different coordinates now refer to the same cell. It is also not possible to accommodate the special situation where one cell contains two different pieces. Even more difficult are maneuvers like stuffing another chess board into a cell. For (ii), it is difficult to code the relations between the cells. Presumably, the cells will be connected to one another using a graph. For a square board, if one cell wants to access another cell on the board based on a location, the time complexity is O(N), since all edges between them must be traversed. This is unlike the case of (iii) where the time complexity is O(1) since there is a map from the set of coordinates to the set of cells. If we code a coordinate set into every one of the cells, there will be N^2 many coordinate sets, and therefore every change in the cell structure will take O(N^2) time. If we have the coordinate set as a static field or some other implementation of global variable, that would be logically more similar to (iii) than (ii). On the rationale of maximizing flexibility, we choose (iii) instead of the other two. The board is an epifyte, while each cell is an epifyte binding to the board.
- The intuitive idea of a chess piece is separated into two components in this side-project: The Chess Piece and the Identity Bundle. The Chess Piece component merely represents the fact that the cell has an occupant, as well as links the piece to the controller (the player). The Identity Bundle is an epifyte modifier that collects the set of behaviors of the chess piece. In standard chess, it is the component that decides what kind of chess piece it is (e.g. Rook, Bishop, Knight, Queen). The reason why it is a separate component from the Chess Piece itself is a consideration of the mechanisms of promotion in standard chess. When a pawn promotes to a queen, the behavior of the piece is changed. However, there may be situations where we want the chess piece to keep memories of its previous moves or retain its previous modifications (e.g. buffs). In this case, simply replacing the chess piece would mean losing those things along with it. It is also not an accurate reflection of reality - imagine if one day your boss gives you a "promotion", only to fire you and hire a new worker in the position you are supposedly promoted to. Therefore, the idea is to retain the chess piece itself while replacing its behavior. When a pawn is promoted, the original identity bundle will be detached from the Chess Piece epifyte and the identity bundle for the new piece type will be attached. Then, for example, if we want to give the pawn a modification or augmentation that it would keep after being promoted, we simply have to bind the epifyte modifier of that modification/augmentation to the Chess Piece epifyte instead of to the Identity Bundle epifyte.
- The identity bundle of a standard chess piece consists of four components: The type case, which stores ("encases") the name of the type of the chess piece, the texture case, which stores the display object of the chess piece, the move arm, which enables and defines the movement of a chess piece, and the attack arm, which enables and defines the attack of a chess piece. Each arm by its own only allows the action to be made and determines the most basic things that happen when the action is made, but not what whether or not a particular action can be made, nor the more advanced things that happen when the action is made. The latter two are decided by stack of epifyte modifiers that bind to the arm. For the case of a rook, for example, the move arm modifier stack consists of two epifyte modifiers, a basic movement range modifier at the bottom that allows the chess piece to move anywhere horizontally or vertically, and an obstruction detector modifier at the top that invalidates the cells that are obstructed by another chess piece. The move arm modifier stack of a pawn would, in addition to rules regarding where it can move to, also trigger a promotion event if it notices that the pawn has reached the end row.
- The texture case serves as the interface between the tangible layer and the interactive layer. Just as there is a hierarchical structure between most non-texture-case-epifytes, there is also a hierarchical structure between texture cases (for both, the direction as illustrated in the diagram is from bottom to top). One can consider the latter as an incomplete "projection" of the former from the tangible layer to the interactive layer. A mathematical visualization of the said projection: We define the Tangible category as the category with the non-texture-case epifytes as the objects and the directed paths (here we extend the definition of directed path to include empty paths) between them as the arrows, where the directed edges in question are binding relations. We define the Display category as the category with the texture case epifytes as the objects and the arrows defined likewise, only with respect to texture-case epifytes instead of non-texture-case epifytes. Then there is a faithful functor from the Display category to the Tangible category such that every texture case is mapped to the non-texture-case epifyte that it binds to.