♟2026.05.18
Spirales
et chevaliers | 3. deux joueurs, et plus?
Dans la première partie, nous avons dessiné une spirale carrée. Dans la seconde, nous avons reproduit le motif de la première visualisation montrée dans la vidéo de Numberphile qui a inspiré ce petit projet. Essayons maintenant de généraliser notre code pour arriver aux formes plus complexes, avec plusieurs couleurs et déplacements.
Plusieurs joueurs
Première chose à faire: que notre générateur de points accepte une liste de joueurs. On va essayer d’avancer pas à pas, en gardant le code fonctionnel à chaque étape, donc on ne change pour l’instant rien au reste du code et on ne traite que le premier joueur de la liste:
# spiral.py
def valid_points_generator(board: Board, max_points: int, players: Iterable[Player]) -> Generator[tuple[int, int, Player], None, None]:
n_points = 0
player = players[0]
# ...
if __name__ == "__main__":
board = Board()
player = Player(color='b', offsets=[(1, 2), (-1, 2), (2, 1), (-2, 1), (1, -2), (-1, -2), (2, -1), (-2, -1)])
show_points(valid_points_generator(board, 1000, [player]))Détachons ensuite la logique de “trouver le prochain point pour un joueur”, pour pouvoir itérer sur la liste des joueurs dans le générateur. Chaque joueur devra avoir son générateur de prochain point potentiel indépendant: ce n’est pas parce qu’on a passé un point et qu’il n’était pas valable pour le joueur 1 qu’il ne sera pas valable pour les autres.
# spiral.py
def _next_valid_point_for_player(points: Iterable[tuple[int, int]],
board: Board,
player: Player) -> tuple[int, int]:
for x, y in points:
if not board.is_available((x, y)):
continue
if board.is_visible_from((x, y), player):
continue
return x, y
def valid_points_generator(board: Board, max_points: int, players: Iterable[Player]) -> Generator[tuple[int, int, Player], None, None]:
n_points = 0
generators = [(player, position_generator()) for player in players]
while True:
for player, generator in generators:
x, y = _next_valid_point_for_player(generator, board, player)
board.put((x, y), player)
n_points += 1
yield x, y, player
if n_points >= max_points:
returnTout fonctionne toujours, mais le
board.is_visible_from((x, y), player) n’est pas
juste. Ou plutôt, ce ne sera pas juste dans la version avec
plusieurs joueurs, puisqu’il faudra vérifier si la case est vue
par l’un des autres joueurs.
On doit donc passer une liste de joueurs à
next_valid..., et vérifier si l’on est visible de
n’importe lequel d’entre eux:
# spiral.py
def _next_valid_point_for_player(points: Iterable[tuple[int, int]],
board: Board,
players: Iterable[Player]) -> tuple[int, int]:
for x, y in points:
if not board.is_available((x, y)):
continue
if any(board.is_visible_from((x, y), player) for player in players):
continue
return x, yEt dans le générateur, on distinguera le cas un ou plusieurs joueurs. Dans le premier cas, le point est valide s’il n’est pas visible par les cases déjà coloriée par le joueur lui-même. Dans le second, s’il n’est pas visible par les cases coloriées par l’un des autres.
# spiral.py
def valid_points_generator(board: Board, max_points: int, players: Iterable[Player]) -> Generator[tuple[int, int, Player], None, None]:
n_points = 0
generators = [(player, position_generator()) for player in players]
while True:
for player, generator in generators:
if len(players) == 1:
x, y = _next_valid_point_for_player(generator, board, player)
else:
x, y = _next_valid_point_for_player(generator, board, [p for p in players if p != player])
board.put((x, y), player)
n_points += 1
yield x, y, player
if n_points >= max_points:
returnMettons maintenant deux joueurs:
# spiral.py
if __name__ == "__main__":
board = Board()
players = [
Player(color='r', offsets=[(1, 2), (-1, 2), (2, 1), (-2, 1), (1, -2), (-1, -2), (2, -1), (-2, -1)]),
Player(color='k', offsets=[(1, 2), (-1, 2), (2, 1), (-2, 1), (1, -2), (-1, -2), (2, -1), (-2, -1)])
]
show_points(valid_points_generator(board, 1000, players))Et on obtient:
Ce qui, mis à part une inversion des couleurs, me semble bien correspondre à l’exemple montré dans la vidéo de Numberphile et repris sur le site de l’OEIS.
Un solide point d’arrêt pour cet article, même s’il reste du boulot.