import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/* Interface describing the GUI from the point of view of the virtual player */
interface Forza4Interface{
/* Game level */
public int getLevel();
/* Boardgame */
public void setBoard(int row, int col);
public void setBoard(int row, int col, int color);
public void setBoard(int row, int col, int color, boolean brackets);
public void clearBoard();
public int get_Nb_Rows();
public int get_Nb_Cols();
/* Language */
public boolean Italiano();
public boolean English();
/* Feedback */
public void setMessage(String msg);
/* Available pieces */
int YELLOW = -1;
int RED = 1;
}
/* Interface describing the virtual player from the of view of the GUI */
interface VirtualPlayerInterface{
/* Moves */
public void HumanMove(int col);
public void VirtualPlayerMove();
}
public class Forza4 extends Applet implements Forza4Interface{
boolean english;
int Nb_Rows = 6;
int Nb_Cols = 7;
int EASY_LEVEL = 4;
int MIDDLE_LEVEL = 6;
int HARD_LEVEL = 8;
VirtualPlayer Computer = null;
Button[][] Cells;
String msg_title,msg_level, msg_easy, msg_middle, msg_hard,
msg_newgame,msg_welcome;
Label Message;
Checkbox easy, middle, hard;
void init_msg(){
msg_title = english ? "Connect 4" : "Forza 4";
msg_level = english ? "Level" : "Livello";
msg_easy = english ? "Easy" : "Facile";
msg_middle = english ? "Middle" : "Medio";
msg_hard = english ? "Hard" : "Difficile";
msg_newgame = english ? "New Game" : "Nuova partita";
msg_welcome = english ? "Java Connect 4" : "Forza 4 in java";
}
public void init(){
/* Language */
english = getParameter("lang").equals("en");
init_msg();
/* Panels */
Panel TitlePanel = new Panel(),
LevelPanel = new Panel(),
NewGamePanel = new Panel(),
GamePanel = new Panel(),
MsgPanel = new Panel(),
ConfigPanel = new Panel();
setFont(new Font("Times New Roman",Font.PLAIN,10));
/* TitlePanel */
TitlePanel.setForeground(Color.red);
TitlePanel.setFont(new Font("Times New Roman",Font.BOLD,16));
TitlePanel.add(new Label(msg_title,Label.CENTER));
/* LevelPanel */
LevelPanel.setLayout(new GridLayout(4,1));
LevelPanel.add(new Label(msg_level,Label.CENTER));
CheckboxGroup LevelGroup = new CheckboxGroup();
easy = new Checkbox(msg_easy,true,LevelGroup);
LevelPanel.add(easy);
middle = new Checkbox(msg_middle,false,LevelGroup);
LevelPanel.add(middle);
hard = new Checkbox(msg_hard,false,LevelGroup);
LevelPanel.add(hard);
/* NewGamePanel */
Button NewGame = new Button(msg_newgame);
NewGame.addActionListener(new NewGameListener());
NewGamePanel.add(NewGame);
/* GamePanel */
GamePanel.setLayout(new GridLayout(Nb_Rows+1,Nb_Cols));
Cells = new Button[Nb_Cols][Nb_Rows];
for (int row=Nb_Rows-1;row>=0;row--)
{
for (int col=0;col<Nb_Cols;col++)
{
Cells[col][row] = new Button();
Cells[col][row].addActionListener(new ColumnListener(col));
Cells[col][row].setFont(new Font("Times New Roman",Font.BOLD,12));
GamePanel.add(Cells[col][row]);
}
}
for (int col=0;col<Nb_Cols;col++)
{
Label n = new Label(Integer.toString(col+1),Label.CENTER);
n.setFont(new Font("Courier New",Font.PLAIN,10));
GamePanel.add(n);
}
/* MessagePanel
*/
MsgPanel.setForeground(Color.blue);
Message = new Label(msg_welcome,Label.LEFT);
MsgPanel.add(Message);
/* ConfigPanel
*/
ConfigPanel.setLayout(new GridLayout(2,1));
ConfigPanel.add(LevelPanel);
ConfigPanel.add(NewGamePanel);
/* Adding Panels
*/
setLayout(new BorderLayout());
setBackground(Color.lightGray);
add(TitlePanel,"North");
add(ConfigPanel,"West");
add(GamePanel,"Center");
add(MsgPanel,"South");
/* First virtual player
*/
Computer = new VirtualPlayer(this);
}
class NewGameListener implements ActionListener
{
boolean Computer_Turn = false;
public void actionPerformed(ActionEvent e)
{
Computer_Turn = !Computer_Turn;
Computer = new VirtualPlayer(Forza4.this);
if (Computer_Turn) { Computer.VirtualPlayerMove(); }
}
}
class ColumnListener implements ActionListener
{
int column;
public ColumnListener(int column)
{
this.column = column;
}
public void actionPerformed(ActionEvent e)
{
Computer.HumanMove(column);
}
}
public boolean Italiano()
{
return !english;
}
public boolean English()
{
return english;
}
public int getLevel()
{
return
easy.getState() ? EASY_LEVEL:
middle.getState() ? MIDDLE_LEVEL:
hard.getState() ? HARD_LEVEL:
EASY_LEVEL;
}
public void setBoard(int col, int row, int color, boolean brackets)
{
String pattern = (color==YELLOW) ? "X" : (color==RED) ? "O" : "";
if (brackets) pattern = "[ "+pattern+" ]";
Cells[col][row].setForeground((color==YELLOW)?Color.yellow:(color==RED)?Color.red:Color.black);
Cells[col][row].setLabel(pattern);
}
public void setBoard(int col, int row, int color)
{
setBoard(col,row,color,false);
}
public void setBoard(int col, int row)
{
setBoard(col,row,0,false);
}
public void clearBoard()
{
for (int row=0;row<Nb_Rows;row++)
{
for (int col=0;col<Nb_Cols;col++)
{
setBoard(col,row);
}
}
}
public int get_Nb_Rows() { return Nb_Rows; }
public int get_Nb_Cols() { return Nb_Cols; }
public void setMessage(String msg)
{
Message.setText(" "+msg);
}
}
class VirtualPlayer
implements VirtualPlayerInterface
{
Forza4Interface GUI;
int NR, NC;
int INFINITY = 1000;
int[][] board;
int[] height;
int winner = 0;
public VirtualPlayer(Forza4Interface GUI)
{
this.GUI = GUI;
GUI.clearBoard();
NR = GUI.get_Nb_Rows();
NC = GUI.get_Nb_Cols();
board = new int[NC][NR];
height = new int[NC];
init_msg();
}
String
msg_i_lost, msg_i_won,
msg_full,
msg_wait, msg_turn, msg_play,
msg_give_up,
msg_w_win, msg_w_lose;
void init_msg()
{
boolean english = GUI.English();
msg_i_lost = english ? "Yep, quite good." : "Ouais, bof, pas mal.";
msg_i_won = english ? "I won" : "Ho vinto io !";
msg_full = english ? "Full Column" : "Colonna completa";
msg_wait = english ? "Please wait, I'm thinking..." : "Aspetta..sto pensando";
msg_give_up = english ? "I give up" : "Mi ritiro";
msg_w_win = english ? "I'll soon win!!!" : "Sto per vincere io !!!";
msg_w_lose = english ? "I'll lose..." : "Sto per perdere ...";
msg_turn = english ? "Your turn" : "Tocca a te";
msg_play = english ? "I played" : "Ho giocato";
}
public void HumanMove(int col)
{
if (winner!=0) return;
if (Play(GUI.YELLOW,col))
{
GUI.setBoard(col,height[col]-1,GUI.YELLOW);
if (won(col)) { winner = GUI.YELLOW; GUI.setMessage(msg_i_lost); }
if (winner==0) VirtualPlayerMove();
if (winner!=0) ShowWinner();
}
else
{
GUI.setMessage(msg_full);
}
}
public void VirtualPlayerMove()
{
int level = GUI.getLevel();
String diag = "";
GUI.setMessage(msg_wait);
Move move=search(level);
int col = move.Column();
int strength = move.Strength();
if (col==-1)
{
GUI.setMessage(msg_give_up);
winner = -2;
return;
}
Play(GUI.RED,col);
GUI.setBoard(col,height[col]-1,GUI.RED);
if (strength>=+INFINITY) diag = msg_w_win;
if (strength<=-INFINITY) diag = msg_w_lose;
GUI.setMessage(msg_play+" "+(col+1)+". "+diag+" "+msg_turn);
if (won(col))
{
winner = GUI.RED;
GUI.setMessage(msg_i_won);
}
}
int line(int player, int c, int r, int dc, int dr)
{
int n;
if (dc==0 && dr==0) return 0;
n=1;
c+=dc; r+=dr;
while (inside(c,r) && board[c][r]==player)
{
n++;
c+=dc; r+=dr;
}
return n;
}
int victory()
{
int col, row, player, di, dj;
for (col=0;col<NC;col++)
{
for (row=0;row<height[col];row++)
{
player=board[col][row];
for (di=0;di<=1;di++)
{
for (dj=-1;dj<=1;dj++)
{
if (line(player,col,row,di,dj)>3) return player;
}
}
}
}
return 0;
}
class Move
{
private int column, strength;
public Move(int column, int strength)
{
this.column = column;
this.strength = strength;
}
public int Column() { return column; }
public int Strength() { return strength; }
public void Neg() { strength = -strength; }
}
Move search(int level)
{
return negamax(GUI.RED,level,-INFINITY-1,+INFINITY+1);
}
Move negamax(int player, int level, int alpha, int beta)
{
Move[] moves = new Move[NC];
int nbmoves, col, strength, i, j;
Move bestmove = null;
if (level<1) return new Move(-1,evaluate());
/* Move generation with preordering
*/
nbmoves = 0;
for (col=0;col<NC;col++)
{
if (Play(player,col))
{
int cv = -2*height[col]+Math.abs(2*col-NC);
// int cv = evaluate();
Undo(col);
for (i=0;i<nbmoves && moves[i].Strength()>cv;i++) ;
for (j=nbmoves;j>i;j--) moves[j] = moves[j-1];
moves[i] = new Move(col,cv);
nbmoves++;
}
}
/* Move evaluation
*/
if (nbmoves==0) return new Move(-1,evaluate());
for (i=0;i<nbmoves && alpha<beta;i++)
{
Move mv;
col = moves[i].Column();
Play(player,col);
if (won(col))
{
Undo(col);
return new Move(col,+INFINITY);
}
mv = negamax(-player,level-1,-beta,-alpha);
mv.Neg();
Undo(col);
strength = mv.Strength();
if (strength>alpha)
{
alpha = strength;
bestmove = new Move(col,strength);
}
}
return (bestmove!=null) ? bestmove : new Move(-1,-INFINITY);
}
boolean inside(int c, int r)
{
return c>=0 && c<NC && r>=0 && r<NR;
}
void markline(int player, int c, int r, int dc, int dr)
{
do
{
GUI.setBoard(c,r,player,true);
c+=dc;
r+=dr;
} while (inside(c,r) && board[c][r]==player);
}
void ShowWinner()
{
int n[] = new int[1];
int c, r, dir;
for (c=0;c<NC;c++)
{
for (r=0;r<NR;r++)
{
int player=board[c][r];
if (player!=0)
{
for (dir=0;dir<4;dir++)
{
int dc=cd[dir];
int dr=rd[dir];
n[0]=1;
maxline(player,c,r, dc, dr,n);
maxline(player,c,r,-dc,-dr,n);
if (n[0]>3)
{
markline(player,c,r, dc, dr);
markline(player,c,r,-dc,-dr);
}
}
}
}
}
}
boolean Play(int player, int col)
{
if (height[col]<NR)
{
board[col][height[col]++] = player;
return true;
}
return false;
}
void Undo(int col)
{
board[col][--height[col]] = 0;
}
void posmaxline(int player, int c, int r, int dc, int dr, int max[])
// max={max, posmax, maxleft}
{
c+=dc; r+=dr;
while (inside(c,r) && max[1]<4)
{
if (board[c][r]==player) { max[0]++; max[1]++; }
else break;
c+=dc; r+=dr;
}
while (inside(c,r) && max[2]<4)
{
if (board[c][r]==player) { max[1]++; }
else if (board[c][r]==0) { max[1]++; max[2]++; }
else break;
c+=dc; r+=dr;
}
}
void maxline(int player, int c, int r, int dc, int dr, int max[])
{
c+=dc; r+=dr;
while (inside(c,r) && board[c][r]==player && max[0]<4)
{
max[0]++;
c+=dc; r+=dr;
}
}
int[] cd = { 1, 1, 1, 0};
int[] rd = { 1, 0,-1,-1};
boolean won(int col)
{
int max[], dc, dr, dir, player, row;
max=new int[1];
row=height[col]-1;
player=board[col][row];
for (dir=0;dir<4;dir++)
{
dc=cd[dir];
dr=rd[dir];
max[0]=1;
maxline(player,col,row, dc, dr,max);
maxline(player,col,row,-dc,-dr,max);
if (max[0]>3) return true;
}
return false;
}
int evaluate()
{
int v, v0, col, row, player, dc, dr, max[], dir;
max = new int[3];
v=0;
for (col=0;col<NC;col++)
{
for (row=0;row<height[col];row++)
{
player=board[col][row];
v0=0;
for (dir=0;dir<4;dir++)
{
dc=cd[dir];
dr=rd[dir];
max[0]=1;
max[1]=1;
max[2]=0;
posmaxline(player,col,row, dc, dr,max);
posmaxline(player,col,row,-dc,-dr,max);
if (max[0]>3) v0+=INFINITY;
if (max[1]>3) v0+=2<<(4-max[2]);
}
v+=player*v0;
}
}
if (v>+INFINITY) return +INFINITY;
if (v<-INFINITY) return -INFINITY;
return v;
}
} |