//**************************//
//Vier gewinnt © t.heyn 2000//
//**************************//

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

public class VierGewinntApplet extends Applet implements ActionListener, ItemListener
	{
	final int GROESSE=50, HOEHE=6, BREITE=7, STRICH=10, ZEIT=40;
	final int ANFAENGER=1, NORMALO=2, PROFI=3, ROT=1, BLAU=2;
	final String kommentar[] = {"Viel Glück!!","Rot Gewinnt!","Blau gewinnt!","Unentschieden",
										 "Weiter so!","Gib alles!","Wow!","Nicht nachlassen!","Sehr gut!"};
	final int testdaten[] =
			{
			0,0,-1,0,-2,0,-3,0,     1,0,0,0,-1,0,-2,0,    2,0,1,0,0,0,-1,0,    3,0,2,0,1,0,0,0,		//waagrecht
			0,0,-1,-1,-2,-2,-3,-3,  1,1,0,0,-1,-1,-2,-2,  2,2,1,1,0,0,-1,-1,   3,3,2,2,1,1,0,0,		//diagonal
			0,0,-1,1,-2,2,-3,3,     1,-1,0,0,-1,1,-2,2,   2,-2,1,-1,0,0,-1,1,  3,-3,2,-2,1,-1,0,0,	//diagonal
			0,-3,0,-2,0,-1,0,0,     0,-2,0,-1,0,0,0,1,    0,-1,0,0,0,1,0,2,    0,0,0,1,0,2,0,3		//senkrecht
			};//nach priorität geordnet
	int feld[][] = new int[HOEHE][BREITE];
	int zeichnen[][] = new int[HOEHE][BREITE];
	int gefahr[][][] = new int[HOEHE][BREITE][ROT+BLAU];
	int hoehe[] = new int[BREITE];
	int prioritaet[] = new int[BREITE];
	TextArea text= new TextArea("",15,12,TextArea.SCROLLBARS_NONE);
   Choice anfang = new Choice();
   Choice level = new Choice();
	Image bild[] = new Image[3];
	int werBeginnt=ROT;
	int amZug=ROT;
	int derLevel=ANFAENGER;
	int sieger,total;
	long startzeit, endzeit;
	AudioClip yahoo, laugh, muh, boing, gong;
	Sprite sprite[] = new Sprite[ROT+BLAU];
	Image HintergrundBild = null;
   Graphics HintergrundGrafik = null;
   Dimension HintergrundGroesse = null;	
	FallThread fallThread;

	public void init()
		{
		laden();
		showStatus("Vier gewinnt  © t.heyn 2000");
    	addMouseListener(new MyMouse());							//GUI erstellen
    	setLayout(new BorderLayout());
    	Panel anzeige = new Panel();
    		anzeige.setLayout(new BorderLayout());
    		add("East",anzeige);
    	Label titel = new Label("Vier gewinnt",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("Rot  beginnt");	
      	anfang.add("Blau 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);
    	sprite[ROT] = new Sprite(bild[ROT],0,0);
		sprite[BLAU] = new Sprite(bild[BLAU],0,0);
  		neuesSpiel();
		}

	public void paint(Graphics g)									//aktuelles applet zeichnen
		{
		sprite[ROT].paint(g);
		sprite[BLAU].paint(g);
		for (int s=0;s<BREITE;s++)
			{
			for (int z=0;z<HOEHE;z++)
				{
				g.drawImage(bild[0],s*(GROESSE+STRICH),(HOEHE-z-1)*(GROESSE+STRICH),this);
				if (zeichnen[z][s] != 0)
					g.drawImage(bild[zeichnen[z][s]],STRICH+s*(GROESSE+STRICH),STRICH+(HOEHE-z-1)*(GROESSE+STRICH),this);
				}
			}
		}
			
	public final void update(Graphics g) //Flimmerfreie Grafik mit Double Buffering
		{
		Dimension dim = getSize();
		if((HintergrundBild==null) || (dim.width != HintergrundGroesse.width) || (dim.height != HintergrundGroesse.height))
			{
			HintergrundBild = createImage(dim.width, dim.height);
			HintergrundGroesse = dim;
			HintergrundGrafik = HintergrundBild.getGraphics();
			}
		HintergrundGrafik.clearRect(0, 0, HintergrundGroesse.width, HintergrundGroesse.height);
		paint(HintergrundGrafik);
		g.drawImage(HintergrundBild,0,0,null);
		}
		
	public void start()
		{
		FallThread fallThread=new FallThread();
		fallThread.start();
		}
		
	public void stop()
		{
		if (fallThread != null) fallThread.interrupt();
		}	

	public void itemStateChanged(ItemEvent e)					// all die doofen listener
		{
		String farbe=(String) anfang.getSelectedItem();
			if (farbe=="Rot  beginnt") werBeginnt=ROT;
			if (farbe=="Blau beginnt") werBeginnt=BLAU;
		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 MyMouse extends MouseAdapter
		{
		public void mousePressed(MouseEvent e)
			{
			schreiben(kommentar[sieger]);
			int s=(int)((e.getX()-(STRICH/2))/(GROESSE+STRICH));
			if (s>=BREITE || hoehe[s]>=HOEHE || sieger>0 || sprite[ROT].isMoving || sprite[BLAU].isMoving) return;
			sieger=siegtest(ROT,s);
			setzen(ROT,s);
			if (total==HOEHE*BREITE && sieger==0) sieger=3;
			kommentar[0]=kommentar[(int)(Math.random()*4+4)];
			schreiben(kommentar[0]);
			if (sieger>0) schluss();
			}
		}

	private class FallThread extends Thread
		{
		public void run()
			{
			while (! isInterrupted())
				{
				if (! sprite[amZug].isMoving) continue;
				startzeit = System.currentTimeMillis();
				//bewegen
				sprite[amZug].setSpeed(sprite[amZug].getSpeed()+3);//fall beschleunigen
				sprite[amZug].tick();
				//wenn unten kommt anderer dran
				int spalte = (int)((sprite[amZug].x - GROESSE/2 - STRICH)/(GROESSE+STRICH));
				if (sprite[amZug].y>=(HOEHE-hoehe[spalte])*(GROESSE+STRICH)+(GROESSE/2))
					{
					sprite[amZug].stopMovement();
					sprite[amZug].setSpeed(1);
					sprite[amZug].y=-GROESSE;
					zeichnen[hoehe[spalte]-1][spalte]=amZug;
					update(getGraphics());
					amZug=3-amZug;
					if(boing != null) boing.play();
					if (amZug==BLAU && sieger==0)
						{
						sieger=computerZugBerechnen();
						if (total==HOEHE*BREITE && sieger==0) sieger=3;
						if (sieger>0) schluss();
						}
					}
				//schlafen
				try
     	       	{
					update(getGraphics());
					endzeit = System.currentTimeMillis();	//nur solange schlafen wie noetig
					if (endzeit - startzeit < ZEIT) sleep(ZEIT - endzeit + startzeit);
					}
				catch (Exception e)
					{
					break;
					}
				}
			}
		}

	private void neuesSpiel()										
		{
		if (gong != null) gong.play();
		total=0;
		sieger=0;
		for (int farbe=ROT;farbe<=BLAU;farbe++)
			{
			sprite[farbe].setPosition(0,-GROESSE);
			sprite[farbe].stopMovement();
			sprite[farbe].setSpeed(1);
			}		
		for (int s=0;s<BREITE;s++)
			{
			for (int z=0;z<HOEHE;z++)
				{
				feld[z][s]=0;
				zeichnen[z][s]=0;
				gefahr[z][s][ROT ]=0;
				gefahr[z][s][BLAU]=0;
				}
			hoehe[s]=0;
			}
		repaint();
		schreiben(kommentar[sieger]);
		amZug=werBeginnt;
		if (amZug==BLAU) setzen(BLAU,(int)(Math.random()*BREITE));
		}

	private int computerZugBerechnen()						//berechnet den besten zug für den computer
		{
		int s;
		int dringendste=-90;
		if	(derLevel != ANFAENGER) gefahrtest();					//testet ganzes brett nach gefahren ab
		for (s=0;s<BREITE;s++)											//tests für jede spalte einzeln
			{
			prioritaet[s]=0;												//standardpriorität der spalte
			if (hoehe[s]==HOEHE) 										//test ob spalte voll
				{
				prioritaet[s]=-1000;
				continue;
				}
			//tests level anfänger
			if (prioritaet[s]==0)
				{
				if (siegtest(BLAU,s)==BLAU)							//siegtest -> sofort setzen
					{
					setzen(BLAU,s);
					return BLAU;
					}
				if (siegtest(ROT,s)==ROT)								//siegtest -> sofort setzen
					{
					setzen(BLAU,s);
					return 0;
					}
				if (tabutest(ROT,s)) 									//test ob spalte tabu -> nächste spalte
					{
					prioritaet[s]=-90;
					continue;
					}
				}
			//tests level normalo
			if (derLevel != ANFAENGER)
				{
				if (hoehe[s]==0) prioritaet[s]=-10;					//test auf leere spalte
				if (tabutest(BLAU,s)) prioritaet[s]=-85;			//test ob chance selbst verbaut
				if (prioritaet[s]>-80) prioritaet[s]+=20*naechsterZugTest(ROT,s);
				}
			//tests level profi
			if (derLevel==PROFI)	
				{
				if (doppeltest(BLAU,s)) prioritaet[s]=200;		//forcieren wenn 2 blaue übereinander
				if (prioritaet[s]>-80)
					{
					if (werBeginnt==ROT && hoehe[s]%2==1) prioritaet[s] += 5;//besser in ungerade zeilen
					if (werBeginnt==BLAU && hoehe[s]%2==0) prioritaet[s] += 5;
					prioritaet[s]+=25*naechsterZugTest(BLAU,s);
					prioritaet[s]-=10*uebernaechsterZugTest(BLAU,s);
					}
				}
			if (prioritaet[s]>=dringendste) dringendste=prioritaet[s];
			}
		//zufällig setzen in eine der spalten mit dringendster priorität
		boolean suchen=true;
		while (suchen)
			{
			s=(int)(Math.random()*BREITE);
			if (prioritaet[s]==dringendste) suchen=false;
			}
		setzen(BLAU,s);
		return 0;
		}

	private int siegtest(int farbe,int s)					//testet, ob 4 geschafft
		{
		int anzahl=0;
		int resultat=0;
		int sieger=0;
		feld[hoehe[s]][s]=farbe;
		for (int i=0;i<4;i++)//4 richtungen
			{
			for (int j=0;j<4;j++)//4 positionen
				{
				anzahl=0;
				for (int k=0;k<8;k+=2)//4*2 koordinaten
					{
					try
						{
						resultat=feld[hoehe[s]+testdaten[(32*i)+(8*j)+k+1]] [s+testdaten[(32*i)+(8*j)+k]];
						if (resultat==farbe) anzahl++;
						if (resultat==3-farbe) anzahl=0;
						}
					catch (ArrayIndexOutOfBoundsException e) {}
					if (anzahl==4)
						{
						feld[hoehe[s]][s]=0;
						sieger=farbe;
						}
					}
				}
			}
		feld[hoehe[s]][s]=0;
		return sieger;
		}

	private int naechsterZugTest(int farbe,int s)		//testet den nächsten zug
		{
		int hTemp=0;		
		int anzahl=0;
		feld[hoehe[s]][s]=farbe;										//setzen
		hoehe[s]++;
		for (int sx=0;sx<BREITE;sx++)									//anzahl entstehende siegpositionen zählen
			{
			if (hoehe[sx]==HOEHE) continue;							//wenn spalte voll dann nächste
			hTemp=hoehe[sx];
			for (int h=hTemp;h<HOEHE;h++)
				{
				if (gefahr[h][sx][farbe]==farbe) continue;
				feld[h][sx]=farbe;
				hoehe[sx]=h;
				if (siegtest(farbe,sx)==farbe)						//anzahl gefahren der spalte erhöhen
					{
					anzahl++;
					if (h>0 && h<HOEHE-1 && derLevel==PROFI)		//test auf doppelsiegposition (wichtig!!)
						{
						if (gefahr[h+1][sx][farbe]==farbe || gefahr[h-1][sx][farbe]==farbe) anzahl=9;
						}
					}
				feld[h][sx]=0;
				}
			hoehe[sx]=hTemp;
			}
		hoehe[s]--;
		feld[hoehe[s]][s]=0;
		return anzahl;
		}

	private int uebernaechsterZugTest(int farbe,int s)	//testet den übernächsten zug
		{
		int hTemp=0;
		int anzahl=0;
		if (hoehe[s]<=HOEHE-2)
			{
			feld[hoehe[s]][s]=farbe;
			hoehe[s]++;
			anzahl=naechsterZugTest((3-farbe),s);					//schauen, ob andere farbe gefahren schafft
			hoehe[s]--;
			feld[hoehe[s]][s]=0;
			}
		return anzahl;
		}

	private void gefahrtest()									//testet, ob hier 4 erreicht werden könnten
		{
		int hTemp=0;
		for (int s=0;s<BREITE;s++)
			{
			hTemp=hoehe[s];
			for (int h=hTemp;h<HOEHE;h++)
				{
				hoehe[s]=h;
				for (int farbe=BLAU;farbe>=ROT;farbe--)
					{
					feld[h][s]=farbe;
					gefahr[h][s][farbe]=siegtest(farbe,s);			//wenn gefahr, dann markieren
					feld[h][s]=0;
					}
				}
			hoehe[s]=hTemp;
			}
		}

	private boolean tabutest(int farbe, int s)			//testet, ob hier setzen verboten ist
		{
		boolean tabu=false;
		if (hoehe[s]<HOEHE-1)
			{
			if (gefahr[hoehe[s]+1][s][farbe]==farbe) tabu=true;
			}
		return tabu;
		}

	private boolean doppeltest(int farbe, int s)			//testet, ob blau eine doppelsiegposition hat
		{
		boolean doppelter=false;
		for (int h=hoehe[s];h<HOEHE-1;h++)
			{
			if (gefahr[h][s][farbe]+gefahr[h+1][s][farbe]==2*farbe) doppelter=true;
			}
		return doppelter;
		}
	
	private void schluss()
		{
		schreiben(kommentar[sieger]);
		if (sieger==ROT && yahoo != null) yahoo.play();
		if (sieger==BLAU && laugh != null) laugh.play();
		if (sieger==3 && muh != null) muh.play();
		}

	private void setzen(int farbe, int s)
		{
		feld[hoehe[s]][s]=farbe;
		hoehe[s]++;
		total++;
		sprite[farbe].startMovement();
		sprite[farbe].x = (int)(STRICH + GROESSE/2 + s*(GROESSE+STRICH));
		}

	private void schreiben(String kommentar)
		{
		text.setText("Wer zuerst vier waagrecht, senkrecht oder diagonal hat, gewinnt. Du hast Rot,"
						+" der Computer Blau. Klicke in die Spalte, in welche du setzen willst.");
		text.appendText("\n\n\n"+kommentar);
		}

	private void laden()
		{
		yahoo= getAudioClip(getCodeBase(),"yahoo.au");
		muh=  getAudioClip(getCodeBase(),"muh.au");
		laugh= getAudioClip(getCodeBase(),"laugh.au");
		boing= getAudioClip(getCodeBase(),"boing.au");
		gong= getAudioClip(getCodeBase(),"gong.au");
    	MediaTracker tracker=new MediaTracker(this);
    	for (int i=0;i<=2;i++)
    		{
    		bild[i] = getImage(getCodeBase(),i+".gif");
    		tracker.addImage(bild[i],1);
    		}
    	try
    		{
    		tracker.waitForAll();
    		}
    	catch(InterruptedException e) {}
		}
	}