Instances of objects in Java, strange behavior -


i wrote simple tic tac toe console app in java, using bitboard approach (just fun). works 2 human players. objective figure out minimax algorith , implement computer player. did before, (very naive) game of "nim", , same general object oriented approach worked. wanted use same structure. in case, when computer goes make move, defaces whole board variable when searches next move. shouldn't so, because makemove method creates brand new board object. question is, why strange thing happen? here code, loosely commented, straight netbeans:

thanks in advance has patience take look. want mention looked cloneable interface , clone() method, no avail. figured that shouldn't cause, because way makemove method works. why computer player destroy board?

package tictactoe;  import java.util.*;  public class tictactoe {      public static void main(string[] args) {         game game = new game();         game.start();     }  }  class game {     arraylist<player> players = new arraylist(); // arraylist players      public game() { // determine if players human or cpu         scanner input = new scanner(system.in);         string answer;          system.out.printf("would player 1 cpu? [yes/no] ");         answer = input.nextline();         if(answer.tolowercase().startswith("y")) players.add(new computerplayer(0, 3));         else players.add(new player());          system.out.printf("would player 2 cpu? [yes/no] ");         answer = input.nextline();         if(answer.tolowercase().startswith("y")) players.add(new computerplayer(1, 3));         else players.add(new player());     }      public void start() {         scanner input = new scanner(system.in);          while(true) {             clearscreen();             board board = new board();              while(!board.isgameover()) {                 board = board.makemove(players.get(board.getcurrentplayer()).getmove(board));             }              board.display();              int winner = board.checkwinner();             if(winner >= 0) {                 players.get(winner).addwin();                 system.out.printf("player %d wins. has %d wins vs %d.\nrematch? [yes/no] ", winner + 1, players.get(winner).getwins(), players.get(winner == 0 ? 1 : 0).getwins());             }             else {                 system.out.printf("the game tie.\nrematch? [yes/no] ");             }             string answer = input.nextline();             if(answer.tolowercase().startswith("n")) break;              else {                 player temp = players.remove(0);                 players.add(temp);                 for(int = 0; < 2; i++) { // computer player track own id                     players.get(i).flipid();                 }             }         }          system.out.printf("game aborted. thank playing.");     }      public static void clearscreen() {         for(int = 0; < 30; i++) system.out.printf("\n");     }    }  class board implements cloneable {      private int[] board;   // 2 dimensional array storing player x's ,                 // player o's moves separately. or them                 // moves made.      private final int[] map = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}; // simple                 // way of mapping digits 1 -> 9 (like on numpad)                 // bits of board. bitwise operations                 // against map[n] - n being digit.                 // numpad-like mapping looks this:                 // 7 8 9 //  in memory bits stored thus:                 // 4 5 6 //  987654321                 // 1 2 3 //      private final int[] win = {7, 56, 73, 84, 146, 273, 292, 448};  // mapping                 // of possible winning combinations translated decimal                 // numbers. listed in order: 1,2,3; 4,5,6; 1,4,7; 3,5,7;                 // 2,5,8; 1,5,9; 3,6,9; 7,8,9.      private int currentplayer; // player turn is. 0 x, 1 o.     private int opponent;      // opponent. opposite.       // normal constructor. takes arguments current state of     // board, represented 2 dimensional integer, , player     // turn is, represtented 0 or 1     public board(int[] theboard, int player) {         board = theboard;         currentplayer = player;         opponent = player == 0 ? 1 : 0;     }      // if passed no arguments, construct bord default values,     // e.g. empty board both players , x's turn.     public board() {         this(new int[2], 0);     }      // usual suspects. accesors attributes.         public int[] getboard() {         return board;     }      public int getcurrentplayer() {         return currentplayer;     }      public int getopponent() {         return opponent;     }      // first check against win maps, both players, see if of them     // got 3 symbols in row. if not, check if board full.     public boolean isgameover() {         for(int player = 0; player < 2; player++) {             for(int n: win) {                 if((board[player] & n) == n) return true;             }         }          return (board[0] | board[1]) == 511;     }      // returns -1 if nobody won, or returns 0 or 1 in case either of     // players did.     public int checkwinner() {         for(int = 0; < 2; i++) {             for(int m: win) {                 if((board[i] & m) == m) return i;             }         }         return -1;     }      // find possible moves on board, returned in array     public int[] getmoves() {          // count number of possible moves, prerequisite initializing         // array of moves later returned.         int allmoves = (board[0] | board[1]);         int count = countbits(allmoves);          // populate array of possible moves , return         int[] moves = new int[9 - count];         int j = 0;         for(int = 1; < 10; i++) {             if((allmoves & map[i]) == 0) {                 moves[j] = i;                 j++;             }         }          return moves;     }      // return number of activated bits in integer     // (in case 8 bit integer)     public static int countbits(int board) {         int count = 0;         for(int = 1; <= 256; <<= 1) {             if((board & i) != 0) count++;         }         return count;     }      // static evaluation function, used minmax algorithm.     // returns 3 / -3 victory, or number of symbols player     // has on given line, if there's no opponent's symbol on it.     // returns 0 otherwise      public int evaluate(int player) {         int allmoves = board[0] | board[1];         int ret = 0, max = 0, min = 0;          for(int p = 0; p < 2; p++) {             for(int w: win) {                 int line = board[p] & w;                 if(line == w) { // if victory condition found, return                     if(p == player) return 3;                     else return -3;                 }                  if((line ^ allmoves) == 0) { // no moves on line opp.                     if(p == player) max = countbits(line) > max ? countbits(line) : max;                     else min = -countbits(line) < min ? -countbits(line) : min;                 }             }         }          if(math.abs(min) != max) {             ret = math.abs(min) > max ? min : max;         }          return ret;     }      // tricky part... method returns new     // board object. when minimax method calls it, sure doesn't     // behave way      public board makemove(int move) {             int[] newboard = board;             newboard[currentplayer] |= map[move];             return new board(newboard, opponent);     }      // tried use this, @ 1 point, realized     // won't me understand problem. may use @ later time, tho      /*     public board undomove(int move) {         int[] newboard = board;         newboard[opponent] ^= map[move];         return new board(newboard, opponent);     }     */      // method (very plainly) display board      public void display() {         for(int = 6; >= 0; -= 3) {             for(int j = 1; j <= 3; j++) {                 if(((board[0] | board[1]) & map[i + j]) == 0) system.out.printf("%d", + j);                 else if((board[0] & map[i + j]) != 0) system.out.printf("x");                 else system.out.printf("o");             }             system.out.printf("\n");         }     }      // returns true/false whether move valid on board      public boolean isvalidmove(int move) {         if(move < 1 || move > 9) return false;         return ((board[0] | board[1]) & map[move]) == 0;     } }  class player {     int wins = 0; // simple way of keeping track of number of wins.      // accessor win atr.      public int getwins() {         return wins;     }      // add win      public void addwin() {         wins++;     }      public void flipid() {         // overridden computerplayer class     }      // query user valid move      public int getmove(board board) {         scanner input = new scanner(system.in);         int move;          board.display();          {             system.out.printf("input valid move: ");             move = input.nextint();         } while(!board.isvalidmove(move));          //game.clearscreen();         return move;     } }  class computerplayer extends player {     int self; // keep track of own place in players array     int maxsearchdepth; // seach depth setting minimax algorithm      public computerplayer(int n, int m) { // constructor         self = n;         maxsearchdepth = m;     }      @override     public void flipid() {         self = self == 0 ? 1 : 0;     }       // implementation of minimax algorithm     @override     public int getmove(board board) {         int[] temp = minimax(board, 0, maxsearchdepth);         return temp[1];     }      public int[] minimax(board mmboard, int depth, int maxdepth) {         int[] ret = new int[2]; //ret[0] = bestscore, ret[1] = bestmove         int currentscore, bestscore, bestmove;          if(mmboard.isgameover() || depth == maxdepth) {             ret[0] = mmboard.evaluate(mmboard.getcurrentplayer());             ret[1] = 0;             return ret;         }          bestmove = 0;         bestscore = mmboard.getcurrentplayer() == self ? -4 : 4;          for(int move: mmboard.getmoves()) {             // system.out.printf("board: %s, depth: %d. moves: %s. trying: %d\n", arrays.tostring(mmboard.getboard()), depth, arrays.tostring(mmboard.getmoves()), move);             board newboard = mmboard.makemove(move); // problem call...             // system.out.printf("original: %s new: %s", mmboard, newboard);             int[] temp = minimax(newboard, depth + 1, maxdepth);             currentscore = temp[0];              if(mmboard.getcurrentplayer() == self) {                 if(currentscore > bestscore) {                     bestscore = currentscore;                     bestmove = move;                 }             }             else {                 if(currentscore < bestscore) {                     bestscore = currentscore;                     bestmove = move;                 }             }         }          ret[0] = bestscore;         ret[1] = bestmove;         return ret;     } } 

note, did not read through code, there no minimal example, saw issue here:

public board makemove(int move) {     int[] newboard = board;     //               ^^^^^     newboard[currentplayer] |= map[move];     return new board(newboard, opponent); } 

you in fact not making new board here, new board(...) has reference old board's int[].

by calling statement int[] newboard = board; assigning reference of board new integer array , not making copy, in other words: both board objects pointing same int[]

to make actual copy, need clone array using system.arraycopy();

so new method this:

public board makemove(int move) {     int[] newboard = new int[board.length];     system.arraycopy(board, 0, newboard, 0, board.length);      newboard[currentplayer] |= map[move];     return new board(newboard, opponent); } 

note have not read through code, assumption made in method not correct


Comments

Popular posts from this blog

javascript - Jquery show_hide, what to add in order to make the page scroll to the bottom of the hidden field once button is clicked -

javascript - Highcharts multi-color line -

javascript - Enter key does not work in search box -