
function SudokuSpiel( nurEindeutige, maxLoesungen, schwierigkeit ) { // Klasse
	// Eigenschaften - Private
	var muster = new Array( 
		"080080080\n" +
		"880000088\n" +
		"008080800\n" +
		"000808000\n" +
		"808080808\n" +
		"000808000\n" +
		"008080800\n" +
		"880000088\n" +
		"080080080",

		"080000080\n" +
		"888000888\n" +
		"080080080\n" +
		"000080000\n" +
		"008888800\n" +
		"000080000\n" +
		"080080080\n" +
		"888000888\n" +
		"080000080",

		"080080080\n" +
		"800808008\n" +
		"008000800\n" +
		"080080080\n" +
		"800808008\n" +
		"080080080\n" +
		"008000800\n" +
		"800808008\n" +
		"080080080",

		"000000000\n" +
		"088888880\n" +
		"080000080\n" +
		"080808080\n" +
		"080000080\n" +
		"080808080\n" +
		"080000080\n" +
		"088888880\n" +
		"000000000",

		"880888088\n" +
		"000000000\n" +
		"880808088\n" +
		"000000000\n" +
		"808080808\n" +
		"000000000\n" +
		"808080808\n" +
		"000000000\n" +
		"080808080",

		"800080008\n" +
		"080000080\n" +
		"008080800\n" +
		"000808000\n" +
		"808080808\n" +
		"000808000\n" +
		"008080800\n" +
		"080000080\n" +
		"800080008",

		"080000080\n" +
		"888000888\n" +
		"080080080\n" +
		"000000000\n" +
		"008080800\n" +
		"000000000\n" +
		"080080080\n" +
		"888000888\n" +
		"080000080",

		"008000800\n" +
		"080808080\n" +
		"800080008\n" +
		"080000080\n" +
		"008000800\n" +
		"080000080\n" +
		"800080008\n" +
		"080808080\n" +
		"008000800",

		"800000008\n" +
		"008888800\n" +
		"080000080\n" +
		"080000080\n" +
		"080000080\n" +
		"080000080\n" +
		"080000080\n" +
		"008888800\n" +
		"800000008",

		"800080008\n" +
		"000808000\n" +
		"008000800\n" +
		"080080080\n" +
		"800808008\n" +
		"080080080\n" +
		"008000800\n" +
		"000808000\n" +
		"800080008"
	); // ende muster
	var musterIndex = Math.floor( Math.random( ) * (muster.length - 1) + 0.5 );

	// Eigenschaften - oeffentlich
	this.SchwierigkeitsgradNamen = new Array( "leicht", "mittel", "schwer", "sehr schwer" );
	this.Protokoll = "";
	this.Loesung = null;
	this.Vorgabe = null;
	this.Spiel = null;
	this.MaxVerzweigungen = 100;
	this.MaxLoesungen = 20;
	this.ZuVieleVerzweigungen = false;
	this.ZuVieleLoesungen = false;
	this.EindeutigeLoesung = false;
	this.Unloesbar = false;
	this.Loesungen = null;
	this.GroesstesUebel = 0;
	this.GroesstesUebelZeile = -1;
	this.GroesstesUebelSpalte = -1;
	this.KleinstesUebel = 9*9;
	this.KleinstesUebelZeile = -1;
	this.KleinstesUebelSpalte = -1;
	this.Sackgassen = 0;
	this.SackgassenTiefeMaximal = 0;
	this.MoeglichkeitenMinimal = 9*9;
	this.MoeglichkeitenDurchschnittlich = 0;
	this.Schwierigkeit = -1; // unbekannt

	// Methoden - oeffentlich
	
	this.GetSchwierigkeitText = function ( ) {
		if( 0 <= this.Schwierigkeit && this.Schwierigkeit < this.SchwierigkeitsgradNamen.length ) {
			return this.SchwierigkeitsgradNamen[this.Schwierigkeit];
		} else {
			return "unbekannt";
		}
	}
	this.FeldName = function ( zeilenIndex, spaltenIndex ) {
		return String.fromCharCode( 65 + spaltenIndex ) + String( zeilenIndex + 1 );
	}
	
	this.Leeren = function( neuMusterIndex ) {
		musterIndex = -1;
		this.Loesung = null;
		this.Vorgabe = null;
		this.Spiel = new SudokuFeld( );
		this.Loesungen = null;
		this.Sackgassen = 0;
		this.SackgassenTiefeMaximal = 0;
		this.MoeglichkeitenMinimal = 9*9;
		this.MoeglichkeitenDurchschnittlich = 0;
		this.Schwierigkeit = -1; // unbekannt
	}
	this.SetMusterIndex = function( neuMusterIndex ) {
		try {
			if( neuMusterIndex != null ) {
				if( 0 > neuMusterIndex || neuMusterIndex >= muster.length ) neuMusterIndex = 0;
				musterIndex = neuMusterIndex;
				this.Vorgabe = new SudokuFeld( muster[musterIndex] );
			}
			if( this.Loesung ) {
				for( var z = 0; z < 9; z++ ) {
					for( var s = 0; s < 9; s++ ) {
						var v = this.Vorgabe.Get( z, s );
						if( v != "0" ) this.Vorgabe.Set( z, s, this.Loesung.Get( z, s ) );
					}
				}
				this.Spiel = new SudokuFeld( this.Vorgabe );
			}
		} catch( exception ) { alert( "SetMusterIndex: Fehler!\n" + exception ); }
		return this.Spiel;
	}
	this.GetMusterIndex = function( ) {
		return musterIndex;
	}
	this.NeuesSpielAltV3 = function( maxSchwierigkeit, neuMusterIndex ) {
		if( neuMusterIndex == null ) musterIndex = Math.floor( Math.random( ) * (muster.length - 1) + 0.5 );
		else musterIndex = neuMusterIndex;
		if( ! this.Loesung ) this.Loesung = new SudokuFeld( );
		this.Loesung.Permutation( );
		
		var aktMusterListe = new Array( muster.length );
		var maxVersuche = 100;
		do {
			var aktSpiel = aktMusterListe[musterIndex];
			if( ! aktSpiel ) aktSpiel = this.SetMusterIndex( musterIndex );
			this.Loesen( 1, 20, false );
			if( ! this.EindeutigeLoesung || this.Schwierigkeit > maxSchwierigkeit ) {
				aktMusterListe[musterIndex] = this.Vereinfachen( );
				musterIndex++;
				if( musterIndex >= muster.length ) musterIndex = 0;
			} else {
				break; // Fertig
			}
		} while( maxVersuche-- > 0 );
		this.Vorgabe = new SudokuFeld( this.Spiel );
		return this.Spiel;	
	}
	this.NeuesSpiel = function( nurEindeutige, maxLoesungen, maxSchwierigkeit ) {
		try {
			var wenigsteUneindeutigkeiten = 9*9;
			var bestesSpiel = null;
			var besterMusterIndex = -1;
			var wenigsteUneindeutigkeitenListe = null;
			if( ! this.Loesung ) this.Loesung = new SudokuFeld( );
			this.Loesung.Permutation( );
			musterIndex = Math.floor( Math.random( ) * (muster.length - 1) + 0.5 );
	//alert( "neues Spiel nurEindeutige = " + nurEindeutige + "   maxLoesungen = " + maxLoesungen + "   maxSchwierigkeit = " + maxSchwierigkeit );
			for( var m = 0; m < muster.length; m++ ) {
					var aktSpiel = null;
					var vorgabeUneindeutigkeiten = 0;
					var aktUneindeutigkeitenListe = new Array( );
					if( musterIndex >= muster.length ) musterIndex = 0;
					try {
						aktSpiel = this.SetMusterIndex( musterIndex );
					} catch( exception ) { 
						throw( "NeuesSpiel: Fehler in SetMusterIndex( )!\n" + exception ); 
					}
					vorgabeUneindeutigkeiten = this.Loesung.VorgabeUneindeutigkeiten( aktSpiel, aktUneindeutigkeitenListe );
					if( wenigsteUneindeutigkeiten > vorgabeUneindeutigkeiten ) {
						wenigsteUneindeutigkeiten = vorgabeUneindeutigkeiten;
						try {
							bestesSpiel = new SudokuFeld( aktSpiel );
						} catch( exception ) { 
							throw( "NeuesSpiel: Fehler in new SudokuFeld( )!\n" + exception ); 
						}
						besterMusterIndex = musterIndex;
						wenigsteUneindeutigkeitenListe = aktUneindeutigkeitenListe;
						if( wenigsteUneindeutigkeiten == 0 ) break; // Besser geht nicht...
					}
					musterIndex++;
			}
	///alert( "Bestes Muster " + besterMusterIndex + " Uneindeutigkeiten " + wenigsteUneindeutigkeiten + " (" + wenigsteUneindeutigkeitenListe.length + ")" );
			this.Spiel = bestesSpiel;
			// Restliche Uneindeutigkeiten beseitigen...
			for( var u = 0; u < wenigsteUneindeutigkeitenListe.length; u++ ) {
				var n = Math.floor( Math.random( ) * (wenigsteUneindeutigkeitenListe[u].length - 1) + 0.5 );
				var i = wenigsteUneindeutigkeitenListe[u][n];
				var z = Math.floor( i / 9 );
				var s = Math.floor( i % 9 );
				try {
					this.Spiel.Set( z, s, this.Loesung.Get( z, s ) );
				} catch( exception ) { 
					throw( "NeuesSpiel: Fehler in Uneindeutigkeiten beseitigen!\n" + exception ); 
				}
	//alert( "Index " + i + " ==> Zeile " + z + " Spalte " + s );			
			} 
			var maxVersuche = 100;
			do {
				try {
					this.Loesen( nurEindeutige ? 3 : maxLoesungen, nurEindeutige ? 20 : 100, false );
				} catch( exception ) { 
					throw( "NeuesSpiel: Fehler in Loesen( )!\n" + exception ); 
				}
				if( ( nurEindeutige && ! this.EindeutigeLoesung ) || ! this.Loesungen || this.Loesungen.length > maxLoesungen || this.Schwierigkeit > maxSchwierigkeit ) {
					this.Vereinfachen( maxSchwierigkeit < 2 );
				} else {
					break; // Fertig
				}
			} while( maxVersuche-- > 0 );
			try {
				this.Vorgabe = new SudokuFeld( this.Spiel );
			} catch( exception ) { 
				throw( "NeuesSpiel: Fehler in new SudokuFeld( ) beim setzen der Vorgabe!\n" + exception ); 
			}
		} catch( exception ) { 
			this.Spiel = new SudokuFeld( );
			throw( "NeuesSpiel: Fehler!\n" + exception ); 
		}
		return this.Spiel;	
	}
	// SudokuSpiel Methoden
	// SudokuSpiel:GetHTMLText
	this.GetHTMLText = function( feld, moeglichZuege ) {
		var hell =		"#efefff";
		var dunkel =	"#bcbccc";
		if( ! feld ) feld = this.Spiel;
		if( ! feld ) return "";

		var text = "<TABLE border=\"0\" rules=\none\" cellpadding=\"1\" cellspacing=\"1\">";
		text += "<TR align=\"center\" valign=\"bottom\"><TD><FONT size=1>&nbsp;</FONT></TD><TD><FONT size=1>A</FONT></TD><TD><FONT size=1>B</FONT></TD><TD><FONT size=1>C</FONT></TD><TD><FONT size=1>D</FONT></TD><TD><FONT size=1>E</FONT></TD><TD><FONT size=1>F</FONT></TD><TD><FONT size=1>G</FONT></TD><TD><FONT size=1>H</FONT></TD><TD><FONT size=1>I</FONT></TD></TR>";
		for ( var z=0; z < 9; z++) {
			text += "<TR><TD align=\"right\" valign=\"center\"><FONT size=1>" + String( z + 1 ) + "</FONT></TD>";
			for ( var s=0; s < 9; s++) {
				if( (Math.floor(s/3) % 2) == 1 ) {
					if( (Math.floor(z/3) % 2) == 1 ) {
						farbe = hell;
					} else {
						farbe = dunkel;
					}
				} else {
					if( (Math.floor(z/3) % 2 ) != 1 ) {
						farbe = hell;
					} else {
						farbe = dunkel;
					}
				}
				var zahl = feld.Get( z, s );
				if( moeglichZuege ) {
					if( zahl != 0 ) {
						text += "<TD bgcolor=\"" + farbe + "\">&nbsp;" + zahl + "&nbsp;";
					} else {
						text += "<TD bgcolor=\"" + farbe + "\">";
					}
					text += "<font size=1>";	
					for( var m = 0; m < moeglichZuege[z][s].length; m++ ) {
						text += " " + moeglichZuege[z][s].charAt( m );
						if( m == 2 || m == 5 ) text += "<BR>";	
					}
					text += "</font></TD>";	
				} else {
					if( zahl == 0 ) zahl = "&nbsp;&nbsp;"
					text += "<TD bgcolor=\"" + farbe + "\">&nbsp;" + zahl + "&nbsp;</TD>";
				}	
				
				
			} // for s
			text +="<TD align=\"right\" valign=\"center\"><FONT size=1>" + String( z + 1 ) + "</FONT></TD></TR>";
		} // for z
		text += "<TR align=\"center\" valign=\"bottom\"><TD><FONT size=1>&nbsp;</FONT></TD><TD><FONT size=1>A</FONT></TD><TD><FONT size=1>B</FONT></TD><TD><FONT size=1>C</FONT></TD><TD><FONT size=1>D</FONT></TD><TD><FONT size=1>E</FONT></TD><TD><FONT size=1>F</FONT></TD><TD><FONT size=1>G</FONT></TD><TD><FONT size=1>H</FONT></TD><TD><FONT size=1>I</FONT></TD></TR>";
		text +="</TABLE>";
		return text;
	} // SudokuSpiel:GetHTMLText( )

	// SudokuSpiel Methoden
	this.GetInfoHTML = function(  ) {
		var text = "";
		if( this.EindeutigeLoesung ) {
			text += "Eindeutige L&ouml;sung!<BR>";
		} else {
			if( this.ZuVieleVerzweigungen )	text += "Zu viele Verzweigungen!<BR>";
			if( this.ZuVieleLoesungen )	text += "Zu viele L&ouml;sungen!<BR>";
			if( this.Unloesbar )	text += "Unl&ouml;sbar!<BR>";
		}
		text += "Schwierigkeit: " + GetSchwierigkeitText( ) + "<BR>";

		if( this.Sackgassen > 0 ) {
			text += String( this.Sackgassen ) + " Sackgassen<BR>";
			text += "Maximale Tiefe " + String( this.SackgassenTiefeMaximal ) + "<BR>";
		}
		if( this.MoeglichkeitenMinimal > 0 ) {
			text += "Mindestens " + this.MoeglichkeitenMinimal + " ";
		}
		text += "durchschnittlich " + this.MoeglichkeitenDurchschnittlich + " eindeutige Z&uuml;ge<BR>";
		
		return text;
	}

	// SudokuSpiel Methoden
	this.Vereinfachen = function( symmetrisch ) {
		var aktZeile = -1;
		var aktSpalte = -1;
		if( ! (this.Spiel && this.Spiel.IstSudokuFeld( ) )  ) throw( "SudokuSpiel.Vereinfachen: Fehler - Kein gueltiges Spiel!!!" );
		if( ! (this.Loesung && this.Loesung.IstSudokuFeld( ) )  ) throw( "SudokuSpiel.Vereinfachen: Fehler - Keine gueltige Loesung!!!" );
		if( this.Loesungen && this.Loesungen.length > 1 ) {
			var bisLoesung = this.Loesungen.length - 1;
			do {
				for( var z = 0; z < 9; z++ ) {
					for( var s = 0; s < 9; s++ ) {
						var unterschiedlich = true;
						for( var l = 0; unterschiedlich && l < bisLoesung; l++ ) {
							if( this.Loesungen[l].Get( z, s ) == this.Loesungen[bisLoesung].Get( z, s ) ) unterschiedlich = false;
						}
						if( unterschiedlich ) {
//alert( "Unterschiedlich in " + ( bisLoesung + 1 ) + " Loesungen: " + this.FeldName( z, s ) );
							aktZeile = z;
							aktSpalte = s;
							z = s = 9;
						}
					}
				}
				if( aktZeile != -1 && aktSpalte != -1 ) {
					break;
				} else {
					bisLoesung--; // wenn sich nicht alle unterscheiden, ansruch zurueckschrauben	
				}
//alert( "Kein Unterschiedlich in " + ( bisLoesung + 1 ) + " Loesungen!" );
			} while( bisLoesung > 0 );
		}		
		if( aktZeile == -1 || aktSpalte == -1 ) {
			aktZeile = this.KleinstesUebelZeile;
			aktSpalte = this.KleinstesUebelSpalte;
		}
		if( aktZeile != -1 && aktSpalte != -1 ) {
//document.writeln( "<font size=1>SudokuSpiel:Vereinfachen - Zeile: " + aktZeile + " Spalte: " + aktSpalte + "</font><BR>" );
		
			try {
				this.Spiel.Set( aktZeile, aktSpalte, this.Loesung.Get( aktZeile, aktSpalte ) );
				if( symmetrisch ) {
					this.Spiel.Set( 9-1-aktZeile, aktSpalte, this.Loesung.Get( 9-1-aktZeile, aktSpalte ) );
					this.Spiel.Set( aktZeile, 9-1-aktSpalte, this.Loesung.Get( aktZeile, 9-1-aktSpalte ) );
					this.Spiel.Set( 9-1-aktZeile, 9-1-aktSpalte, this.Loesung.Get( 9-1-aktZeile, 9-1-aktSpalte ) );
				}
			} catch( exception ) {
				throw( "SudokuSpiel:Vereinfachen - Fehler!\nZeile: " + aktZeile + " Spalte: " + aktSpalte + "\n" + exception );
			}			
		} else {
			throw( "SudokuSpiel:Vereinfachen - Kleinstes Uebel ist ungueltig!" + this.KleinstesUebel + " z " + this.KleinstesUebelZeile + " s " + this.KleinstesUebelSpalte );
		}
		return this.Spiel;	
	}
	
	// SudokuSpiel:Loesen
	this.Loesen = function( maxLoesungen, maxVerzweigungen, protokollieren ) {
		var zeilenMoeglich = new Array( "", "", "", "", "", "", "", "", "" );
		var spaltenMoeglich = new Array( "", "", "", "", "", "", "", "", "" );
		var unterfeldMoeglich = new Array( "", "", "", "", "", "", "", "", "" );
		var einsBisNeun = "123456789";
		var aufgaben = new Array( );
		var moeglichZuege = new Array( 9 );
		var durchlauf = 0;
		var durchlaufGesamt = 0;

		this.Loesungen = new Array( );
		this.ZuVieleVerzweigungen = false;
		this.ZuVieleLoesungen = false;
		this.EindeutigeLoesung = false;
		this.Unloesbar = false;
		this.KleinstesUebel = 9*9;
		this.KleinstesUebelZeile = -1;
		this.KleinstesUebelSpalte = -1;
		this.GroesstesUebel = 0;
		this.GroesstesUebelZeile = -1;
		this.GroesstesUebelSpalte = -1;
		
		this.Sackgassen = 0;
		this.SackgassenTiefeMaximal = 0;
		this.MoeglichkeitenMinimal = 9*9;
		this.MoeglichkeitenDurchschnittlich = 0;
		this.Schwierigkeit = -1; // unbekannt

		
		try {
			aufgaben[0] = new SudokuFeld( this.Spiel );
		} catch( exception ) {
			var meldung = "";
			if( this.Spiel == null ) meldung += "Spiel ist NULL!\n";
			if( ! this.Spiel.IstSudokuFeld( ) ) meldung += "Spiel ist kein SudokuFeld!\n";
			else meldung += "Spiel Text = " + this.Spiel.GetText( ) + "\n";
		
			throw( "SudokuSpiel:Loesen - Fehler! Kein gueltiges Spiel!\n" + meldung + exception );
		}
		
		if( ! maxLoesungen ) maxLoesungen = this.MaxLoesungen;		
		if( ! maxVerzweigungen ) maxVerzweigungen = this.MaxVerzweigungen;		

		if( protokollieren ) {
			this.Protokoll += "<H2>L&ouml;sung des Spiels</H2><BR>";
			this.Protokoll += this.GetHTMLText( aufgabe );
		}
		for( var a = 0; aufgaben && a < aufgaben.length; a++ ) {
			var aufgabe = aufgaben[a];
			var leereFelder = 0;
			var eindeutigMoegliche = 0;
			var kleinstesUebel;
			var kleinstesUebelZeile = -1;
			var kleinstesUebelSpalte = -1;
			durchlauf = 0;
			for( var n = 0; n < 9; n++ ) {
				var zeile = aufgabe.GetZeile( n );
				var spalte = aufgabe.GetSpalte( n );
				var unterfeld = aufgabe.GetUnterfeld( n );
				zeilenMoeglich[n] = "";			
				spaltenMoeglich[n] = "";			
				unterfeldMoeglich[n] = "";			
				for( var c = 0; c < einsBisNeun.length; c++ ) {
					var zahl = einsBisNeun.charAt(c);
					var exp = RegExp( zahl );
					if( ! exp.test( zeile ) )	zeilenMoeglich[n] += zahl;			
					if( ! exp.test( spalte ) )	spaltenMoeglich[n] += zahl;			
					if( ! exp.test( unterfeld ) )	unterfeldMoeglich[n] += zahl;			
				}
			}
			do {
				var weiter = false;
				var zuege = new SudokuFeld( );
				durchlauf++;
				leereFelder = 0;
				eindeutigMoegliche = 0;
				kleinstesUebel = 9*9;
				kleinstesUebelZeile = -1;
				kleinstesUebelSpalte = -1;
				if( protokollieren ) this.Protokoll += "<BR>\nAufgabe " + (a+1) + " Durchlauf " + durchlauf + "<BR>\n";
				
				for( var z = 0; z < 9; z++ ) {
					var u = Math.floor( z / 3 ) * 3;
					moeglichZuege[z] = new Array( 9 );
					for( var s = 0; s < 9; s++ ) {
						moeglichZuege[z][s] = "";
						if( aufgabe.Get( z, s ) == "0" ) {
							var aktText = zeilenMoeglich[z].length < spaltenMoeglich[s].length ? zeilenMoeglich[z] : spaltenMoeglich[s];
							if( aktText.length > unterfeldMoeglich[u].length  ) aktText = unterfeldMoeglich[u];
							leereFelder++;
							for( var c = 0; c < aktText.length; c++ ) {
								var zahl = aktText.charAt(c);
								var exp = RegExp( zahl );
								if( exp.test( zeilenMoeglich[z] ) &&
										exp.test( spaltenMoeglich[s] ) &&
										exp.test( unterfeldMoeglich[u] ) 
								) {
									moeglichZuege[z][s] += zahl;
								}
							} // ende for c
						} // ende if == "0"
						if( (s % 3) == (3-1) ) u++;
					} // ende for s
				} // ende for 
				if( protokollieren ) this.Protokoll += this.GetHTMLText( aufgabe, moeglichZuege );
				if( leereFelder > 0 ) { // Kombinieren
					for( var z = 0; z < 9; z++ ) {
						var u = Math.floor( z / 3 ) * 3;
						for( var s = 0; s < 9; s++ ) {
							if( moeglichZuege[z][s].length == 1 ) {
								var zahl = moeglichZuege[z][s];
								if( protokollieren ) this.Protokoll += this.FeldName( z, s ) + "=" + zahl + " ";
								zuege.Set( z, s, zahl );
								eindeutigMoegliche++;
								weiter = true;
							} else if( moeglichZuege[z][s].length > 1 ) {
							
								if( 
									kleinstesUebel > moeglichZuege[z][s].length ||
									( kleinstesUebel == moeglichZuege[z][s].length && Math.random( ) >= 0.5 )
								) {
									kleinstesUebel = moeglichZuege[z][s].length;
									kleinstesUebelZeile = z;
									kleinstesUebelSpalte = s;
								}
								if( this.GroesstesUebel < moeglichZuege[z][s].length ) {
									this.GroesstesUebel = moeglichZuege[z][s].length;
									this.GroesstesUebelZeile = z;
									this.GroesstesUebelSpalte = s;
//this.Protokoll += "Neues groesstes Uebel:" + this.GroesstesUebel + " z " + this.GroesstesUebelZeile + " s " + this.GroesstesUebelSpalte;
								}

								for( var m = 0; m < moeglichZuege[z][s].length; m++ ) {
									var zahl = moeglichZuege[z][s].charAt(m);
									var exp = RegExp( zahl );
									var nurEinmalImSatz = false;
									if( ! nurEinmalImSatz ) {
										nurEinmalImSatz = true;
										for( var zz = 0; nurEinmalImSatz && zz < 9; zz++ ) {
											if( zz != z && moeglichZuege[zz][s].match( exp ) ) {
												nurEinmalImSatz = false;
											}
										}
									}
									if( ! nurEinmalImSatz ) {
										nurEinmalImSatz = true;
										for( var ss = 0; nurEinmalImSatz && ss < 9; ss++ ) {
											if( ss != s && moeglichZuege[z][ss].match( exp ) ) {
												nurEinmalImSatz = false;
											}
										}
									}
									if( ! nurEinmalImSatz ) {
										nurEinmalImSatz = true;
										var unterZeileVon = Math.floor( u / 3 ) * 3;
										var unterSpalteVon = Math.floor( u % 3 ) * 3;
										var unterZeileBis = unterZeileVon + 3;
										var unterSpalteBis = unterSpalteVon + 3;
										for( var zz = unterZeileVon; nurEinmalImSatz && zz < unterZeileBis; zz++ ) {
											for( var ss = unterSpalteVon; nurEinmalImSatz && ss < unterSpalteBis; ss++ ) {
												if( ! (zz == z && ss == s) && moeglichZuege[zz][ss].match( exp ) ) {
													nurEinmalImSatz = false;
												} else {
												}
											}
										}								
									}						
									if( nurEinmalImSatz	) {
										if( protokollieren ) this.Protokoll += this.FeldName( z, s ) + "!=" + zahl + " ";
										zuege.Set( z, s, zahl );
										eindeutigMoegliche++;
										weiter = true;
										break; // for m
									}
								} // ende for m
							}
							if( (s % 3) == (3-1) ) u++;
						} // ende for s
					} // ende for z
				}
				if( leereFelder > 0 && ! weiter ) { // Aufspalten
					var z = kleinstesUebelZeile;
					var s = kleinstesUebelSpalte;
					if( 0 <= z && z < 9 && 0 <= s && s < 9 ) {
						if( this.KleinstesUebelZeile == -1 || this.KleinstesUebelSpalte == -1 ) {
							this.KleinstesUebel = kleinstesUebel;
							this.KleinstesUebelZeile = kleinstesUebelZeile;
							this.KleinstesUebelSpalte = kleinstesUebelSpalte;
						}
						if( aufgaben.length + moeglichZuege[z][s].length - 1 > maxVerzweigungen ) {
							if( protokollieren ) this.Protokoll += "<H2>Abbruch! zuviele Verzweigungen!</H2>";
							this.ZuVieleVerzweigungen = true;
							aufgaben = null;
						} else {
							var zahl = moeglichZuege[z][s].charAt(0);
							if( protokollieren ) this.Protokoll += this.FeldName( z, s ) + "?=" + zahl + " (" + moeglichZuege[z][s] + ")  ";
							for( var m = 1; m < moeglichZuege[z][s].length; m++ ) {
								try {
									var aktZahl = moeglichZuege[z][s].charAt(m);
									var neuAufgabe = new SudokuFeld( aufgabe );
									if( aktZahl != zahl ) {
										neuAufgabe.Set( z, s, aktZahl );
										aufgaben[aufgaben.length] = neuAufgabe;
									} else {
										alert( "Unmoeglich!!!" );
									}
								} catch( exception ) {
									throw( "SudokuSpiel:Loesen - Fehler! Keine gueltige Aufgabe!" );
								}
							}
							zuege.Set( z, s, zahl );
							weiter = true;
						}
					} // ende if z s
				} // ende Aufspalten
				if( leereFelder > 0 && weiter ) { // Zuege ausfuehren
					for( var z = 0; z < 9; z++ ) {
						var u = Math.floor( z / 3 ) * 3;
						for( var s = 0; s < 9; s++ ) {
							var zahl = zuege.Get( z, s );
							if( zahl != "0" ) {
								var exp = RegExp( zahl );
								aufgabe.Set( z, s, zahl );
//if( protokollieren ) this.Protokoll += "<BR>L&ouml;sche " + exp +  " fuer " + this.FeldName( z, s ) + " (Unterfeld " + u + ")";
/*								// Nur Test
								var vorherZeile = zeilenMoeglich[z];
								var vorherSpalte = spaltenMoeglich[s];
								var vorherUnterfeld = unterfeldMoeglich[u];
								
*/								// ende nur Test
								zeilenMoeglich[z] = zeilenMoeglich[z].replace( exp, "" );
								spaltenMoeglich[s] = spaltenMoeglich[s].replace( exp, "" );
								unterfeldMoeglich[u] = unterfeldMoeglich[u].replace( exp, "" );
/*								// Nur Test
								if( exp.test( zeilenMoeglich[z] ) ) alert( "Exp. " + exp + " unmoeglich in Zeile " + z + " " + zeilenMoeglich[z] + "\n" + vorherZeile );
								if( exp.test( spaltenMoeglich[s] ) ) alert( "Exp. " + exp + " unmoeglich in Spalte " + s + " " + spaltenMoeglich[s] + "\n" + vorherSpalte );
								if( exp.test( unterfeldMoeglich[u] ) ) alert( "Exp. " + exp + " unmoeglich in Unterfeld " + u + " " + unterfeldMoeglich[u] + "\n" + vorherUnterfeld );
*/								// ende nur Test
							}
							if( (s % 3) == (3-1) ) u++;
						} // ende for s
					} // ende for z
				}
				if( protokollieren ) this.Protokoll += "<BR>";
				if( eindeutigMoegliche < leereFelder ) {
					this.MoeglichkeitenDurchschnittlich += eindeutigMoegliche;
					durchlaufGesamt++; 
					if( this.MoeglichkeitenMinimal > eindeutigMoegliche ) this.MoeglichkeitenMinimal = eindeutigMoegliche;					
				}

			} while( weiter );
			if( leereFelder == 0 && aufgabe.Teste( ) ) {
				this.Loesungen[this.Loesungen.length] = aufgabe;
				if( protokollieren ) {
					if( aufgaben.length > 1 ) {
						this.Protokoll += "<H2>L&ouml;sung " + this.Loesungen.length + " f&uuml;r Aufgabe " + (a+1) + "</H2><BR>";
					} else {
						this.Protokoll += "<H2>L&ouml;sung " + this.Loesungen.length + "</H2><BR>";
					}
					this.Protokoll += this.GetHTMLText( aufgabe ) + "<BR>";
				}
				if( this.Loesungen.length > maxLoesungen ) {
					if( protokollieren ) this.Protokoll += "<H2>Abbruch! zuviele L&ouml;sungen!</H2>";
					this.ZuVieleLoesungen = true;
					break;
				}			
			} else {
				if( protokollieren ) this.Protokoll += "<H2>Aufgabe " + (a+1) + " kann nicht gel&ouml;st werden</H2><BR>";
				if( this.SackgassenTiefeMaximal < durchlauf ) {
					this.SackgassenTiefeMaximal = durchlauf;
				}
				this.Sackgassen++;
			}
		} // for a
		this.MoeglichkeitenDurchschnittlich /= durchlaufGesamt;
		this.MoeglichkeitenDurchschnittlich = Math.round( this.MoeglichkeitenDurchschnittlich * 100 ) / 100; 
		if( this.Sackgassen > 0 || this.ZuVieleVerzweigungen || this.ZuVieleLoesungen ) {
			this.MoeglichkeitenMinimal = 0;
			if( this.ZuVieleVerzweigungen || this.ZuVieleLoesungen || this.SackgassenTiefeMaximal > 2 ) {
				this.Schwierigkeit = 3;
			} else {
				this.Schwierigkeit = 2;
			}
		} else if( this.MoeglichkeitenDurchschnittlich <= 5 || this.MoeglichkeitenMinimal <= 2) {
			this.Schwierigkeit = 1;
		} else {
			this.Schwierigkeit = 0;
		}
		if( ! this.ZuVieleVerzweigungen && this.Loesungen.length <= 0 ) {
			this.Unloesbar = true;		
		}
		if( ! ( this.ZuVieleVerzweigungen || this.ZuVieleLoesungen ) && this.Loesungen.length == 1 ) {
			this.EindeutigeLoesung = true;
		}
		return this.Loesungen;
	} // ende SudokuSpiel:Loesen( )
	this.MusterHTMLAnzeige = function( ) {
		var text = "";
		for( var m = 0; m < muster.length; m++ ) {
			text += "Muster " + m + "   ";
			try {
				var feld = new SudokuFeld( muster[m] );
				text += " Leerefelder: " + feld.AnzahlLeereFelder( ) + "<BR>";			
				text += feld.GetHTMLText( ) + "<BR>";
			} catch( exception ) { 
				text += "<h2>Fehler: " + exception + "</h2><br>"; 
			}
		}
		return text;
	}
	
	// SudokuSpiel Konstruktor
	if( typeof( nurEindeutige ) != 'boolean' ) {
		this.Leeren( );
	} else {
		this.NeuesSpiel( nurEindeutige, maxLoesungen, schwierigkeit );
	}
} // ende SudokuSpiel

