Download
/*
 * SICSTUS CLPFD DEMONSTRATION PROGRAM
 * Purpose   : Social Golfer Problem
 * Author    : Mats Carlsson
 *
 * We have 32 golfers, individual play.
 * We will golf for W weeks.
 * Set up the foursomes so that each person only golfs with the same
 * person once.
 *
 * | ?- golf(8,4,9,[min],bycolall,bounds).
 */

:- module(golf, [golf/6]).

:- use_module(library(lists)).
:- use_module(library(clpfd)).

golf(G, S, W, LabelOpt, VarOrder, Consistency) :-
	Opt = [consistency(Consistency)],
	golfer(G, S, W, Schedule, Byrow, Bycol, Opt),
	var_order(VarOrder, Byrow, Bycol, All),
	(   foreach(Set,All),
	    param(LabelOpt)
	do  labeling(LabelOpt, Set)
	),
	(   foreach(Round,Schedule),
	    count(Wk,1,_)
	do  format('Week ~d:\n', [Wk]),
	    (   foreach(Four,Round)
	    do  format('                    ~d ~d ~d ~d\n', Four)
	    )
	).

var_order(bycol, _, All, All).
var_order(byrow, All, _, All).
var_order(bycolall, _, Cols, [All]) :-
	append(Cols, All).
var_order(byrowall, Rows, _, [All]) :-
	append(Rows, All).

golfer(G, S, W, Schedule, PlayersByRow, PlayersByCol, Opt) :-
	schedule(0, G, S, W, Schedule, PlayersByRow, PlayersByCol, Opt),
	Schedule = [FirstS|RestS],
	append(FirstS, Players),
	labeling([enum], Players), !,
	(   foreach(Week,RestS),
	    param(S)
	do  (   foreach([P|Ps],Week),
		param(S)
	    do  P/S #= Q0,
		(   foreach(P1,Ps),
		    fromto(Q0,Q1,Q2,_),
		    param(S)
		do  P1/S #= Q2,
		    Q1 #< Q2
		)
	    ),
	    seed_week(0, S, Week)
	),
	ordered_players_by_week(PlayersByRow),
	players_meet_disjoint(Schedule, G, S, Opt),
	first_s_alldiff(0, S, RestS, Opt).


schedule(W, _, _, W, [], [], [], _) :- !.
schedule(I, G, S, W, [Week|Schedule], [ByRow|ByRows], [ByCol|ByCols], Opt) :-
	(   for(_,1,G),
	    foreach(Group,Week),
	    param([G,S])
	do  length(Group, S),
	    GS is G*S-1,
	    domain(Group, 0, GS)
	),
	append(Week, ByRow),
	all_different(ByRow, Opt),
	transpose(Week, WeekT),
	append(WeekT, ByCol),
	J is I+1,
	schedule(J, G, S, W, Schedule, ByRows, ByCols, Opt).

players_meet_disjoint(Schedule, G, S, Opt) :-
	append(Schedule, Groups),
	groups_meets(Groups, Tuples, [], MeetVars, []),
	GS is G*S,
	(   foreach([A,B,C],Tuples),
	    param([GS,Opt])
	do  scalar_product([GS,1], [A,B], #=, C, Opt)
	),
	all_distinct(MeetVars, Opt).

groups_meets([], Tuples, Tuples) --> [].
groups_meets([Group|Groups], Tuples1, Tuples3) -->
	group_meets(Group, Tuples1, Tuples2),
	groups_meets(Groups, Tuples2, Tuples3).

group_meets([], Tuples, Tuples) --> [].
group_meets([P|Ps], Tuples1, Tuples3) -->
	group_meets(Ps, P, Tuples1, Tuples2),
	group_meets(Ps, Tuples2, Tuples3).

group_meets([], _, Tuples, Tuples) --> [].
group_meets([Q|Qs], P, [[P,Q,PQ]|Tuples1], Tuples2) --> [PQ],
	group_meets(Qs, P, Tuples1, Tuples2).

seed_week(S, S, Week) :- !,
	S1 is S-1,
	seed_week(Week, S1).
seed_week(I, S, [[I|_]|Week]) :-
	J is I+1,
	seed_week(J, S, Week).

seed_week([], _).
seed_week([[J|_]|Week], I) :-
	I #< J,
	seed_week(Week, J).

ordered_players_by_week([W|Ws]) :-
	ordered_players_by_week(Ws, W).

ordered_players_by_week([], _).
ordered_players_by_week([W|Ws], V) :-
	W = [_,Y|_],
	V = [_,X|_],
	X #< Y,
	ordered_players_by_week(Ws, W).

first_s_alldiff(S, S, _Schedule, _) :- !.
first_s_alldiff(I, S, Schedule, Opt) :-
	(   foreach(Week,Schedule),
	    foreach(Ith,Part),
	    param(I)
	do  nth0(I, Week, [_|Ith])
	),
	append(Part, Conc),
	all_different(Conc, Opt),
	J is I+1,
	first_s_alldiff(J, S, Schedule, Opt).