You could make a two-dimensional grid made of Array of IElement type. I imagine String or int would be enough for an element's name/id (e.g. "Diamond" or "Chest"), but your request for the game being easily upgradeable implies elements may have some state or even a functionality.
The IElement would be an interface with following methods:
1. canSwap ( swapWith:IElement ):Boolean
2. isHeavy ():Boolean
3. onActivate ():Array
3. onExplode ( power:int ):Array
Ad 1. Can a chest change places with a stone, or for some reason not? For example in some game type you could mark some elements as heavy (e.g. a heavy chest) and make a rule that two heavy items can't be swapped.
Ad 3. This method will be fired when a user selects the element as first. The method update's element's state (if needed) and returns an Array of actions to be done outside of the element, e.g. [RemoveFromGridAction, ExplodeAction] for a BombElement
Ad 4. As above, but here the method will be used when the element is a neighbour of an exploding bomb. A WoodenBoxElement would return [RemoveFromGridAction] Array, a BombElement would return [RemoveFromGridAction, ExplodeAction], and a MetalCubeElement would return [] (an empty Array).
All of this enforce on you, to make either various versions of Classes implementing IElement interface, or put there an additional method for every new game mechanic you add.
If you want to make a Bejeweled-like game with dozens or more of rule variations, then that would become a huge project and I suggest a MVC pattern:
Model - the grid of dictionaries (or dynamic objects, or associative arrays). These dictionaries are simply value objects that store any data you want, e.g. {'type':'diamond', 'x':0, 'y':5, 'heavy':true, 'explode_radius': 1, 'bonus':true, 'value': 115, 'lifetime_in_turns': '5' }
View - components responsible for drawing the Elements, animating the process of swapping them, new elements falling down etc.
Controller - Various commands responsible for almost everything:
- ChangeContextCommand - this command receives an event with a ruleset, unregisters
current commands and registers commands responsible for a new ruleset
chosen by a user.
ActivateCommand - this command receives an event with X and Y coordinates, gets data from Model for these coordinates, and decides what to do next. Actionscript 3 + Robotlegs example
public class ActivateElementCommand extends Command {
private const MAX_HUMIDITY_TO_EXPLODE:int = 3;
[Inject]
public var e:ElementActionEvent;
[Inject]
public var grid:GridProxy;
override public function execute ():void
{
var x:int = e.x;
var y:int = e.y;
var element:Object = grid.getElement ( x, y );
if ( element.explode_radius ) {
// it's gonna blow!
var neighbours:Vector.<Object> = grid.getElementsSurrounding ( x, y );
var humidity:int = 0;
for ( var i:int = 0; i < neighbours.length; i++ ) {
if ( neighbours[i].type == TypeConstants.WATER ) humidity += 1;
}
if ( humidy > MAX_HUMIDITY_TO_EXPLODE ) return; // can't fire up the match!
grid.removeElement ( x, y );
neighbours = grid.getElementsSurrounding ( x, y, element.explode_radius );
for ( var i:int = 0; i < neighbours.length; i++ ) {
el:Object = neighbours[i];
dispatch ( new ExplodeEvent(el.x, el.y, x, y, element.explode_radius) );
}
}
}
}
ExplodeCommand - receives an event with X and Y coordinates of element being currently processed, as well as X and Y coordinates of element that blew up and it's explosion_range to be able to calculate the fire_power on the element (if needed). The fire_power could decrease humidity for example.
as You can see, you can easily create different ActionCommands for different rulesets. and then register the appropriate commands when game mode is changed. You could also not register an ActionCommand at all, if you don't want elements to do anything when clicked, in some particular ruleset.