/*****************************/
/*VierGewinnt3D © t.heyn 2001*/
/*****************************/

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.Vector;

public class VierGewinnt3D extends Applet implements Konstanten, ActionListener, ItemListener
	{
   Cube3D platte = new Cube3D(new Point3D(-50,0,-50), new Vector3D(100,10,100));//platte
	Stab stab[][] = new Stab[4][4];//stäbe
	Stab temp[] = new Stab[3];
   Matrix matrix = new Matrix(new double[4][4]);//globale tranformationsmatrix
	Vector sorter = new Vector(16);
	Color farbe [] = {new Color(140,90,20),new Color(140,140,50),new Color(0,0,0),new Color(140,90,20)};
	int gitter[][][] = new int[4][4][4];
   int distanz=100;
   int werBeginnt=HELL, derLevel=ANFAENGER;
   int total, sieger=0;
   int mausX, mausY;
   int woHatsVier;
	double xtheta=0.6, ytheta=0.4;
   TextArea text= new TextArea("",15,12,TextArea.SCROLLBARS_NONE);
   Choice anfang = new Choice();
   Choice level = new Choice();
   AudioClip ton[] = new AudioClip[5];
	Image HintergrundBild = null;
	Image hintergrund;
	Graphics HintergrundGrafik = null;
   
   public void init()
      {
      laden();
      showStatus("Vier gewinnt 3D © t.heyn 2001");
    	setLayout(new BorderLayout());
		addMouseMotionListener(new MyMouseMotion());
		addMouseListener(new MyMouse());
    	Panel anzeige = new Panel();
    		anzeige.setLayout(new BorderLayout());
    		add("East",anzeige);
    	Label titel = new Label("Vier gewinnt 3D",Label.CENTER);
			titel.setBackground(Color.yellow);
			titel.setFont(new Font("SansSerif",Font.BOLD,18));
			anzeige.add("North",titel);
    		anzeige.add("Center",text);
    		text.setEnabled(false);
    	Panel controls = new Panel();
    		controls.setLayout(new BorderLayout());
    		anzeige.add("South",controls);
        	anfang.add("Weiss  beginnt");	
      	anfang.add("Schwarz beginnt");
      	anfang.addItemListener(this);
      	controls.add("Center",anfang);
      	level.add("Anfänger");
      	level.add("Normalo");
      	level.add("Profi");
      	level.addItemListener(this);
      	controls.add("North",level);
     	Button neu= new Button("Neues Spiel");
    		neu.addActionListener(this);
    		controls.add("South",neu);
      //initialisieren der globalen tranformationsmatrix
      matrix.initMatrix();
		matrix.rotate(xtheta,ytheta,0); //nach vorne kippen
		//grundplatte berechnen
   	platte.setColor(farbe[PLATTE]);
   	platte.updateCube(matrix,DISTANZ);
		//stäbe
		neuesSpiel();
      }

   public void paint(Graphics g)
      {
      boolean sortiert=false;
      g.drawImage(hintergrund,0,0,null);
      g.translate(X_URSPRUNG,Y_URSPRUNG);
      if (platte.r[3].visible) platte.paint(g);
      //zuerst alle stäbe in vector einlesen
		for (int i=0;i<4;i++)
			{
			for (int j=0;j<4;j++)
				{
   			sorter.addElement(stab[i][j]);
				}
			}
      //dann sortieren nach entfernung vom betrachter mit bubble sort
      while (! sortiert)
      	{
      	sortiert=true;
			for (int i=0;i<15;i++)
				{
				temp[1]=(Stab)sorter.elementAt(i);
				temp[2]=(Stab)sorter.elementAt(i+1);
   			if (temp[2].p[0].wz > temp[1].p[0].wz)
   				{
   				sorter.setElementAt(temp[2],i);
   				sorter.setElementAt(temp[1],i+1);
   				sortiert=false;
   				}
   			}
			}
      //und von hinten nach vorne zeichnen
     	for (int i=0;i<16;i++)
			{
			temp[0]=(Stab)sorter.elementAt(i);
 			if (! platte.r[3].visible) temp[0].vonUnten=true;
 				else temp[0].vonUnten=false;
			temp[0].paint(g);
			}
		sorter.removeAllElements();
		if (! platte.r[3].visible) platte.paint(g);
      }
      
	public final void update(Graphics g) //double buffering
		{
		if(HintergrundBild==null) HintergrundBild = createImage(2*X_URSPRUNG,2*Y_URSPRUNG);
		HintergrundGrafik = HintergrundBild.getGraphics();
		HintergrundGrafik.clearRect(0,0,2*X_URSPRUNG,2*Y_URSPRUNG);
		paint(HintergrundGrafik);
		g.drawImage(HintergrundBild,0,0,null);
		}
 
	public void itemStateChanged(ItemEvent e)					// all die doofen listener
		{
		String farbe=(String) anfang.getSelectedItem();
			if (farbe=="Weiss  beginnt") werBeginnt=HELL;
			if (farbe=="Schwarz beginnt") werBeginnt=DUNKEL;
		String grad=(String) level.getSelectedItem();
			if (grad=="Anfänger") derLevel=ANFAENGER;
			if (grad=="Normalo")  derLevel=NORMALO;
			if (grad=="Profi")    derLevel=PROFI;
		neuesSpiel();
		}

	public void actionPerformed(ActionEvent e)
		{
		neuesSpiel();
		}

	private class MyMouseMotion extends MouseMotionAdapter
		{
		public void mouseDragged(MouseEvent e)
			{
			matrix.initMatrix();
			xtheta += (e.getY() - mausY) * (5.0f / size().width);
			ytheta += (mausX - e.getX()) * (5.0f / size().height);
			matrix.rotate(xtheta,ytheta,0);
			platte.updateCube(matrix,distanz);
         for (int i=0;i<4;i++)
				{
				for (int j=0;j<4;j++)
					{
   				stab[i][j].updateCube(matrix,distanz);
					}			
				}
			mausX=e.getX();
			mausY=e.getY();
   		update(getGraphics());
			}	
		}

	private class MyMouse extends MouseAdapter
		{		
		public void mousePressed(MouseEvent e)
			{
			mausX=e.getX();
			mausY=e.getY();
			if (sieger>0) return;
			//stab berechnen
			int dx, dy;
			for (int i=0;i<4;i++)
				{
				for (int j=0;j<4;j++)
					{
   				dx= (int) stab[i][j].p[3].sx-mausX+X_URSPRUNG;
   				dy= (int) stab[i][j].p[3].sy-mausY+Y_URSPRUNG;
   				if (dx*dx+dy*dy<=49 && stab[i][j].anzahlKugeln<4)
   					{
						setzen(HELL,i,j);
						if (siegtest(HELL)) schluss(HELL);
						if (total==64) schluss(REMIS);
						if (sieger>0) return;
    					computerZugBerechnen();
    					if (total==64) schluss(REMIS);
    					return;
  						}
					}
				}
			}
		}

	private void computerZugBerechnen()
		{
		int dringendste=TABU;
		gefahrtest();
		for (int i=0;i<4;i++)
			{
			for (int j=0;j<4;j++)
				{
				stab[i][j].prioritaet=0;
				//nächster stab wenn dieser voll ist
				if (stab[i][j].anzahlKugeln>=4) 
					{
					stab[i][j].prioritaet=GERINGSTE;
					continue;
					}
				//eigenen sieg suchen und setzen
   			gitter[i][j][stab[i][j].anzahlKugeln]=DUNKEL;
   			if (siegtest(DUNKEL)) 
   				{
   				setzen(DUNKEL,i,j);
   				schluss(DUNKEL);
   				return;
   				}
 				//sieg gegner verhindern
   			gitter[i][j][stab[i][j].anzahlKugeln]=HELL;
   			if (siegtest(HELL)) 
   				{
   				setzen(DUNKEL,i,j);
   				return;
   				}
   			gitter[i][j][stab[i][j].anzahlKugeln]=0;
   			//test ob stab tabu für schwarz
   			if (tabutest(HELL,i,j) && derLevel != ANFAENGER) 
   				{
   				stab[i][j].prioritaet=TABU;
   				continue;
   				}
				//test auf doppelmoppel für schwarz
   			if (doppeltest(DUNKEL,i,j) && derLevel == PROFI)
   				{
   				stab[i][j].prioritaet=200;
   				dringendste=200;
   				continue;
   				}
   			//test ob stab tabu für weiss
   			if (tabutest(DUNKEL,i,j) && derLevel != ANFAENGER)
   				{
   				stab[i][j].prioritaet=TABU+10;
   				continue;
   				}
  				//nächsten zug testen
  				if (derLevel == PROFI) stab[i][j].prioritaet += 10*naechsterZugTest(HELL,i,j);
  				if (derLevel != ANFAENGER) stab[i][j].prioritaet += 5*naechsterZugTest(DUNKEL,i,j);
 				//besser nicht auf leeren stab setzen
   			if (derLevel == PROFI && stab[i][j].anzahlKugeln==0) stab[i][j].prioritaet -=5;
   			//dringendsten stab rausfinden
   			if (stab[i][j].prioritaet>dringendste) dringendste=stab[i][j].prioritaet;
				}			
			}
		//zufällig setzen auf einen stab mit höchster priorität
		int i=0,j=0;
		boolean suchen=true;
		while (suchen)
			{
			i=(int)(Math.random()*4);
			j=(int)(Math.random()*4);
			if (stab[i][j].prioritaet==dringendste) suchen=false;
			}
		setzen(DUNKEL,i,j);
		return;
		}
		
	private boolean siegtest(int farbe)
		{
		int anzahl;
		for (int test=0;test<76;test++)
			{
			anzahl=0;
			for (int kugel=0;kugel<4;kugel++)
				{
				if (gitter[testdaten[4*test+kugel][0]] [testdaten[4*test+kugel][1]] [testdaten[4*test+kugel][2]] == farbe) anzahl++;
				}
			if (sieger>0 && anzahl==4)//wenn spiel fertig dann markieren
				{
				for (int kugel=0;kugel<4;kugel++)
					{
					stab[testdaten[4*test+kugel][0]] [testdaten[4*test+kugel][1]].kugel[testdaten[4*test+kugel][2]].markieren();
					}
				}
			if	(anzahl==4) return true;
			}
		return false;
		}
		
	private void gefahrtest()//testet alle leeren positionen, ob hier 4 erreicht werden könnten
		{
		for (int i=0;i<4;i++)
			{
			for (int j=0;j<4;j++)
				{
				int hoehe=stab[i][j].anzahlKugeln;
				for (int h=0;h<4;h++) //gefahren setzen
					{
					if (gitter[i][j][h] != 0) continue;
					gitter[i][j][h]=DUNKEL;
 		  			if (siegtest(DUNKEL)) stab[i][j].gefahr[h]=DUNKEL;
					gitter[i][j][h]=HELL;
  		 			if (siegtest(HELL)) stab[i][j].gefahr[h]=HELL;
 		  			gitter[i][j][h]=0;
					}
				}
			}
		}
			
	private int naechsterZugTest(int farbe, int i, int j)
		{
  		gitter[i][j][stab[i][j].anzahlKugeln]=farbe;
  		stab[i][j].anzahlKugeln++;
		int anzahl=0;
		//alle stäbe ausprobieren
		for (int x=0;x<4;x++)
			{
			for (int y=0;y<4;y++)
				{
				if (stab[x][y].anzahlKugeln>=4) continue;
				int dieserStab=0;
				for (int h=stab[x][y].anzahlKugeln;h<4;h++)
					{
					gitter[x][y][h]=farbe;
					if (siegtest(farbe))
						{
						anzahl++;
						//wenn 2 siege übereinander möglich dann wichtig
						dieserStab++;
						if (dieserStab==2) anzahl+=20;
						}
					else dieserStab=0;	
					gitter[x][y][h]=0;
					}
				}
			}
		stab[i][j].anzahlKugeln--;
  		gitter[i][j][stab[i][j].anzahlKugeln]=0;
		return anzahl;
		}
	
	private boolean tabutest(int farbe, int i, int j)
		{
		if (stab[i][j].anzahlKugeln < 3)
			{
			if (stab[i][j].gefahr[stab[i][j].anzahlKugeln+1]==farbe) return true;
			}
		return false;
		}
		
	private boolean doppeltest(int farbe, int i, int j)
		{
		if (stab[i][j].anzahlKugeln >= 3) return false;
		int doppel=0;
		if (tabutest(DUNKEL,i,j)) doppel++;
  		gitter[i][j][stab[i][j].anzahlKugeln]=farbe;
  		stab[i][j].anzahlKugeln++;
  		if (tabutest(DUNKEL,i,j)) doppel++;
  		stab[i][j].anzahlKugeln--;
  		gitter[i][j][stab[i][j].anzahlKugeln]=0;
  		if (doppel==2) return true;
  		return false;
		}
		
	private void schluss(int farbe)
		{
		sieger=farbe;
		text.append("\n\n"+kommentar[sieger]);
		siegtest(sieger);
    	update(getGraphics());
		ton[sieger].play();
		}

	private void neuesSpiel()
		{
		sieger=0;
		total=0;
		for (int i=0;i<4;i++)
			{
			for (int j=0;j<4;j++)
				{
   			stab[i][j] = new Stab(new Point3D(-40+(25*i),-43,-40+(25*j)), new Vector3D(1,43,1));
   			stab[i][j].setColor(farbe[STAB]);
   			stab[i][j].updateCube(matrix,distanz);
   			for (int k=0;k<4;k++)
					{
					gitter[i][j][k]=0;
					}
				}
			}
		schreiben();
		if (werBeginnt==DUNKEL) setzen(DUNKEL, (int)(Math.random()*4), (int)(Math.random()*4));
		update(getGraphics());
		ton[0].play();
		}

	private void setzen(int was, int i, int j)
		{
		schreiben();
		total++;
		gitter[i][j][stab[i][j].anzahlKugeln]=was;
   	stab[i][j].kugelDazu(farbe[was]);
   	stab[i][j].updateCube(matrix,distanz);
    	update(getGraphics());
    	ton[4].play();
		}

	private void schreiben()
		{
		text.setText("Wer zuerst vier waagrecht, senkrecht oder diagonal hat, gewinnt. Du hast Weiss, "
						+"der Computer Schwarz. Klicke die Spitze des Stabes an, wenn du setzen willst. "
						+"\n\nDu kannst das ganze Spiel mit der Maus drehen.");
		//for (int t=0;t<4;t++) text.append("\n"+stab[t][0].prioritaet+" "+stab[t][1].prioritaet+" "+stab[t][2].prioritaet+" "+stab[t][3].prioritaet+" ");
		}

	private void laden()
		{
		for (int t=0;t<5;t++)
			{
			ton[t]= getAudioClip(getCodeBase(),""+t+".au");
			}
  		MediaTracker tracker=new MediaTracker(this);
    	hintergrund = getImage(getCodeBase(),"hintergrund.gif");
    	tracker.addImage(hintergrund,1);
    	try
    		{
    		tracker.waitForAll();
    		}
    	catch(InterruptedException e) {}
		}
	}

Zusätzliche Klassen

//Stab//

import java.awt.*;

public class Stab extends Cube3D implements Konstanten
	{
	int anzahlKugeln, prioritaet, distance;
	boolean istAktiv, vonUnten;
	Sphere3D kugel[] = new Sphere3D[4];
	int gefahr[] = new int[4];
	
	public Stab(Point3D p0, Vector3D v)
		{
		super(p0,v);
		anzahlKugeln = 0;
		istAktiv=false;
		vonUnten=false;
		prioritaet=0;
		}
		
	public void kugelDazu(Color color)
		{
		if (anzahlKugeln>=4) return;
		kugel[anzahlKugeln] = new Sphere3D(new Point3D(p[0].lx,-5-10*anzahlKugeln,p[0].lz),5.5);
		kugel[anzahlKugeln].setColor(color);
		anzahlKugeln++;
		if (anzahlKugeln==4) prioritaet=GERINGSTE;
		for (int i=4; i<8; i++)
			{
			p[i].ly -= 9.9;
			}
		updateCube(matrix,distance);
		}
	
	public void paint(Graphics g)
		{
		if (! vonUnten)	//kugeln zeichnen wenn von oben
			{
			for (int i=0; i<anzahlKugeln; i++)
     	   	{
				kugel[i].paint(g);
     			}
			super.paint(g);
     		}
     	else					//kugeln zeichnen wenn von unten
     		{
			super.paint(g);
			for (int i=anzahlKugeln-1; i>=0; i--)
     	   	{
				kugel[i].paint(g);
     			}
     		}
		}
		
	public void updateCube(Matrix matrix,int distance)
		{
		super.updateCube(matrix, distance);
		for (int i=0; i<4; i++)
			{
 			if (kugel[i] != null) kugel[i].updateSphere(matrix,distance);
 			}
		}
	}

//Kugel//

import java.awt.*;

public class Sphere3D extends Panel
	{
	Point3D z;
	double radius, r;
	Color color;
	Matrix matrix;
	
	//konstruiert aus dem zentrum und radius die kugel
	public Sphere3D (Point3D z, double radius)
		{
		this.z=z;
		this.radius=radius;
		setColor(new Color(0,0,0));   
		}
	
	public void updateSphere(Matrix matrix, int distance)
		{
		this.matrix=matrix;
		project3DTo2D(distance);
		createWorldCoordinates();
		}
		
	public void setColor(Color color)
		{
		this.color=color;
		repaint();
		}
		
	public void markieren()
		{
		setColor(new Color((color.getRed()+40)%255,color.getGreen(),color.getBlue()));
		}

	public void paint(Graphics g)
		{
		int red=color.getRed();
		int green=color.getGreen();
		int blue=color.getBlue();
      //kreise zeichnen
		for (int i=(int)(r);i>0;i-=2)
			{
			if (red<235) red+=20;
			if (green<235) green+=20;
			if (blue<235) blue+=20;
			g.setColor(new Color(red,green,blue));
			g.fillOval((int)(z.sx-i),(int)(z.sy-i),i*2,i*2);
			}
		}
		
   //Projiziert die 3DVektoren der WELT-Koordinaten des Objektes auf 2DVektoren, die die BILDSCHIRM-
   //Koordinaten des Objektes angeben.
   public void project3DTo2D(int distance)
      {
      z.sx = 200 * z.wx / (z.wz+distance);
      z.sy = 200 * z.wy / (z.wz+distance);
      r = 200 * radius / (z.wz+distance);
      }
	//Multipliziert die 3DVektoren der LOKALEN-Koordinaten des Objektes mit der Transformationsmatrix,
   //die die Daten für Rotation,Verschiebung,Skalierung enthält, und speichert die Berechnungen 
   //als die WELT-Koordinaten des Objektes ab.
   public void createWorldCoordinates()
      {
      z.wx = z.lx*matrix.mm[0][0] + z.ly*matrix.mm[1][0] + z.lz*matrix.mm[2][0] + matrix.mm[3][0];
      z.wy = z.lx*matrix.mm[0][1] + z.ly*matrix.mm[1][1] + z.lz*matrix.mm[2][1] + matrix.mm[3][1];
      z.wz = z.lx*matrix.mm[0][2] + z.ly*matrix.mm[1][2] + z.lz*matrix.mm[2][2] + matrix.mm[3][2];
      }
	}

//Quader//

import java.awt.*;

public class Cube3D extends Panel
	{
   Rectangle3D r[] = new Rectangle3D[6];
   Point3D p[] = new Point3D[8];//eckpunkte
   Matrix matrix;

	//konstruiert aus dem scheitelpunkt und dem vektor der körperdiagonalen den quader (normal)
   public Cube3D(Point3D p0, Vector3D v)
   	{  
      p[0] = new Point3D(p0.lx, p0.ly, p0.lz);
      p[1] = new Point3D(p0.lx, p0.ly, p0.lz+v.z);
      p[2] = new Point3D(p0.lx+v.x, p0.ly, p0.lz+v.z);
      p[3] = new Point3D(p0.lx+v.x, p0.ly, p0.lz);
      p[4] = new Point3D(p0.lx, p0.ly+v.y, p0.lz);
      p[5] = new Point3D(p0.lx, p0.ly+v.y, p0.lz+v.z);
      p[6] = new Point3D(p0.lx+v.x, p0.ly+v.y, p0.lz+v.z);
      p[7] = new Point3D(p0.lx+v.x, p0.ly+v.y, p0.lz);
      r[0] = new Rectangle3D(p[3],p[0],p[4],p[7]);	// vorne
      r[1] = new Rectangle3D(p[6],p[5],p[1],p[2]);	// hinten
      r[2] = new Rectangle3D(p[5],p[6],p[7],p[4]);	// oben      
      r[3] = new Rectangle3D(p[2],p[1],p[0],p[3]);	// unten      
      r[4] = new Rectangle3D(p[2],p[3],p[7],p[6]);	// rechts
      r[5] = new Rectangle3D(p[0],p[1],p[5],p[4]); // links
      setColor(new Color(200,200,200));   
      }

	public void paint(Graphics g)
		{
      for (int i=0; i<6; i++) //sechs seiten zeichnen wenn sichtbar
        	{
			if (r[i].visible)
				{
				int arx[] = { (int)r[i].p1.sx, (int)r[i].p2.sx, (int)r[i].p3.sx, (int)r[i].p4.sx };
				int ary[] = { (int)r[i].p1.sy, (int)r[i].p2.sy, (int)r[i].p3.sy, (int)r[i].p4.sy };
				g.setColor(r[i].color);
				g.fillPolygon(arx,ary,arx.length);
				}
			}
		}
		
	public void setColor(Color c)
		{
		Color c1 = c.darker();
      r[0].color = c.brighter();
      r[1].color = c1.darker();
      r[2].color = c;
      r[3].color = c;
      r[4].color = c.darker();
      r[5].color = c.darker();
		}
	
	public void updateCube(Matrix matrix,int distance)
		{
		this.matrix=matrix;
		for (int i=0; i<6; i++)
			{
			createWorldCoordinates(r[i]);
			project3DTo2D(r[i],distance);
			backfaceCulling(r[i]);
			}
		}
	
	//Multipliziert die 3DVektoren der LOKALEN-Koordinaten des Objektes mit der Transformationsmatrix,
   //die die Daten für Rotation,Verschiebung,Skalierung enthält, und speichert die Berechnungen als die WELT-
   //Koordinaten des Objektes ab.
   public void createWorldCoordinates(Rectangle3D r)
      {
      Point3D p[] = { r.p1, r.p2, r.p3, r.p4 };
      for (int i=0; i<4; i++)
         {
         p[i].wx = p[i].lx*matrix.mm[0][0] + p[i].ly*matrix.mm[1][0] + p[i].lz*matrix.mm[2][0] + matrix.mm[3][0];
         p[i].wy = p[i].lx*matrix.mm[0][1] + p[i].ly*matrix.mm[1][1] + p[i].lz*matrix.mm[2][1] + matrix.mm[3][1];
         p[i].wz = p[i].lx*matrix.mm[0][2] + p[i].ly*matrix.mm[1][2] + p[i].lz*matrix.mm[2][2] + matrix.mm[3][2];
         }
      }

   //Projiziert die 3DVektoren der WELT-Koordinaten des Objektes auf 2DVektoren, die die BILDSCHIRM-Koordinaten
   //des Objektes angeben.
   public void project3DTo2D(Rectangle3D r, int distance)
      {
      Point3D p[] = { r.p1, r.p2, r.p3, r.p4 };
      for (int i=0; i<4; i++)
         {
         p[i].sx = 200 * p[i].wx / (p[i].wz+distance);
         p[i].sy = 200 * p[i].wy / (p[i].wz+distance); 
         }
      }

   //Berechnet, in welche Richtung das Polygon zeigt. Die Formel ist die letzte Zeile des Vektorproduktes
   //und gibt die z-Koordinate des Normalenvektors aus den ersten drei Punkten des Polygons an (BILDSCHIRM-
   //Koordinaten). Ist z positiv, so zeigt die Fläche mindestens 90 Grad vom Betrachter weg.
   public void backfaceCulling(Rectangle3D r)
      {
      double z = (r.p2.sx-r.p1.sx) * (r.p3.sy-r.p1.sy) - (r.p2.sy-r.p1.sy) * (r.p3.sx-r.p1.sx);
      if (z>0) r.visible=false;
      else r.visible=true;
      }
	}

public class Point3D 
	{
   double lx, ly, lz;  // lokale Koordinaten
 	double wx, wy, wz;  // Weltkoordinaten
 	double sx, sy;      // Bildschirmkoordinaten

   //Konstruiert aus den übergebenen double Werten einen Punkt mit 3D Koordinaten
   public Point3D(double X, double Y, double Z)
      {
      this.lx = X;
      this.ly = Y;
      this.lz = Z;
      }
	}

public class Vector3D 
	{
	double x, y, z;
	double abs; //betrag

   //Konstruiert aus den übergebenen double Werten einen 3D Vektor
   public Vector3D(double x, double y, double z)
      {
      this.x = x;
      this.y = y;
      this.z = z;
      abs = Math.sqrt(x*x + y*y + z*z);
      }
	}

public class Rectangle3D 
	{
   protected Point3D p1, p2, p3, p4;   // Die vier Eckpunte
   protected boolean visible = true;   // Für BackfaceCulling
   protected Color color;              // Farbe der Fläche

   //Konstruiert aus den übergebenen Scheitelpunkten ein Rechteck mit 3D Koordinaten.
   public Rectangle3D(Point3D P1, Point3D P2, Point3D P3, Point3D P4)
      {
      this.p1 = P1;
      this.p2 = P2;
      this.p3 = P3;
      this.p4 = P4;
      }
	}

public class Matrix
	{
	double mm[][];
	
	public Matrix(double mm[][])
		{
		this.mm=mm;
		}

   // Berechnet die Rotationsmatrizen aller drei Achsen
   // und multipliziert diese mit der Transformationsmatrix.
   // Rotationswinkel der Achsen in Radian
   public void rotate(double ax, double ay, double az)
		{
      if (ax!=0.0)// x-Rotationsmatrix
      	{
         double mat1[][] = new double[4][4];
         double xmat[][] = new double[4][4];
         xmat[0][0]=1;					xmat[0][1]=0;
         xmat[0][2]=0;					xmat[0][3]=0;
         xmat[1][0]=0;					xmat[1][1]=Math.cos(ax);
         xmat[1][2]=Math.sin(ax);	xmat[1][3]=0;
         xmat[2][0]=0;					xmat[2][1]=-1*Math.sin(ax);
         xmat[2][2]=Math.cos(ax);	xmat[2][3]=0;
         xmat[3][0]=0;					xmat[3][1]=0;
         xmat[3][2]=0;					xmat[3][3]=1;
         matrixMultiplication(mat1,xmat,mm);
         matrixCopy(mm,mat1);
         }
      if (ay!=0.0)// y-Rotationsmatrix
      	{
         double mat1[][] = new double[4][4];
         double ymat[][] = new double[4][4];
         ymat[0][0]=Math.cos(ay);   ymat[0][1]=0; 
         ymat[0][2]=-1*Math.sin(ay);ymat[0][3]=0;
         ymat[1][0]=0;              ymat[1][1]=1;
         ymat[1][2]=0;              ymat[1][3]=0;
         ymat[2][0]=Math.sin(ay);   ymat[2][1]=0;
         ymat[2][2]=Math.cos(ay);   ymat[2][3]=0;
         ymat[3][0]=0;              ymat[3][1]=0;
        	ymat[3][2]=0;              ymat[3][3]=1;
         matrixMultiplication(mat1,ymat,mm);
         matrixCopy(mm,mat1);
         }
      if (az!=0.0)// z-Rotationsmatrix
      	{
         double mat1[][] = new double[4][4];
         double zmat[][] = new double[4][4];
         zmat[0][0]=Math.cos(az);	zmat[0][1]=Math.sin(az);
         zmat[0][2]=0;					zmat[0][3]=0;
         zmat[1][0]=-1*Math.sin(az);zmat[1][1]=Math.cos(az);    
         zmat[1][2]=0;					zmat[1][3]=0;
         zmat[2][0]=0;					zmat[2][1]=0;
         zmat[2][2]=1;					zmat[2][3]=0;
         zmat[3][0]=0;					zmat[3][1]=0;  
         zmat[3][2]=0;					zmat[3][3]=1;
         matrixMultiplication(mat1,zmat,mm);
         matrixCopy(mm,mat1);
         }
      }

	// Methoden für die Matrixrechnungen

   //Initialisiert die übergebene 4x4 Matrix als Identitätsmatrix
   public void initMatrix()
      {
      mm[0][0]=1;   mm[0][1]=0;   mm[0][2]=0;   mm[0][3]=0;
      mm[1][0]=0;   mm[1][1]=1;   mm[1][2]=0;   mm[1][3]=0;
      mm[2][0]=0;   mm[2][1]=0;   mm[2][2]=1;   mm[2][3]=0;
      mm[3][0]=0;   mm[3][1]=0;   mm[3][2]=0;   mm[3][3]=1;
      }

   //Multipliziert die übergebenen Matrizen
   //result[][] Speichert das Ergebnis der Multiplikation
   //mat1[][] Der erste Faktor der Multiplikation
   //mat2[][] Der zweite Faktor der Multiplikation
   public void matrixMultiplication(double result[][], double mat1[][], double mat2[][])
      {
      for (int i=0; i<4; i++)
         for (int j=0; j<4; j++)
            for (int k=0; k<4; k++)
               result[i][j] += mat1[i][k] * mat2[k][j];
      }

   //Kopiert die übergebenen Matrizen
   //dest[][] In diese Matrix wird kopiert
   //source[][] Aus dieser Matrix wird kopiert
   public void matrixCopy(double dest[][], double source[][])
      {
      for (int i=0; i<4; i++) 
         for (int j=0; j<4; j++)
            dest[i][j] = source[i][j];
      }
	}

//damit die hauptklasse nicht zu gross wird

public interface Konstanten 
	{
	public static final int PLATTE=0, HELL=1, DUNKEL=2, STAB=3;
	public static final int REMIS=3;
	public static final int ANFAENGER=0, NORMALO=1, PROFI=2;
	public static final int X_URSPRUNG=250, Y_URSPRUNG=200;
	public static final int DISTANZ=100;
	public static final int GERINGSTE=-1000, HOECHSTE=1000, NORMAL=0, TABU=-90;
	public final String kommentar[] = {"","Weiss Gewinnt!","Schwarz gewinnt!","Unentschieden"};									 
	public static final int testdaten[][] =
		{
		//waagrecht seitwärts
		{0,0,0},{1,0,0},{2,0,0},{3,0,0},		{0,1,0},{1,1,0},{2,1,0},{3,1,0},		
		{0,2,0},{1,2,0},{2,2,0},{3,2,0},		{0,3,0},{1,3,0},{2,3,0},{3,3,0},		
		{0,0,1},{1,0,1},{2,0,1},{3,0,1},		{0,1,1},{1,1,1},{2,1,1},{3,1,1},		
		{0,2,1},{1,2,1},{2,2,1},{3,2,1},		{0,3,1},{1,3,1},{2,3,1},{3,3,1},		
		{0,0,2},{1,0,2},{2,0,2},{3,0,2},		{0,1,2},{1,1,2},{2,1,2},{3,1,2},		
		{0,2,2},{1,2,2},{2,2,2},{3,2,2},		{0,3,2},{1,3,2},{2,3,2},{3,3,2},		
		{0,0,3},{1,0,3},{2,0,3},{3,0,3},		{0,1,3},{1,1,3},{2,1,3},{3,1,3},		
		{0,2,3},{1,2,3},{2,2,3},{3,2,3},		{0,3,3},{1,3,3},{2,3,3},{3,3,3},		
		//waagrecht nach hinten
		{0,0,0},{0,1,0},{0,2,0},{0,3,0},		{1,0,0},{1,1,0},{1,2,0},{1,3,0},		
		{2,0,0},{2,1,0},{2,2,0},{2,3,0},		{3,0,0},{3,1,0},{3,2,0},{3,3,0},		
		{0,0,1},{0,1,1},{0,2,1},{0,3,1},		{1,0,1},{1,1,1},{1,2,1},{1,3,1},		
		{2,0,1},{2,1,1},{2,2,1},{2,3,1},		{3,0,1},{3,1,1},{3,2,1},{3,3,1},		
		{0,0,2},{0,1,2},{0,2,2},{0,3,2},		{1,0,2},{1,1,2},{1,2,2},{1,3,2},		
		{2,0,2},{2,1,2},{2,2,2},{2,3,2},		{3,0,2},{3,1,2},{3,2,2},{3,3,2},		
		{0,0,3},{0,1,3},{0,2,3},{0,3,3},		{1,0,3},{1,1,3},{1,2,3},{1,3,3},		
		{2,0,3},{2,1,3},{2,2,3},{2,3,3},		{3,0,3},{3,1,3},{3,2,3},{3,3,3},		
		//senkrecht
		{0,0,0},{0,0,1},{0,0,2},{0,0,3},		{1,0,0},{1,0,1},{1,0,2},{1,0,3},		
		{2,0,0},{2,0,1},{2,0,2},{2,0,3},		{3,0,0},{3,0,1},{3,0,2},{3,0,3},		
		{0,1,0},{0,1,1},{0,1,2},{0,1,3},		{1,1,0},{1,1,1},{1,1,2},{1,1,3},		
		{2,1,0},{2,1,1},{2,1,2},{2,1,3},		{3,1,0},{3,1,1},{3,1,2},{3,1,3},		
		{0,2,0},{0,2,1},{0,2,2},{0,2,3},		{1,2,0},{1,2,1},{1,2,2},{1,2,3},		
		{2,2,0},{2,2,1},{2,2,2},{2,2,3},		{3,2,0},{3,2,1},{3,2,2},{3,2,3},		
		{0,3,0},{0,3,1},{0,3,2},{0,3,3},		{1,3,0},{1,3,1},{1,3,2},{1,3,3},		
		{2,3,0},{2,3,1},{2,3,2},{2,3,3},		{3,3,0},{3,3,1},{3,3,2},{3,3,3},		
		//diagonal waagrecht
		{0,0,0},{1,1,0},{2,2,0},{3,3,0},		{3,0,0},{2,1,0},{1,2,0},{0,3,0},		
		{0,0,1},{1,1,1},{2,2,1},{3,3,1},		{3,0,1},{2,1,1},{1,2,1},{0,3,1},		
		{0,0,2},{1,1,2},{2,2,2},{3,3,2},		{3,0,2},{2,1,2},{1,2,2},{0,3,2},		
		{0,0,3},{1,1,3},{2,2,3},{3,3,3},		{3,0,3},{2,1,3},{1,2,3},{0,3,3},		
		//diagonal seitwärts senkrecht
		{0,0,0},{1,0,1},{2,0,2},{3,0,3},		{0,0,3},{1,0,2},{2,0,1},{3,0,0},		
		{0,1,0},{1,1,1},{2,1,2},{3,1,3},		{0,1,3},{1,1,2},{2,1,1},{3,1,0},		
		{0,2,0},{1,2,1},{2,2,2},{3,2,3},		{0,2,3},{1,2,2},{2,2,1},{3,2,0},		
		{0,3,0},{1,3,1},{2,3,2},{3,3,3},		{0,3,3},{1,3,2},{2,3,1},{3,3,0},		
		//diagonal nach hinten senkrecht
		{0,0,0},{0,1,1},{0,2,2},{0,3,3},		{0,0,3},{0,1,2},{0,2,1},{0,3,0},		
		{1,0,0},{1,1,1},{1,2,2},{1,3,3},		{1,0,3},{1,1,2},{1,2,1},{1,3,0},		
		{2,0,0},{2,1,1},{2,2,2},{2,3,3},		{2,0,3},{2,1,2},{2,2,1},{2,3,0},		
		{3,0,0},{3,1,1},{3,2,2},{3,3,3},		{3,0,3},{3,1,2},{3,2,1},{3,3,0},	
		//körperdiagonalen
		{0,0,0},{1,1,1},{2,2,2},{3,3,3},		{0,0,3},{1,1,2},{2,2,1},{3,3,0},		
		{3,0,0},{2,1,1},{1,2,2},{0,3,3},		{3,0,3},{2,1,2},{1,2,1},{0,3,0},
		};
	}