/*************************************************** * * Abalone * * * The obligatory stuff: * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted without fee, provided that this license * information and copyright notice appear in all copies. Modifications to * the software have to be marked as such in the source code. * * FRANK BERGMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL FRANK BERGMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Representation: * * The Abalone board is represented by an 11x11 array of "fields". Each * can have the value "white", "black", "none" or "outside": * * . . . . . . . . . . . 110 . . . . . . . . . . . * . - - - - - . . . . . 99 . - - - - - . . . . . * . - - - - - - . . . . 88 . - - - - - - . . . . * . * - - - - - * . . . 77 . * - - - - - * . . . * . * * * * * * * * . . 66 . * * * * * * * * . . * . * * * * * * * * * . 55 . * * * * # * * * * . * . . * * * * * * * * . <=> 44 . . * * * * * * * * . * . . . * + + + + + * . 33 . . . * + + + + + * . * . . . . + + + + + + . 22 . . . . + + + + + + . * . . . . . + + + + + . 11 . . . . . + + + + + . * . . . . . . . . . . . 0 . . . . . . . . . . . * 0 1 2 3 4 5 6 7 8 9 10 * * white + white stone * black - black stone * outside . outside of the game board * none * empty field inside the game board * * * Moves can be made for each stone in 6 different directions. A maximum * of 3 stones can be moved each time. If several stones are moved in the * direction of their line, it is possible to push opponent stones back, * if there are less opponent stones than own ones. If a stone is pushed * out of the physical board, it is lost. Goal of the game is to push out * all stones of the opponent. * * To move a stone from one field to the next in a certain direction [0..5], * the array "move_offs[] = {-1,10,11,1,-10,-11}" determines the * difference of the field indices. * * The current EVALUATION FUNCTION for Abalone is based on the strategy to * keep all stones as close as possible in the middle. This is achived by * giving each field (of the board) a fixed value that depends on its distance * to the middle of the game board. To determine the value of a position, * just add the values of the fields occupied by your stones and subtract * the value of the fields occupied by the opponent's stones. * * Original by Frank Bergmann * Changes Implemented By Stephen Friedman and Beltran Ibarra * - Alpha-Beta pruning * - Multiple heuristic evaluation function * - Computer vs. Computer play * - Scoring/End Game detection * - Computer Move timing * - Move counting * - Move logging * - Better Color Scheme * - Resizeable board ***************************************************/ import java.util.*; import java.awt.*; import java.applet.Applet; import java.lang.Integer; import java.lang.Math; import javax.swing.*; /************************************************** * AbaloneBoard * * Performs all game related functions but * doesn't do any display or GUI works. **************************************************/ class AbaloneBoard { // Constants to determine the state of a field: final static int BOARD_NONE = 0; final static int BOARD_OUTSIDE = 2; final static int BOARD_WHITE = 1; final static int BOARD_BLACK = -1; final static int BOARD_ERROR = 3; // Constants for board sizes // Change BOARD_WIDTH here to play on different sized boards. // Ensure that you also change it in AbalonePanel so that the // display works properly. final static int BOARD_WIDTH = 11; // Board width and height must be odd numbers final static int BOARD_HEIGHT = BOARD_WIDTH; // the hexagonal side size is (BOARD_WIDTH-1)/2 because // we want to leave a boarder of nothing around the board final static int BOARD_MAX = BOARD_WIDTH*BOARD_HEIGHT; // end of (logical) board index final static int BOARD_MIN = 0; // start of (logical) board index final static int BOARD_START = BOARD_WIDTH + (BOARD_WIDTH-1)/2; // first (physical) field to be used final static int BOARD_END = (BOARD_HEIGHT - 2)*BOARD_WIDTH + (BOARD_WIDTH+1)/2; // last (physical) field +1 final static int BOARD_MIDDLE = BOARD_WIDTH*(BOARD_HEIGHT-1)/2+(BOARD_WIDTH-1)/2; // middle of board final static int BOARD_TOP_LEFT = BOARD_WIDTH*(BOARD_WIDTH-2) + 1; final static int BOARD_BOTTOM_RIGHT = BOARD_WIDTH+BOARD_WIDTH-2; // Display color for the different states of a field final Color WHITE_COLOR = Color.red; final Color BLACK_COLOR = Color.black; final Color NONE_COLOR = Color.pink; final Color OUTSIDE_COLOR = Color.white; // Define the offsets between fields when moving: static final int move_offs[] = {-1, BOARD_WIDTH-1, BOARD_WIDTH, 1, -(BOARD_WIDTH-1), -BOARD_WIDTH}; final static int DIR_LEFT = 0; final static int DIR_UP_LEFT = 1; final static int DIR_UP_RIGHT = 2; final static int DIR_RIGHT = 3; final static int DIR_DOWN_RIGHT = 4; final static int DIR_DOWN_LEFT = 5; // Minimum number of pieces a player must have to continue playing final static int PLAYER_MIN_PIECES = (BOARD_WIDTH+7)/2; // Number of pieces the player starts with. final static int PLAYER_NUM_PIECES = BOARD_WIDTH+(BOARD_WIDTH-5)/2; // Weights for combining the heuristic values final static int HEURISTIC_WEIGHT_1 = 1; final static int HEURISTIC_WEIGHT_2 = 0; final static int HEURISTIC_WEIGHT_3 = 1; final static int HEURISTIC_WEIGHT_4 = 0; // Define a value for each field of the game board. // This value is used to evaluate the quality of a position static int glob_field_val[]; AbalonePanel abalone_panel; int fields[] = new int[BOARD_MAX]; int value; int mvt_field; int mvt_dir; /* * Initialize the values of the board fields as the * distance to the middle field. This is used in the * Gravity Center heuristic. */ static { glob_field_val = new int[BOARD_MAX]; for (int i=BOARD_MIN; i= 0; y--) { char spaces[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',}; System.err.print(String.copyValueOf(spaces,0,y)); for (int x=0; x < BOARD_WIDTH; x++) { switch (fields[x+BOARD_WIDTH*y]) { case BOARD_WHITE: System.err.print("+ "); break; case BOARD_BLACK: System.err.print("- "); break; case BOARD_NONE: System.err.print(". "); break; case BOARD_OUTSIDE: System.err.print(" "); break; } } System.err.println(""); } } /* * Function that transforms our coding for the fields into * the notation used by the international rules * Warning: does not work properly with resized boards. */ public String intoLetterCode (int number) { int rest = (number-5)%10; StringBuffer value = new StringBuffer(); switch((number-5)/10){ case 1 : if (rest >= 1 && rest <= 5) value.append('A').append(rest); else value.append("OUT"); break; case 2 : if (rest >= 1 && rest <= 6) value.append('B').append(rest); else value.append("OUT"); break; case 3 : if (rest >= 1 && rest <= 7) value.append('C').append(rest); else value.append("OUT"); break; case 4 : if (rest >= 1 && rest <= 8) value.append('D').append(rest); else value.append("OUT"); break; case 5 : if (rest >= 1 && rest <= 9) value.append('E').append(rest); else value.append("OUT"); break; case 6 : if (rest >= 2 && rest <= 9) value.append('F').append(rest); else value.append("OUT"); break; case 7 : if (rest >= 3 && rest <= 9) value.append('G').append(rest); else value.append("OUT"); break; case 8 : if (rest >= 4 && rest <= 9) value.append('H').append(rest); else value.append("OUT"); break; case 9 : if (rest >= 5 && rest <= 9) value.append('I').append(rest); else value.append("OUT"); break; } return value.toString(); } /* * Calculate the distance of a field from a center point * (in terms of hexagonal moves) */ static int calcHexagonalDistance(int field, int center) { int X, Y, dx, dy; int centerX, centerY; int dist; X = field % BOARD_WIDTH; Y = field / BOARD_WIDTH; centerX = center % BOARD_WIDTH; centerY = center / BOARD_WIDTH; dx = Math.abs(centerX - X); dy = Math.abs(centerY - Y); dist = 0; while ((dx > 0) && (dy > 0)) { dx--; dy--; dist++; } return dist + dx + dy; } /* * Perform a user iniated move on the current board * and return the new board or null if the move was * not possible. */ public boolean makeUserMove(int from_field, int to_field, int selection[]) { int my_color = abalone_panel.my_color; int move_dir = to_field - from_field; // Check that moving exactly one field: boolean found = false; for (int i=0; i<6; i++) { if (move_dir == move_offs[i]) found = true; } if (!found) return false; // Count how many fields are selected except the from field int num_selected = 0; for (int i=BOARD_MIN; i 2) return false; // without from_field // Make a local copy of selection: int my_selection[] = new int[BOARD_MAX]; for (int i=BOARD_MIN; i the marked fields are somewere else :-( if (neighbour_found == 0) return false; // If there was only one other field marked then we found it and OK if (num_selected == 1) return true; // Now we're looking for the second neighbour which can either be // "behind" neighbour_found or "before" from_field. int neighbour_dir = neighbour_found - from_field; if (my_selection[neighbour_found + neighbour_dir] != 0) return true; if (my_selection[from_field - neighbour_dir] != 0) return true; return false; } /* * Start evaluation tree: * Create a list of all possible moves for the opponent, * evaluate them and return the best one. */ public void makeOpponentMove() { int alpha = -100000000; int beta = 100000000; //Vector board_list = moveGen(-abalone_panel.my_color); AbaloneBoard best_move = null; if(abalone_panel.my_color == BOARD_WHITE) //Comp is playing black. best_move = minAlphaBeta(this, abalone_panel.recursion_depth, alpha, beta); else // Comp is playing white best_move = maxAlphaBeta(this, abalone_panel.recursion_depth, alpha, beta); if (best_move == null) return; // runtime error // copy the best position to the current position for (int i=BOARD_MIN; i=BOARD_START; field--) { if (fields[field] != my_color) continue; // Line moves (move a balls axial pushing other balls) for (int direction=0; direction<6; direction++) { if (!canPushLine(field, move_offs[direction], my_color)) continue; next_sit = new AbaloneBoard(this); next_sit.pushLine(field, move_offs[direction], my_color); next_sit.mvt_dir = direction; next_sit.mvt_field = field; move_list.addElement(next_sit); } } return move_list; } /* * Check if the stones beginning at in direction * can be moved to direction . */ boolean canMakeSideMove(int field, int select_dir, int move_dir, int len) { if (abalone_panel.debug_level > 2) { Integer field_int = new Integer(field); Integer move_dir_int = new Integer(move_dir); Integer len_int = new Integer(len); System.err.println("canPushLine(" + field_int.toString()+"," + move_dir_int.toString() + len_int.toString() + ")"); } // Eliminate axial directions: if (move_dir == select_dir) return false; // axial if (move_dir == -select_dir) return false; // -axial for (int i=0; i 2) { Integer start_field_int = new Integer(start_field); Integer dir_int = new Integer(dir); System.err.println("canPushLine("+start_field_int.toString()+","+dir_int.toString()+")"); } // Loop while still on my own stones while (fields[field] == my_color) { field += dir; my_stones++; } if (abalone_panel.debug_level > 3) { Integer my_stones_int = new Integer(my_stones); System.err.println("canPushLine: my_stones="+my_stones_int.toString()); } // Can't move more than 3 stones at a time: if (my_stones > 3) return false; switch (fields[field]) { case BOARD_OUTSIDE: return false; // can't throw away my own stones case BOARD_NONE: return true; // yes, found an empty field } // Field must be of opposite color if (fields[field] != -my_color) return false; // runtime error // Loop for foreign stones: while (fields[field] == -my_color) { field += dir; his_stones++; } if (abalone_panel.debug_level > 2) { Integer his_stones_int = new Integer(his_stones); System.err.println("canPushLine: his_stones="+his_stones_int.toString()); } // My_stones must have majority to move his_stones: if (his_stones >= my_stones) return false; // Behind the last of his stones must be NONE or OUTSIDE if ((fields[field] != BOARD_NONE) && (fields[field] != BOARD_OUTSIDE)) return false; return true; } /* * Push a (single) stone into a certain direction. */ public boolean pushLine(int start_field, int dir, int my_color) { int my_stones = 0; int his_stones = 0; int field = start_field; int pending = BOARD_NONE; // set an "empty" stone at beginning int tmp; while (fields[field] == my_color) { tmp = fields[field]; fields[field] = pending; pending = tmp; field += dir; my_stones++; } while (fields[field] == -my_color) { tmp = fields[field]; fields[field] = pending; pending = tmp; field += dir; his_stones++; } switch (fields[field]) { case BOARD_NONE: fields[field] = pending; if(abalone_panel.debug_level == -1) { dispText(); } return true; case BOARD_OUTSIDE: if(abalone_panel.debug_level == -1) { dispText(); } return true; // stone is lost case BOARD_WHITE: // runtime error case BOARD_BLACK: // runtime error } return true; } /* * Evaluation Function: treat material and center position * NOTE: This EV does not treat offensive strategies. */ int oldEvaluateBoard() { int s = 0; // Multiply each stone with the "value" of its field for (int i=BOARD_MIN; i=2) string_count = 2; if(last == BOARD_WHITE) score+=string_count; if(last == BOARD_BLACK) score-=string_count; string_count = 0; } last = fields[curr]; curr += move_offs[dir]; } return score; } /* * Keep Packed heuristic - Gives high scores to configurations * with many neigboring pieces. */ int heuristic3() { int s = 0; int my_color = BOARD_WHITE; for (int i=BOARD_MIN; i 0) && (board.value > best_value)) || ((min_max < 0) && (board.value < best_value))) { best_value = board.value; best_board = board; } } return best_board; } /* * Perform the max half(WHITE player) of the Alpha Beta pruned * MinMax Search. */ AbaloneBoard maxAlphaBeta(AbaloneBoard board, int depth, int alpha, int beta) { AbaloneBoard next_board; AbaloneBoard best_board = null; int best_value = -100000000; int my_color = BOARD_WHITE; // Test for endgame if(board.isEnd() || depth == 0) { board.value = board.evaluateBoard()+depth; return board; } // Iterate possible moves for every field for (int field=BOARD_END; field>=BOARD_START; field--) { // Not our color, we can't move this piece if (board.fields[field] != my_color) continue; // Line moves (move a balls axial pushing other balls) for (int direction=3; direction<9; direction++) { if (!board.canPushLine(field, move_offs[direction%6], my_color)) continue; next_board = new AbaloneBoard(board); next_board.pushLine(field, move_offs[direction%6], my_color); // If the move is better, this is the new best board next_board.value = (minAlphaBeta(next_board, depth-1, alpha, beta)).value; if (abalone_panel.debug_level > 0) System.err.print(next_board.value + " "); // If board positions are of equal value, // randomly pick whether or not to keep the // old value. double rand_max = Math.random(); if ((best_value == next_board.value) && (rand_max > 0.999)) { best_board = next_board; best_board.mvt_field = field; best_board.mvt_dir = direction%6; best_value = best_board.value; } if(best_value < next_board.value) { best_board = next_board; best_board.mvt_field = field; best_board.mvt_dir = direction%6; best_value = best_board.value; } if(best_board.value >= beta) return best_board; alpha = best_board.value; } } if (abalone_panel.debug_level > 0) { System.err.println(""); for(int i=0; i=BOARD_START; field--) { // Not our color, we can't move this piece if (board.fields[field] != my_color) continue; // Line moves (move a balls axial pushing other balls) for (int direction=3; direction<9; direction++) { if (!board.canPushLine(field, move_offs[direction%6], my_color)) continue; next_board = new AbaloneBoard(board); next_board.pushLine(field, move_offs[direction%6], my_color); // If the move is better, this is the new best board next_board.value = (maxAlphaBeta(next_board, depth-1, alpha, beta)).value; if (abalone_panel.debug_level > 0) System.err.print(next_board.value + " "); // If board positions are of equal value, // randomly pick whether or not to keep the // old value. double rand_min = Math.random(); if ((best_value == next_board.value) && (rand_min > 0.999)) { best_board = next_board; best_board.mvt_field = field; best_board.mvt_dir = direction%6; best_value = best_board.value; } if(best_value > next_board.value) { best_board = next_board; best_board.mvt_field = field; best_board.mvt_dir = direction%6; best_value = best_board.value; } if(best_board.value <= alpha) return best_board; beta = best_board.value; } } if (abalone_panel.debug_level > 0) { System.err.println(""); for(int i=0; i 0) { Integer rec_level_int = new Integer(rec_level); Integer min_max_int = new Integer(min_max); System.err.println("findBest(" + rec_level_int.toString() + "," + min_max_int.toString() + ")"); } // Base of recursion: use evaluation function if (rec_level == 0) { for (Enumeration e = board_list.elements(); e.hasMoreElements();) { board = (AbaloneBoard)e.nextElement(); board.value = board.oldEvaluateBoard(); } return findMinMax(board_list, min_max); } // recursion loop: eval positions by finding min_max of subpositions for (Enumeration e = board_list.elements(); e.hasMoreElements();) { board = (AbaloneBoard)e.nextElement(); Vector sub_list = board.moveGen(-min_max); best_pos = findBest(sub_list, rec_level-1, -min_max); if (best_pos == null) { board.value = 10000*min_max; // won or lost game } else { board.value = best_pos.value; // value of best pos } } return findMinMax(board_list, min_max); } } /************************************************** * AbalonePanel * * This class is responsible for displaying * the Abalone board and managing user input. **************************************************/ class AbalonePanel extends Panel implements Runnable { Thread relaxer; // State of a field: final static int BOARD_NONE = 0; final static int BOARD_OUTSIDE = 2; final static int BOARD_WHITE = 1; final static int BOARD_BLACK = -1; final static int BOARD_ERROR = 3; // Constants for the board final static int BOARD_WIDTH = 11; // Board width and height must be odd numbers final static int BOARD_HEIGHT = BOARD_WIDTH; // the hexagonal side size is (BOARD_WIDTH-1)/2 because // we want to leave a boarder of nothing around the board final static int BOARD_MAX = BOARD_WIDTH*BOARD_HEIGHT; // end of (logical) board index final static int BOARD_MIN = 0; // start of (logical) board index final static int BOARD_START = BOARD_WIDTH + (BOARD_WIDTH-1)/2; // first (physical) field to be used final static int BOARD_END = (BOARD_HEIGHT - 2)*BOARD_WIDTH + (BOARD_WIDTH+1)/2; // last (physical) field +1 final static int BOARD_MIDDLE = BOARD_WIDTH*(BOARD_HEIGHT-1)/2+(BOARD_WIDTH-1)/2; // middle of board final static int BOARD_TOP_LEFT = BOARD_WIDTH*(BOARD_WIDTH-2) + 1; final static int BOARD_BOTTOM_RIGHT = BOARD_WIDTH+BOARD_WIDTH-2; // Display color for the different states of a field final Color WHITE_COLOR = Color.blue; final Color BLACK_COLOR = Color.black; final Color NONE_COLOR = Color.white; final Color OUTSIDE_COLOR = Color.gray; AbaloneApplet abalone_app; // pointer "upward" AbaloneBoard abalone_board; // the "real" position AbaloneBoard drag_board; // position during mouseDrag operations // Parameters changed by user interface int recursion_depth = 1; int my_color = BOARD_WHITE; boolean human_plays = true; final static int TURN_TIMEOUT = 200; // Min turn time in ms for comp. vs comp. int debug_level = 0; // 0=silent, 1=few, .. 3=verbose // Var's needed for comp vs. comp play public final static int MOVE_DELAY = 200; int current_turn = BOARD_WHITE; java.util.Timer playTimer; Date startTime; Date endTime; long white_time; long black_time; Label move_counter_label; Label white_label; Label black_label; Label score_label; Label white_time_label; Label black_time_label; JTextArea mvt_log; int selection[] = new int[BOARD_MAX]; Dimension dimension = new Dimension(); // size of the panel int x_dist, y_dist; // extension of fields int diam; // diameter of a field int picked_field = 0; // field of mouseDown int drag_mode = 0; // =1 if draging (=> drag_board) // Graphics variables boolean pickfixed; Image offscreen; Dimension offscreensize; Graphics offGraphics; //counting the number of moves so far int move_counter = 0; // Constructor: initialize board and set selection to empty AbalonePanel(AbaloneApplet abalone) { this.abalone_app = abalone; abalone_board = new AbaloneBoard(this); // Reset the selection to empty cleanSelection(); // Initialize the size of the display dimension.width = 300; dimension.height = 300; x_dist = dimension.width / (BOARD_WIDTH+1); y_dist = dimension.height / (BOARD_HEIGHT+1); diam = Math.min(dimension.width,dimension.height) / 15; } void reset(String arg) { if ("White".equals(arg)) my_color = BOARD_WHITE; if ("Black".equals(arg)) my_color = BOARD_BLACK; if ("Level 1".equals(arg)) recursion_depth = 1; if ("Level 2".equals(arg)) recursion_depth = 2; if ("Level 3".equals(arg)) recursion_depth = 3; if ("Level 4".equals(arg)) recursion_depth = 4; if ("Level 5".equals(arg)) recursion_depth = 5; if ("Human".equals(arg)) human_plays = true; if ("Computer".equals(arg)) human_plays = false; if(playTimer != null) { playTimer.cancel(); playTimer = null; } abalone_board = new AbaloneBoard(this); cleanSelection(); white_time = 0; black_time = 0; move_counter=0; if(score_label != null) score_label.setText(""); if (move_counter_label!=null) move_counter_label.setText("0"); if(mvt_log != null) mvt_log.setText("Abalone Game Log\n"); if(white_time_label != null) white_time_label.setText("0"); if(black_time_label != null) black_time_label.setText("0"); if (human_plays == false) { if(playTimer != null) { playTimer.cancel(); } playTimer = new java.util.Timer(); playTimer.schedule(new RunManager(abalone_board), TURN_TIMEOUT, TURN_TIMEOUT); } else { // Let white do the first move if I'm black if (my_color == BOARD_BLACK) { abalone_board.makeOpponentMove(); } } } /* * Reset the selection to empty */ public void cleanSelection() { for (int i=0; i 0) { offGraphics.setColor(Color.blue); offGraphics.drawArc(x-diam/2-1, y-diam/2-1, diam+2, diam+2, 0, 360); } } g.drawImage(offscreen, 0, 0, null); } /* * Clicking on a field toggles if this field is selected or not. */ public synchronized boolean mouseDown(Event evt, int x, int y) { picked_field = kooToField(x,y); // Show drag_board instead of abalone_board drag_board = new AbaloneBoard(abalone_board); // copy content drag_mode = 1; // switch to drag_board repaint(); return true; } /* * We are in drag mode which means that the "drag_board" * instead of the "abalone_board" is displayed. Thus we must * first copy all _content_ from abalone_board to drag_board * and then we can perform any moves on drag_board. */ public synchronized boolean mouseDrag(Event evt, int x, int y) { // Copy the original board to drag_board; drag_board.copyContent(abalone_board); if (picked_field == 0) return true; // exception() int draged_field = kooToField(x,y); // where is the mouse now? if (picked_field != draged_field) { // draged the mouse from picked_field to draged_field: boolean result = drag_board.makeUserMove(picked_field,draged_field,selection); if (!result) { // Bad move - restore original drag_board.copyContent(abalone_board); } } repaint(); return true; } /* * The mouse has been released and the move is executed */ public synchronized boolean mouseUp(Event evt, int x, int y) { boolean result = false; // Switch from drag mode back to real board drag_board = null; // delete drag board drag_mode = 0; // switch back to normal mode if (picked_field == 0) return true; // exception() int draged_field = kooToField(x,y); // where is the mouse now? if (debug_level > 1) { Integer picked_field_integer = new Integer(picked_field); Integer draged_field_integer = new Integer(draged_field); System.err.println("mouseUp(" + picked_field_integer.toString() + "," + draged_field_integer.toString() + ")"); } if (picked_field == draged_field) { // Single click on a field: toggle selection // Commenting this out prevents broadside moves. //selection[picked_field] = 1-selection[picked_field]; result = false; } else { // Draged the mouse from picked_field to draged_field: result = abalone_board.makeUserMove(picked_field,draged_field,selection); if (result) { updateScore(); abalone_board.makeOpponentMove(); move_counter++; move_counter_label.setText(new Integer(move_counter).toString()); updateScore(); } cleanSelection(); } picked_field = 0; repaint(); return result; } public void start() { relaxer = new Thread(this); relaxer.start(); } public void stop() { relaxer.stop(); } /* * Provide the Panel with access to the appropriate labels it will want to * update mid game */ public void registerLabels(Label mc_label, Label w_label, Label b_label, Label s_label, Label wt_label, Label bt_label, JTextArea logText) { move_counter_label = mc_label; white_label = w_label; black_label = b_label; score_label = s_label; white_time_label = wt_label; black_time_label = bt_label; mvt_log = logText; } /* * Update the score display, check for winner. */ void updateScore() { if(abalone_board != null && white_label != null && black_label != null && score_label != null) { int white_count, black_count; white_count = abalone_board.getWhiteLosses(); black_count = abalone_board.getBlackLosses(); white_label.setText(new Integer(white_count).toString()); black_label.setText(new Integer(black_count).toString()); if(abalone_board.isEnd()) { if(black_count > white_count) score_label.setText("White Wins!"); else score_label.setText("Black Wins!"); } } mvt_log.append("\n" + abalone_board.intoLetterCode(abalone_board.mvt_field) + "-"+ abalone_board.intoLetterCode(abalone_board.mvt_field + abalone_board.move_offs[abalone_board.mvt_dir])); //mvt_log.append(new String(abalone_board.mvt_log)); //abalone_board.mvt_log.delete(0,abalone_board.mvt_log.length()); } /* * This manages running a computer vs. computer game * By scheduling a timer to execute this class's run() * function, we can make the computer take turns with itself. */ class RunManager extends TimerTask { AbaloneBoard board = null; int colorTurn = BOARD_WHITE; public RunManager(AbaloneBoard newBoard) { colorTurn = BOARD_WHITE; board = newBoard; return; } public void run(){ if(board!=null && !board.isEnd() && human_plays == false) { if(colorTurn == my_color) { startTime = new Date(); board.makeComputerUserMove(); endTime = new Date(); move_counter_label.setText(new Integer(move_counter++).toString()); } else { startTime = new Date(); board.makeOpponentMove(); endTime = new Date(); } long elapsedTime = endTime.getTime() - startTime.getTime(); if(colorTurn == BOARD_WHITE) { white_time += elapsedTime; white_time_label.setText(new Long(white_time).toString()); } else { black_time += elapsedTime; black_time_label.setText(new Long(black_time).toString()); } updateScore(); if(abalone_board.isEnd()) playTimer.cancel(); } else { playTimer.cancel(); } if(colorTurn == BOARD_WHITE) colorTurn = BOARD_BLACK; else colorTurn = BOARD_WHITE; } } } /************************************************** * AbaloneApplet **************************************************/ public class AbaloneApplet extends Applet { AbalonePanel panel; Choice level_choice; Choice color_choice; public void init() { setLayout(new BorderLayout()); panel = new AbalonePanel(this); add("Center", panel); Panel button_panel = new Panel(); add("South",button_panel); Choice color_choice = new Choice(); color_choice.addItem("White"); color_choice.addItem("Black"); button_panel.add(color_choice); Choice level_choice = new Choice(); level_choice.addItem("Level 1"); level_choice.addItem("Level 2"); level_choice.addItem("Level 3"); level_choice.addItem("Level 4"); level_choice.addItem("Level 5"); button_panel.add(level_choice); Choice player_choice = new Choice(); player_choice.addItem("Human"); player_choice.addItem("Computer"); button_panel.add(player_choice); button_panel.add(new Button("New Game")); Panel info_panel = new Panel(); add("North", info_panel); info_panel.setLayout(new BorderLayout()); Panel tally_panel = new Panel(); Label white_score = new Label(" "); Label white_label = new Label("White Loss:"); Label black_score = new Label(" "); Label black_label = new Label("Black Loss:"); Label score_label = new Label(" "); Label move_counter = new Label("0"); tally_panel.add(move_counter); tally_panel.add(white_label); tally_panel.add(white_score); tally_panel.add(score_label); tally_panel.add(black_label); tally_panel.add(black_score); info_panel.add("North", tally_panel); Panel time_panel = new Panel(); info_panel.add("South",time_panel); Label white_time = new Label(" "); Label spacer = new Label(" "); Label black_time = new Label(" "); time_panel.add(white_time); time_panel.add(spacer); time_panel.add(black_time); JTextArea logTextField = new JTextArea(); JScrollPane scrollPane = new JScrollPane(logTextField); logTextField.append("Abalone Game Log"); scrollPane.setSize(200,350); scrollPane.setPreferredSize(new Dimension(150,350)); add("East", scrollPane); logTextField.setEditable(false); scrollPane.setVerticalScrollBar(scrollPane.createVerticalScrollBar()); panel.registerLabels(move_counter, white_score, black_score, score_label, white_time, black_time, logTextField); setSize(450,350); } public void start() { panel.start(); } public void stop() { panel.stop(); } public boolean action(Event evt, Object arg) { // Check for the color choice button if (evt.target instanceof Choice) { panel.reset((String)arg); } if ("New Game".equals(arg)) { panel.reset("New Game"); // play(getCodeBase(), "audio/computer.au"); return true; } return false; } public static void main(String args[]) { Frame f = new Frame("ArcTest"); AbaloneApplet abalone_app = new AbaloneApplet(); abalone_app.init(); abalone_app.start(); f.add("Center", abalone_app); f.setSize(400, 400); f.show(); } }