Sapien
Sapien

Reputation: 3

Prolog Round-robin schedule Home and Away

I'm currently trying to program a round robin schedule in Prolog and have managed to get all teams to play each other once, I would now like to program it such that all teams play each other twice, both home and away, e.g. [1, 2] and [2, 1]. The code I have so far is as follows:

%table of allocated matches
:- dynamic(match_table/2).

%get all teams from 1 .. NumTeams
forTeams(T, T, X) :-
    T =< X.
forTeams(I, T, X) :-
    T < X,
    T1 is T + 1,
    forTeams(I, T1, X).

%teams represented by integers more than 1
check_num_input(T) :-
    integer(T),
    T > 1.

%resets the allocation table of matches
reset_allocations :-
    retractall(match_table(_, _)).

%check the match has not already been allocated
%empty list for once recursion is complete
check_not_allocated(_, []).
%recursively search through allocation list to see if team is allocated
check_not_allocated(T, [X | CurrentMatchesTail]) :-
    \+ match_table(T, X),
    \+ match_table(X, T),
    check_not_allocated(T, CurrentMatchesTail).

%recursively fetch match allocation
get_match_allocation(_, 0, CurrentMatches, CurrentMatches).
get_match_allocation(NumTeams, RemainingNumTeamsPerMatch, CurrentMatches, 
Matches) :-
    RemainingNumTeamsPerMatch > 0,
    forTeams(T, 1, NumTeams),
    \+ member(T, CurrentMatches),
    check_not_allocated(T, CurrentMatches),
    append(CurrentMatches, [T], NewMatches),
    Remaining1 is RemainingNumTeamsPerMatch - 1,
    get_match_allocation(NumTeams, Remaining1, NewMatches, Matches).

%recursively store/ add matches into allocation list
store_allocation_1(_, []).
store_allocation_1(T, [X | MatchesTail]) :-
    assertz(match_table(T, X)),
    store_allocation_1(T, MatchesTail).

%recursively store allocation from match list
store_allocation([_]).
store_allocation([T | MatchesTail]) :-
    store_allocation_1(T, MatchesTail),
    store_allocation(MatchesTail).

%recursively check all required matches are allocated
check_plays_all(_, []).
check_plays_all(T, [Team | TeamsTail]) :-
    %check head team from teams list plays next head team from remaining 
teams list
    (    match_table(T, Team)
    ;    match_table(Team, T)
    ),
    check_plays_all(T, TeamsTail).

check_all_play_all([_]).
%get head team of teams list
check_all_play_all([T | TeamsTail]) :-
    check_plays_all(T, TeamsTail),
    check_all_play_all(TeamsTail).

do_round_robin(NumTeams, _, T, []) :-
    T > NumTeams.
do_round_robin(NumTeams, NumTeamsPerMatch, T, [Matches | MatchesTail]) :-
    T =< NumTeams,
    get_match_allocation(NumTeams, NumTeamsPerMatch, [T], Matches),
    !,
    store_allocation(Matches),
    do_round_robin(NumTeams, NumTeamsPerMatch, T, MatchesTail).
do_round_robin(NumTeams, NumTeamsPerMatch, T, Matches) :-
    T =< NumTeams,
    T1 is T + 1,
    do_round_robin(NumTeams, NumTeamsPerMatch, T1, Matches).

round_robin(NumTeams, NumTeamsPerMatch, Matches) :-
    check_num_input(NumTeams),
    check_num_input(NumTeamsPerMatch),
    reset_allocations,
    NumTeamsPerMatch1 is NumTeamsPerMatch - 1, %1
    do_round_robin(NumTeams, NumTeamsPerMatch1, 1, Matches), %(NumTeams, 1, 
1, Matches_List)
    findall(T, forTeams(T, 1, NumTeams), Teams), %finds all teams from 1 .. 
NumTeams
    check_all_play_all(Teams),
    !,
    reset_allocations.
round_robin(_, _, _) :-
    reset_allocations,
    fail.

To output the schedule where 2 teams play in one game the query is round_robin(6, 2, Schedule). Where 6 is the number of teams and 2 is the amount of teams playing each game.

I'm quite new to Prolog and logic programming so would appreciate the help :)

Thank you,

BD.

Upvotes: 0

Views: 376

Answers (1)

user7473772
user7473772

Reputation:

Maybe even better?

home_away(N, A-B) :-
    between(1, N, A),
    between(1, N, B),
    A \== B.

This will order all possibilities lexicographically.

?- findall(X, home_away(3, X), Xs).
Xs = [1-2, 1-3, 2-1, 2-3, 3-1, 3-2].

Below the older answers.


Easier to do with between/3.

home_away(N, X) :-
    succ(N0, N), between(1, N0, A),
    succ(A, A1), between(A1, N, B),
    (   X = A-B
    ;   X = B-A
    ).

Now not even choicepoint:

?- home_away(3, X).
X = 1-2 ;
X = 2-1 ;
X = 1-3 ;
X = 3-1 ;
X = 2-3 ;
X = 3-2.

Below you find still older answer.


Your code is really difficult. Maybe this is not useful idea but you can try to give number of teams and get all possible games where each pair of numbers is home-guest.

home_away(N, X) :-
    numlist(1, N, Teams),
    append(_, [A|T], Teams),
    member(B, T),
    (   X = A-B
    ;   X = B-A
    ).

Now with this you can give number of teams and you get teams numbered 1,2,...,N as home and away

?- home_away(3, X).
X = 1-2 ;
X = 2-1 ;
X = 1-3 ;
X = 3-1 ;
X = 2-3 ;
X = 3-2 ;
false.

?- bagof(X, home_away(4, X), Xs).
Xs = [1-2, 2-1, 1-3, 3-1, 1-4, 4-1, 2-3, 3-2, 2-4, 4-2, 3-4, 4-3].

Upvotes: 1

Related Questions