|
|
Line 1: |
Line 1: |
− | {{code}}
| + | <a href="http://somacn.com/">buy generic soma</a> |
− | package player;
| |
− |
| |
− | /* Gameboard class
| |
− | * contains the gameboard ADT and methods for handling it
| |
− | * in addition, contains methods for generating and evaluating new moves
| |
− | **/
| |
− |
| |
− | public class Gameboard {
| |
− |
| |
− | // Allows for a hypothetical expansion of the board
| |
− | public final static int DIMENSION = 8;
| |
− | public final static int ADD = 1;
| |
− | public final static int STEP = 2;
| |
− | // used for signaling a white/black win; needs to be large and prime
| |
− | public final static int WHITEWIN = 2047;
| |
− | public final static int BLACKWIN = -2047;
| |
− |
| |
− | private Chip[][] board = new Chip[DIMENSION][DIMENSION];
| |
− | private int x = DIMENSION;
| |
− | private int y = DIMENSION;
| |
− | private int pieces;
| |
− |
| |
− | // returns the Chip at a given x & y coord
| |
− | /* we use a "try" so that if we try to access a location that is off the board,
| |
− | i.e. we attempt to get non-existent coords, then just return null */
| |
− | public Chip cellContents(int x, int y) {
| |
− | try {
| |
− | return board[x][y];
| |
− | } catch (ArrayIndexOutOfBoundsException e) {
| |
− | return null;
| |
− | }
| |
− | }
| |
− |
| |
− | /** @return an array of Chip neighbors around a given position (x,y) **/
| |
− | public Chip[] neighborarray(int x, int y) {
| |
− | Chip[] array = new Chip[8];
| |
− | array[0] = cellContents(x-1, y-1); // nw
| |
− | array[1] = cellContents(x, y-1); // n
| |
− | array[2] = cellContents(x+1, y-1); // ne
| |
− | array[3] = cellContents(x+1, y); // e
| |
− | array[4] = cellContents(x+1, y+1); // se
| |
− | array[5] = cellContents(x, y+1); // s
| |
− | array[6] = cellContents(x-1, y+1); // sw
| |
− | array[7] = cellContents(x-1, y); // w
| |
− | return array;
| |
− | }
| |
− |
| |
− | /** takes in rec = 1 on initial search
| |
− | @return the number of the same-color neighbors around a given position (x, y) **/
| |
− | public int neighbor(int color, int x, int y, int rec) {
| |
− | if (rec < 0) {
| |
− | return 0;
| |
− | } else {
| |
− | Chip[] array = neighborarray(x, y);
| |
− | int number = 0;
| |
− | for (int i=0; i<array.length; i++) {
| |
− | Chip target = array[i];
| |
− | if (target != null && target.getColor()==color) {
| |
− | number = number + neighbor(color, target.getX(), target.getY(), rec-1);
| |
− | number++;
| |
− | }
| |
− | }
| |
− | return number;
| |
− | }
| |
− | }
| |
− |
| |
− | /* inserts a chip of the given color at (x, y) if this is acceptable
| |
− | returns true if the insertion is legal, false otherwise */
| |
− | public boolean insertChip(int color, int x, int y) {
| |
− | if (validMove(color, new Move(x, y))) {
| |
− | board[x][y] = new Chip(color, x, y);
| |
− | pieces++;
| |
− | return true;
| |
− | } else {
| |
− | return false;
| |
− | }
| |
− | }
| |
− |
| |
− | /* moves Chip c to a new location at (x, y)
| |
− | returns true if the move is legal, false otherwise */
| |
− | public boolean moveChip(Chip c, int x, int y) {
| |
− | if (validMove(c.getColor(), new Move(x, y))) {
| |
− | board[c.getX()][c.getY()] = null;
| |
− | board[x][y] = new Chip(c.getColor(), x, y);
| |
− | return true;
| |
− | } else {
| |
− | return false;
| |
− | }
| |
− | }
| |
− |
| |
− | /* performs the requested move for a player of the given color
| |
− | returns true if the move is legal, false otherwise */
| |
− | public boolean performMove(int color, Move m) {
| |
− | if (m.moveKind == Move.ADD) {
| |
− | return insertChip(color, m.x1, m.y1);
| |
− | } else if (m.moveKind == Move.STEP) {
| |
− | return moveChip(retrieveChip(m.x2, m.y2), m.x1, m.y1);
| |
− | } else {
| |
− | return true;
| |
− | }
| |
− | }
| |
− |
| |
− | // legacy method for returning a chip at (x, y)
| |
− | public Chip retrieveChip(int x, int y) {
| |
− | return cellContents(x, y);
| |
− | }
| |
− |
| |
− | /** Verifies that a given move is valid for a plyer of the given color.
| |
− | * the following conditions must be met:
| |
− | * No chip may be placed in any of the four corners
| |
− | * No chip may be placed in a goal of the opposite color
| |
− | * No chip may be placed in a square that is already occupied
| |
− | * A player may not have more than two chips in a connected group, whether connected orthogonally or diagonally
| |
− | takes a move and the player's color performing the move
| |
− | @return true if move is valid
| |
− | */
| |
− | public boolean validMove(int color, Move m) {
| |
− | if (m.moveKind == Move.QUIT) {
| |
− | return true;
| |
− | } else {
| |
− | if ((m.x1==0 && (m.y1==0 || m.y1==DIMENSION-1)) || (m.y1==DIMENSION-1 && (m.x1==0 || m.x1==DIMENSION-1))) {
| |
− | return false; // in the corners
| |
− | } else if (board[m.x1][m.y1] != null) {
| |
− | return false; // target cell isn't empty
| |
− | } else if (((color == Chip.BLACK) && (m.x1==0 || m.x1==DIMENSION-1)) || ((color == Chip.WHITE) && (m.y1==0 || m.y1==DIMENSION-1))) {
| |
− | return false; // target cell is in opponent's goal
| |
− | } else if (!checkClusterSize(color, m.x1, m.y1)) {
| |
− | return false; // resulting cluster too big
| |
− | } else {
| |
− | return true; // looks like this move is good
| |
− | }
| |
− | }
| |
− | }
| |
− |
| |
− | // helper method for validMove()
| |
− | // @return true if an insertion at the given position is possible w/respect to cluster size; false otherwise
| |
− | public boolean checkClusterSize(int color, int x, int y) {
| |
− | if (neighbor(color, x, y, 1) > 1) {
| |
− | return false;
| |
− | } else {
| |
− | return true;
| |
− | }
| |
− | }
| |
− |
| |
− | // generates a 2-d array of moves and resulting gameboards for a given color
| |
− | // @return[0] is an array of Gameboards (Gameboard[x])
| |
− | // @return[1] is an array of Moves corresponding to each board in return[0] (Move[x])
| |
− | // thus the board after performing g.performMove(color, return[1][i]) is return[0][i]
| |
− | public Object[][] generateMoves(int color) {
| |
− | {{notmine|Chanh Vo|fragment=yes}}
| |
− | }
| |
− |
| |
− | // @return an exact clone of "this" gameboard
| |
− | public Gameboard clone() {
| |
− | {{notmine|Chanh Vo|fragment=yes}}
| |
− | }
| |
− |
| |
− | // return the current Move kind for this gameboard, depending on number of pieces
| |
− | // @return either STEP or ADD
| |
− | public int moveKind() {
| |
− | if (pieces < 20) {
| |
− | return ADD;
| |
− | } else {
| |
− | return STEP;
| |
− | }
| |
− | }
| |
− |
| |
− | /* chooses a move to a given depth for a given color using alpha-beta pruning (hopefully)
| |
− | / first two moves involve the random placement of a chip in each of the goals without any analysis
| |
− | / @param color - current player's color
| |
− | depth - number of levels remaining to search (i.e. depth = 1 means no look-ahead)
| |
− | alpha, beta - best-case and worst-case scores
| |
− | side - true for white, false for black
| |
− | factor - multiplier used to score a win many levels down lower than a win in the first level
| |
− | / @return an Alphamove with the best move and its corresponding score */
| |
− | public Alphamove chooseMove(int color, int depth, int alpha, int beta, boolean side, int factor) {
| |
− | Alphamove bestme = new Alphamove();
| |
− | Alphamove bestopp;
| |
− | if (side) {
| |
− | bestme.score = alpha;
| |
− | } else {
| |
− | bestme.score = beta;
| |
− | }
| |
− | if (pieces < 2) { // first move for each color
| |
− | // place a random piece in the top (black) or left (white) goal
| |
− | int target = (((int) (Math.random() * 10.0)) % (DIMENSION - 2)) + 1;
| |
− | if (color == Chip.BLACK) {
| |
− | bestme.move = new Move(target, 0);
| |
− | } else {
| |
− | bestme.move = new Move(0, target);
| |
− | }
| |
− | return bestme;
| |
− | } else if (pieces < 4) { // second move for each color
| |
− | // place a random piece in the bottom (black) or right (white) goal
| |
− | int target = (((int) (Math.random() * 10.0)) % (DIMENSION - 2)) + 1;
| |
− | if (color == Chip.BLACK) {
| |
− | bestme.move = new Move(target, DIMENSION-1);
| |
− | } else {
| |
− | bestme.move = new Move(DIMENSION-1, target);
| |
− | }
| |
− | return bestme;
| |
− | } else {
| |
− | // at least 4 pieces are on the board now, so this is the 3rd+ move
| |
− | // go ahead and generate the possible moves from the current board
| |
− | Object[][] arr = generateMoves(color);
| |
− | if (depth == 1) {
| |
− | // this is our last level, so end the recursion
| |
− | for (int i=0; i<arr[0].length; i++) {
| |
− | if (arr[0][i] != null) {
| |
− | Move targetmove = (Move) arr[1][i];
| |
− | int score = ((Gameboard) arr[0][i]).winner()*factor;
| |
− | System.out.println("Got score " + score + " for board " + ((Gameboard) arr[0][i]) + ", factor " + factor);
| |
− | if ((((score/factor)==WHITEWIN) && (color==Chip.WHITE)) || (((score/factor)==BLACKWIN) && (color==Chip.BLACK))) {
| |
− | // we've reached a winner, so stop here and return it
| |
− | bestme.score = score;
| |
− | bestme.move = targetmove;
| |
− | return bestme;
| |
− | } else if ((score > bestme.score && side) || (score < bestme.score && !side)) {
| |
− | bestme.score = score;
| |
− | bestme.move = targetmove;
| |
− | }
| |
− | } else { // we've run out of moves to evalute; stop
| |
− | break;
| |
− | }
| |
− | }
| |
− | return bestme;
| |
− | } else {
| |
− | for (int ii=0; ii<arr[0].length; ii++) {
| |
− | if (arr[0][ii] != null) {
| |
− | // eventually, this is where we will check to make sure we still have enough time
| |
− | System.out.println("side: " + side);
| |
− | Move targetmove = (Move) arr[1][ii];
| |
− | int score = ((Gameboard) arr[0][ii]).winner()*factor;
| |
− | if (depth ==2) { System.out.println("Got score " + score + " for board " + ((Gameboard) arr[0][ii]) + ", factor " + factor); }
| |
− | if ((((score/factor)==WHITEWIN) && (color==Chip.WHITE)) || (((score/factor)==BLACKWIN) && (color==Chip.BLACK))) {
| |
− | // we've reached a winner, so stop here and return it
| |
− | bestme.score = score;
| |
− | bestme.move = targetmove;
| |
− | return bestme;
| |
− | } else {
| |
− | bestopp = ((Gameboard) arr[0][ii]).chooseMove(generateOpponent(color), depth-1, alpha, beta, !side, factor-1); //hopefully we never search for more than 9 levels...
| |
− | System.out.println("Opponent's best response is " + bestopp.score + ", current best score is " + bestme.score + " (side && (bestopp.score > bestme.score)) returns " + (side && (bestopp.score > bestme.score)) + " for side = " + side);
| |
− | targetmove = (Move) arr[1][ii];
| |
− | if (side && (bestopp.score > bestme.score)) {
| |
− | bestme.move = targetmove;
| |
− | bestme.score = bestopp.score;
| |
− | alpha = bestopp.score;
| |
− | System.out.println("Found better move for white, score is " + bestopp.score + " move is " + bestme.move);
| |
− | } else if (!side && (bestopp.score < bestme.score)) {
| |
− | bestme.move = targetmove;
| |
− | bestme.score = bestopp.score;
| |
− | beta = bestopp.score;
| |
− | // System.out.println("Found better move for black, score is " + bestopp.score);
| |
− | } if (alpha >= beta) {
| |
− | return bestme;
| |
− | }
| |
− | }
| |
− | } else { // we've run out of moves to evalute; stop
| |
− | break;
| |
− | }
| |
− | }
| |
− | return bestme;
| |
− | }
| |
− | }
| |
− | }
| |
− |
| |
− | // returns the best possible move that can be made by the given color to a search depth of "depth" using the "this" board
| |
− | // @return the best current Move
| |
− | public Move evalTree(int color, int depth) {
| |
− | int alpha = (BLACKWIN*10)-1;
| |
− | int beta = (WHITEWIN*10)+1;
| |
− | boolean side;
| |
− | if (color == Chip.BLACK) {
| |
− | side = false;
| |
− | } else {
| |
− | side = true;
| |
− | }
| |
− | Alphamove winningmove = this.chooseMove(color, depth, alpha, beta, side, 10);
| |
− | return winningmove.move;
| |
− | }
| |
− |
| |
− | // simple method to generate the opponent's color
| |
− | // given a color, @return the color of the opponent
| |
− | public static int generateOpponent(int color) {
| |
− | if (color == Chip.WHITE) {
| |
− | return Chip.BLACK;
| |
− | } else {
| |
− | return Chip.WHITE;
| |
− | }
| |
− | }
| |
− |
| |
− | // evaluates the given network for a winning color
| |
− | // returns a probability of winning if there is no win (positive for white advantage, negative for black advantage)
| |
− | // returns WHITEWIN if WHITE wins, BLACKWIN if BLACK wins
| |
− | // @return an int with this board's score
| |
− | public int winner() {
| |
− | {{notmine|Jordan Berk|fragment=yes}}
| |
− | }
| |
− |
| |
− | // helper function for winner - recursively finds networks and assigns 1 point per connection and 10000 for a win
| |
− | // @return an int with the score of the network
| |
− | int findNetwork(Gameboard g, int x, int y, int color, int num, String currentDirection, String lastDirection) {
| |
− | {{notmine|Jordan Berk|fragment=yes}}
| |
− | }
| |
− |
| |
− | // debugging method to print a gameboard
| |
− | // @return a string represeting the board
| |
− | public String toString() {
| |
− | String output = "\n|";
| |
− | for (y=0; y<DIMENSION; y++) {
| |
− | for (x=0; x<DIMENSION; x++) {
| |
− | if (board[x][y] == null) {
| |
− | output = output + " .";
| |
− | } else {
| |
− | output = output + " " + ((Chip) board[x][y]).getColor();
| |
− | }
| |
− | }
| |
− | output = output + "|\n|";
| |
− | }
| |
− | return output;
| |
− | }
| |
− | }
| |