fourrow.java
avail
bew1
bewerte
bewerte
calc
constant
exec
field
field
fourrow
gameover
get
get
get
getbew
getcol
getcount
getheight
getnum
getval
getzug
init
init
mouseDown
move
next
paint
table
tableentry
teste
zug
zug
zug_val
zuggen
// 4 in a row
// Copyright (C) 1996 August Hoerandl (hoerandl@elina.htlw1.ac.at)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.applet.*;
import java.lang.*;
class constant {
final static int XMAX = 7; // feldgroesse
final static int YMAX = 6;
final static int SIZE = XMAX*YMAX + 1; // anzahl der felder + 1
final static int black = 0;
final static int white = 1;
final static int empty = 2;
final static int MINBEW = -30000;
final static int MAXBEW = 30000;
final static int BEW4 = 7000;
static table thetable;
constant()
{
thetable = new table();
}
}
class zug {
private int num;
private int f;
zug() {};
zug(int x, int y, int who)
{
num = y*constant.XMAX+x;
f = who;
}
int getcol() { return f; }
int getnum() { return num; }
}
class zuggen {
field f;
int c;
int slot;
zuggen(field b, int col)
{
f = b;
c = col;
slot = -1;
next();
}
zug get()
{
return new zug(slot, f.getheight(slot), c);
}
boolean avail()
{
return (slot < constant.XMAX);
}
void next()
{
for(;;) {
slot++;
if (! avail())
return;
if (f.getheight(slot) != constant.YMAX)
return;
}
}
}
class field {
private int bew;
private int count;
private int data[];
private int height[];
field()
{
data = new int[constant.SIZE];
height = new int[constant.XMAX];
for(int i=0; i < constant.SIZE; i++)
data[i] = constant.empty;
for(int i=0; i < constant.XMAX; i++)
height[i] = 0;
bew = 0;
count = 0;
}
field(field f)
{
data = new int[constant.SIZE];
height = new int[constant.XMAX];
for(int i=0; i < constant.SIZE; i++)
data[i] = f.data[i];
for(int i=0; i < constant.XMAX; i++)
height[i] = f.height[i];
bew = f.bew;
count = f.count;
}
int get(zug z)
{
return data[z.getnum()];
}
int get(int n)
{
return data[n];
}
int getcount()
{
return count;
}
int getheight(int x)
{
return height[x];
}
int getbew()
{
return bew;
};
void exec(zug z)
{
count ++;
data[z.getnum()] = z.getcol();
height[z.getnum() % constant.XMAX] ++;
// add up points
if (z.getcol() == constant.white)
bew += constant.thetable.bewerte(z, this);
else
bew -= constant.thetable.bewerte(z, this);
}
boolean gameover()
{
return (count >= constant.SIZE-1 ||
bew > constant.BEW4/2 ||
bew < -constant.BEW4/2);
}
}
class tableentry {
static int dummy = constant.SIZE-1;
int r[]; // rechts 4 nachbarn + ende-entry
int ru[];
int u[];
int lu[];
int l[];
int ld[];
int d[];
int rd[];
tableentry(int pos)
{
r = new int[5]; // rechts 4 nachbarn + ende-entry
ru = new int[5];
u = new int[5];
lu = new int[5];
l = new int[5];
ld = new int[5];
d = new int[5];
rd = new int[5];
init(pos);
}
int calc(int xpos, int ypos)
{
if (xpos >= 0 && xpos < constant.XMAX &&
ypos >= 0 && ypos < constant.YMAX) {
zug tmp = new zug(xpos,ypos, constant.empty);
return tmp.getnum();
} else
return dummy;
}
void init(int pos)
{
int xpos = pos % constant.XMAX;
int ypos = pos / constant.XMAX;
zug tmp = new zug(xpos,ypos, constant.empty);
//if (tmp.getnum() != pos)
// exit(2);
for(int i=0; i < 5; i++)
r[i]=ru[i]=u[i]=lu[i]=l[i]=ld[i]=d[i]=rd[i]=dummy;
for(int i=1; i < 5; i++) {
r[i-1] = calc(xpos+i, ypos );
ru[i-1]= calc(xpos+i, ypos+i );
u[i-1] = calc(xpos , ypos+i );
lu[i-1]= calc(xpos-i, ypos+i );
l[i-1] = calc(xpos-i, ypos );
ld[i-1]= calc(xpos-i, ypos-i );
d[i-1] = calc(xpos , ypos-i );
rd[i-1]= calc(xpos+i, ypos-i );
}
r[4]=ru[4]=u[4]=lu[4]=l[4]=ld[4]=d[4]=rd[4]=dummy;
}
int bew1(int n1, int n2, int nextpos1, int nextpos2, field f)
{
// n1 anzahl in richtung 1
// n2 anzahl gegenrichtung
// nextpos1 naechste pos in richtung 1
// nextpos2 naechste pos in gegenrichtung
// punkte: anzahl nachbarn
// eine seite frei
// andere seite frei
int points[][][] = {
{ { 0, 1 }, // 0 nachbarn, eine seite bes bb, bf
{ 1, 3 } }, // 0 nachbarn, eine seite frei fb, ff
{ { 5, 10 }, // 1 nachbar
{ 10, 25 } },
{ { 10, 80 }, // 2 nachbarn
{ 80, 500 } }
};
int sum = n1+n2;
if (sum >= 3)
return constant.BEW4;
return points[sum]
[(nextpos1 != dummy && f.get(nextpos1) == constant.empty) ? 1 : 0]
[(nextpos2 != dummy && f.get(nextpos2) == constant.empty) ? 1 : 0];
}
int bewerte(int col, field f)
{
int nr, nru, nu, nlu, nl, nld, nd, nrd; // anzahl col in eine richtung
for(nr=0; f.get(r[nr]) == col; nr++)
;
for(nru=0; f.get(ru[nru]) == col; nru++)
;
for(nu=0; f.get(u[nu]) == col; nu++)
;
for(nlu=0; f.get(lu[nlu]) == col; nlu++)
;
for(nl=0; f.get(l[nl]) == col; nl++)
;
for(nld=0; f.get(ld[nld]) == col; nld++)
;
for(nd=0; f.get(d[nd]) == col; nd++)
;
for(nrd=0; f.get(rd[nrd]) == col; nrd++)
;
return bew1(nr , nl , r[nr], l[nl], f) +
bew1(nu, nd, u[nu], d[nd], f) +
bew1(nru, nld, ru[nru], ld[nld], f) +
bew1(nrd, nlu, rd[nrd], lu[nlu], f);
}
}
class table {
tableentry tab[];
table()
{
tab = new tableentry[constant.SIZE];
for(int i=0; i < constant.SIZE; i++)
tab[i] = new tableentry(i);
}
int bewerte(zug last, field f)
{
return tab[last.getnum()].bewerte(last.getcol(), f);
}
}
class zug_val {
private zug _z;
private int _val;
zug_val(zug z, int val)
{
_z = z;
_val = val;
}
zug getzug()
{ return _z; }
int getval()
{ return _val; }
}
public
class fourrow extends Applet {
final int XMAX = constant.XMAX;
final int YMAX = constant.YMAX;
private field brett;
private constant c;
private AudioClip audio;
private boolean domove;
private zug lastmove;
/**
* Initialize the applet. Resize and load images.
*/
public void init() {
audio = getAudioClip(getCodeBase(), "oops.au");
}
public fourrow()
{
c = new constant();
brett = new field();
domove = false;
lastmove = new zug(XMAX, YMAX, constant.empty);
}
/**
* The user has clicked in the applet. Figure out where
* and see if a legal move is possible. If it is a legal
* move, respond with a legal move (if possible).
*/
public boolean mouseDown(Event evt, int x, int y) {
if (brett.gameover()) {
audio.play();
brett = new field();
repaint();
} else {
if (! (evt.shiftDown() && brett.getcount()==0)) {
Dimension d = size();
int xoff = d.width / XMAX;
int spalte = x / xoff;
int zeile = brett.getheight(spalte);
if (zeile < constant.YMAX) {
lastmove = new zug(spalte, zeile, constant.white);
brett.exec(lastmove);
if (brett.gameover())
audio.play();
} else
audio.play();
}
repaint();
domove = true;
}
return true;
}
void move() {
showStatus("Thinking ...");
if (brett.gameover()) {
audio.play();
brett = new field();
repaint();
} else {
//setCursor(Frame.WAIT_CURSOR);
zug_val zv = teste(brett,
constant.black,
3 + brett.getcount()/8,
constant.MINBEW);
lastmove = zv.getzug();
brett.exec(lastmove);
if (brett.gameover())
audio.play();
//setCursor(Frame.DEFAULT_CURSOR);
repaint();
}
if (! brett.gameover())
showStatus("Enter Move ...");
else
showStatus("Game over - click to restart");
}
/**
* Paint it.
*/
public void paint(Graphics g) {
Dimension d = size();
int xoff = d.width / XMAX;
int yoff = d.height / YMAX;
int xm = xoff / 8;
int ym = yoff / 8;
g.setColor(Color.blue);
//g.fillOval(0, 0, d.width, d.height);
g.setColor(Color.black);
for(int i = 1; i < XMAX; i++)
g.drawLine(i*xoff, 0, i*xoff, d.height);
for(int i = 1; i < YMAX; i++)
g.drawLine(0, i*yoff, d.width, i*yoff);
for(int i = 0; i < XMAX; i++) {
for(int j=0; j < YMAX; j++) {
g.setColor(Color.black);
g.drawOval(i*xoff+xm, j*yoff+ym, xoff-2*xm, yoff-2*ym);
zug tmp = new zug(i, YMAX-1-j, constant.empty);
int m = tmp.getnum() == lastmove.getnum() ? 2 : 0;
switch (brett.get(tmp)) {
case constant.white:
g.setColor(Color.yellow);
g.fillOval(i*xoff+xm+m, j*yoff+ym+m, xoff-2*(xm+m), yoff-2*(ym+m));
break;
case constant.black:
g.setColor(Color.red);
g.fillOval(i*xoff+xm+m, j*yoff+ym+m, xoff-2*(xm+m), yoff-2*(ym+m));
break;
default:
//g.setColor(Color.white);
//g.fillOval(i*xoff+xm+m, j*yoff+ym+m, xoff-2*(xm+m), yoff-2*(ym+m));
break;
}
}
}
if (domove) {
domove = false;
//g.setCursor(Frame.WAIT_CURSOR);
move();
} else if (brett.getcount() == 0)
showStatus("Click on a column to play (shift-click for computer to move first)");
}
zug_val teste(field f, int who, int tiefe, int minmaxval)
{
boolean minmax;
int other;
int val;
zug best = new zug(0,0, constant.empty);
if (who == constant.black) {
minmax = false; /* suche minimum */
val = constant.MAXBEW;
other = constant.white;
} else {
minmax = true; /* suche maximum */
val = constant.MINBEW;
other = constant.black;
}
int t1 = tiefe-1;
for(zuggen zg = new zuggen(f, who); zg.avail(); zg.next() ) {
field neu = new field(f);
neu.exec(zg.get());
int bew = 0;
if (tiefe <= 0) {
int tmp = (constant.XMAX+constant.SIZE-neu.getcount())/(constant.SIZE/10);
bew = neu.getbew() + (int)(Math.random()*tmp);
} else if (neu.gameover()) {
if (who == constant.white)
bew = constant.BEW4 - neu.getcount();
else
bew = -constant.BEW4 + neu.getcount();
} else {
zug_val b = teste(neu, other, t1, val);
bew =b.getval();
}
if ( (bew > val) == minmax) {
val = bew;
best = zg.get();
if ( ( val > minmaxval) == minmax )
break;
}
}
return new zug_val(best, val);
}
}