Download
"""
PyCSP3 Model (see pycsp.org)
Data can come:
- either directly from a JSON file
- or from an intermediate parser
Examples:
python TravelingTournamentWithPredefinedVenues.py -data=Ttppv_circ8bbal.json -variant=a2
python TravelingTournamentWithPredefinedVenues.py -data=Ttppv_circ8bbal.json -variant=a3
"""
from pycsp3 import *
nTeams, venues = data
nRounds = nTeams - 1
assert nTeams % 2 == 0, "an even number of teams is expected"
nConsecutiveGames = 2 if variant("a2") else 3 # used in one comment
def table(i, at_end=False): # when at_end is True, this is for the first or last game of the ith team
def cd(v1, v2): # circular distance
return min(abs(v1 - v2), nTeams - abs(v1 - v2))
if at_end: # note that when playing at home (whatever the opponent), the travel distance is 0
return {(1, ANY, 0)} | {(0, j, cd(i, j)) for j in range(nTeams) if j != i}
else:
return ({(1, 1, ANY, ANY, 0)} |
{(0, 1, j, ANY, cd(j, i)) for j in range(nTeams) if j != i} |
{(1, 0, ANY, j, cd(i, j)) for j in range(nTeams) if j != i} |
{(0, 0, j, k, cd(j, k)) for j in range(nTeams) for k in range(nTeams) if different_values(i, j, k)})
def automaton():
qi, q01, q02, q03, q11, q12, q13 = states = "q", "q01", "q02", "q03", "q11", "q12", "q13"
tr2 = [(qi, 0, q01), (qi, 1, q11), (q01, 0, q02), (q01, 1, q11), (q11, 0, q01), (q11, 1, q12), (q02, 1, q11), (q12, 0, q01)]
tr3 = [(q02, 0, q03), (q12, 1, q13), (q03, 1, q11), (q13, 0, q01)]
return Automaton(start=qi, final={q for q in states if q != qi}, transitions=tr2 if variant("a2") else tr2 + tr3)
# o[i][k] is the opponent (team) of the ith team at the kth round
o = VarArray(size=[nTeams, nRounds], dom=range(nTeams))
# h[i][k] is 1 iff the ith team plays at home at the kth round
h = VarArray(size=[nTeams, nRounds], dom={0, 1})
# t[i][k] is the travelled distance by the ith team at the kth round. An additional round is considered for returning at home.
t = VarArray(size=[nTeams, nRounds + 1], dom=range(nTeams // 2 + 1))
satisfy(
# a team cannot play against itself
[o[i][k] != i for i in range(nTeams) for k in range(nRounds)],
# ensuring predefined venues
[venues[i][o[i][k]] == h[i][k] for i in range(nTeams) for k in range(nRounds)],
# ensuring symmetry of games: if team i plays against j, then team j plays against i
[o[:, k][o[i][k]] == i for i in range(nTeams) for k in range(nRounds)],
# each team plays once against all other teams
[AllDifferent(row) for row in o],
# at most 'nConsecutiveGames' consecutive games at home, or consecutive games away
[h[i] in automaton() for i in range(nTeams)],
# handling travelling for the first game
[(h[i][0], o[i][0], t[i][0]) in table(i, at_end=True) for i in range(nTeams)],
# handling travelling for the last game
[(h[i][-1], o[i][-1], t[i][-1]) in table(i, at_end=True) for i in range(nTeams)],
# handling travelling for two successive games
[(h[i][k], h[i][k + 1], o[i][k], o[i][k + 1], t[i][k + 1]) in table(i) for i in range(nTeams) for k in range(nRounds - 1)],
# at each round, opponents are all different tag(redundant-constraints)
[AllDifferent(col) for col in columns(o)],
# tag(symmetry-breaking)
o[0][0] < o[0][-1]
)
minimize(
# minimizing summed up travelled distance
Sum(t)
)