/*
Dammen versie 1.11 Applet
Hermen Jan Hupkes
22 mei 2000
Bug verwijderd!!!
Implementatie van een simpel damprogramma met behulp van het alpha-beta pruning algoritme.
Vereist Java 1.1 en werkt dus niet met Java 1.0
*/




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

public class pListNode {
    pListNode next;
    Zet z;
} // class pListNode
public class Stapel {
    pListNode root;
    public void Push(Zet z) {
	pListNode p;
	p=new pListNode();
	p.next =root;
	root=p;
	p.z=z; // let op -> na afloop moeten we dus NIET de binnengekomen z verwijderen...
    }
    public Zet Pop() {
	pListNode p;
	p=root;
	root=p.next;
	Zet tpz;
	tpz=p.z;
	//delete p;
	return tpz;
    }
    public boolean IsEmpty() {return root == null;}
    public Stapel() {root = null;}
    // Gooi alles uit de stapel...
    public void Flush() {
      root=null;
    }
    //public ~Stapel() {
    // Flush();
    //}  ?????? Waarom mag ik geen destructors?
}  // class Stapel
public class HashTab {
  public pListNode[] t;
  public HashTab() {
    t = new pListNode[10000];
    int i;
    for (i=0;i<10000;i++) {
      t[i]=null;
    }
  } // constructor
  public void Flush() {
    for (int i=0;i<10000;i++) {
      t[i]=null;
    }
  } // Flush
  // Voegt zet z toe aan de Hashtable
  public void Add(Zet z) {
    int tpidx;
    tpidx =z.HashCode();
    pListNode q;
    q=new pListNode();
    q.z=z;
    q.next=t[tpidx];
    t[tpidx]=q;
  }
  // Kijkt of Zet z, tot en met nZetten overeenkomt met
  // zet uit de HashTable
  public boolean Search(Zet z, int nZetten, int totN) {
    int tpidx;
    tpidx =z.HashCode(totN);
    pListNode p;
    p=t[tpidx];
    while (p!=null) {
      if (p.z.Equals(z, nZetten)) {
        // Nu even een aantal gegevens aanvullen in zet z
        p.z.MakeEqual(z, nZetten);
        return true;
      }
      p=p.next;
    } // while
    // niet gevonden...
    return false;
  } // search
} // class HashTab
public class Zet {
    public int[] x; // array met x coordinaten van verschillende stappen xi in (0..10)
    public int[] y; // array met y coordinaten van verschillende stappen yi in (0..10)
    public int n; // geeft aan uit hoeveel stappen de zet is opgebouwd. n= 0 -> schuifzet...
    public int[] xg;
    public int[] yg;
    public boolean[] dam;
    public boolean DamGeworden=false;
    // non default constructor die tpZet meekrijgt
    public Zet(tpZet tpZ) {
      int i;
      n = tpZ.n;
      if (tpZ.n==0) {
        x = new int[2];
        x[0]=tpZ.x[0];
        x[1]=tpZ.x[1];
        y = new int[2];
        y[0]=tpZ.y[0];
        y[1]=tpZ.y[1];
      } else {
        x = new int[tpZ.n+1];
        y = new int[tpZ.n+1];
        xg = new int[tpZ.n];
        yg = new int[tpZ.n];
        dam = new boolean[tpZ.n];
        for (i=0;i<tpZ.n;i++) {
          x[i] = tpZ.x[i];
          y[i]=tpZ.y[i];
          xg[i]=tpZ.xg[i];
          yg[i]=tpZ.yg[i];
          dam[i]=tpZ.dam[i];
        }
        x[n]=tpZ.x[n];
        y[n]=tpZ.y[n];
      } // if tpZ.n ==0
    }
    // de volgende waarde geeft de HashCode terug van de huidige Zet.
    // Dit is een waarde tussen de 0 en 10000 die hopelijk een beetje uniek is...
    // Op deze manier kan sneller gecontroleerd worden of een zet geldig is...
    public int HashCode() {
      int tpn;
      if (n==0) {
        tpn=1;
      } else {
        tpn = n;
      }
      return (((tpn+1)*(x[0]*y[0]*1000 + x[1]*y[1])) % 10000);
    }
    // Deze aangepaste versie van Hashcode wordt gebruikt voor het geval
    // een zet nog niet helemaal gevuld is...
    public int HashCode(int q) {
      return (((q+1)*(x[0]*y[0]*1000 + x[1]*y[1])) % 10000);
    }
    // Geeft terug of Zet z identiek is aan deze zet...
    // (tot en met nZetten onderdelen)
    public boolean Equals(Zet z, int nZetten) {
      int i;
      int tpn;
      if (n==0) {
        tpn=1;
      } else {
        tpn = n;
      }
      if (nZetten > tpn) {
        return false;
      } else {
        for (i=0;i<=nZetten;i++) {
          if ((x[i] != z.x[i]) || (y[i] != z.y[i]) ) {
            return false;
          }
        }
      }
      // ze zijn gelijk...
      return true;
    }
    // Gegevens over geslagen stukken aanvullen aan Zet z
    public void MakeEqual(Zet z, int nZetten) {
      int i;
      if (n!=0) {
        z.xg = new int[nZetten];
        z.yg = new int[nZetten];
        z.dam = new boolean[nZetten];
        for (i=0;i<nZetten;i++) {
          z.xg[i]=xg[i];
          z.yg[i]=yg[i];
          z.dam[i]=dam[i];
        } // for i
      }  // if n!=0
    } // MakeEqual


} // class Zet
// Zet met vaste grootte, voor tijdelijke zetten die aangevuld worden...
public class tpZet {
    public int[] x; // array met x coordinaten van verschillende stappen xi in (0..10)
    public int[] y; // array met y coordinaten van verschillende stappen yi in (0..10)
    public int[] xg; // array met x coordinaten van geslagen stukken
    public int[] yg; // array met y coordinaten van geslagen stukken...
    public boolean[] dam; // array die aangeeft of geslagen stenen dammen waren...
    public int n; // geeft aan uit hoeveel stappen de zet is opgebouwd.
    public tpZet() {
      x = new int[20];
      y = new int[20];
      xg = new int[20];
      yg = new int[20];
      dam = new boolean[20];
    } // tpZet
}
public class Vakje {
    int k; // kleur
    boolean dam; // wel of geen dam
}
public class Dambord implements Runnable {
  final int nRows = 10;
  final int nCols = 10;
  final int sqWhite = 1;
  final int sqBlack = 2;
  final int sqEmpty = 0;
  public Vakje[][] Bord;
  public int mark_x=-1;
  public int mark_y=-1;
  public boolean blAfgelopen=false;
  public boolean blWitGewonnen = false;
  public boolean blComputerZetBepaald;
  public Zet ComputerZet;
  private Thread AIThread;
    // initialiseren dambord op begin opstelling...
    public Dambord(int niveau) {
      Bord = new Vakje[nCols][nRows];
      maxDiepte=niveau;
      //AIThread = new Thread(this, "AIThread");
      int i,j;
      for (i=0;i<nRows;i++) {
        for (j=0;j<nCols;j++) {
          Bord[j][i]=new Vakje();
          if (((i+j) % 2) == 0) {
	    if (i <=3) {
		Bord[j][i].k=sqWhite;
		Bord[j][i].dam=false;
	    } else if ( i >=6 ) {
		Bord[j][i].k=sqBlack;
		Bord[j][i].dam=false;
	    } else {
		Bord[j][i].k=sqEmpty;
		Bord[j][i].dam=false;
	    }
	  } else {
	    Bord[j][i].k=sqEmpty;
	    Bord[j][i].dam=false;
	  }
	} // for j
       } // for i
	// Nu zijn alle stukken neergezet voor begin opstelling.
       VulStapel(sqWhite);
    } // DamBord()
    // Copyconstructor...
    public Dambord(Dambord d) {
      Bord = new Vakje[nCols][nRows];
      int i,j;
      for (i=0;i<nCols;i++) {
        for (j=0;j<nRows;j++) {
          Bord[i][j] = new Vakje();
          Bord[i][j].k=d.Bord[i][j].k;
          Bord[i][j].dam = d.Bord[i][j].dam;
        } // for j
      } // for i
    } // Dambord(Dambord d)
    public int tpMaxSlagen;
    private Stapel ZetStapel;
    private HashTab ZetHashTable;
    private void ZetOpStapel(tpZet tpZ, Stapel s) {
      Zet z;
      int i;
      z = new Zet(tpZ);
      if (s==null) {
        ZetStapel.Push(z);
        ZetHashTable.Add(z);
      } else {
        s.Push(z);
      }
    }
    private boolean IsOpVeld(int x , int y) {
      if ((x < 0) || (x >= nCols) || (y < 0) || (y >=nRows)) {
        return false;
      } else {
        return true;
      } // if
    } // IsOpVeld

    // Bepaal maximale aantal slagen wat gemaakt kan worden met de steen op (x,y)
    // van Speler speler. Indien we een maximum hebben, kopieren we de gevonden zet
    // en zetten we hem op de stapel van gevonden zetten...
    private void BepaalMaxSlagen(tpZet tpZ, int speler, int x , int y , Stapel s ) {
       int i,j,k;
       boolean blSlagZetGevonden=false;
       boolean tpDam;
       boolean blMagNeerstrijken;
       boolean blKlaar;
       int tpXSlag=0;
       int tpYSlag=0;
       if (!Bord[x][y].dam) {
         for (i=-1;i<=1;i+=2) {
           for (j=-1;j<=1;j+=2) {
             if (IsOpVeld(x+2*i , y+2*j)) {
             if ((Bord[x+i][y+j].k==3-speler) && (Bord[x+2*i][y+2*j].k==sqEmpty)) {
               // We hebben een slagzet te pakken!
               blSlagZetGevonden = true;
               tpZ.n++;
               tpZ.x[tpZ.n]=x+2*i;
               tpZ.y[tpZ.n]=y+2*j;
               tpZ.xg[tpZ.n-1]=x+i;
               tpZ.yg[tpZ.n-1]=y+j;
               tpZ.dam[tpZ.n-1]=Bord[x+i][y+j].dam;
               // Zet ook uitvoeren
               Bord[x][y].k=sqEmpty;
               tpDam = Bord[x+i][y+j].dam;
               Bord[x+i][y+j].k=sqEmpty;
               Bord[x+2*i][y+2*j].k=speler;
               Bord[x+2*i][y+2*j].dam=false;
               BepaalMaxSlagen(tpZ, speler, x+2*i , y+2*j,s);
               // Nu bord weer terugzetten...
               tpZ.n--;
               Bord[x][y].k=speler;
               Bord[x][y].dam=false;
               Bord[x+i][y+j].k=3-speler;
               Bord[x+i][y+j].dam=tpDam;
               Bord[x+2*i][y+2*j].k=sqEmpty;
             } // if
             } // if op veld.
           } // for j
         } // for i
         if (!blSlagZetGevonden) {
           if (tpZ.n >= tpMaxSlagen) {
             // Eerst kijken of we nog schuifzetten mogen doen...
             // Voor elke toegestane schuifzet hem op de stapel zetten...
             if (tpZ.n==0) {
               for (i=-1;i<=1;i+=2) {
                 if (speler ==sqWhite) {
                   // alleen vooruit
                    if (IsOpVeld(x+i, y+1)) {
                    if (Bord[x+i][y+1].k==sqEmpty) {
                       tpZ.x[1]=x+i;
                       tpZ.y[1]=y+1;
                       ZetOpStapel(tpZ,s);
                    }
                    } // if IsOpVeld
                  } else {
                    if (IsOpVeld(x+i, y-1)) {
                    if (Bord[x+i][y-1].k==sqEmpty) {
                      tpZ.x[1]=x+i;
                      tpZ.y[1]=y-1;
                      ZetOpStapel(tpZ,s);
                    }
                    } // if isOpVeld
                  } // else
               } // for i
             } else {
               if (tpZ.n > tpMaxSlagen) {
                 if (s==null) {
                   ZetStapel.Flush();
                   ZetHashTable.Flush();
                 } else {
                   s.Flush();
                 }
                 tpMaxSlagen = tpZ.n;
               }
               ZetOpStapel(tpZ,s);
             }
           }
         }
       } else {
         // nu het geval dat er een dam staat.
         // Dit is dus veel moeilijker...
         // Eerst richting bepalen
         for (i=-1;i<=1;i+=2) {
           for (j=-1;j<=1;j+=2) {
             blKlaar=false;
             blMagNeerstrijken=false;
             k=1;
             while (!blKlaar) {
               if (!IsOpVeld(x+k*i, y+k*j)) {
                 blKlaar = true;
               } else {
                 if (!blMagNeerstrijken) {
                   if (Bord[x+k*i][y+k*j].k == speler ) {
                     blKlaar = true;
                   } else if (Bord[x+k*i][y+k*j].k == 3 - speler) {
                     tpXSlag = x+k*i;
                     tpYSlag = y+k*j;
                     blMagNeerstrijken = true;
                   }
                 } else {
                   if (Bord[x+k*i][y+k*j].k == sqEmpty) {
                     // we hebben een geldig neerstrijkpunt voor een slag zet...
                     blSlagZetGevonden = true;
                     tpZ.n++;
                     tpZ.x[tpZ.n]=x+k*i;
                     tpZ.y[tpZ.n]=y+k*j;
                     tpZ.xg[tpZ.n-1]=tpXSlag;
                     tpZ.yg[tpZ.n-1]=tpYSlag;
                     tpZ.dam[tpZ.n-1]=Bord[tpXSlag][tpYSlag].dam;
                     // Zet ook uitvoeren
                     Bord[x][y].k=sqEmpty;
                     tpDam = Bord[tpXSlag][tpYSlag].dam;
                     Bord[tpXSlag][tpYSlag].k=sqEmpty;
                     Bord[x+k*i][y+k*j].k=speler;
                     Bord[x+k*i][y+k*j].dam=true;
                     BepaalMaxSlagen(tpZ, speler, x+k*i , y+k*j,s);
                     // Nu bord weer terugzetten...
                     tpZ.n--;
                     Bord[x][y].k=speler;
                     Bord[x][y].dam=true;
                     Bord[tpXSlag][tpYSlag].k=3-speler;
                     Bord[tpXSlag][tpYSlag].dam=tpDam;
                     Bord[x+k*i][y+k*j].k=sqEmpty;
                   } else {
                     blKlaar = true;
                   }
                 } // if !blMagNeerstrijken
               }
               k++;
             } // while !blKlaar
           } // for j
         } // for i
         // Nu afhandelen van de huidige zet...
         if (!blSlagZetGevonden) {
           if (tpZ.n >= tpMaxSlagen) {
             // Eerst kijken of we nog schuifzetten mogen doen...
             // Voor elke toegestane schuifzet hem op de stapel zetten...
             if (tpZ.n==0) {
               for (i=-1;i<=1;i+=2) {
                 for (j=-1;j<=1;j+=2) {
                   blKlaar = false;
                   k=1;
                   while (!blKlaar) {
                     if (!IsOpVeld(x+k*i, y+k*j)) {
                       blKlaar = true;
                     } else {
                       if (Bord[x+k*i][y+k*j].k==sqEmpty) {
                         tpZ.x[1]=x+k*i;
                         tpZ.y[1]=y+k*j;
                         ZetOpStapel(tpZ,s);
                       } else {
                         blKlaar = true;
                       }
                     }
                     k++;
                   } // while
                 } // for j
               } // for i
             } else {
               if (tpZ.n > tpMaxSlagen) {
                 if (s==null) {
                   ZetStapel.Flush();
                   ZetHashTable.Flush();
                 } else {
                   s.Flush();
                 }
                 tpMaxSlagen = tpZ.n;
               }
               ZetOpStapel(tpZ,s);
             }
           }
         } // if !blSlagZet Gevonden
       } // if dam
    } // Bepaal Max Slagen
    public void VulStapel(int speler) {
      ZetStapel = new Stapel();
      ZetHashTable = new HashTab();
      tpMaxSlagen = 0;
      int i,j;
      tpZet tpZ;
      for (i=0;i<nCols;i++) {
        for (j=0;j<nRows;j++) {
          if (Bord[i][j].k==speler) {
            tpZ = new tpZet();
            tpZ.n=0;
            tpZ.x[0]=i;
            tpZ.y[0]=j;
            BepaalMaxSlagen(tpZ , speler, i , j, null);
            tpZ=null;
          }
        } // for j
      } // for i
    } // VulStapel...
    // Speciale versie voor bij berekenen beste zet computer...
    public void VulStapel(int speler, Stapel s) {
      tpMaxSlagen = 0;
      int i,j;
      tpZet tpZ;
      for (i=0;i<nCols;i++) {
        for (j=0;j<nRows;j++) {
          if (Bord[i][j].k==speler) {
            tpZ = new tpZet();
            tpZ.n=0;
            tpZ.x[0]=i;
            tpZ.y[0]=j;
            BepaalMaxSlagen(tpZ , speler, i , j , s);
            tpZ=null;
          }
        } // for j
      } // for i
    } // VulStapel...
    // de Volgende functie bepaalt een computerzet om te doen...
    public Zet ComputerZet() {
      // Bepaal eerst alle mogelijke computer zetten...
      VulStapel(sqBlack);
      // En geeft nu de eerste de beste zet terug...
      return ZetStapel.Pop();
    }
    // de volgende functie evalueert de huidige stand.
    // Een hoge score is gunstig voor zwart, een lage score is gunstig voor wit...
    // Dit gaan we verbeteren...
    // Dammen tellen nog maar voor 5 stenen mee...
    // Een steen op de 9de rij moeten we voor 4 meetellen
    // en een steen op de 8de rij voor 3...
    // en een steen op de 7de rij voor 2...
    private int Evaluate() {
      int tp=0;
      int i,j;
      int tpZwartCount=0, tpWitCount=0;
      for (i=0;i<nCols;i++) {
        for (j=0;j<nRows;j++) {
          if (Bord[i][j].k==sqWhite) {
            if (Bord[i][j].dam) {
              tp-=6;
            } else {
              if (j >=(nRows - 4)) {
                tp-=j-3;
              } else {
                tp-=2;
              }
            }
            tpWitCount++;
          } else if (Bord[i][j].k==sqBlack) {
            if (Bord[i][j].dam) {
              tp+=6;
            } else {
              if (j<=3) {
                tp+=6-j;
              } else {
                tp+=2;
              }
            }
            tpZwartCount--;
          }
        } // for j
      }// for i
      if (tpWitCount==0) {
        return 1000;
      } else {
        if (tpZwartCount==0) {
          return -1000;
        } else {
          return tp;
        }
      }
    }  // Evaluate
    public void VerwerkSpelerZet(Zet z) {
      ApplyZet(z);
      // Dan computerzet uitvoeren...
      AIThread = new Thread(this, "AIThread");
      AIThread.start();
    } // VewerkSpelerZet(Zet z)
    public void run() {
      Thread me = Thread.currentThread();
      Zet z;
      if (me==AIThread) {
        z=ComputerZetAI();
        if (z==null) {
          blAfgelopen=true;
          blWitGewonnen=true;
        } else {
          ApplyZet(z);
          VulStapel(sqWhite);
          if (ZetStapel.IsEmpty()) {
            blAfgelopen=true;
            blWitGewonnen=false;
          }
        }
        SetComputerZet(z);
        //blComputerZetBepaald=true;
        //notifyAll();
      } // if me=AIThread
    }
    public synchronized void SetComputerZet(Zet z) {
      blComputerZetBepaald = true;
      ComputerZet=z;
      notifyAll();
    }
    public synchronized Zet GetComputerZet() {
      while (!blComputerZetBepaald) {
        try {
          wait();
        } catch (InterruptedException E) {};
      }
      return ComputerZet;
    }
    // de volgende functie bepaalt een niet zo maar willekeurige computer zet om te doen...
    public Zet ComputerZetAI() {
      tpBeste=null;
      BestEval(0,-MaxEval, MaxEval);
      return tpBeste;
    }
    private Zet tpBeste;
    public int maxDiepte =2 ;
    final int MaxEval = 10000;
    private int BestEval(int diepte, int alpha, int beta) {
      int tpAlpha, tpBeta;
      Zet z;
      int tpEval;
      boolean blBereikt;
      boolean tpDam;
      boolean blEerste = true;
      Stapel s;
      s = new Stapel();
      if (diepte==maxDiepte) {
        return Evaluate();
      } else {
        if ((diepte % 2)==0) {
          // Zwart moet een zet doen...
          VulStapel(sqBlack,s);
          if (s.IsEmpty()) {
            // We hebben verloren...
            return -1000;
          } else {
            // Onze BestEval is het maximum van alle evals
            // die we kunnen krijgen door de zetten uit
            // de stapel toe te passen...
            // Als we een zet vinden die eval > beta teruggeeft
            // kunnen we direct kappen...
            // We hebben alleen iets aan evals die we terugkrijgen
            // die hoger zijn dan alpha en lager dan beta...
            tpAlpha = alpha;
            tpBeta=beta;
            blBereikt=false; // geeft aan dat alpha nog niet is bereikt...
            while (!s.IsEmpty()) {
              z=s.Pop();
              if (blEerste) {
                if ((diepte==0) && s.IsEmpty()) {
                  // we kunnen meteen deze zet teruggeven...
                  // want er is maar 1 zet toegestaan...
                  tpBeste = z;
                  return 0;
                }
                blEerste =false;
              }
              tpDam = Bord[z.x[0]][z.y[0]].dam;
              ApplyZet(z);
              tpEval=BestEval(diepte+1 , tpAlpha, tpBeta);
              DeApplyZet(z);
              Bord[z.x[0]][z.y[0]].dam=tpDam;
              // oppassen met dammen etc...
              // Als tpEval groter is dan onze bovengrens,
              // kunnen we direct afkappen en MaxEval teruggeven
              if (tpEval > beta) {
                return MaxEval;
              } else {
                // Indien tpEval groter is dan tpAlpha, betekent dit
                // dat we een beter maximum hebben gevonden...
                // We kunnen nu dus de ondergrens aanscherpen
                if (tpEval >= tpAlpha) {
                  tpAlpha=tpEval;
                  blBereikt = true;
                  if (diepte==0) {
                    // Ook nog even de zet zeggen die
                    // aanleiding gaf tot deze hogere evaluatie...
                    tpBeste=z;
                  }
                } // if tpEval > tpAlpha
              } // if tpEval > beta)
            } // while
            if (blBereikt) {
              return tpAlpha;
            } else {
              return -MaxEval;
            }
          } // if zetstapel.IsEmpty;
        } else { // if diepte % 2 ==0
          // nu is dus wit aan zet
          VulStapel(sqWhite,s);
          if (s.IsEmpty()) {
            // Wit heeft verloren -> zwart heeft gewonnen...
            return +1000;
          } else {
            // Onze BestEval is het minimum van alle evals
            // die we kunnen krijgen door de zetten uit
            // de stapel toe te passen...
            // Als we een zet vinden die eval < alpha teruggeeft
            // kunnen we direct kappen...
            // We hebben alleen iets aan evals die we terugkrijgen
            // die hoger zijn dan alpha en lager dan beta...
            tpAlpha = alpha;
            tpBeta=beta;
            blBereikt=false; // geeft aan dat beta nog niet is bereikt...
            while (!s.IsEmpty()) {
              z=s.Pop();
              tpDam = Bord[z.x[0]][z.y[0]].dam;
              ApplyZet(z);
              tpEval=BestEval(diepte+1 , tpAlpha, tpBeta);
              DeApplyZet(z);
              Bord[z.x[0]][z.y[0]].dam=tpDam;
              // oppassen met dammen etc...
              // Als tpEval kleiner is dan onze ondergrens alpha,
              // kunnen we direct afkappen en MinEval teruggeven
              if (tpEval < alpha) {
                return -MaxEval;
              } else {
                // Indien tpEval kleiner is dan tpBeta, betekent dit
                // dat we een kleiner minimum hebben gevonden...
                // We kunnen nu dus de bovengrens aanscherpen
                if (tpEval <= tpBeta) {
                  tpBeta=tpEval;
                  blBereikt = true;
                } // if tpEval <= tpBeta
              } // if tpEval > beta)
            } // while
            if (blBereikt) {
              return tpBeta;
            } else {
              return MaxEval;
            }
          } // if zetstapel.IsEmpty;
        } // else (if diepte % 2 ==1)
      } //  if diepte == maxDiepte
    } // BestEval

    // geeft terug of een zet is toegestaan...
    public boolean IsZetToegestaan(Zet z, int nZetten, int totN) {
      return ZetHashTable.Search(z, nZetten, totN);
    }
    // Voert daadwerkelijk een zet uit...
    public void ApplyZet(Zet z) {
      int i;
      boolean tpDam;
      tpDam=Bord[z.x[0]][z.y[0]].dam;
      if (z.n==0) {
        Bord[z.x[1]][z.y[1]].k=Bord[z.x[0]][z.y[0]].k;
        Bord[z.x[1]][z.y[1]].dam=Bord[z.x[0]][z.y[0]].dam;
        Bord[z.x[0]][z.y[0]].k=sqEmpty;
        Bord[z.x[0]][z.y[0]].dam=false;
        if ((Bord[z.x[1]][z.y[1]].k==sqWhite) && (z.y[1]==nRows-1)) {
           Bord[z.x[1]][z.y[1]].dam=true;
           if (!tpDam) {
             z.DamGeworden=true;
           }
        }
        if ((Bord[z.x[1]][z.y[1]].k==sqBlack) && (z.y[1]==0)) {
           Bord[z.x[1]][z.y[1]].dam=true;
           if (!tpDam) {
             z.DamGeworden=true;
           }
        }
      } else {
        // Bug oplossing!!!
        // Eerst de te verwijderen stenen verwijderen....
        for (i=0;i<z.n;i++) {
          Bord[z.xg[i]][z.yg[i]].k=sqEmpty;
          Bord[z.xg[i]][z.yg[i]].dam=false;
        } // for i
        // Dan pas nieuwe steen plaatsen...
        if (!((z.x[z.n]==z.x[0]) && (z.y[z.n]==z.y[0]))) {
          Bord[z.x[z.n]][z.y[z.n]].k=Bord[z.x[0]][z.y[0]].k;
          Bord[z.x[z.n]][z.y[z.n]].dam=Bord[z.x[0]][z.y[0]].dam;
          Bord[z.x[0]][z.y[0]].k=sqEmpty;
          Bord[z.x[0]][z.y[0]].dam=false;
        }
        if ((Bord[z.x[z.n]][z.y[z.n]].k==sqWhite) && (z.y[z.n]==nRows-1)) {
           Bord[z.x[z.n]][z.y[z.n]].dam=true;
           if (!tpDam) {
             z.DamGeworden=true;
           }
        }
        if ((Bord[z.x[z.n]][z.y[z.n]].k==sqBlack) && (z.y[z.n]==0)) {
           Bord[z.x[z.n]][z.y[z.n]].dam=true;
           if (!tpDam) {
             z.DamGeworden=true;
           }
        }
      } // if z.n==0
    } // ApplyZet
    // Undo-t een zet ...
    public void DeApplyZet(Zet z) {
      int i;
      int tegenspeler;
      if (z.n==0) {
        Bord[z.x[0]][z.y[0]].k=Bord[z.x[1]][z.y[1]].k;
        tegenspeler = 3- Bord[z.x[0]][z.y[0]].k;
        Bord[z.x[0]][z.y[0]].dam=Bord[z.x[1]][z.y[1]].dam;
        Bord[z.x[1]][z.y[1]].k=sqEmpty;
        Bord[z.x[1]][z.y[1]].dam=false;
      } else {
        // Eerst steen terugplaatsen...
        if (!((z.x[z.n]==z.x[0]) && (z.y[z.n]==z.y[0]))) {
          Bord[z.x[0]][z.y[0]].k=Bord[z.x[z.n]][z.y[z.n]].k;
          Bord[z.x[0]][z.y[0]].dam=Bord[z.x[z.n]][z.y[z.n]].dam;
          Bord[z.x[z.n]][z.y[z.n]].k=sqEmpty;
          Bord[z.x[z.n]][z.y[z.n]].dam=false;
        }
        tegenspeler = 3- Bord[z.x[0]][z.y[0]].k;
        // dan verwijderde stenen herplaatsen.!
        for (i=0;i<z.n;i++) {
          Bord[z.xg[i]][z.yg[i]].k=tegenspeler;
          Bord[z.xg[i]][z.yg[i]].dam=z.dam[i];
        } // for i
      } // if z.n==0
      if (z.DamGeworden) {
        Bord[z.x[0]][z.y[0]].dam=false;
      }
    } // DeApplyZet
    public void GetVakje(Dimension d , int xpos , int ypos) {
      int xoff = d.width / 10;
      int yoff = d.height / 10;
      mark_x = (xpos / xoff);
      mark_y = nRows - 1 - (ypos / yoff);
    }

}
public class DamSpel extends Panel implements MouseListener , Runnable {
  private Dambord Bord;
  private Dambord tpBord;
  private Thread Processing;
  private int status;
  private int simulStatus;
  private int simulStap;
  final int smstSpelerZet=1;
  final int smstComputerZet=2;
  final int stGewonnen =-2;
  final int stVerloren =-3;
  final int stSimulating = -1;
  final int stAanDeBeurt =0;
  final int stEersteVakGeselecteerd = 1;
  final int sqWhite = 1;
  final int sqBlack = 2;
  final int sqEmpty = 0;
  private tpZet tpz;
  private Zet CompZet;
  private Zet SpelerZet;
  private int tpAantalStappen;
  private Stapel ZetStapel;
  private Dimension BigDim;
  private StatusPanel Stat;
  public int niveau=2;
  public DamSpel(Dimension d, StatusPanel s) {
    BigDim=d;
    Stat = s;
    addMouseListener(this);
    Bord = new Dambord(niveau);
    ZetStapel = new Stapel();
    //Processing = new Thread(this, "Processing");
    status=stAanDeBeurt;
    tpAantalStappen=1;
  } // init
    public Dimension preferredSize() {
 	return minimumSize();
    }
    public synchronized Dimension minimumSize() {
	Dimension d = new Dimension();
	d.width = BigDim.width;
	d.height = BigDim.height * 82 / 100;
	return d;
    }
    public String strNiveau() {
      if (niveau==2) {
        return "Niveau: Makkelijk";
      } else if (niveau==4) {
        return "Niveau: Neutraal";
      } else if (niveau == 6) {
        return "Niveau: Moeilijk";
      } else if (niveau==8) {
        return "Niveau: Onmogelijk";
      }
      return "";
    }
  public void MaakVakjeGroen(int x, int y, boolean blGroen ) {
    Graphics g = getGraphics();
    int i=x,j=y;
    if (g!=null) {
      Dimension d = getSize();
      int xoff = d.width / 10;
      int yoff = d.height / 10;
      Dambord db = tpBord;
      if (blGroen) {
        g.setColor(Color.green);
      } else {
        g.setColor(Color.gray);
      }
      g.fillRect(x*xoff, (db.nRows -1 - y)*yoff , xoff , yoff);
      if (db.Bord[x][y].k==sqWhite) {
        g.setColor(Color.white);
        g.fillOval(xoff * i + (xoff / 4) , yoff * (db.nCols - 1 -j) + (yoff / 4) , xoff /2 , yoff /2);
        if (db.Bord[i][j].dam) {
          g.setColor(Color.black);
          g.fillOval(xoff * i + (xoff *3 / 8) , yoff * (db.nCols -1-j) + (yoff * 3 / 8) , xoff / 4 , yoff / 4);
        }
      } else if (db.Bord[i][j].k==sqBlack) {
        g.setColor(Color.black);
        g.fillOval(xoff *  i + (xoff / 4) , yoff * (db.nCols -1 -j)+(yoff / 4) , xoff / 2 , yoff / 2);
        if (db.Bord[i][j].dam) {
          g.setColor(Color.white);
          g.fillOval(xoff * i + (xoff *3 / 8) , yoff * (db.nCols -1-j) + (yoff * 3 / 8) , xoff / 4 , yoff / 4);
        }
      }
    }
  } // MaakVakjeGroen
  public void paint(Graphics g) {
    Dimension d = getSize();
    int xoff = d.width / 10;
    int yoff = d.height / 10;


    Dambord db;
    if (status==stSimulating) {
      db = tpBord;
    } else {
      db = Bord;
    }
      int tpx=-1, tpy=-1;
      if ((status == stSimulating) && (simulStatus!=-1)) {
        if (simulStatus==smstSpelerZet) {
          tpx=tpz.x[simulStap];
          tpy=tpz.y[simulStap];
        } else {
          if (simulStatus ==smstComputerZet) {
              tpx=CompZet.x[simulStap];
              tpy=CompZet.y[simulStap];
             }
        }
        g.setColor(Color.green);
        g.fillRect(tpx*xoff, (db.nRows -1 - tpy)*yoff , xoff , yoff);
      } // if (Status ==stSimulating)
      // vierkanten tekenen.
      int i,j,k;
      for (i=0;i<=9;i++) {
        for (j=0;j<=9;j++) {
          if (((i+j) % 2)==0)  {
            g.setColor(Color.gray);
          } else {
            g.setColor(Color.white);
          }
          // Alleen als geen speciaal vakje, vakje kleuren...
          if ((tpx!=i) || (tpy!=j)) {
            g.fillRect(i*xoff, (db.nRows -1-j)*yoff , xoff , yoff);
          }
          if ((status>0) && (tpz.x[0] ==i) && (tpz.y[0]==j)) {
            g.setColor(Color.red);
            g.fillRect(i*xoff, (db.nRows -1 - j)*yoff , xoff , yoff);
          }
          if (db.Bord[i][j].k==sqWhite) {
            g.setColor(Color.white);
            g.fillOval(xoff * i + (xoff / 4) , yoff * (db.nCols - 1 -j) + (yoff / 4) , xoff /2 , yoff /2);
            if (db.Bord[i][j].dam) {
              g.setColor(Color.black);
              g.fillOval(xoff * i + (xoff *3 / 8) , yoff * (db.nCols -1-j) + (yoff * 3 / 8) , xoff / 4 , yoff / 4);
            }
          } else if (db.Bord[i][j].k==sqBlack) {
            g.setColor(Color.black);
            g.fillOval(xoff *  i + (xoff / 4) , yoff * (db.nCols -1 -j)+(yoff / 4) , xoff / 2 , yoff / 2);
            if (db.Bord[i][j].dam) {
              g.setColor(Color.white);
              g.fillOval(xoff * i + (xoff *3 / 8) , yoff * (db.nCols -1-j) + (yoff * 3 / 8) , xoff / 4 , yoff / 4);
            }
          }
        } // for j
      } // for i
      // Alle speciale vakjes blauw maken...
      if (status > 0) {
        g.setColor(Color.blue);
        for (k=1;k<=tpAantalStappen - status;k++) {
          g.fillRect(tpz.x[k] * xoff, (db.nRows - 1 -tpz.y[k]) * yoff , xoff , yoff);
        }
      } // if status > 0
      // Allerlaatst rechthoekje tekenen om bord heen.
      //g.setColor(Color.black);
      //g.drawRect(0,0,xoff*db.nCols, yoff*db.nRows);
      //if (status == stGewonnen) {
      //  //Dammen.font.size=20;
      //  g.drawString("Gewonnen!",d.width / 20 , d.height / 2);
      //} else if (status==stVerloren) {
      //  //Dammen.font.size=20;
      //  g.drawString("Verloren!" , d.width / 20, d.height / 2);
      //}
  } // paint
  public void run() {
    Thread me = Thread.currentThread();
    int nStappen =0,i;
    status = stSimulating;
    simulStatus=-1;
    if (me==Processing) {
      // Zet speler simuleren
      // Deze zet is opgeslagen in tpz
      simulStatus =smstSpelerZet;
      if (tpz.n==0) {
        nStappen=1;
      } else {
        nStappen = tpz.n;
      }
      for (i=0;i<=nStappen;i++) {
        simulStap=i;
        //repaint();
        MaakVakjeGroen(tpz.x[simulStap],tpz.y[simulStap],true);
        try {
          Thread.sleep(500);
        } catch (InterruptedException E) {}
        MaakVakjeGroen(tpz.x[simulStap],tpz.y[simulStap],false);
      }
      // Even speler zet uitvoeren...
      tpBord.ApplyZet(SpelerZet);
      simulStatus = -1;
      repaint();
      try {
        Thread.sleep(750);
      } catch (InterruptedException E) {}
      Stat.SetLabelText("Bepalen computerzet...");
      // Wachten tot computer zet is bepaald
      CompZet=Bord.GetComputerZet();
      // Computer zet tekenen...


      simulStatus = smstComputerZet;
      if (CompZet!=null) {
        if (CompZet.n==0) {
          nStappen=1;
        } else {
          nStappen = CompZet.n;
        }
        ZetStapel.Push(SpelerZet);
        ZetStapel.Push(CompZet);
        for (i=0;i<=nStappen;i++) {
          simulStap=i;
          //repaint();
          MaakVakjeGroen(CompZet.x[simulStap],CompZet.y[simulStap],true);
          try {
            Thread.sleep(500);
          } catch (InterruptedException E) {}
          MaakVakjeGroen(CompZet.x[simulStap],CompZet.y[simulStap],false);
        }
        // We nemen aan dat de stapel met zetten al is gevuld
        // door de thread die de computer zet regelt...
        tpAantalStappen = Bord.tpMaxSlagen;
        if (tpAantalStappen ==0) {
          tpAantalStappen=1;
        }
        //try {
        //  Thread.sleep(1000);
        //} catch (InterruptedException E) {}
      } // if COmpZet!=null
      if (Bord.blAfgelopen) {
        if (Bord.blWitGewonnen) {
          status = stGewonnen;
          Stat.SetLabelText("U heeft gewonnen! (" + strNiveau() + ")");
        } else {
          status = stVerloren;
          Stat.SetLabelText("U heeft verloren! (" + strNiveau() + ")");
        }
      } else {
        status = stAanDeBeurt;
        Stat.SetLabelText("U bent aan zet. (" + strNiveau() + ")");
      }
      repaint();
    } // if me==Processing
  } // run
  public void VerwerkZet(Zet z) {
    SpelerZet=z;
    Bord.blComputerZetBepaald=false;
    // Eerst Bord overkopieren om te gebruiken bij het simuleren
    // van de zetten...
    tpBord = new Dambord(Bord);
    status=stSimulating;
    // Aparte thread voor simuleren van bewegingen...
    Processing = new Thread(this, "Processing");
    Processing.start();
    Bord.VerwerkSpelerZet(z);
    //Bord.ApplyZet(z);
    // Dan computerzet uitvoeren...
    //Bord.ApplyZet(Bord.ComputerZetAI());
    // Weer alle mogelijke zetten voor wit uitrekenen...
    //Bord.VulStapel(sqWhite);
    // En aantal stappen invoeren...
    //tpAantalStappen = Bord.tpMaxSlagen;
    //if (tpAantalStappen ==0) {
     // tpAantalStappen=1;
    //}
    //status = stAanDeBeurt;
  }
  public void mouseClicked(MouseEvent e) {
  }
  public void mouseEntered(MouseEvent e) {
  }
  public void mouseExited(MouseEvent e) {
  }
  public void mousePressed(MouseEvent e) {
  }
  public void mouseReleased(MouseEvent e) {
    if (status==stSimulating) {
      return;
    }
    Dimension d = getSize();
    Bord.GetVakje(d,e.getX(),e.getY());
    // Eerst plaats opvragen waar geklikt is... (row,col)
    int row=Bord.mark_y;
    int col=Bord.mark_x;
    Zet z;
    if (status == stAanDeBeurt) {
        if (Bord.Bord[col][row].k==sqWhite) {
          tpz= new tpZet();
          tpz.x[0]=col;
          tpz.y[0]=row;
          tpz.n=Bord.tpMaxSlagen;
          status = tpAantalStappen;
        }
    } else {
      // Eerst kijken of gebruiker iets ongedaan wilt maken
      if ((tpz.x[tpAantalStappen - status]!=col) || (tpz.y[tpAantalStappen - status]!=row)) {
        if (status == 1) {
          // we moeten kijken of we de zet kunnen afmaken...
          tpz.x[tpAantalStappen]=col;
          tpz.y[tpAantalStappen]=row;
          z=new Zet(tpz);
          if (Bord.IsZetToegestaan(z,tpAantalStappen, tpAantalStappen)) {
            VerwerkZet(z);
          }
        } else {
          // status is blijkbaar groter dan 1.
          // er moeten dus nog een aantal zetten gebeuren voordat de zet verwerkt kan worden.
          tpz.x[tpAantalStappen+1-status]=col;
          tpz.y[tpAantalStappen +1 - status]=row;
          z=new Zet(tpz);
          if (Bord.IsZetToegestaan(z, tpAantalStappen +1 - status, tpAantalStappen)) {
            // Toegestane zet -> status verminderen...
            status--;
          }
        } // if status = 1
      } else {
        // de gebruiker wil blijkbaar zijn laatste plaatsing ongedaan maken...
        // status 1 vermeerderen
        if (status == tpAantalStappen) {
          status = stAanDeBeurt;
        } else {
          status++;
        }
      }
    } // if status == stAanDeBeurt
    if (status!=stSimulating) {
      repaint();
    }
  }
  public void NewGame() {
    Bord=null;
    Bord = new Dambord(niveau);
    ZetStapel = new Stapel();
    status=stAanDeBeurt;
    tpAantalStappen=1;
    repaint();
    Stat.SetLabelText("U bent aan zet. (" + strNiveau() + ")");
  }
  // De volgende functie stelt ons in staat een
  // zet van de computer en van de speler terug te nemen
  public void TakeBack() {
    if (status==stAanDeBeurt) {
      if (!ZetStapel.IsEmpty()) {
        Bord.DeApplyZet(ZetStapel.Pop());
        Bord.DeApplyZet(ZetStapel.Pop());
        Bord.VulStapel(sqWhite);
        tpAantalStappen = Bord.tpMaxSlagen;
        if (tpAantalStappen ==0) {
          tpAantalStappen=1;
        }
        repaint();
      };
    }
  } // TakeBack
  // verandert het niveau...
  // (Dus de diepte waarop wordt nagedacht.)
  // Dit vindt pas doorgang de volgende
  // keer dat een nieuw spel wordt gespeeld
  public void ChangeNiveau(int niv) {
    niveau = niv;
  }
} // class DamSpel
public class ActionReset implements ActionListener {
  private DamSpel Spel;
  public ActionReset(DamSpel s) {
    Spel = s;
  }
  public void actionPerformed(ActionEvent e) {
    Spel.NewGame();
  }
}
public class ActionTakeBack implements ActionListener {
  DamSpel Spel;
  public ActionTakeBack(DamSpel s) {
    Spel = s;
  }
  public void actionPerformed(ActionEvent e) {
    Spel.TakeBack();
  }
}
public class ButtonPanel extends Panel implements ItemListener{
  private DamSpel Spel;
  Button btnReset, btnTakeBack;
  Choice chcNiveau;
  public ButtonPanel(DamSpel s) {
    Spel = s;
    setLayout(new FlowLayout());
    btnReset=new Button("New Game");
    btnTakeBack = new Button("Undo Move");
    chcNiveau = new Choice();
    chcNiveau.addItem("Makkelijk");
    chcNiveau.addItem("Neutraal");
    chcNiveau.addItem("Moeilijk");
    //chcNiveau.addItem("Onmogelijk");
    chcNiveau.addItemListener(this);
    btnReset.addActionListener(new ActionReset(Spel));
    btnTakeBack.addActionListener(new ActionTakeBack(Spel));
    add(chcNiveau);
    add(btnReset);
    add(btnTakeBack);
  }
  public void itemStateChanged(ItemEvent e) {
    Spel.ChangeNiveau(2*(chcNiveau.getSelectedIndex() +1));
  }
}
public class StatusPanel extends Panel {
  Label lbStatus;
  public StatusPanel() {
    lbStatus = new Label("U bent aan zet. (Niveau: Makkelijk)");
    setLayout(new GridLayout(0,1));
    add(lbStatus);
  }
  public void SetLabelText(String s) {
    lbStatus.setText(s);
  }
} // class StatusPanel
public class Dammen extends Applet {
  DamSpel Spel;
  ButtonPanel Buttons;
  StatusPanel Status;
  public void init() {
    Dimension d = getSize();
    Status = new StatusPanel();
    Spel = new DamSpel(d, Status);
    Buttons = new ButtonPanel(Spel);
    setLayout(new BorderLayout());
    add("North" ,Spel);
    add("Center" , Buttons);
    add("South" , Status);
  }
} // class Dammen