Marcus,

I have more questions for you, as well as a possible solution (though it's a bit more verbose than I would have liked to offer).

Question:

  Problem A:  Are you looking to identify the complete set of
  possible options for constructing the triples of pairs?

  Problem B: Are you looking only to construct one set that
  satisfies the problem? [see my lousy solution]

You may observe that the question I ask is quite similar to the question asked by Francesco [0].

If you are asking about the complete set of possible options (Problem A), then I submit to you that you are asking a mathematical question, not a Python question. If that's the case, perhaps you should look further into the Steiner system and/or ask again on the list.

If you are asking about finding an individual solution satisfying the constraints, I submit to you that either my approach or Francesco's approach could work for you. If that's the case, then using random.sample may offer you some help. See my sample, below--it should work on Python 2.x or Python 3.x.

Comments:

  * There are rules in your system about when a player can play
    again.  The rules were not fully clear to me, so I allowed
    players to be selecteda second time, if there were no more
    players who had not already been chosen.

  * This program produces only one possible scenario for 7
    rounds of 3 distinct, simultaneously played 2-player games.
    No player can play twice in the same round.  (Simple arithmetic
    determines the minimum number of players.)

If your question is Problem A, then I wonder if you know anybody who knows combinatorics? I do not.

-Martin

 [0] https://mail.python.org/pipermail/tutor/2015-September/106820.html


#! /usr/bin/python

from __future__ import print_function

import sys
import string
import random
import logging

logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logger = logging.getLogger()


class NotEnoughPlayers(Exception):
    pass


def choose_game_participants(players, pcount=2):
    '''returns a tuple of players for a single game
    '''
    if len(players) < pcount:
        raise NotEnoughPlayers("not enough players, need %d, have only %d: %r" %
                               (pcount, len(players), players))
    game = tuple(sorted(random.sample(players, pcount)))
    return game


def choose_match_games(players, pcount=2, gcount=3):
    '''returns a list of games where no player is duplicated
    '''
    mcount = pcount * gcount
    if len(players) < mcount:
        raise NotEnoughPlayers("not enough players, need %d, have only %d: %r" %
                               (mcount, len(players), players))
    eligible_players = random.sample(players, mcount)
    match = list()
    while eligible_players:
        m = choose_game_participants(eligible_players, pcount)
        for x in m:
            eligible_players.remove(x)
        match.append(m)
    match.sort()  # optional
    return match


def generate_rounds(players, pcount, gcount, rcount):
    games = set()
    matches = list()
    mcount = pcount * gcount
    eligible_players = list(players)
    if mcount > len(eligible_players):
        raise NotEnoughPlayers("not enough players (%d) to guarantee %d %d-player 
games per match" %
                               (len(eligible_players), gcount, pcount))
    r = 1
    while r <= rcount:
        try:
            proposed_match = choose_match_games(eligible_players, pcount, 
gcount)
        except NotEnoughPlayers:
            logger.info("adding %d additional players in round %d to meet minimum 
pool requirements",
                        mcount, r)
            how_many = mcount - len(eligible_players)
            eligible_players.extend(random.sample(players, how_many))
            continue
        already_played = games.intersection(set(proposed_match))
        if already_played:
            logger.info("discarding %d %r because %r have already played",
                        r, proposed_match, list(already_played))
            continue
        else:
            games.update(proposed_match)
            matches.append(proposed_match)
            logger.info('Proposed match %r', proposed_match)
            for game in proposed_match:
                for player in game:
                    eligible_players.remove(player)
        r = r + 1
    return matches


def log_match_info(matches, detail=False):
    for mnum, match in enumerate(matches, 1):
        logger.info("match summary %d: %r", mnum, match)
        for gnum, game in enumerate(match, 1):
            if not detail:
                continue
            logger.info("match detail %d, game %d: players %r",
                        mnum, gnum, game)


def log_match_summary(matches):
    log_match_info(matches, detail=False)


def log_match_detail(matches):
    log_match_info(matches, detail=True)


if __name__ == '__main__':
    players = list(string.ascii_uppercase)
    random.shuffle(players)
    # players = set('ABCDEFGHIJ')
    pcount = 2                              # players per game
    gcount = 3                              # games per match
    rcount = 7                              # rounds (of matches)
    matches = generate_rounds(players, pcount, gcount, rcount)
    log_match_detail(matches)

# -- end of file


--
Martin A. Brown
http://linux-ip.net/
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to