/*  Chaos:		    The Chess HAppening Organisation System	V5.1a
    Copyright (C)   1993    Jochen Wiedmann

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


    $RCSfile: Losen.c,v $
    $Revision: 1.1 $
    $Date: 1993/08/19 14:06:03 $

    This file contains the pairing-functions. The algorithm of the
    swiss-pairing is described in the function LoseGruppe().

    Computer:	Amiga 1200		    Compiler:	Dice 2.07.54 (3.0)

    Author:	Jochen Wiedmann
		Am Eisteich 9
	  72555 Metzingen
		Tel. 07123 / 14881
		Internet: wiedmann@mailserv.zdv.uni-tuebingen.de
*/


#ifndef CHAOS_H
#include "chaos.h"
#endif
#ifndef CLIB_ALIB_PROTOS_H
#include <clib/alib_protos.h>
#endif
#ifndef CLIB_ALIB_STDIO_PROTOS_H
#include <clib/alib_stdio_protos.h>
#endif
#ifndef CLIB_UTILITY_PROTOS_H
#include <clib/utility_protos.h>
#endif
#include <clib/macros.h>

#ifdef AZTEC_C
#ifndef __PRAGMAS_UTILITY_LIB_H
#include <pragmas/utility_lib.h>
#endif
#endif


/*
    Eine lokal verwendete Struktur
*/
struct Gruppe
  { struct Gruppe *Next;
    struct Teilnehmer *First;
    struct Teilnehmer *Last;
    int NumMembers;
    int NumAllocMembers;
  };



/*
    Einige Funktionen zur Verwaltung einer doppelt verketteten Liste.
    Sie ähneln den entsprechenden Exec-Funktionen.
*/
void MyRemove(struct Gruppe *g, struct Teilnehmer *t)

  {
    if (t->LT_Pred == NULL)
      { g->First = t->LT_Succ;
      }
    else
      { t->LT_Pred->LT_Succ = t->LT_Succ;
      }
    if (t->LT_Succ == NULL)
      { g->Last = t->LT_Pred;
      }
    else
      { t->LT_Succ->LT_Pred = t->LT_Pred;
      }
    g->NumMembers--;
  }


void MyAddTail(struct Gruppe *g, struct Teilnehmer *t)

  { if ((t->LT_Pred = g->Last)  ==  NULL)
      { g->First = t;
      }
    else
      { g->Last->LT_Succ = t;
      }
    t->LT_Succ = NULL;
    g->Last = t;
    g->NumMembers++;
  }


void MyAddHead(struct Gruppe *g, struct Teilnehmer *t)

  { if ((t->LT_Succ = g->First)  ==  NULL)
      { g->Last = t;
      }
    else
      { g->First->LT_Pred = t;
      }
    t->LT_Pred = NULL;
    g->First = t;
    g->NumMembers++;
  }


void MyInsert(struct Gruppe *g, struct Teilnehmer *t, struct Teilnehmer *pred)

  { if (pred == NULL)
      { MyAddHead(g, t);
      }
    else
      { if (pred->LT_Succ == NULL)
	  { MyAddTail(g, t);
	  }
	else
	  { t->LT_Succ = pred->LT_Succ;
	    t->LT_Pred = pred;
	    pred->LT_Succ->LT_Pred = t;
	    pred->LT_Succ = t;
	    g->NumMembers++;
	  }
      }
  }


void MyEnqueue(struct Gruppe *g, struct Teilnehmer *t)

  { struct Teilnehmer *succ;

    for (succ = g->First;  succ != NULL;  succ = succ->LT_Succ)
      { if (t->Nr < succ->Nr)
	  { break;
	  }
      }
    if (succ == NULL)
      { MyAddTail(g, t);
      }
    else
      { MyInsert(g, t, succ->LT_Pred);
      }
  }



/*  Die Funktion SpielAdresse() liefert die Adresse der Game-Struktur des
    Spiels eines gegebenen Teilnehmers und einer gegebenen Runde.
    (Runde=0 liefert die Adresse von t->First_Game)
*/
struct Game *SpielAdresse(struct Teilnehmer *t, int Runde)

  { struct Game *g;

    for (g = (struct Game *) &(t->First_Game);  Runde != 0;
	 g = g->Next, Runde--)
      {
      }
    return(g);
  }



/*  Die Funktion FreeGames() gibt das für die Spiele einer Runde belegte
    RAM wieder frei.
*/
static void FreeGames(int Runde)

  { struct Teilnehmer *t;
    struct Game *g;

    for (t = ((struct Teilnehmer *) Teilnehmerliste.lh_Head);
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { if ((g = SpielAdresse(t, Runde))->Next  !=  NULL)
	  { FreeMem(g->Next, sizeof(*g));
	    g->Next = NULL;
	  }
      }
  }



/*  Die Funktion NewGames() erzeugt neue Game-Strukturen für jeden Teilnehmer.
    Sie geht davon aus, daß der zukünftige Gegner im Gegner-Feld der
    Teilnehmer-Struktur steht, die Flags (Freilos, Weiss) im GFlags-Feld
    und die Brettnummer im BrettNr-Feld.
*/
static int NewGames(int FreilosPunkte)

  { struct Game *g;
    struct Teilnehmer *t;
    int NumSpiele = 0;

    /*	Versuche neue Spielstrukturen zu bekommen. Falls der Versuch
	fehlschlägt, ist die Auslosung ungültig.
    */
    for (t = ((struct Teilnehmer *) Teilnehmerliste.lh_Head);
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { if (t->Flags & TNFLAGSF_GELOESCHT)
	  { t->GFlags |= GMFLAGSF_KAMPFLOS;
	  }
	else if (t->Gegner == NULL)
	  { t->GFlags |= GMFLAGSF_KAMPFLOS|GMFLAGSF_FREILOS;
	  }

	if ((g = ALLOCSTRUCT(Game))  ==  NULL)
	  { MemoryError();
	    FreeGames(NumRunden);
	    return(FALSE);
	  }
	SpielAdresse(t, NumRunden)->Next = g;
      }

    /*	Jetzt kann nichts mehr schiefgehen; die Auslosung ist gültig.
    */
    NumRunden++;
    for (t = ((struct Teilnehmer *) Teilnehmerliste.lh_Head);
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { g = SpielAdresse(t, NumRunden);
	g->Next = NULL;
	g->Gegner = t->Gegner;
	g->Flags = t->GFlags;
	g->BrettNr = t->BrettNr;

	if (g->Flags & GMFLAGSF_KAMPFLOS)
	  { if (t->Flags & TNFLAGSF_GELOESCHT)
	      { g->Ergebnis = 0;
		g->Flags = GMFLAGSF_SELBERRAUS;
	      }
	    else
	      { t->Punkte += FreilosPunkte;
		g->Ergebnis = FreilosPunkte;
		t->Flags |= TNFLAGSF_HATTEFREILOS;
	      }
	  }
	else
	  { NumSpiele++;
	    g->Ergebnis = -1;
	    if (g->Flags & GMFLAGSF_WEISS)
	      { t->WieOftWeiss++;
		if (t->WieOftWeissZuletzt > 0)
		  { t->WieOftWeissZuletzt++;
		  }
		else
		  { t->WieOftWeissZuletzt = 1;
		  }
	      }
	    else
	      { t->WieOftWeiss--;
		if (t->WieOftWeissZuletzt < 0)
		  { t->WieOftWeissZuletzt--;
		  }
		else
		  { t->WieOftWeissZuletzt = -1;
		  }
	      }
	  }
      }

    NumFehlendeSpiele = NumSpiele/2;
    return(TRUE);;
  }



/*  Die Funktion MakeRangliste() erstellt die erste Rangliste.
    Hat ein Teilnehmer eine ELO-Zahl, dann bestimmt diese den Ranglistenplatz.
    Bei Teilnehmern ohne ELO-Zahl bestimmt die DWZ-Zahl den Ranglistenplatz.
    Teilnehmer ohne jede Wertungszahl erscheinen am Ende der Tabelle.
*/
void MakeRangliste(int *NumMinWertung)

  { struct Teilnehmer *t, **tptr;
    int t1, t2;
    int MinWertung;

    RanglistenErster = NULL;
    *NumMinWertung = -1;    /*	Erzwinge Initialisierung von MinWertung in
				der folgenden Schleife.
			    */
    for (t = (struct Teilnehmer *) Teilnehmerliste.lh_Head;
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { if ((t1 = t->ELO)  ==  0)
	  { t1 = tdwz(t);
	  }
	if (*NumMinWertung == -1)
	  { MinWertung = t1;
	    *NumMinWertung = 0;
	  }
	if (t1 < MinWertung)
	  { MinWertung = t1;
	    *NumMinWertung = 0;
	  }
	if (t1 == MinWertung)
	  { (*NumMinWertung)++;
	  }

	for (tptr = &RanglistenErster;  *tptr != NULL;
	     tptr = &((*tptr)->RangNext))
	  { if (t1 != 0)
	      { if ((t2 = (*tptr)->ELO)  ==  0)
		  { t2 = tdwz(*tptr);
		  }
		if (t1 > t2)
		  { break;
		  }
	      }
	  }

	t->RangNext = *tptr;
	*tptr = t;

	t->Gegner = NULL;
	t->GFlags = 0;
      }
  }



/*  Die Funktion SchwLosenErsteRunde() übernimmt die Auslosung der ersten
    Runde eines Schweizer-System-Turniers.
*/
static void SchwLosenErsteRunde(void)

  { struct Teilnehmer *t, *thelp;
    int NumSpiele, NumMinWertung;
    int i, j;
    int flag;

    MakeRangliste(&NumMinWertung);

    /*	Ein Freilos vergeben, falls nötig.
    */
    NumSpiele = NumTeilnehmer/2;
    if (NumTeilnehmer % 2  !=  0)
      { if (NumMinWertung < 5)
	  { NumMinWertung = MIN(5, NumSpiele);
	  }
	j = RangeRand(NumMinWertung)+1;
	for (t = RanglistenErster, i = 0;  i < NumTeilnehmer-j;
	     t = t->RangNext, i++)
	  {
	  }
	t->GFlags = GMFLAGSF_FREILOS|GMFLAGSF_KAMPFLOS;
      }

    /*	Die restlichen Spiele verteilen. t zeigt auf die obere und thelp auf
	die untere Hälfte der Rangliste.
    */
    thelp = RanglistenErster;	/*  thelp initialisieren    */
    for (i = 0;  i < NumSpiele; thelp = thelp->RangNext)
      { if ((thelp->GFlags & GMFLAGSF_FREILOS)  ==  0)
	  { i++;
	  }
      }
    t = RanglistenErster;	/*  t initialisieren	    */
    flag = RangeRand(2);        /*  flag initialisieren     */

    for (i = 0;  i < NumSpiele;  i++)
      { /*  Dafür sorgen, daß t und thelp nicht auf Freilose zeigen.
	*/
	if ((t->GFlags & GMFLAGSF_FREILOS)  !=  0)
	  { t = t->RangNext;
	  }
	if ((thelp->GFlags & GMFLAGSF_FREILOS)  !=  0)
	  { thelp = thelp->RangNext;
	  }

	t->Gegner = thelp;
	thelp->Gegner = t;
	t->BrettNr = thelp->BrettNr = i;
	if (flag != 0)
	  { t->GFlags = GMFLAGSF_WEISS;
	  }
	else
	  { thelp->GFlags = GMFLAGSF_WEISS;
	  }
	flag = 1-flag;
	t = t->RangNext;
	thelp = thelp->RangNext;
      }
  }



/*  Die Funktion PartieMoeglich() prüft, ob zwei Spieler gegeneinander
    spielen können.
*/
static char *PartieMoeglichArray;
static int PartieMoeglich(struct Teilnehmer *t1, struct Teilnehmer *t2)

  { struct Game *g;
    char *cptr, c;

    cptr = &PartieMoeglichArray[t1->Nr*(NumTeilnehmer+1)+t2->Nr];
    if((c = *cptr) == 0)
      { if (t1 == t2  ||
	    t1->WieOftWeissZuletzt*t2->WieOftWeissZuletzt  ==  4)
	  { c = -1;
	  }
	else
	  { c = 1;
	    for (g = t1->First_Game;  g != NULL;  g = g->Next)
	      { if (g->Gegner == t2)
		  { c = -1;
		    break;
		  }
	      }
	  }
	*cptr = c;
      }
    return((c > 0)  ?  TRUE  :  FALSE);
  }



/*  Die Funktion LosePartie() versucht eine Partie in der aktuellen
    Gruppe zu erzeugen.

    Mögliches Ergebnis: 0  = Keine Auslosung möglich
			1  = Erfolg, alles beenden
			-1 = Bei gerader Teilnehmerzahl konnten die restlichen
			     Gruppen nicht ausgelost werden. Dann muß sofort
			     ein Spieler in die nächste Gruppe geschoben
			     werden.
*/
static int LosePartie(struct Gruppe *g, struct Teilnehmer *t, int BrettNr,
		      int MayChangeColors, int MayGoUpperHalf, int MayGoDown)

  { struct Teilnehmer *tg, *thelp, *LowerHalf;
    int LoseGruppe(struct Gruppe *, int BrettNr);
    int result;
    int IsLowerHalf, i;

    switch (g->NumMembers-g->NumAllocMembers)
      { case 1:     /*	Restlichen Teilnehmer in nächste Gruppe schieben
			oder Freilos zuteilen
		    */
	  for (t = g->First;  t->Gegner != NULL;  t = t->LT_Succ)
	    {  /*  Freien Spieler suchen
	       */
	    }

	  if (g->Next == NULL)  /*  Letzte Gruppe, also Freilos */
	    { if (t->Flags & TNFLAGSF_HATTEFREILOS)
		{ return(0);
		}
	      t->GFlags |= GMFLAGSF_FREILOS|GMFLAGSF_KAMPFLOS;
	      return(1);
	    }

	  if (t->Flags & TNFLAGSF_NICHTRUNTER)
	    { return (FALSE);
	    }
	  thelp = t->LT_Pred;
	  MyRemove(g, t);
	  MyEnqueue(g->Next, t);
	  if ((result = LoseGruppe(g->Next, BrettNr))  ==  FALSE)
	    /*	Merken, daß keine Auslosung möglich, wenn dieser Spieler
		in die nächste Gruppe geschoben wird.
	    */
	    { t->Flags |= TNFLAGSF_NICHTRUNTER;
	    }
	  MyRemove(g->Next, t);
	  MyInsert(g, t, thelp);
	  return(result);

	case 0:     /*	Gruppe fertig, nächste Gruppe;	ist LoseGruppe()
			jetzt nicht erfolgreich, dann muß die Gruppe
			verändert werden
		    */
	  return((LoseGruppe(g->Next, BrettNr) == 0)  ?  -1  :  1);

	default:    /*	Es müssen noch Spiele in dieser Gruppe gelost
			werden.
		    */

	  /*  Zunächst freien Spieler suchen.
	  */
	  while (t->Gegner != NULL)
	    { t = t->LT_Succ;
	    }

	  /*  Wenn der aktuelle Spieler aus der unteren Hälfte ist, dann
	      darf die untere Hälfte nur ab ihm und die obere Hälfte gar
	      nicht abgesucht werden.
	  */
	  IsLowerHalf = TRUE;
	  LowerHalf = g->First;
	  i = (g->NumMembers/2);
	  do
	    { if (LowerHalf == t)
		{ IsLowerHalf = FALSE;
		}
	      LowerHalf = LowerHalf->LT_Succ;
	    }
	  while(--i);

	  /*  Nach Gegner in der unteren Gruppenhälfte mit Farbwechsel
	      suchen.
	  */
	  for (tg = IsLowerHalf ? t->LT_Succ : LowerHalf;
	       tg != NULL;  tg = tg->LT_Succ)
	    { if (tg->Gegner == NULL  &&
		  t->WieOftWeissZuletzt*tg->WieOftWeissZuletzt <= 0  &&
		   PartieMoeglich(t, tg))
		{ t->Gegner = tg;
		  tg->Gegner = t;
		  t->BrettNr = tg->BrettNr = BrettNr;
		  g->NumAllocMembers += 2;
		  if ((result = LosePartie(g, t->LT_Succ, BrettNr+1,
					   MayChangeColors, MayGoUpperHalf,
					   MayGoDown))	!=  0)
		    { return(result);
		    }
		  g->NumAllocMembers -= 2;
		  t->Gegner = tg->Gegner = NULL;
		}
	    }

	  /*  Dann nach Gegner in der unteren Gruppenhälfte ohne Farbwechsel
	      suchen.
	  */
	  if (MayChangeColors != 0)
	    { for (tg = IsLowerHalf ? t->LT_Succ : LowerHalf;
		    tg != NULL;  tg = tg->LT_Succ)
		{ if (tg->Gegner == NULL  &&
			t->WieOftWeissZuletzt*tg->WieOftWeissZuletzt > 0  &&
			PartieMoeglich(t, tg))
		    { t->Gegner = tg;
			tg->Gegner = t;
			t->BrettNr = tg->BrettNr = BrettNr;
			g->NumAllocMembers += 2;
			if ((result = LosePartie(g, t->LT_Succ, BrettNr+1,
						MayChangeColors-1,
						MayGoUpperHalf,
						MayGoDown))  !=  0)
			{ return(result);
			}
			g->NumAllocMembers -= 2;
			t->Gegner = tg->Gegner = NULL;
		    }
		}
	    }

	  /*  Dann nach Gegner in der oberen Gruppenhälfte mit Farbwechsel
	      suchen.
	  */
	  if (MayGoUpperHalf != 0)
	    { for (tg = IsLowerHalf ? NULL : LowerHalf->LT_Pred;
		    tg != NULL	&&  tg != t;  tg = tg->LT_Pred)
		{ if(tg->Gegner == NULL  &&
			t->WieOftWeissZuletzt*tg->WieOftWeissZuletzt <= 0  &&
			PartieMoeglich(t, tg))
		    { t->Gegner = tg;
			tg->Gegner = t;
			t->BrettNr = tg->BrettNr = BrettNr;
			g->NumAllocMembers += 2;
			if ((result = LosePartie(g, t->LT_Succ, BrettNr+1,
						MayChangeColors,
						MayGoUpperHalf-1,
						MayGoDown))  !=  0)
			{ return(result);
			}
			g->NumAllocMembers -= 2;
			t->Gegner = tg->Gegner = NULL;
		    }
		}
	    }

	  /*  Schließlich nach Gegner in der oberen Gruppe und ohne Farb-
	      wechsel suchen. Das wird doch hoffentlich wirklich nicht
	      nötig sein!
	  */
	  if (MayChangeColors != 0  &&  MayGoUpperHalf != 0)
	    { for (tg = IsLowerHalf ? NULL : LowerHalf->LT_Pred;
		    tg != NULL	&&  tg != t;  tg = tg->LT_Pred)
		{ if(tg->Gegner == NULL  &&
			t->WieOftWeissZuletzt*tg->WieOftWeissZuletzt > 0  &&
			PartieMoeglich(t, tg))
		    { t->Gegner = tg;
			tg->Gegner = t;
			t->BrettNr = tg->BrettNr = BrettNr;
			g->NumAllocMembers += 2;
			if ((result = LosePartie(g, t->LT_Succ, BrettNr+1,
						MayChangeColors-1,
						MayGoUpperHalf-1,
						MayGoDown))  !=  0)
			{ return(result);
			}
			g->NumAllocMembers -= 2;
			t->Gegner = tg->Gegner = NULL;
		    }
		}
	    }

	  /*  Schließlich gibt es noch die Möglichkeit, diesen Spieler
	      auszulassen.
	  */
	  if (MayGoDown)
	    { return(LosePartie(g, t->LT_Succ, BrettNr,
				MayChangeColors, MayGoUpperHalf, 0));
	    }
      }
    return(0);
  }



/*  Die Funktion LoseGruppe() übernimmt die Auslosung einer Gruppe punkt-
    gleicher Teilnehmer gemäß dem Schweizer System.
    Mögliche Ergebnisse: 0 = Die Gruppe konnte nicht gelost werden
			 1 = Die Gruppe konnte gelost werden. Die Auslosung
			     wird erfolgreich beendet.

    Zum Algorithmus der Auslosung: Dieser entspricht (soweit möglich) den
    Richtlinien aus dem "Turnierleiterhandbuch des Deutschen Schachbundes".
    Dazu ist allerdings zu sagen, daß diese Richtlinien nicht klar und un-
    genügend sind. Es wird beispielsweise nicht definiert, was zu geschehen
    hat, wenn mehrere punktgleiche Spieler gemeinsam an der Spitze liegen,
    aber alle schon gegeneinander gespielt haben. (Sicher kein allzu
    abwegiger Fall.)

    Vor der Auslosung werden die Teilnehmer zunächst in Gruppen punktgleicher
    Teilnehmer eingeordnet. Mit Hilfe dieser Gruppen wird eine neue Rangliste
    gebildet. (Innerhalb einer Gruppe entscheidet die jeweils letzte
    Rangliste, so daß Spieler mit hohen Wertungszahlen bzw. großem Erfolg
    am Turnieranfang höher bewertet werden.) Es wird nun für jede Gruppe die
    Funktion LoseGruppe() aufgerufen, die versucht, die Spieler die Spieler
    der jeweiligen Gruppe durch Aufruf von LosePartie() gegeneinander zu
    losen versucht. Ist LoseGruppe() erfolgreich, so wird die Funktion für
    die nächste Gruppe aufgerufen.

    Prinzipielle Vorgehensweise innerhalb einer Gruppe:
    Die Spieler werden zunächst in eine obere und eine untere Hälfte geteilt.
    (Bei ungerader Spielerzahl enthält die untere Hälfte einen Spieler mehr.)
    Die beiden Hälften werden wiederum danach aufgeteilt, ob sie als nächstes
    Weiß oder Schwarz haben sollten. Beispiel (die linken Spieler sollten als
    nächstes Weiß haben):

	Obere Hälfte:	1   3
			2
	Untere Hälfte:	4   5
			7   6

    Nun wird zunächst mit LosePartie() ein Gegner für den Spieler 1 gesucht.
    Man versucht es zunächst mit Spieler 5, dann mit Spieler 6. Gelingt das,
    dann wird analog ein Gegner für Spieler 2 gesucht. Gelingt auch dies,
    so ein Gegner für Spieler 3. Bei drei ausgelosten Partien ist die Gruppe
    erfolgreich gelost und der übriggebliebene Spieler wird in die nächste
    Gruppe geschoben.

    Gelingt die Paarung so nicht, dann wird erlaubt, daß ein Spieler einen
    Gegner von der gleichen Seite erhält (etwa 1-4). Gelingt die Paarung
    wiederum nicht, dann dürfen zwei Spieler farbgleiche Gegner erhalten usw.
    Hilft auch dies nicht, so wird erlaubt, daß ein Spieler aus der oberen
    Hälfte einen Gegner aus der oberen Hälfte bekommt, dann evtl. zwei Spieler
    usw. Schließlich gibt es noch die Möglichkeit, daß ein Spieler aus der
    oberen Hälfte in die nächste Gruppe geschoben wird. Dies wird über die
    Variablen MayChangeColor, MayGoUpperHalf und MayGoDown geregelt.

    Eine Partie gilt als möglich, wenn
	a) Die Gegner noch nicht gegeneinander gespielt haben
	b) Keiner der beiden Gegner zum drittenmal hintereinander dieselbe
	   Farbe haben müßte (dagegen ist es im Turnierleiterhandbuch
	   ausdrücklich erlaubt, daß etwa ein Spieler nach 5 Runden viermal
	   Schwarz hatte!)
	c) unter der Voraussetzung dieser Partie die restlichen noch nicht
	   gepaarten Spieler gelost werden können
*/
static int LoseGruppe(struct Gruppe *g, int BrettNr)

  { struct Teilnehmer *t;
    int result;
    int MayChangeColors, MayGoUpperHalf, MayGoDown;

#ifdef DEBUG	/*  Gruppenliste ausgeben   */
    { extern struct Gruppe *ErsteGruppe;
      struct Gruppe *myg;
      struct Teilnehmer *myt;
      char *myptr;

      for (myg = ErsteGruppe;  myg != NULL;  myg = myg->Next)
	{ printf("(");
	  for(myt = myg->First;  myt != NULL;  myt = myt->LT_Succ)
	    { if (myt != myg->First)
		{ printf(",");
		}
	      myptr = index(myt->Name, '(')+1;
	      while (*myptr != ')')
		{ printf("%c", *(myptr++));
		}
	    }
	  printf(")");
	}
	printf("\n");
    }
#endif

    if (g == NULL)
      { return(1);
      }

    while(g->NumMembers == 0)
      { g = g->Next;
	if (g == NULL)
	  { return(1);
	  }
      }

    for(t = g->First;  t != NULL;  t = t->LT_Succ)
      { t->Flags &= ~TNFLAGSF_NICHTRUNTER;
      }

    MayGoDown = 0;
    do
      { MayGoUpperHalf = 0;
	do
	  { MayChangeColors = 0;
	    do
	      { switch (LosePartie(g, g->First, BrettNr,
				   MayChangeColors, MayGoUpperHalf,
				   MayGoDown))
		  { case 1:
		    return(1);
		    case -1:
		    for (t = g->First;  t != NULL;  t = t->LT_Succ)
			{ t->Gegner = NULL;
			}
		    g->NumAllocMembers = 0;
		    goto changegroup;
		  }
	      }
	    while (++MayChangeColors <= g->NumMembers/2);
	  }
	while (++MayGoUpperHalf <= g->NumMembers/4);
      }
    while (++MayGoDown <= g->NumMembers % 2);

changegroup:
    /*	Da es nicht möglich ist, die Teilnehmer der Gruppe gegeneinander
	zu losen, muß einer in die nächste Gruppe geschoben werden.
	Falls die Gruppe nur einen Teilnehmer enthält, dann wurde das schon
	von LosePartie versucht.
    */
    if (g->Next != NULL  &&  g->NumMembers != 1)
      { t = g->Last;
	MyRemove(g, t);
	MyEnqueue(g->Next, t);
	result = LoseGruppe(g, BrettNr);
	if (result)
	  { return(TRUE);
	  }
	MyRemove(g->Next, t);
	MyAddTail(g, t);
      }

    /*	Es hilft alles nichts. Da bleibt nur noch das Eingeständnis der
	Aufgabe.
    */
    return(FALSE);
  }




/*  Die Funktion SchweizerSystem übernimmmt die Auslosung von Schweizer-
    System-Turnieren ab Runde 2.
*/
#ifdef DEBUG
struct Gruppe *ErsteGruppe;
#endif
static int SchweizerSystem(void)

  { struct Teilnehmer *t, *thelp, **tptr;
    struct Remember *RKey = NULL;
    struct Gruppe *g, **gptr;
    short gpunkte;
    int i;
#ifndef DEBUG
    struct Gruppe *ErsteGruppe = NULL;
#else
    ErsteGruppe = NULL;
#endif

    /*	Zunächst die neue Rangliste erzeugen.
    */
    t = RanglistenErster;
    RanglistenErster = NULL;
    while (t != NULL)
      { for (tptr = &RanglistenErster;  *tptr != NULL;
	     tptr = &((*tptr)->RangNext))
	  { if (t->Punkte > (*tptr)->Punkte)
	      { break;
	      }
	  }
	thelp = t->RangNext;
	t->RangNext = *tptr;
	*tptr = t;
	t = thelp;
      }


    /*	Als nächstes die Gruppen punktgleicher Teilnehmer erzeugen.
    */
    gpunkte = -1;   /*	Initialisierung von g und gpunkte in der folgenden
			Schleife erzwingen.
		    */
    gptr = &ErsteGruppe;
    i = 0;
    for (t = RanglistenErster;  t != NULL;  t = t->RangNext)
      { if (t->Flags & TNFLAGSF_GELOESCHT)
	  { continue;
	  }
	if (gpunkte != t->Punkte)   /*  Neue Gruppe erzeugen    */
	  { if ((g = (struct Gruppe *)
		     AllocRemember(&RKey, sizeof(*g), MEMF_ANY|MEMF_CLEAR))
		   ==  NULL)
	      { FreeRemember (&RKey, TRUE);
		MemoryError();
		return(FALSE);
	      }
	    *gptr = g;
	    gptr = &(g->Next);
	    gpunkte = t->Punkte;
	  }

	MyAddTail(g, t);
	t->Nr = i++;
      }

    if ((PartieMoeglichArray = (char *)
		    AllocRemember(&RKey, (NumTeilnehmer+1)*(NumTeilnehmer+1),
				  MEMF_ANY|MEMF_CLEAR))  ==  NULL)
      { FreeRemember(&RKey, TRUE);
	MemoryError();
	return(FALSE);
      }

    if (!LoseGruppe(ErsteGruppe, 0))
      { ShowError((char *) GetChaosString(MSG_NO_PAIRING));
	return(FALSE);
      }

    for (t = RanglistenErster;  t != NULL;  t = t->RangNext)
      { if (t->Flags & TNFLAGSF_GELOESCHT)
	  { continue;
	  }
	if (t->Gegner == NULL)
	  { t->GFlags = GMFLAGSF_FREILOS|GMFLAGSF_KAMPFLOS;
	  }
	else
	  { /*	Farben auswählen
	    */
	    thelp = t->Gegner;
	    if (t->WieOftWeissZuletzt < thelp->WieOftWeissZuletzt  ||
		(t->WieOftWeissZuletzt == thelp->WieOftWeissZuletzt  &&
		 (t->WieOftWeiss < thelp->WieOftWeiss  ||
		  (t->WieOftWeiss == thelp->WieOftWeiss  &&
		   t->Nr > thelp->Nr))))
	      { t->GFlags = GMFLAGSF_WEISS;
	      }
	  }
      }
    return(TRUE);
  }



/*  Die Funktion VollLosen übernimmt die Auslosung eines vollrundigen
    Turniers. Hier werden (im Unterschied zum Schweizer System) alle
    Runden auf einmal ausgelost.
*/
static int VollLosen(int mode)

  { struct Teilnehmer *t, **ttab, *tg;
    struct Gruppe g;
    int i, j, k, l;
    int NumSpiele;
    int numfehlendespiele = 0;
    short flag, BrettNr;

    if ((ttab = (struct Teilnehmer **)
		AllocMem(sizeof(*ttab)*NumTeilnehmer,MEMF_ANY|MEMF_CLEAR))
	      ==  NULL)
      { MemoryError();
	return(FALSE);
      }

    /*	Zunächst jedem Teilnehmer eine zufällige Nummer zuteilen.
    */
    g.First = g.Last = NULL;
    g.NumMembers = 0;
    for (t = (struct Teilnehmer *) Teilnehmerliste.lh_Head;
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { MyAddTail(&g, t);
      }
    while (g.NumMembers > 0)
      { i = RangeRand(g.NumMembers);
	for (t = g.First;  i > 0;  i--, t = t->LT_Succ)
	  {
	  }
	t->Nr = g.NumMembers;
	MyRemove(&g, t);
	ttab[g.NumMembers] = t;
      }
    NumSpiele = (NumTeilnehmer+1)/2;

    if ((mode & TNMODEF_RUTSCHSYSTEM)  ==  0)
      { /*  Falls n=NumTeilnehmer bzw. n=NumTeilnehmer+1 (bei ungerader
	    Teilnehmerzahl), dann finden in der ersten Runde folgende Spiele
	    statt: 1 gegen n, 2 gegen n-1, 3 gegen n-2, 4 gegen n-3 usw.
	    Bei ungerader Teilnehmerzahl steht n als Gegner für spielfrei.
	*/
	for (i = 0;  i < NumSpiele; i++)
	  { t = ttab[i];
	    if(i == 0  &&  ((NumTeilnehmer%2) != 0))    /*  Spielfrei   */
	      { t->Gegner = NULL;
		t->BrettNr = i;
		t->GFlags = GMFLAGSF_FREILOS|GMFLAGSF_KAMPFLOS;
	      }
	    else
	      { tg = ttab[NumSpiele*2-i-1];

		t->Gegner = tg;
		tg->Gegner = t;
		t->BrettNr = tg->BrettNr = i+1;
		t->GFlags = GMFLAGSF_WEISS;
		tg->GFlags = 0;
		numfehlendespiele++;
	      }
	  }
	if (!NewGames(0))
	  { goto Error;
	  }

	/*  Die Spiele der folgenden Runden werden jeweils nach einem
	    einfachen Algorithmus abgeleitet. (vgl. Ernst Schubart, Helmut
	    Nöttger: "Turnierleiterhandbuch des Deutschen Schachbundes",
	    S. 64)

	    - In den ungeraden Runden spielen die Spieler mit den Nummern
	      1,2,3 usw. gegen den Spieler mit der höchsten Nummer (bzw.
	      haben spielfrei). In den geraden Runden sind dies die Spieler
	      mit den Nummern NumSpiele+1, NumSpiele+2, NumSpiele+3 usw.
	    - Alle anderen Spieler spielen jeweils gegen den Spieler, dessen
	      Nummer um eins höher ist, als die ihres Gegners aus der vorigen
	      Runde. Falls dabei der Spieler mit der höchsten Nummer ihr
	      Gegner wäre (bei gerader Teilnehmerzahl) bzw. die Nummer zu
	      hoch wird (bei ungerader Teilnehmerzahl), so wird bei Nummer 1
	      wieder begonnen.

	    - Bei gerader Teilnehmerzahl haben die Spieler 1,2,...,NumSpiele
	      Weiß, wenn sie gegen den Spieler mit der höchsten Nummer
	      spielen. Die anderen Spieler haben in diesem Fall Schwarz.
	    - Bei allen anderen Spielen hat jeweils der Spieler mit der
	      niedrigeren Nummer Weiß, falls die Summer der Nummern ungerade
	      ist, andernfalls der Spieler mit der höheren Nummer.
	*/
	for (j = 1;  j < NumSpiele*2-1;  j++)
	  { /*	Zunächst Gegner für Spieler mit der höchsten Nummer bzw.
		spielfreien Teilnehmer bestimmen.
	    */
	    k = (((j%2) == 0) ? 0 : NumSpiele) + j/2 + 1;
	    t = ttab[k-1];
	    if ((NumTeilnehmer%2) == 0) /*  Kein Freilos    */
	      { tg = ttab[NumTeilnehmer-1];
		t->BrettNr = tg->BrettNr = BrettNr = 0;
		t->GFlags = (t->Nr <= NumSpiele) ? GMFLAGSF_WEISS : 0;
		tg->GFlags = GMFLAGSF_WEISS - t->GFlags;
		tg->Gegner = t;
		numfehlendespiele++;
	      }
	    else
	      { tg = NULL;
		t->BrettNr = BrettNr = -1;
		t->GFlags = GMFLAGSF_FREILOS|GMFLAGSF_KAMPFLOS;
	      }
	    t->Gegner = tg;

	    /*	Dann die restlichen Spiele in Schleife festlegen.
	    */
	    for (i = 1;  i < NumSpiele;  i++)
	      { if (++k == NumSpiele*2)
		  { k = 1;
		  }
		t = ttab[k-1];
		if ((tg = SpielAdresse(t, j)->Gegner) == NULL  ||
		    tg->Nr == NumSpiele*2)
		  { l = t->Nr;
		  }
		else
		  { l = tg->Nr;
		  }
		if (++l == NumSpiele*2)
		  { l = 1;
		  }
		tg = ttab[l-1];
		flag = (((t->Nr+tg->Nr) % 2) != 0) ? GMFLAGSF_WEISS : 0;
		if (t->Nr > tg->Nr)
		  { flag = GMFLAGSF_WEISS-flag;
		  }
		t->GFlags = flag;
		tg->GFlags = GMFLAGSF_WEISS-flag;
		t->BrettNr = tg->BrettNr = ++BrettNr;
		t->Gegner = tg;
		tg->Gegner = t;
		numfehlendespiele++;
	      }

	    if (!NewGames(0))
	      { goto Error;
	      }
	  }
      }
    else
      { /*  Der Algorithmus des Rutschsystems ist sehr einfach nachzuvoll-
	    ziehen. Die Bretter werden mit jeweils wechselnden Farben auf-
	    gestellt und alle Spieler setzen sich für die erste Runde.
	    Nach jeder Runde rutschen alle Spieler einen Platz im Uhrzeiger-
	    sinn. Bei gerader Teilnehmerzahl bleibt der Spieler mit der
	    höchsten Nummer sitzen und dreht sein Brett nach jeder Runde.
	    Bei ungerader Teilnehmerzahl ist ein Platz für den spielfreien
	    Spieler reserviert.
	*/
	int lastflag;

	for (i = 0;  i < NumSpiele*2-1;  i++)
	  { for (j = 0, flag = GMFLAGSF_WEISS;  j < NumSpiele; j++)
	      { t = ttab[j];
		if (j+NumSpiele < NumTeilnehmer)
		  { tg = ttab[j+NumSpiele];
		    t->GFlags = flag;
		    flag = GMFLAGSF_WEISS-flag;
		    tg->GFlags = flag;
		    tg->Gegner = t;
		    tg->BrettNr = j+1;
		  }
		else
		  { tg = NULL;
		  }
		t->Gegner = tg;
		t->BrettNr = j+1;
	      }
	    if (i == 0)
	      { lastflag = flag;
	      }
	    else if (NumTeilnehmer == NumSpiele*2)
	      { t = ttab[NumSpiele-1];
		tg = ttab[NumSpiele*2-1];
		t->GFlags = lastflag;
		lastflag = GMFLAGSF_WEISS-lastflag;
		tg->GFlags = lastflag;
	      }
	    if (!NewGames(0))
	      { goto Error;
	      }

	    /*	Alle Teilnehmer außer dem letzten (evtl. virtuellen) rotieren.
	    */
	    t = ttab[0];
	    for (j = 0;  j < NumSpiele-1;  j++)
	      { ttab[j] = ttab[j+1];
	      }
	    ttab[NumSpiele-1] = ttab[NumSpiele*2-2];
	    for (j = NumSpiele*2-3;  j >= NumSpiele;  j--)
	      { ttab[j+1] = ttab[j];
	      }
	    ttab[NumSpiele] = t;
	  }
      }

    FreeMem(ttab, sizeof(*ttab)*NumTeilnehmer);
    NumFehlendeSpiele = numfehlendespiele;
    return(TRUE);

Error:
    for (i = NumRunden;  i >= 0;  i--)
      { FreeGames(i);
      }
    FreeMem(ttab, sizeof(*ttab)*NumTeilnehmer);
    NumRunden = 0;
    return(FALSE);
  }



/*  Die folgende Funktion wird bei von main() aus aufgerufen.
    aus
*/
void LoseRunde(int mode)

  { struct Teilnehmer *t, *ranglistenerster;
    char *name;
    char oldTurnierfilename[TRNFILENAME_LEN+1];
    char endung[20];
    int len, endlen, oldNumRunden = NumRunden;

    /*	Zunächst die Ranglistenzeiger kopieren. Falls ein Fehler auftritt,
	können sie dann wiederhergestellt werden.
	Dabei werden gleich die Felder Gegner und GFlags der Teilnehmer-
	Struktur initialisiert.
    */
    ranglistenerster = RanglistenErster;
    for (t = (struct Teilnehmer *) Teilnehmerliste.lh_Head;
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { t->Hilfszeiger = t->RangNext;
	t->Gegner = NULL;
	t->GFlags = 0;
      }

    if (mode & TNMODEF_SCHWEIZER_SYSTEM)
      { if (NumRunden == 0)
	  { SchwLosenErsteRunde();
	  }
	else
	  { if (!SchweizerSystem())
	      { goto Error;
	      }
	  }
	NewGames(2);
      }
    else
      { if (!VollLosen(mode))
	  { goto Error;
	  }
      }
    TurnierModus |= mode;
    IsSaved = FALSE;

    /*	Biete dem Benutzer das Abspeichern an. Falls beim Dateinamen bislang
	die Konvention "name.rundennummer.cdat" eingehalten wurde, dann soll
	dies auch beibehalten werden.
    */
    strcpy(oldTurnierfilename, Turnierfilename);
    sprintf((STRPTR) endung, (STRPTR) ".%d.cdat", oldNumRunden);
    endlen = strlen(endung);
    len = strlen(Turnierfilename);
    if (len >= endlen  &&
	Stricmp((STRPTR) Turnierfilename+(len-endlen), (STRPTR) endung)
		== 0)
      { sprintf((STRPTR) Turnierfilename+(len-endlen), (STRPTR) ".%d.cdat",
		NumRunden);
      }
    name = FileRequest(TRUE, NULL, NULL);
    strcpy(Turnierfilename, oldTurnierfilename);
    if (name  !=  NULL  &&  *name != '\0')
      { SpeichereTurnier(name);
      }
    return;

Error:
    /*	Wegen des Fehlers die alte Rangliste wiederherstellen.
    */
    RanglistenErster = ranglistenerster;
    for (t = (struct Teilnehmer *) Teilnehmerliste.lh_Head;
	 t->Tn_Node.ln_Succ != NULL;
	 t = (struct Teilnehmer *) t->Tn_Node.ln_Succ)
      { t->RangNext = t->Hilfszeiger;
      }
  }
