While I was, sort of, studying the various TCGs around, concepts, similarities, differences and how they could be implemented, if designed to be in a virtual environment (e.g. PC game) I obviously noticed the most common element between them all: The Board
As a gaming developer community, you may know that The Board is where all the "action" takes place. It's similar to an MVC FrontController, handling everything of the game.
Programmatically speaking, several Observers would be implemented within it, to catch-up and interfere if needed in between every step taken by every card.
Most of them comes in a pyramid-a-like format like this:
+------+
| 12 |
+------+
// \\
+------+ +------+
| 10 | == | 11 |
+------+ +------+
// \\ // \\
+------+ +------+ +------+
| 07 | == | 08 | == | 09 |
+------+ +------+ +------+
|| \\ // || \\ // ||
-----------------------------------
+------+ +------+ +------+
| 01 | == | 02 | == | 03 |
+------+ +------+ +------+
\\ // \\ //
+------+ +------+
| 04 | == | 05 |
+------+ +------+
\\ //
+------+
| 06 |
+------+
This varies from game to game and can be 1x1, 3x3, 6x6, more or even less. This also can be like I designed above, vertically (which is easier to draw >.<), or horizontally, which I think is the best way for a programming implementation, specially considering a multi-platform goal.
In the diagram above, the lines and doubled equal signs represent possible interactions between cards, like, for example, supportive cards which sole objective in the game is to support (doh) adjacent cards somehow while disengaged ("not in combat").
Last but not least, cards may move between available spaces, be them on their own side (below/above the middle line) or in opponent's territory (the other half).
However, although I took notice of all these raw aspects, I couldn't find a way to make it work, again, programmatically speaking:
- Should I use a multi-dimensional arrays or something better?
- How to identify that some space is free or occupied?
- If occupied, how to identify if it is by a "team mate" card or opponent's
- If it's free, how to find its neighborhood to identify possible "team mates" and targets
I tried to hard-code a super structure with all possible attacker-opponent combinations, considering all possible targets for each attacker card on the front line group of 3 cards and also their adjacent supporters.
At the beginning of an hypothetical match of an hypothetical game (really), considering the cards as numbered above I came up to this, in a PHP array:
$board = [];
$board[ 1 ] = [ 't' => [ 7, 8 ], 's' => [ 2, 4 ] ];
$board[ 2 ] = [ 't' => [ 7, 8, 9 ], 's' => [ 1, 3, 4, 5 ] ];
$board[ 3 ] = [ 't' => [ 8, 9 ], 's' => [ 2, 5 ] ];
Each index of $board would represent a front line card with its possible targets under t and its supportive cards, if any, under s
Sounded good at first.
Then I simulated, all future possibilities if the first person, here named as the one on the lower half of the board, decided to make a move going to 1-7, 1-8, 2-7, 2-8 and so on and, after winning, automatically moving itself to the spot of its defeated opponent (which is mandatory in all TCGs I've seen to keep the board rotating).
This resulted in 7 different arrays:
// Active Player engages 1-7 and wins. Auto-move applied
$d1 = [];
$d1[ 8 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 9, 10, 11 ] ];
$d1[ 9 ] = [ 't' => [ 2, 3 ], 's' => [ 8, 11 ] ];
$d1[ 10 ] = [ 't' => [ 1 ], 's' => [ 8, 11, 12 ] ];
// Active Player engages 1-8 and wins. Auto-move also applied
$d2 = [];
$d2[ 7 ] = [ 't' => [ 1, 2 ], 's' => [ 10 ] ];
$d2[ 9 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 11 ] ];
$d2[ 10 ] = [ 't' => [ 1 ], 's' => [ 7, 11, 12 ] ];
$d2[ 11 ] = [ 't' => [ 1 ], 's' => [ 9, 10, 12 ] ];
// Active Player engages 2-7, wins. Auto-move also applied
$d3 = [];
$d3[ 8 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 9, 10, 11 ] ];
$d3[ 9 ] = [ 't' => [ 3 ], 's' => [ 8, 11 ] ];
$d3[ 10 ] = [ 't' => [ 2 ], 's' => [ 8, 10, 11 ] ];
// Active Player engages 2-8 and wins. Auto-move also applied
$d4 = [];
$d4[ 7 ] = [ 't' => [ 1, 2 ], 's' => [ 10 ] ];
$d4[ 9 ] = [ 't' => [ 2, 3 ], 's' => [ 11 ] ];
$d4[ 10 ] = [ 't' => [ 2 ], 's' => [ 7, 11, 12 ] ];
$d4[ 11 ] = [ 't' => [ 2 ], 's' => [ 9, 10, 11, 12 ] ];
// Active Player engages 2-9 and wins. Auto-move also applied
$d5 = [];
$d5[ 7 ] = [ 't' => [ 1, 2 ], 's' => [ 8, 10 ] ];
$d5[ 8 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 7, 10, 11 ] ];
$d5[ 11 ] = [ 't' => [ 2 ], 's' => [ 8, 10, 12 ] ];
// Active Player engages 3-8 and wins. Auto-move also applied
$d6 = [];
$d6[ 7 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 10 ] ];
$d6[ 9 ] = [ 't' => [ 2, 3 ], 's' => [ 11 ] ];
$d6[ 10 ] = [ 't' => [ 8 ], 's' => [ 7, 11, 12 ] ];
$d6[ 11 ] = [ 't' => [ 8 ], 's' => [ 9, 10, 12 ] ];
// Active Player engages 3-9 and wins. Auto-move also applied
$d7 = [];
$d7[ 7 ] = [ 't' => [ 1, 2 ], 's' => [ 8, 10 ] ];
$d7[ 8 ] = [ 't' => [ 1, 2, 3 ], 's' => [ 7, 10, 11 ] ];
$d7[ 11 ] = [ 't' => [ 3 ], 's' => [ 8, 10, 12 ] ];
It grew a lot, but since all mathematical combinations that usually results in a moderate overload (specially for a web-based environment with PHP) was hard-coded, I didn't see a problem at first then and I kept going.
The next step was to consider all possible scenarios if the so called Active Player, now the former Opponent, in his own turn, decided to go in your first possible target 8-1
Assuming a winning scenario, 8 could move itself to where 1 was or then 10 could be moved instead. It's mandatory, at least one card must always exchange positions when winning, otherwise the board doesn't rotate.
However, here comes the mathematically scary, although expected, part:
- If 8 Moves into 1, then:
- 9, 10 or 11 can move over the previous location of 8, and then:
- If 9 moves, 11 can move towards the former position of 9, and then:
- If 11 moves, 10 or 12 can go to where 11 was, and then:
- If 10 goes, 12 can jump to where 10 was
- If 11 moves, 10 or 12 can go to where 11 was, and then:
- If 10 moves instead, 11 or 12 can move towards 10, In this case:
- If 11 moves, 12 can go to 11
- If 12 goes instead, 11 can be pulled back to 12
- Now if 11 moves, then
- 9, 10 or 12 can go to where 11 was, and then
- If 9 goes, the flow stops
- If 10 goes, 12 can go to 10
- If 12 goes, 10 can pull back where 12 was
- 9, 10 or 12 can go to where 11 was, and then
- If 9 moves, 11 can move towards the former position of 9, and then:
- 9, 10 or 11 can move over the previous location of 8, and then:
And here I stopped designing that data-structure mainly because this exponential grow wouldn't never reduce as far as other cards start leaving the fray, because in every "turn" several possible destination would still be available.
Their movement would still be restricted at once per turn, regardless the number of free spots, but still there are a lot of possibilities and I'm struggling to figure out a way to do make it viable.
All of this without mention cards I've seen in several TCGs with special abilities that allow them to jump more than one slot (usually the known as flying type). But this a future matter.
And by viable I kind of mean lightweight because, in order to explain here it obviously appears to be light, but in a real case scenario, each occupied space, for example, would store a object, with its own logic, dependency and thus, overload.
I thought about using a Binary Tree, but the nested "lower to left, higher to right" concept, at first, sounded not applicable. But I never used one before, so I might be wrong here.
I'd really appreciate some enlightenment.