//
// KReversi.java
// The Othello Game, based on the algorithm of Muffy Barkocy
// (muffy@fish.com).
//
// The strategy is very very simple. The best move for the computer
// is the move that flip more pieces (preferring boards line and
// corners) and give less pieces to move to the opponent.
//
// Author: Alex "Kazuma" Garbagnati (kazuma@energy.it)
// Date: 20 Jan 96
// L.R.: 26 Jan 96
// Note:
//
import java.awt.*;
import java.applet.*;
import java.lang.*;
import java.net.*;
import java.io.*;
public class KReversi extends Applet {
static final String VERSION = "1.00"; // Version
static final int YSHIFT = 33;
static final int ENGLISH = 0; //
static final int ITALIAN = 1; // Languages
static final int EXTERNAL = 2; //
static final Color BLACK = Color.black; //
static final Color BLACK_S = Color.darkGray; //
static final Color WHITE = Color.white; // Colors
static final Color WHITE_S = Color.lightGray; //
static final Color BOARD = new Color(0, 128, 0); //
static final int NOMOVE = 0; //
static final int REALMOVE = 1; // Type of move
static final int PSEUDOMOVE = 2; //
static final int Empty = 0; //
static final int User = 1; // Board's owners
static final int Computer = 2; //
boolean UserMove = true;
boolean GameOver = false;
boolean CopyWinOn = false;
boolean StillInitiated = false; // This solve a little
// problem on reinit.
int TheBoard[][]; // Board
int Score[][];
int OpponentScore[][];
int Language;
String WinMsg_W,
WinMsg_T,
WinMsg_L,
BtnNew,
BtnCopy,
MyMove,
YourMove,
PressWin;
//
// getParameter
// (if exists a valid parameter read it and
// return it, else return a default value)
//
// Input: value (string),
// default (string)
// Output: parameter (string)
// Notes:
//
String getParameter(String p, String def) {
p = getParameter(p);
if (p == null) return def;
return p;
}
// End of getParameter
//
// SetLanguage
// (set program language and prepare all
// strings)
//
// Input: literal language (string)
// Output: int language (int)
// Notes:
//
int SetLanguage(String TheLang) {
if ((TheLang.compareTo("external") == 0) &&
(GetExternalLanguage())) {
return EXTERNAL;
} else if (TheLang.compareTo("italian") == 0) {
WinMsg_W = "HO VINTO !";
WinMsg_T = "PATTA !";
WinMsg_L = "HAI VINTO !";
BtnNew = "Nuova Partita";
BtnCopy = "Copyright";
MyMove = "Tocca a me";
YourMove = "Tocca a te";
PressWin = "Click del mouse per riprendere il gioco";
return ITALIAN;
}
WinMsg_W = "I WON !";
WinMsg_T = "IT'S TIE !";
WinMsg_L = "YOU WON !";
BtnNew = "New Game";
BtnCopy = "Copyright";
MyMove = "My turn";
YourMove = "Your turn";
PressWin = "Mouse click to resume game";
return ENGLISH;
}
// End of getParameter
//
// ExternalLanguageVariable
// (process the variable line, assigning the
// new value to the correct variable message)
//
// Input: line to work with (String)
// Output: is all ok, baby ? (boolean)
// Notes:
//
public boolean ExternalLanguageVariable(String WorkLine) {
String lTag = "";
String lValue = "";
int lEqPos = -1;
boolean retVal = false;
WorkLine = WorkLine.substring(1);
lEqPos = WorkLine.indexOf("=");
if (lEqPos == -1) {
return false;
}
lTag = WorkLine.substring(0, lEqPos);
lValue = WorkLine.substring(lEqPos+1);
if (lTag.compareTo("NEW_GAME_BUTTON") == 0) {
BtnNew = lValue;
retVal = true;
} else if (lTag.compareTo("COPYRIGHT_BUTTON") == 0) {
BtnCopy = lValue;
retVal = true;
} else if (lTag.compareTo("CLICK_MESSAGE") == 0) {
PressWin = lValue;
retVal = true;
} else if (lTag.compareTo("COMPUTER_WON") == 0) {
WinMsg_W = lValue;
retVal = true;
} else if (lTag.compareTo("TIE_GAME") == 0) {
WinMsg_T = lValue;
retVal = true;
} else if (lTag.compareTo("USER_WON") == 0) {
WinMsg_L = lValue;
retVal = true;
} else if (lTag.compareTo("COMPUTER_MOVE") == 0) {
MyMove = lValue;
retVal = true;
} else if (lTag.compareTo("USER_MOVE") == 0) {
YourMove = lValue;
retVal = true;
}
return retVal;
}
// End of ExternalLanguageVariable
//
// GetExternalLanguage
// (read external file language.txt to create strings
// for another language)
//
// Input: none
// Output: read was fine ? (boolean)
// Notes:
//
public boolean GetExternalLanguage() {
String thisURL,
newURL,
TheLine;
boolean ReadOK = true;
int SlashPos = -1;
thisURL = getDocumentBase().toString();
SlashPos = thisURL.lastIndexOf("/");
newURL = thisURL.substring(0, (SlashPos + 1)) + "language.txt";
try {
URL url = new URL(newURL);
try {
InputStream TheFile = url.openStream();
try {
DataInputStream MyData = new DataInputStream(TheFile);
try {
while ((TheLine = MyData.readLine()) != null) {
if (TheLine.substring(0,1).compareTo("*") == 0) {
if (!ExternalLanguageVariable(TheLine)) {
ReadOK = false;
break;
}
}
}
} catch (Exception e) {
System.out.println("Error " + e.toString());
ReadOK = false;
}
} catch (Exception e) {
System.out.println("Error " + e.toString());
ReadOK = false;
}
} catch (Exception f) {
System.out.println("Error " + f.toString());
ReadOK = false;
}
} catch (Exception g) {
System.out.println("Error " + g.toString());
ReadOK = false;
}
return ReadOK;
}
// End of GetExternalFile
//
// DrawBoard
// (paint the Othello Board, a 8X8 green square table)
//
// Input: graphic (Graphics)
// Output: none
// Notes:
//
public void DrawBoard(Graphics g) {
// draw upper & lower side of board (321x33)
g.setColor(new Color(0, 64, 0));
g.fillRect(0, 0, 321, 33);
g.fillRect(0, 354, 321, 33);
// draw board (321x321)
g.setColor(BOARD);
g.fillRect(0, 33, 321, 321);
// board lines
g.setColor(BLACK);
for (int i=0; i<=8; i++) {
g.drawLine((40*i),33,(40*i),353); // horizontal
g.drawLine(0,YSHIFT+(40*(i)),319,YSHIFT+(40*(i))); // vertical
}
}
// End of DrawBoard
//
// DrawPiece
// (paint a piece, black or white, I'm using an 8x8 array, so
// from the input values for rows and cols must be
// subtracted 1)
//
// Input: who (int),
// column (int),
// row (int)
// Output: none
// Notes:
//
public void DrawPiece(int Who, int Col, int Row) {
Graphics g = getGraphics();
int pCol = (40*(Col-1)+1);
int pRow = YSHIFT+(40*(Row-1)+1);
Color pColor,
pShadow;
if (Who == User) {
pColor = BLACK;
pShadow = BLACK_S;
} else {
pColor = WHITE;
pShadow = WHITE_S;
}
TheBoard[Col-1][Row-1] = Who;
g.setColor(pShadow);
g.fillOval(pCol+6, pRow+6, 29, 29);
g.setColor(pColor);
g.fillOval(pCol+5, pRow+5, 29, 29);
}
// End of DrawPiece
//
// MsgWhoMove
// (paint the message informing who's move)
//
// Input: is user ? (boolean)
// Output: none
// Notes:
//
public void MsgWhoMove(boolean UM) {
String s = "";
Graphics g = getGraphics();
g.setColor(new Color(0, 64, 0));
g.fillRect(0, 354, 321, 33);
g.setColor(Color.green);
g.drawRect(2,356,26,26);
g.drawRect(290,356,26,26);
g.setColor(BOARD);
g.fillRect(3,357,25,25);
g.fillRect(291,357,25,25);
if (UM) {
g.setColor(BLACK_S);
g.fillOval(6, 360, 20, 20);
g.fillOval(294, 360, 20, 20);
g.setColor(BLACK);
g.fillOval(5, 359, 20, 20);
g.fillOval(293, 359, 20, 20);
g.setColor(Color.green);
g.drawString(YourMove, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(YourMove)))/2))+1, 380);
} else {
g.setColor(WHITE_S);
g.fillOval(6, 360, 20, 20);
g.fillOval(294, 360, 20, 20);
g.setColor(WHITE);
g.fillOval(5, 359, 20, 20);
g.fillOval(293, 359, 20, 20);
g.setColor(Color.green);
g.drawString(MyMove, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(MyMove)))/2))+1, 380);
}
}
// End of MsgWhoMove
//
// FlipRow
// (calculate number of pieces are flipped by a move
// and return it. Eventually do the complete or pseudo
// move)
//
// Input: who (int)
// which board (int[][])
// position col, row (int)
// direction col, row (int)
// make move ? (int)
//
public int FlipRow(int Who, int[][] WhichBoard, int C, int R,
int CInc, int RInc, int MakeMove) {
int NewCol;
int NewRow;
int Opponent = User + Computer - Who;
int CNT = 0;
NewCol = C - 1;
NewRow = R - 1;
while (true) {
if (((NewCol+CInc) < 0) || ((NewCol+CInc) > 7) ||
((NewRow+RInc) < 0) || ((NewRow+RInc) > 7)) {
return 0;
}
if (WhichBoard[NewCol+CInc][NewRow+RInc] == Opponent) {
CNT++;
NewCol += CInc;
NewRow += RInc;
} else if (WhichBoard[NewCol+CInc][NewRow+RInc] == Empty) {
return 0;
} else {
break;
}
}
if (MakeMove != NOMOVE) {
C--;
R--;
for (int v=0; v<=CNT; v++) {
if (MakeMove == REALMOVE) {
DrawPiece(Who, C+1, R+1);
} else {
WhichBoard[C][R] = Who;
}
C += CInc;
R += RInc;
}
}
return CNT;
}
// End of FlipRow
//
// IsLegalMove
// (verify that the move is legal)
//
// Input: who (int)
// board (int[][])
// position col, row (int)
// Output: is legal ? (boolean)
// Notes:
//
public boolean IsLegalMove(int Who, int[][] WhichBoard, int C, int R) {
if (WhichBoard[C-1][R-1] != Empty) {
return false;
}
for (int CInc=-1; CInc<2; CInc++) {
for (int RInc=-1; RInc<2; RInc++) {
if (FlipRow(Who, WhichBoard, C, R, CInc, RInc, NOMOVE) > 0) {
return true;
}
}
}
return false;
}
// End of IsLegalMove
//
// MakeMove
// (make the move)
//
// Input: who (int)
// position col, row (int)
// Output: false=EndGame, true=next player (boolean)
// Notes:
//
public boolean MakeMove(int Who, int C, int R) {
for (int CInc=-1; CInc<2; CInc++) {
for (int RInc=-1; RInc<2; RInc++) {
FlipRow(Who, TheBoard, C, R, CInc, RInc, REALMOVE);
}
}
if (IsBoardComplete() ||
((!ThereAreMoves(Computer, TheBoard)) && (!ThereAreMoves(User, TheBoard)))) {
return false;
}
int Opponent = (User + Computer) - Who;
if (ThereAreMoves(Opponent, TheBoard)) {
UserMove = !UserMove;
}
return true;
}
// End of MakeMove
//
// EndGame
// (shows the winning message)
//
// Input: none
// Output: none
// Notes:
//
public void EndGame() {
int CompPieces = 0;
int UserPieces = 0;
String TheMsg;
Graphics g = getGraphics();
int StrWidth;
for (int c=0; c<8; c++) {
for (int r=0; r<8; r++) {
if (TheBoard[c][r] == Computer) {
CompPieces++;
} else {
UserPieces++;
}
}
}
if (CompPieces > UserPieces) {
TheMsg = WinMsg_W;
} else if (UserPieces > CompPieces) {
TheMsg = WinMsg_L;
} else {
TheMsg = WinMsg_T;
}
g.setFont(new Font("Helvetica", Font.BOLD, 48));
StrWidth = g.getFontMetrics(g.getFont()).stringWidth(TheMsg);
g.setColor(new Color(0,0,128));
g.drawString(TheMsg, ((int)((321-StrWidth)/2))+1, 209);
g.setColor(new Color(0,255,255));
g.drawString(TheMsg, ((int)((321-StrWidth)/2)), 208);
}
// End of EndGame
//
// IsBoardComplete
// (checks if the board is complete)
//
// Input: none
// Output: the board is complete ? (boolean)
// Notes:
//
public boolean IsBoardComplete() {
for (int i=0; i<8; i++) {
for (int j=0; j<8; j++) {
if (TheBoard[i][j] == Empty) {
return false;
}
}
}
return true;
}
// End of IsBoardComplete
//
// ThereAreMoves
// (checks if there are more valid moves for the
// player)
//
// Input: player (int)
// board (int[][])
// Output: there are moves ? (boolean)
// Notes:
//
public boolean ThereAreMoves(int Who, int[][] WhichBoard) {
for (int i=1; i<=8; i++) {
for (int j=1; j<=8; j++) {
if (IsLegalMove(Who, WhichBoard, i, j)) {
return true;
}
}
}
return false;
}
// End of ThereAreMoves
//
// CalcOpponentScore
// (calculate the totalScore of opponent after
// a move)
//
// Input: position x, y (int)
// Output: score (int)
// Notes:
//
public int CalcOpponentScore(int CP, int RP) {
int OpScore = 0;
int tempBoard[][] = new int[8][8];
for (int c=0; c<8; c++) {
for (int r=0; r<8; r++) {
tempBoard[c][r] = TheBoard[c][r];
}
}
for (int CInc=-1; CInc<2; CInc++) {
for (int RInc=-1; RInc<2; RInc++) {
FlipRow(Computer, tempBoard, CP+1, RP+1, CInc, RInc, PSEUDOMOVE);
}
}
if (ThereAreMoves(User, tempBoard)) {
for (int C=0; C<8; C++) {
for (int R=0; R<8; R++) {
OpScore += RankMove(User, tempBoard, C, R);
}
}
}
return OpScore;
}
// End of CalcOpponentScore()
//
// RankMoves
// (rank all moves for the computer)
//
// Input: none
// Output: none
// Notes:
//
public void RankMoves() {
for (int C=0; C<8; C++) {
for (int R=0; R<8; R++) {
Score[C][R] = RankMove(Computer, TheBoard, C, R);
if (Score[C][R] != 0) {
OpponentScore[C][R] = CalcOpponentScore(C, R);
} else {
OpponentScore[C][R] = 0;
}
}
}
}
// End of RankMoves
//
// RankMove
// (rank a move for a player on a board)
//
// Input: who moves (int)
// on which board (int[][])
// position col, row (int)
// Output: flipped pieces (int)
// Notes: best are corner, then border lines,
// worst are line near to border lines
//
public int RankMove(int Who, int[][] WhichBoard, int Col, int Row) {
int CNT = 0;
int MV = 0;
if (WhichBoard[Col][Row] != Empty) {
return 0;
}
for (int CInc=-1; CInc<2; CInc++) {
for (int RInc=-1; RInc<2; RInc++) {
MV = FlipRow(Who, WhichBoard, Col+1, Row+1, CInc, RInc, NOMOVE);
CNT += MV;
}
}
if (CNT > 0) {
if (((Col == 0) || (Col == 7)) ||
((Row == 0) || (Row == 7))) {
CNT = 63;
}
if (((Col == 0) || (Col == 7)) &&
((Row == 0) || (Row == 7))) {
CNT = 64;
}
if ((((Col == 0) || (Col == 7)) && (Row == 1) || (Row == 6)) &&
(((Col == 1) || (Col == 6)) && (Row == 0) || (Row == 7)) &&
(((Col == 1) || (Col == 6)) && (Row == 1) || (Row == 6))) {
CNT = 1;
}
}
return CNT;
}
// End of RankMove
//
// BestMove
// (calculate and execute the best move)
//
// Input: none
// Output: value, col & row (int[3])
// Notes:
//
public int[] BestMove() {
int retval[];
retval = new int[3];
retval[0] = -998; // move value;
retval[1] = 0; // column
retval[2] = 0; // row
RankMoves();
for (int C=0; C<8; C++) {
for (int R=0; R<8; R++) {
if ((Score[C][R] == 0) && (OpponentScore[C][R] == 0)) {
Score[C][R] = -999;
} else if (Score[C][R] != 64) {
Score[C][R] = Score[C][R] - OpponentScore[C][R];
}
}
}
for (int C=0; C<8; C++) {
for (int R=0; R<8; R++) {
if (Score[C][R] > retval[0]) {
retval[1] = C;
retval[2] = R;
retval[0] = Score[C][R];
}
}
}
retval[1]++;
retval[2]++;
return retval;
}
// End of BestMove
//
// ShowAbout
// (show the About Box)
//
// Input: none
// Output: none
// Notes:
//
public void ShowAbout() {
Graphics g = getGraphics();
g.setColor(Color.lightGray);
g.fill3DRect(20,53,281,281, true);
g.setColor(Color.black);
g.setFont(new Font("Helvetica", Font.BOLD, 24));
String TheMsg = "K-Reversi (v." + VERSION +")";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 110);
g.setFont(new Font("Helvetica", Font.BOLD, 12));
TheMsg = "By: Alessandro A. 'Kazuma' Garbagnati";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 150);
TheMsg = "kazuma@energy.it";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 165);
g.setFont(new Font("Helvetica", Font.PLAIN, 12));
TheMsg = "thanks to: Muffy Barkocy";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 185);
TheMsg = "muffy@fish.com";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 200);
g.setFont(new Font("Helvetica", Font.ITALIC, 16));
TheMsg = "HAKUNA MATATA";
g.drawString(TheMsg, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(TheMsg)))/2))+1, 270);
g.setFont(new Font("System", Font.BOLD, 12));
g.drawString(PressWin, ((int)((321-(g.getFontMetrics(g.getFont()).stringWidth(PressWin)))/2))+1, 320);
CopyWinOn = true;
}
// End of ShowAbout
//
// paint
//
public void paint(Graphics g) {
DrawBoard(g);
MsgWhoMove(UserMove);
if (!CopyWinOn) {
for (int i=0; i<8; i++) {
for (int j=0; j<8; j++) {
if (TheBoard[i][j] != Empty) {
DrawPiece(TheBoard[i][j], i+1, j+1);
}
}
}
} else {
ShowAbout();
}
}
// End of paint
//
// init
//
public void init() {
// This is the right size of the applet 321x387
resize(321,387);
// I set twice the language. That's because if anybody
// forget a string in an external language file, are
// used english strings.
Language = SetLanguage("english");
Language = SetLanguage(getParameter("language", "english"));
if (!StillInitiated) {
setFont(new Font("System", Font.PLAIN, 12));
add(new Button(BtnNew));
add(new Button(BtnCopy));
StillInitiated = true;
}
TheBoard = new int[8][8];
Score = new int[8][8];
OpponentScore = new int[8][8];
for (int i=0; i<8; i++) {
for (int j=0; j<8; j++) {
TheBoard[i][j] = Empty;
}
}
TheBoard[3][3] = User;
TheBoard[3][4] = Computer;
TheBoard[4][3] = Computer;
TheBoard[4][4] = User;
UserMove = true;
MsgWhoMove(true);
repaint();
}
// End of init
//
// handleEvent
//
public boolean handleEvent(Event evt) {
int TheCol,
TheRow;
int BMove[];
int BX,
BY;
boolean retval = false;
// Restart
if (BtnNew.equals(evt.arg) && (!CopyWinOn)) {
init();
return true;
}
// Show Copyright
if (BtnCopy.equals(evt.arg) && (!CopyWinOn)) {
ShowAbout();
return true;
}
// Click with copyright message on
if ((CopyWinOn) && (evt.id == Event.MOUSE_UP)) {
CopyWinOn = false;
repaint();
return true;
}
// Process a normal click in the board
BX = evt.x;
BY = evt.y - YSHIFT;
if ((BY >= 0) && (BY <= 321) &&
(evt.id == Event.MOUSE_UP) && (UserMove)) {
TheCol = (int)((BX/40)+1);
TheRow = (int)((BY/40)+1);
if (IsLegalMove(User, TheBoard, TheCol, TheRow)) {
retval = MakeMove(User, TheCol, TheRow);
while (retval && (!UserMove)) {
MsgWhoMove(UserMove);
BMove = BestMove();
retval = MakeMove(Computer, BMove[1], BMove[2]);
MsgWhoMove(UserMove);
}
if (!retval) {
EndGame();
}
}
return true;
}
return false;
}
// End of handleEvent
}
//
// End of KReversi
//