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