/* Use the Java I/O routines */ import java.io.*; /** * See BIO exam paper. This program solves BIO'97 question 2 * using strategy 1 to play Reversi. * * Example run using strategy 1: *
* .0*. * 0..0 * 0000 * 0*.0 * * Strategy 1 * ........ * ........ * ...0*... * ..0..0.. * ..0000.. * ..0*.0.. * ........ * ........ * * 1 * ........ * ........ * ...0*... * ..0..0.. * ..0000.. * ..00.0.. * ....0... * ........ * * 0 * 7 4 * ........ * ........ * ...0*... * ..0..*.. * ..0000*. * ..00.0.. * ....0... * ........ * * 4 * ........ * .....*.. * ...0**.. * ..0*.*.. * .**00**. * ..00.0.. * ....0... * ........ * * 0 * 1 4 * ........ * .....*.. * ...0**.. * ..0*.*.. * 00000**. * ..00.0.. * ....0... * ........ * * -1 ** (End of example for strategy 1.) * * Example run using strategy 2: *
* .0*. * 0..0 * 0000 * 0*.0 * * Strategy 2 * ........ * ........ * ...0*... * ..0..0.. * ..0000.. * ..0*.0.. * ........ * ........ * * * 99 * ******** * ******0* * **0***00 * *000**00 * *00*0000 * *0*00000 * **0**000 * *****000 * * Black wins by 8. ** (End of example for strategy 2.) * * Solution copyright (c) 1997 The British Informatics Olympiad (BIO). * * This program may be freely copied by persons or organisations * involved in the British Informatics Olympiad or the International * Olympiad in Informatics, on condition that no changes are made and * this notice is not altered. Distribution for profit is forbidden * unless permission is first obtained in writing from the BIO. * * This program is for educational purposes only and comes with no * warranty, implied or otherwise, as to its fitness for any purpose. * * Author: Antony Rix * Internet: http://www.christs.cam.ac.uk/bio/ * E-mail: a.rix@lineone.net * S-mail: The British Informatics Olympiad * Christ's College * Cambridge CB2 3BU * United Kingdom */ class Reversi1 extends AppletConsoleApp { /** * This DataInputStream is used for parsing the input. */ DataInput din; /** * Integer representing a blank square (0). */ int blank = 0; /** * Integer representing a square with a white piece (1). */ int white = 1; /** * Integer representing a square with a black piece (2). */ int black = 2; /** * 3-element array giving the symbol for each square/piece. */ char[] symbol = {'.', '0', '*'}; /** * Current board. */ int[][] currentBoard = new int[10][10]; /** * Old board used to back up the current board when moves are made. */ int[][] oldBoard = new int[10][10]; /** * New board used to hold the player's prospective move. */ int[][] newBoard = new int[10][10]; /** * nWhite represents the number of white pieces on the board. * * nBlack represents the number of black pieces on the board. * * nPlayed represents the total number of pieces on the board. */ int nWhite, nBlack, nPlayed; /** * The current player, equal to white (1) or black (2). */ int player; /** * Start the application from the command line. */ public static void main(String[] args) { Reversi1 thisApp = new Reversi1(); thisApp.redirectStreams(System.in, System.out); thisApp.run(); } /** * Main program of Reversi1, this is run from the command line * by the
main() method, or called directly by an
* AppletConsole.
*
* The program reads from the stream in and writes
* to the stream out, which are automatically assigned
* by main() or by the AppletConsole class.
*/
public void run() {
din = new DataInputStream(in);
initialiseBoards();
try {
out.println("BIO'97 Q2: Reversi");
readBoard(currentBoard);
out.println();
out.println("Strategy 1");
showBoard(currentBoard);
play();
out.println("Reversi finished.");
}
catch (IOException e) { out.println("I/O error!"); }
catch (NumberFormatException e) { out.println("Invalid input format!"); }
}
/**
* Initialise the boards to all blanks, including the area of 1
* piece in width which is used to border the boards.
*/
public void initialiseBoards() {
int i, j;
for( i = 0; i < 10; i++)
for( j = 0; j < 10; j++) {
currentBoard[i][j] = blank;
oldBoard[i][j] = blank;
newBoard[i][j] = blank;
}
}
/**
* Read a 4x4 Reversi centre board from the input.
*/
public void readBoard(int[][] Board) throws IOException {
int i, j;
char c;
out.println("Enter 4x4 board as follows:");
out.println(" '.' represents blank");
out.println(" '0' represents white");
out.println(" '*' represents black.");
for( i = 1; i <= 8; i++ )
for( j = 1; j <= 8; j++ )
Board[i][j] = blank;
for( i = 6; i >= 3; i-- ) {
for( j = 3; j <= 6; j++ ) {
c = (char)din.readByte();
switch (c) {
case '.': Board[i][j] = blank; break;
case '0': Board[i][j] = white; break;
case '*': Board[i][j] = black; break;
default:
out.println("Invalid character '" + (int)c + "' taken as blank.");
Board[i][j] = blank; break;
}
}
c = (char)din.readByte();
if( c == '\r')
c = (char)din.readByte();
}
}
/**
* Copy one board onto another.
*/
public void copyBoard(int[][] fromBoard, int[][] toBoard) {
int i, j;
for( i = 1; i <= 8; i++)
for( j = 1; j <= 8; j++)
toBoard[i][j] = fromBoard[i][j];
}
/**
* Display a board on screen.
*/
public void showBoard(int[][] Board) {
int i, j;
for( i = 8; i >= 1; i--) {
for( j = 1; j <= 8; j++)
out.print(symbol[Board[i][j]]);
out.println();
}
out.println();
}
/**
* Play the game.
*/
public void play() throws IOException, NumberFormatException {
int i, j;
String cmd;
int action;
countPieces(currentBoard);
player = white;
action = 0;
if (nPlayed < 64)
do {
cmd = din.readLine();
action = Integer.parseInt(cmd);
if (action > -1) {
if (action == 0) {
cmd = din.readLine();
j = cmd.charAt(0) - (int)'0';
i = cmd.charAt(2) - (int)'0';
if ((i < 1) || (i > 8) || (j < 1) || (j > 8))
out.println("Expected a location between (1,1) and (8,8)");
else if (currentBoard[i][j] != blank)
out.println("That square is occupied!");
else if (!hasNeighbour(currentBoard, i, j))
out.println("That square has no neighbour");
else {
placePiece(currentBoard, player, i, j);
showBoard(currentBoard);
countPieces(currentBoard);
player = enemyPlayer(player);
}
}
else {
while ((nPlayed < 64) && (action > 0)) {
playBestMove1();
countPieces(currentBoard);
player = enemyPlayer(player);
action--;
}
showBoard(currentBoard);
}
}
} while ((nPlayed < 64) && (action >= 0));
if (nPlayed == 64) {
if (nWhite > nBlack)
out.println("White wins by " + (nWhite - nBlack) + ".");
else if (nWhite < nBlack)
out.println("Black wins by " + (nBlack - nWhite) + ".");
else
out.println("Black and white draw.");
}
}
/**
* Counts the number of pieces of either colour in currentBoard
* and sets nWhite etc. accordingly.
*/
public void countPieces(int[][] Board) {
nWhite = 0;
nBlack = 0;
int i, j;
for( i = 1; i <= 8; i++)
for( j = 1; j <= 8; j++)
switch (Board[i][j]) {
case 1: nWhite++; break;
case 2: nBlack++; break;
}
nPlayed = nWhite + nBlack;
}
/**
* Returns the player's opponent, or blank if the argument is not
* a colour.
*/
public int enemyPlayer(int Player) {
if (Player == black)
return white;
else if (Player == white)
return black;
else
return blank;
}
/**
* Returns true if a square has at least one neighbouring piece
* and is therefore available for a move.
*/
public boolean hasNeighbour(int[][] Board, int i, int j) {
if ((Board[i-1][j-1] + Board[i-1][j ] + Board[i-1][j+1] +
Board[i ][j-1] + Board[i ][j+1] +
Board[i+1][j-1] + Board[i+1][j ] + Board[i+1][j+1]) == blank)
return false;
else
return true;
}
/**
* Place a piece for player at (i, j) on Board.
*
* Returns the number of opposing pieces turned.
*/
public int placePiece(int[][] Board, int player, int i, int j) {
Board[i][j] = player;
return makeSwap(Board, player, i, j, -1, -1) +
makeSwap(Board, player, i, j, -1, 0) +
makeSwap(Board, player, i, j, -1, 1) +
makeSwap(Board, player, i, j, 0, -1) +
makeSwap(Board, player, i, j, 0, +1) +
makeSwap(Board, player, i, j, 1, -1) +
makeSwap(Board, player, i, j, 1, 0) +
makeSwap(Board, player, i, j, 1, +1);
}
/**
* Returns true if a move at (i,j) would enable one or more
* enemy pieces to be turned in direction (di,dj).
*/
public boolean canSwap(int[][] Board, int player,
int i, int j, int di, int dj) {
int enemy = enemyPlayer(player);
if (Board[i + di][j + dj] != enemy)
return false;
else {
do {
i = i + di;
j = j + dj;
} while (Board[i][j] == enemy);
return (Board[i][j] == player);
}
}
/**
* Makes swaps on the board and returns the number of swaps made.
*/
public int makeSwap(int[][] Board, int player,
int i, int j, int di, int dj) {
int enemy = enemyPlayer(player);
boolean swapping = canSwap(Board, player, i, j, di, dj);
int turned = 0;
while (swapping) {
i = i + di;
j = j + dj;
swapping = (Board[i][j] == enemy);
if (swapping) {
turned++;
Board[i][j] = player;
}
}
return turned;
}
/**
* Plays the best move for the given player using strategy 2.
*/
public void playBestMove1() {
int advantageBestPlayer = -999;
int iBestPlayer = 0;
int jBestPlayer = 0;
int advantagePlayer;
int iPlayer, jPlayer;
if (nPlayed < 64) {
// This is the algorithm for strategy 1, which we use as there
// is only one go left.
copyBoard(currentBoard, oldBoard);
for( iPlayer = 1; iPlayer <= 8; iPlayer++ )
for( jPlayer = 8; jPlayer >= 1; jPlayer-- )
if ((currentBoard[iPlayer][jPlayer] == blank) &&
(hasNeighbour(currentBoard, iPlayer, jPlayer))) {
advantagePlayer =
placePiece(currentBoard, player, iPlayer, jPlayer);
if (advantagePlayer > advantageBestPlayer) {
advantageBestPlayer = advantagePlayer;
iBestPlayer = iPlayer;
jBestPlayer = jPlayer;
}
copyBoard(oldBoard, currentBoard);
}
}
placePiece(currentBoard, player, iBestPlayer, jBestPlayer);
}
}