⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssched.pro

📁 prolog开发工具
💻 PRO
字号:
/* Amzi! Sports Scheduling Demo

   Amzi! inc.
   40 Samuel Prescott Dr.
   Stow, MA 01775
   USA

   tel. 508/897-7332
   fax. 508/897-2784
   e-mail info@amzi.com
   http://www.amzi.com
   */
  
/* ------------------------------------------------------
   The main schedule interface.  These are the predicates
   that provide services to the user interface portion of
   the program, whether it is the Prolog section above or
   written in a host language such as Delphi in the demo.

   The calling program must first clear the schedule, then
   assert schedule information about teams (team/2) and
   rounds (round/2).  Then it calls schedule/1 and retrieves
   the scheduled rounds (round/3).

   clear_schedule - clear dynamic database for new
      schedule

   init_teams - called to initialize the team list

   schedule(N) - run the main scheduler, N is 1 or 2
      for number of cycles

   saveas(F) - save text version of schedule in file F

   Dynamic database predicates used by schedule:

   team(N, Name) - team number and name for each team,
      asserted by calling program

   round(N, Name) - round number and name for each round,
      asserted by calling program
   */

/* Clear the dynamic database of assertions */

clear_schedule :-
  retractall(team(_,_)),
  retractall(teams(_,_)),
  retractall(round(_,_)),
  retractall(round(_,_,_)),
  retractall(final_schedule(_)),
  retractall(fault_count(_)),
  assert(fault_count(0)).

/* Initialize the list of teams */

init_teams :-
  findall(N, team(N, _), LN),
  length(LN, L),
  (L mod 2 =:= 1 ->
    assert(team(0, bye)), TL = [0|LN], LL is L + 1
    ; TL = LN, LL is L),
  assert(teams(LL, TL)).

/* Run the main scheduler */

schedule(N) :-
  catch( sched(N), Exception, process_ex(Exception) ).

process_ex(give_up) :- fail.
process_ex(_) :- fail.

sched(1) :-
  games(G),
  !,
  first_half(1-I2, G, []+Sr, []+_),
  reverse(Sr, S),
  assert_sched(S).
sched(2) :-
  games(G),
  !,
  first_half(1-I2, G, []+S1, []+G2),
  once( reverse(G2, G2r) ),
  second_half(I2, G2r, []+S2),
  append(S2, S1, R),
  reverse(R, S),
  assert_sched(S).

/* Save the schedule output to a text file */

saveas(F) :-
  string_atom(F, FA),
  final_schedule(S),
  tell(FA),
  disp_sched(S), nl,
  disp_team_scheds(S), nl,
  told.
saveas(F) :- told.


/* The predicates that do the work of scheduling
   --------------------------------------------- */

/* first_half schedules the first half of a full
   2-cycle round robin, second half the second.
   Most all of the predicates have slightly
   different forms for each half.  In particular
   the first half predicates keep a list of the
   rematch games for feeding to the second
   half predicates, which don't need to carry that
   extra argument */
    
first_half(I2-I2, [], S+S, G2+G2).
first_half(I-I2, G, Sa+S, G2a+G2) :-
  sched1_round(I, G, Round, Gx, G2x),
  once( append(G2x, G2a, G2n) ),
  status(I),
  II is I + 1,
  first_half(II-I2, Gx, [Round|Sa]+S, G2n+G2).

second_half(I, [], S+S).
second_half(I, G, Sa+S) :-
  sched2_round(I, G, Round, Gx),
  status(I),
  II is I + 1,
  second_half(II, Gx, [Round|Sa]+S).

status(I) :-
  report(finished(I), Continue),
  (Continue=stop -> throw(give_up); true).
status(I) :-
  retract(fault_count(N)),
  NN is N + 1,
  assert(fault_count(NN)),
  report(fault(NN), Continue),
  (Continue=stop -> throw(give_up); true),
  fail.

/* Initialize a round and get the games for
   that round */

sched1_round(I, G, round(I, Games), Gz, G2) :-
  init1_round(I, Games, G2),
  get1_games(I, G, Games),
  once( left1_to_sched(G, Games, Gz) ).

sched2_round(I, G, round(I, Games), Gz) :-
  init2_round(I, Games),
  get2_games(I, G, Games),
  once( left2_to_sched(G, Games, Gz) ).

/* Each round is initialized by a template.  In
   this case, each is the same general pattern,
   but this mechanism allows specific games to
   be placed on specific rounds. */

init1_round(N, Games1, Games2) :-
  teams(NT, _),
  template1(NT, []+Games1, []+Games2R),
  reverse(Games2R, Games2).

init2_round(N, Games) :-
  teams(NT, _),
  template2(NT, []+Games).

/* The template for a round is a list of games, in
   this case set to a number of variable pairs.  For
   the first half, the template includes two lists, one
   with a game, and the other with the rematch of the
   game.  For the second half, the template is just
   the games. */

template1(0, G1+G1, G2+G2).
template1(NT, G1a+G1, G2a+G2) :-
  NT > 0,
  NT2 is NT-2,
  template1(NT2, [Away-Home|G1a]+G1, [Home-Away|G2a]+G2).

template2(0, G+G).
template2(NT, Ga+G) :-
  NT > 0,
  NT2 is NT - 2,
  template2(NT2, [T1-T2|Ga]+G).

/* After the games have been scheduled for a day, take
   them out of the list of games left to be scheduled. */

left1_to_sched(G, [], G).
left1_to_sched(G, [Away-Home|X], Gl) :-
  delete(Away-Home, G, Gx),
  delete(Home-Away, Gx, Gy),
  left1_to_sched(Gy, X, Gl).

left2_to_sched(G, [], G).
left2_to_sched(G, [Game|X], Gl) :-
  delete(Game, G, Gx),
  left2_to_sched(Gx, X, Gl).

/* Generate games for a given round by walking the pairs
   in the template for the round, unifying them with a game
   from the list of games left to play.  After picking a
   game for the round, clean the list so that no other games
   involving either of those teams will be scheduled that
   round. */

get1_games(_, [], []).
get1_games(I, G, [Away-Home|Gs]) :-
  deal(Away-Home, G, Gx),
  once( clean(Gx, Home, Away, Gn) ),
  get1_games(I, Gn, Gs).

get2_games(_, [], []).
get2_games(I, G, [Away-Home|Gs]) :-
  deal(Away-Home, G, Gx),
  once( clean(Gx, Home, Away, Gn) ),
  get2_games(I, Gn, Gs).


/* Textual display of full schedule
   -------------------------------- */

disp_sched([]).
disp_sched([round(N, Games)|T]) :-
  round(N, Date),
  write(Date), nl,
  disp_games(Games), nl,
  disp_sched(T).

  disp_games([]).
  disp_games([Bye|X]) :-
    (Bye = 0-T; Bye = T-0),
    !,
    team(T, Name),
    tab(2), write(Name), tab(1),
    write(bye), nl,
    disp_games(X).
  disp_games([H-A|X]) :-
    team(H, Home),
    team(A, Away),
    tab(2), write(Home), tab(1), write('@'),
    tab(1), write(Away), nl,
    disp_games(X).

/* Display the schedule for each team */

disp_team_scheds(S) :-
  teams(_, T),
  team_scheds(T, S).

  team_scheds([], _).
  team_scheds([0|X], S) :-
    team_scheds(X, S).
  team_scheds([T|X], S) :-
    team(T, Name),
    write($\nSchedule for : $), write(Name), nl,
    team_sched(T, S),
    team_scheds(X, S).

  team_sched(_, []).
  team_sched(T, [round(N, Games)|X]) :-
    round(N, Date),
    disp_round(T, round(Date, Games)),
    team_sched(T, X).

  disp_round(T, round(N, Games)) :-
    member(T-O, Games), !,
    team(O, Name),
    write(N),
    (O > 0 -> write($: Away $); write($: $)),
    write(Name), nl.
  disp_round(T, round(N, Games)) :-
    member(O-T, Games), !,
    team(O, Name),
    write(N),
    (O > 0 -> write($: Home $); write($: $)),
    write(Name), nl.


/* Utility Predicates
   ------------------ */

/* Generate a list of games from the list of teams.  Home
   and away games are alternated to provide better balance
   in the schedule.  The game list is the primary input to
   the scheduler, which picks games from the list to build
   each round's schedule.  The game list is of the form:

   [2-1, 1-3, 4-1, 1-5, ...... 1-2, 3-1, 1-4...] */

games(G) :-
  teams(_, T),
  g_list(T, T, []+G).

  g_list([_], [], G+G).
  g_list([_|X], [], A+G) :-
    teams(_, T),
    g_list(X, T, A+G).
  g_list([T|X1], [T|X2], A+G) :-
    g_list([T|X1], X2, A+G).
  g_list([T1|X1], [T2|X2], A+G) :-
    (T1+T2) mod 2 =:= 1, !,
    g_list([T1|X1], X2, [T2-T1|A]+G).
  g_list([T1|X1], [T2|X2], A+G) :-
    g_list([T1|X1], X2, [T1-T2|A]+G).

/* reverse a list */

reverse(F, R) :-
  rev(F, [], R).

  rev([], R, R).
  rev([H|T], A, R) :-
    rev(T, [H|A], R).

/* append two lists together */

append([], Z, Z).
append([H|X], Y, [H|Z]) :-
  append(X, Y, Z).

/* find members of a list */

member(X, [X|_]).
member(X, [_|Y]) :-
  member(X, Y).

/* get the length of a list */

length(L, N) :-
  len(L, 0, N).

  len([], N, N).
  len([_|X], A, N) :-
    AA is A + 1,
    len(X, AA, N).

/* clean removes all the games that no longer make
   sense for a given round from the list of remaining
   games.  If, for example, we've selected game 3-8 for
   the round we're working on, then we don't need to look
   at any other games involving teams 3 or 8. */

clean(Gx, Home, Away, Gn) :-
  remove(Home, Gx, Gy),
  remove(Away, Gy, Gn).

clean(Gx, T, Gn) :-
  remove(T, Gx, Gn).

  remove(T, Gx, Gz) :-
    delete(T-_, Gx, Gy),
    remove(T, Gy, Gz).
  remove(T, Gx, Gz) :-
    delete(_-T, Gx, Gy),
    remove(T, Gy, Gz).
  remove(T, Gx, Gz) :-
    delete(T=T, Gx, Gy),
    remove(T, Gy, Gz).
  remove(_, Gn, Gn).

/* set an element of a list to a certain value */

set_elem(1, X, [_|Z], [X|Z]) :- !.
set_elem(N, X, [A|Z1], [A|Z2]) :-
  NN is N - 1,
  set_elem(NN, X, Z1, Z2).

/* delete an element from a list and return the
   remaining list.  When the first argument is
   a variable, delete/3 can be used in a back-tracking
   loop to select elements from a list. */

delete(A,[A|X],X).
delete(A,[B|X],[B|Y]) :- delete(A,X,Y).

/* deal/3 (like deal cards from a deck) is often identical
   to delete/3.  In this application, deal/3 is used to
   select games to schedule on a given round, and the list of
   games for the round can be in any order.  So, deal/3 is
   modified so that it only returns the remainder of a list,
   thereby eliminating permutations from the search. */

deal(A-H, X, Y) :-
  var(A), var(H),
  !, pick(A-H, X, Y).
deal(A-H, X, Y) :-
  delete(A-H, X, Y).

pick(A,[A|X],X).
pick(A,[B|X],Y) :- pick(A,X,Y).

/* delete all the elements in the first list from the
   second, returning the remainder in the third. */

delete_list([], A, A).
delete_list([G|X], A, Z) :-
  delete(G, A, A2),
  delete_list(X, A2, Z).

/* same as delete_list, except some of the elements in the
   first list might not be in the second.  The third argument
   contains the list of elements that were actually deleted. */

delete2_list([], Del, Del, Gz, Gz).
delete2_list([G|X], Ad, Del, Ag, Gz) :-
  once( delete(G, Ag, AGx) ), !,
  delete2_list(X, [G|Ad], Del, AGx, Gz).
delete2_list([G|X], Ad, Del, Ag, Gz) :-
  delete2_list(X, Ad, Del, Ag, Gz).

/* delete a pattern, find all the elements that match it.
   /3 version just deletes, /5 version returns list of
   deleted elements */

delete_pat(_, [], []).
delete_pat(P, [H|X], X2) :-
  not(not(P=H)), !,
  delete_pat(P, X, X2).
delete_pat(P, [H|X], [H|X2]) :-
  delete_pat(P, X, X2).

delete_pat(_, [], [], Z, Z).
delete_pat(P, [H|X], X2, A, Z) :-
  not(not(P=H)), !,
  delete_pat(P, X, X2, [H|A], Z).
delete_pat(P, [H|X], [H|X2], A, Z) :-
  delete_pat(P, X, X2, A, Z).

/* move all elements of a list that match a pattern to
   the end of the list */

move_to_back(P, G, G2) :-
  delete_pat(P, G, GLeft, [], GDel),
  append(GLeft, GDel, G2).

/* move all elements of a list that match a pattern to
   the front of the list */

move_to_front(P, G, G2) :-
  delete_pat(P, G, GLeft, [], GDel),
  append(GDel, GLeft, G2).
  
/* scramble a list into a random order, can be used
   to scramble starting team or game list so that
   each run produces a random variation of a valid
   schedule */

shuffle(Tin, Tout) :-
  time(_, Min, Sec, _),
  N is Min + Sec,
  set_random(N),
  shuffle1(Tin, [], Tout).

  set_random(N) :-
    for(I, 1, N, 1),
    X is random,
    fail.
  set_random(_).

  shuffle1([], A, A).
  shuffle1(Tin, A, Tout) :-
    length(Tin, L),
    N is 1 + integer( random * L ),
    deleteN(N, Elem, Tin, Tx),
    shuffle1(Tx, [Elem|A], Tout).
    
  deleteN(1, H, [H|Z], Z).
  deleteN(_, _, [], []) :- !, fail.
  deleteN(N, H, [X|Z], [X|Z2]) :-
    NN is N - 1,
    deleteN(NN, H, Z, Z2).

/* translate schedule list into separate assertions
   that are easier to pick up by a host language
   program */

assert_sched(S) :-
  retractall(round(_,_,_)),
  assert(final_schedule(S)),
  asrt_sched(S).

  asrt_sched([]).
  asrt_sched([round(N, Games)|X]) :-
    as_sch(N, Games),
    asrt_sched(X).

    as_sch(_, []).
    as_sch(N, [A-H|X]) :-
      team(A, Away),
      team(H, Home),
      round(N, Round),
      assert(round(Round, Away, Home)),
      as_sch(N, X).

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -