/*
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