Accueil > Développement > Variations sur un thème

Variations sur un thème

06/01/2009

De temps en temps, j’essaye d’écrire Monarques sous forme de jeu vidéo. Je n’en finirai sans doute jamais aucune version, mais ça m’occupe, et me permet de simplifier le jeu papier.

Un des points que j’aime bien, c’est la définition d’un château. Dans Monarques, c’est tout simplement une série de cases adjacentes, qui peuvent recruter des unités. Il faut pouvoir vérifier si deux cases font partie d’un même château, afin d’éviter que deux armées débutent au même endroit.

Les variations qui suivent sont bâties sur un principe identique :

  • on part d’une case (x, y)
  • si c’est une case d’un château, et que le château en cours de construction ne la contient pas déjà, on l’ajoute
  • et on récurse sur les cases voisines

Variation impérative / objet
La première que j’ai écrite. Très simple, mais un peu laid, au niveau de la récursion sur les cases adjacentes. Peut mieux faire.

public class Castle
{
	private Map map;
	private Collection elems;
 
	protected Castle(Map map, int x, int y)
	{
		this.map = map;
		this.elems = new ArrayList();
 
		MapElement elem = map.getElementAt(x, y);
		buildCastle(elem);
	}
 
	private void buildCastle(MapElement elem)
	{
		if(elem == null || !elem.canRecruitUnits() || elems.contains(elem))
			 return;
 
		elems.add(elem);
 
		Coordinates coord = elem.getCoordinates();
		buildCastle(map.getElementAt(coord.getX()-1, coord.getY()-1));
		buildCastle(map.getElementAt(coord.getX()-1, coord.getY()));
		buildCastle(map.getElementAt(coord.getX()-1, coord.getY()+1));
		buildCastle(map.getElementAt(coord.getX(), coord.getY()-1));
		buildCastle(map.getElementAt(coord.getX(), coord.getY()+1));
		buildCastle(map.getElementAt(coord.getX()+1, coord.getY()-1));
		buildCastle(map.getElementAt(coord.getX()+1, coord.getY()));
		buildCastle(map.getElementAt(coord.getX()+1, coord.getY()+1));
	}
}

Variation fonctionnelle
J’ai gardé le type de données, les conditions d’arrêt sont identiques. La récursion est un peu plus complexe a cause du foldl, mais on a une liste de cases adjacentes plutôt que du code dupliqué, c’est toujours ça de pris.

data Castle = Castle { castleCoords :: [Coords] }
 
buildCastle :: Map -> Castle -> Coords -> Castle
buildCastle m c xy
  | not (canRecruitUnits m xy) = c
  | xy `elem` (castleCoords c) = c
  | otherwise = foldl (buildCastle m) c' (near xy)
  where
    c' = Castle (xy:(castleCoords c))
    variations x = [x-1, x, x+1]
    near (x, y) = [(a, b) | a <- variations x, b <- variations y, (a, b) `isInMap` m && (a, b) /= (x, y)]

Variation simplifiée
La dernière en date. Finalement, je n’ai pas besoin de type de données particulier, vérifier si les deux coordonnées sont dans la liste qui constitue un château me suffit. J’ai repris l’idée de la liste des cases adjacentes (growable), mais l’itération dessus me semble plus claire que le foldl.

class Board:
  #...
  def buildCastle(self, x, y, accumulator):
    if (x, y) in accumulator or not self.isCastleArea(x, y):
      return accumulator
 
    accumulator.append( (x, y) )
    growable = [(a, b) for a in (x-1, x, x+1)
                       for b in (y-1, y, y+1)
                       if self.isInBoard(a, b) and not (a, b) in accumulator]
    for x, y in growable:
      self.buildCastle(x, y, accumulator)
    return accumulator

Développement

Les commentaires sont fermés.