From 1c28085bf50b27d6099538bb6606342d2418a77c Mon Sep 17 00:00:00 2001 From: gumartinm Date: Sat, 1 Sep 2012 19:12:27 +0200 Subject: [PATCH] First steps AIThread. Bonus: code refactoring, trying to improve the OOP --- res/layout/activity_reversi.xml | 3 +- src/de/android/reversi/AIThread.java | 20 ++ src/de/android/reversi/Board.java | 130 +++++++++++++ src/de/android/reversi/CheckMovement.java | 12 +- src/de/android/reversi/ReversiView.java | 260 ++++++------------------- src/de/android/reversi/logic/ReversiLogic.java | 56 ++++++ 6 files changed, 272 insertions(+), 209 deletions(-) create mode 100644 src/de/android/reversi/AIThread.java create mode 100644 src/de/android/reversi/Board.java create mode 100644 src/de/android/reversi/logic/ReversiLogic.java diff --git a/res/layout/activity_reversi.xml b/res/layout/activity_reversi.xml index b93e2bd..9bdc956 100644 --- a/res/layout/activity_reversi.xml +++ b/res/layout/activity_reversi.xml @@ -15,7 +15,7 @@ android:id="@+id/txtPlayer1" android:layout_width="wrap_content" android:layout_height="wrap_content"/> - listAllowedMovements; + private final Board board; + private final Player player; + + + public AIThread(final Board board, final Player player, final String nameThread) { + super(nameThread); + this.board = board; + this.player = player; + } + + public void setListAllowedMovements(final List listAllowedMovements) { + this.listAllowedMovements = listAllowedMovements; + } +} diff --git a/src/de/android/reversi/Board.java b/src/de/android/reversi/Board.java new file mode 100644 index 0000000..c784a4e --- /dev/null +++ b/src/de/android/reversi/Board.java @@ -0,0 +1,130 @@ +package de.android.reversi; + +import java.util.List; + +public class Board { + public static final short NUMBER_OF_COLUMNS = 8; + public static final short NUMBER_OF_ROWS = 8; + public static final short TOP_MARGIN = 0; + public static final short LEFT_MARGIN = 0; + + private final Square gameBoard[][] = new Square[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]; + + //Just one dimension because it is a square. + private int squareWidth; + //Just one dimension because it is a square. + private int canvasWidth; + + + public void initBoard() { + for (short column = 0; column < NUMBER_OF_COLUMNS; column++) { + for (short row = 0; row < NUMBER_OF_ROWS; row++) { + this.gameBoard[column][row] = new Square();; + } + } + } + + public void updateBoard(final Player player, final short column, final short row) { + this.updateBoard(player, column, row, false); + } + + public void updateBoard(final Player player, final short column, final short row, + final boolean suggestion) { + gameBoard[column][row].setPlayer(player); + gameBoard[column][row].setSuggestion(suggestion); + } + + public void removeSuggestionsFromBoard(final List listAllowedMovements) { + + for (final Movement iterator : listAllowedMovements) { + this.updateBoard(Player.NOPLAYER, iterator.getColumn(), iterator.getRow()); + } + } + + + public void flipOpponentDiscs(final Square gameBoard[][], final Movement movement, final Player currentPlayer) { + for (final FlippedDisc flippedDisc : movement.getFlippedDiscs()) { + this.updateBoard(currentPlayer, flippedDisc.getColumn(), flippedDisc.getRow()); + } + } + + + public void updateSquareParameters(final int squareWidth) { + for (short column = 0; column < Board.NUMBER_OF_COLUMNS; column++) { + for (short row = 0; row < Board.NUMBER_OF_ROWS; row++) { + + // calculating the square's center + final int cellMediumX = (column * squareWidth + (column + 1) * + squareWidth) / 2; + final int cellMediumY = (row * squareWidth + (row + 1) * + squareWidth) / 2; + + // applying the margins + final int cx = cellMediumX + Board.LEFT_MARGIN; + final int cy = cellMediumY +Board. TOP_MARGIN; + + // the radius + final int radius = (squareWidth - 2) / 2 - 2; + + //update squares + gameBoard[column][row].setRadius(radius); + gameBoard[column][row].setSquareMediumX(cx); + gameBoard[column][row].setSquareMediumY(cy); + } + } + } + + public short transformCoordinateYInRow(final float y) { + + short row = (short) ((y - Board.TOP_MARGIN) / squareWidth); + + if (row < 0 || row >= Board.NUMBER_OF_ROWS) { + row = -1; + } + + return row; + } + + public short transformCoordinateXInColumn(final float x) { + + short column = (short) ((x - Board.LEFT_MARGIN) / squareWidth); + + if (column < 0 || column >= Board.NUMBER_OF_COLUMNS) { + column = -1; + } + + return column; + } + + + public void calculateGraphicParameters(int width, final int height) { + + // getting the minor (the board is a square) + if (height < width) { + width = height; + } + + // converting the dimensions to get them divisible by 8 + while (width % 8 != 0) { + width--; + } + + canvasWidth = width; + squareWidth = (width - Board.LEFT_MARGIN * 2) / Board.NUMBER_OF_COLUMNS; + } + + + /*** Getters ***/ + + public int getCanvasWidth() { + return canvasWidth; + } + + public int getSquareWidth() { + return squareWidth; + } + + public Square[][] getGameBoard() { + return gameBoard; + } +} diff --git a/src/de/android/reversi/CheckMovement.java b/src/de/android/reversi/CheckMovement.java index ab8b1c6..9619918 100644 --- a/src/de/android/reversi/CheckMovement.java +++ b/src/de/android/reversi/CheckMovement.java @@ -64,7 +64,7 @@ public final class CheckMovement { final short column = movement.getColumn(); //Precondition: - if (row >= ReversiView.NUMBER_OF_ROWS -2) { + if (row >= Board.NUMBER_OF_ROWS -2) { return false; } @@ -84,7 +84,7 @@ public final class CheckMovement { final short column = movement.getColumn(); //Precondition: - if (column >= ReversiView.NUMBER_OF_COLUMNS -2) { + if (column >= Board.NUMBER_OF_COLUMNS -2) { return false; } @@ -144,7 +144,7 @@ public final class CheckMovement { final short column = movement.getColumn(); //Precondition: - if (column >= (ReversiView.NUMBER_OF_COLUMNS -2) || row >= (ReversiView.NUMBER_OF_ROWS -2)) { + if (column >= (Board.NUMBER_OF_COLUMNS -2) || row >= (Board.NUMBER_OF_ROWS -2)) { return false; } @@ -164,7 +164,7 @@ public final class CheckMovement { final short column = movement.getColumn(); //Precondition: - if (column <= 1 || row >= (ReversiView.NUMBER_OF_ROWS -2)) { + if (column <= 1 || row >= (Board.NUMBER_OF_ROWS -2)) { return false; } @@ -184,7 +184,7 @@ public final class CheckMovement { final short column = movement.getColumn(); //Precondition: - if (row <= 1 || column >= (ReversiView.NUMBER_OF_COLUMNS -2)) { + if (row <= 1 || column >= (Board.NUMBER_OF_COLUMNS -2)) { return false; } @@ -204,7 +204,7 @@ public final class CheckMovement { boolean match = false; while (row > 0 && column > 0 && - row < ReversiView.NUMBER_OF_ROWS && column < ReversiView.NUMBER_OF_COLUMNS && + row < Board.NUMBER_OF_ROWS && column < Board.NUMBER_OF_COLUMNS && !empty(gameBoard, column, row)) { if (gameBoard[column][row].getPlayer() == player) { diff --git a/src/de/android/reversi/ReversiView.java b/src/de/android/reversi/ReversiView.java index 2de189e..47b0587 100644 --- a/src/de/android/reversi/ReversiView.java +++ b/src/de/android/reversi/ReversiView.java @@ -1,6 +1,5 @@ package de.android.reversi; -import java.util.ArrayList; import java.util.List; import android.app.Activity; @@ -13,49 +12,42 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.TextView; +import de.android.reversi.logic.ReversiLogic; public class ReversiView extends SurfaceView { - public static final short NUMBER_OF_COLUMNS = 8; - public static final short NUMBER_OF_ROWS = 8; - - private static final short TOP_MARGIN = 0; - private static final short LEFT_MARGIN = 0; - - private final Square gameBoard[][] = new Square[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]; + private final Board board = new Board(); + private final ReversiLogic reversiLogic = new ReversiLogic(); private final Player AI = Player.NOPLAYER; + private final Context context; //¿Funciona bien volatile con enum? Ver mi codigo de Singletons y enums. private volatile Player currentPlayer = Player.PLAYER1; private volatile boolean isEnableUserTouch; - private int squareWidth; - private int squareHeight; - private int canvasHeight; - private int canvasWidth; private List listAllowedMovements; public ReversiView(final Context context) { super(context); this.context = context; - this.preInitBoard(); - this.initialize(); + board.initBoard(); + this.init(); } public ReversiView(final Context context, final AttributeSet attrs) { super(context, attrs); this.context = context; - this.preInitBoard(); - this.initialize(); + board.initBoard(); + this.init(); } public ReversiView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); this.context = context; - this.preInitBoard(); - this.initialize(); + board.initBoard(); + this.init(); } private void drawGrid(final Canvas canvas) { @@ -65,70 +57,48 @@ public class ReversiView extends SurfaceView { canvas.drawPaint(paint); - //Square (green) + //Board (green) paint.setColor(Color.rgb(0, 158, 11)); - canvas.drawRect(LEFT_MARGIN, TOP_MARGIN, - NUMBER_OF_COLUMNS * squareWidth + LEFT_MARGIN, - NUMBER_OF_ROWS * squareHeight + TOP_MARGIN, paint); + canvas.drawRect(Board.LEFT_MARGIN, Board.TOP_MARGIN, + Board.NUMBER_OF_COLUMNS * board.getSquareWidth() + Board.LEFT_MARGIN, + Board.NUMBER_OF_ROWS * board.getSquareWidth() + Board.TOP_MARGIN, paint); - //Lines between squares + //Lines creating squares on the board paint.setColor(Color.BLACK); paint.setStrokeWidth(4); - for (int col = 0; col <= NUMBER_OF_COLUMNS; col++) { + for (int col = 0; col <= Board.NUMBER_OF_COLUMNS; col++) { // vertical lines - final int x = col * squareWidth + LEFT_MARGIN; - canvas.drawLine(x, TOP_MARGIN, x, canvasHeight - TOP_MARGIN * 1, paint); + final int x = col * board.getSquareWidth() + Board.LEFT_MARGIN; + canvas.drawLine(x, Board.TOP_MARGIN, x, board.getCanvasWidth() - Board.TOP_MARGIN * 1, paint); } - for (int row = 0; row < NUMBER_OF_ROWS + 1; row++) { - final int y = row * squareHeight + TOP_MARGIN; + for (int row = 0; row < Board.NUMBER_OF_ROWS + 1; row++) { + final int y = row * board.getSquareWidth() + Board.TOP_MARGIN; // horizontal lines - canvas.drawLine(LEFT_MARGIN, y, canvasWidth - (LEFT_MARGIN * 1), y, paint); - } - - } - - private void calculateGraphicParameters(final Canvas canvas, final int width, - final int height) { - canvasWidth = width; - canvasHeight = height; - - // getting the minor (the board is a square) - if (canvasHeight > canvasWidth) { - canvasHeight = canvasWidth; - } else { - canvasWidth = canvasHeight; + canvas.drawLine(Board.LEFT_MARGIN, y, board.getCanvasWidth() - (Board.LEFT_MARGIN * 1), y, paint); } - // converting the dimensions to get them divisible by 8 - while (canvasWidth % 8 != 0) { - canvasWidth--; - canvasHeight--; - } - - squareWidth = (canvasWidth - LEFT_MARGIN * 2) / NUMBER_OF_COLUMNS; - squareHeight = (canvasHeight - TOP_MARGIN * 2) / NUMBER_OF_ROWS; } - private void initialize() { + private void init() { getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(final SurfaceHolder holder) { //White - updateBoard(Player.PLAYER1, (short)3, (short)4); - updateBoard(Player.PLAYER1, (short)4, (short)3); + board.updateBoard(Player.PLAYER1, (short)3, (short)4); + board.updateBoard(Player.PLAYER1, (short)4, (short)3); //Black - updateBoard(Player.PLAYER2, (short)4, (short)4); - updateBoard(Player.PLAYER2, (short)3, (short)3); + board.updateBoard(Player.PLAYER2, (short)4, (short)4); + board.updateBoard(Player.PLAYER2, (short)3, (short)3); //AllowedMovements for Player - listAllowedMovements = allowedMovements(currentPlayer, gameBoard); + listAllowedMovements = reversiLogic.allowedMovements(currentPlayer, board.getGameBoard()); //UpdateBoard with suggestions for (final Movement movement : listAllowedMovements) { - updateBoard(currentPlayer, movement.getColumn(), movement.getRow(), true); + board.updateBoard(currentPlayer, movement.getColumn(), movement.getRow(), true); } } @@ -136,8 +106,8 @@ public class ReversiView extends SurfaceView { public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) { final Canvas canvas = holder.lockCanvas(); - calculateGraphicParameters(canvas, width, height); - updateSquareParameters(); + board.calculateGraphicParameters(width, height); + board.updateSquareParameters(board.getSquareWidth()); drawGrid(canvas); drawPositions(canvas); holder.unlockCanvasAndPost(canvas); @@ -159,16 +129,15 @@ public class ReversiView extends SurfaceView { } if (this.isEnableUserTouch) { - final short column = transformCoordinateXInColumn(event.getX()); - final short row = transformCoordinateYInRow(event.getY()); + final short column = board.transformCoordinateXInColumn(event.getX()); + final short row = board.transformCoordinateYInRow(event.getY()); if (row != -1 && column != -1 ) { Movement movement; - if((movement = retrieveAllowedMovement(row, column)) != null) { - removeSuggestionsFromBoard(gameBoard, listAllowedMovements); - updateBoard(this.currentPlayer, column, row); - flipOpponentDiscs(gameBoard, movement, currentPlayer); - this.mainLoop(); + if((movement = reversiLogic.retrieveAllowedMovement(row, column, + listAllowedMovements)) != null) { + board.removeSuggestionsFromBoard(listAllowedMovements); + this.mainLoop(column, row, movement); } } } @@ -176,30 +145,6 @@ public class ReversiView extends SurfaceView { return true; } - private short transformCoordinateYInRow(final float y) { - - short row = (short) ((y - TOP_MARGIN) / this.squareWidth); - - // if tapped outside the board - if (row < 0 || row >= NUMBER_OF_ROWS) { - row = -1; - } - - return row; - } - - private short transformCoordinateXInColumn(final float x) { - - short column = (short) ((x - LEFT_MARGIN) / this.squareWidth); - - // if tapped outside the board - if (column < 0 || column >= NUMBER_OF_COLUMNS) { - column = -1; - } - - return column; - } - private void drawDisk(final Canvas canvas, final Square square, final short column, final short row) { this.drawCircle(canvas, square.getPlayer(), square.getSquareMediumX(), @@ -244,32 +189,22 @@ public class ReversiView extends SurfaceView { } } - private void updateBoard(final Player player, final short column, final short row) { - this.updateBoard(player, column, row, false); - } - - private void updateBoard(final Player player, final short column, final short row, - final boolean suggestion) { - this.gameBoard[column][row].setPlayer(player); - this.gameBoard[column][row].setSuggestion(suggestion); - } - private void drawPositions(final Canvas canvas) { int player1Score = 0; int player2Score = 0; - for (short column = 0; column < NUMBER_OF_COLUMNS; column++) { - for (short row = 0; row < NUMBER_OF_ROWS; row++) { - if (this.gameBoard[column][row].getPlayer() != Player.NOPLAYER) { - if (this.gameBoard[column][row].getPlayer() == Player.PLAYER1 && - !this.gameBoard[column][row].isSuggestion() ) { + for (short column = 0; column < Board.NUMBER_OF_COLUMNS; column++) { + for (short row = 0; row < Board.NUMBER_OF_ROWS; row++) { + if (board.getGameBoard()[column][row].getPlayer() != Player.NOPLAYER) { + if (board.getGameBoard()[column][row].getPlayer() == Player.PLAYER1 && + !board.getGameBoard()[column][row].isSuggestion() ) { player1Score++; } - if (this.gameBoard[column][row].getPlayer() == Player.PLAYER2 && - !this.gameBoard[column][row].isSuggestion() ) { + if (board.getGameBoard()[column][row].getPlayer() == Player.PLAYER2 && + !board.getGameBoard()[column][row].isSuggestion() ) { player2Score++; } - drawDisk(canvas, this.gameBoard[column][row], column, row); + drawDisk(canvas, board.getGameBoard()[column][row], column, row); } } } @@ -278,49 +213,22 @@ public class ReversiView extends SurfaceView { ((TextView)((Activity)this.context).findViewById(R.id.txtPlayer2Score)).setText(String.format(" %d %s", player2Score, "discs")); } - private void preInitBoard() { - for (short column = 0; column < NUMBER_OF_COLUMNS; column++) { - for (short row = 0; row < NUMBER_OF_ROWS; row++) { - this.gameBoard[column][row] = new Square();; - } - } - } - - private void updateSquareParameters() { - for (short column = 0; column < NUMBER_OF_COLUMNS; column++) { - for (short row = 0; row < NUMBER_OF_ROWS; row++) { + private void mainLoop(final short column, final short row, final Movement movement) { - // calculating the square's center - final int cellMediumX = (column * this.squareWidth + (column + 1) * - this.squareWidth) / 2; - final int cellMediumY = (row * this.squareHeight + (row + 1) * - this.squareHeight) / 2; + board.updateBoard(this.currentPlayer, column, row); + board.flipOpponentDiscs(board.getGameBoard(), movement, currentPlayer); - // applying the margins - final int cx = cellMediumX + LEFT_MARGIN; - final int cy = cellMediumY + TOP_MARGIN; + //Switch player. + this.currentPlayer = reversiLogic.opponent(this.currentPlayer); - // the radius - final int radius = (this.squareWidth - 2) / 2 - 2; - - //update squares - this.gameBoard[column][row].setRadius(radius); - this.gameBoard[column][row].setSquareMediumX(cx); - this.gameBoard[column][row].setSquareMediumY(cy); - } - } - } - - private void mainLoop() { - this.currentPlayer = opponent(this.currentPlayer); if (this.currentPlayer != this.AI) { - //AllowedMovements for Player - listAllowedMovements = allowedMovements(currentPlayer, gameBoard); + //AllowedMovements for player. + listAllowedMovements = reversiLogic.allowedMovements(currentPlayer, board.getGameBoard()); //UpdateBoard with suggestions - for (final Movement movement : listAllowedMovements) { - updateBoard(currentPlayer, movement.getColumn(), movement.getRow(), true); + for (final Movement suggestedMovement : listAllowedMovements) { + board.updateBoard(currentPlayer, suggestedMovement.getColumn(), suggestedMovement.getRow(), true); } @@ -330,66 +238,16 @@ public class ReversiView extends SurfaceView { getHolder().unlockCanvasAndPost(canvas); this.isEnableUserTouch = true; - } - else { - this.isEnableUserTouch = false; - //Launch AI thread. - } - } - private List allowedMovements(final Player player, final Square gameBoard[][]) { - final List list = new ArrayList(); - - for (short column = 0; column < NUMBER_OF_COLUMNS; column++) { - for (short row = 0; row < NUMBER_OF_ROWS; row++) { - final Movement movement = new Movement(row, column); - if (CheckMovement.empty(gameBoard, column, row)) { - final boolean diagonal = CheckMovement.diagonal(gameBoard, movement, player); - final boolean horizontal = CheckMovement.horizontal(gameBoard, movement, player); - final boolean vertical = CheckMovement.vertical(gameBoard, movement, player); - - if(diagonal || horizontal || vertical) { - list.add(movement); - } - } - } + //Going to wait for touch event from human player. } + else { - return list; - } - - private Movement retrieveAllowedMovement(final short row, final short column) { - for (final Movement movement : listAllowedMovements) { - if ((movement.getRow() == row) && (movement.getColumn() == column)) { - return movement; - } - } - - return null; - } - - private void removeSuggestionsFromBoard(final Square gameBoard[][], - final List listAllowedMovements) { - - for (final Movement iterator : listAllowedMovements) { - updateBoard(Player.NOPLAYER, iterator.getColumn(), iterator.getRow()); - } - } - - private Player opponent(final Player currentPlayer) { - switch (currentPlayer){ - case PLAYER1: - return Player.PLAYER2; - case PLAYER2: - return Player.PLAYER1; - default: - return Player.NOPLAYER; - } - } + final AIThread AI = new AIThread(board, currentPlayer, "AI-Thread"); + AI.setListAllowedMovements(listAllowedMovements); - private void flipOpponentDiscs(final Square gameBoard[][], final Movement movement, final Player currentPlayer) { - for (final FlippedDisc flippedDisc : movement.getFlippedDiscs()) { - updateBoard(currentPlayer, flippedDisc.getColumn(), flippedDisc.getRow()); + this.isEnableUserTouch = false; + AI.start(); } } } diff --git a/src/de/android/reversi/logic/ReversiLogic.java b/src/de/android/reversi/logic/ReversiLogic.java new file mode 100644 index 0000000..4abc08a --- /dev/null +++ b/src/de/android/reversi/logic/ReversiLogic.java @@ -0,0 +1,56 @@ +package de.android.reversi.logic; + +import java.util.ArrayList; +import java.util.List; + +import de.android.reversi.Board; +import de.android.reversi.CheckMovement; +import de.android.reversi.Movement; +import de.android.reversi.Player; +import de.android.reversi.Square; + +public class ReversiLogic { + + public List allowedMovements(final Player player, final Square gameBoard[][]) { + final List list = new ArrayList(); + + for (short column = 0; column < Board.NUMBER_OF_COLUMNS; column++) { + for (short row = 0; row < Board.NUMBER_OF_ROWS; row++) { + final Movement movement = new Movement(row, column); + if (CheckMovement.empty(gameBoard, column, row)) { + final boolean diagonal = CheckMovement.diagonal(gameBoard, movement, player); + final boolean horizontal = CheckMovement.horizontal(gameBoard, movement, player); + final boolean vertical = CheckMovement.vertical(gameBoard, movement, player); + + if(diagonal || horizontal || vertical) { + list.add(movement); + } + } + } + } + + return list; + } + + public Movement retrieveAllowedMovement(final short row, final short column, + final List listAllowedMovements) { + for (final Movement movement : listAllowedMovements) { + if ((movement.getRow() == row) && (movement.getColumn() == column)) { + return movement; + } + } + + return null; + } + + public Player opponent(final Player currentPlayer) { + switch (currentPlayer){ + case PLAYER1: + return Player.PLAYER2; + case PLAYER2: + return Player.PLAYER1; + default: + return Player.NOPLAYER; + } + } +} -- 2.1.4