MoveLib.java
/**
 * Title: Pancet<p>
 *
 * Description: The move library for Pancet, a game applet.  This 
 * stores tried moves in the form of a move tree.  It's responsible 
 * for the storage, history, and access to moves. It also creates 
 * new moves when faced with new situations.<p>
 *
 * Copyright: Copyright (c) 2005<p>
 *
 * @author Mark Bondurant
 * @version 3.0
 */

import java.util.HashMap;
import java.util.Random;

class MoveLib implements GameConstants {
    private Hashtable lib = new Hashtable();
    private Random ran = new Random();
  
   /**
    * Parent is our linkage back to the previous move.  The
    * value of the last move made will be added to the next
    * in order to chain them together.  This is used later 
    * when we have to purge branches of the tree that have 
    * proven themselves to be unuseful.
    */
    private int parent[] = {0, 0, 0, 0};

   /**
    * To ease some of the painful process of learning, we load
    * the library with some of the more obvious moves.
    */
    MoveLib() {
        // add some basic smarts
        lib.put(Move.key(new int[] {7, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {6, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {5, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {4, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {3, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {2, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));
        lib.put(Move.key(new int[] {1, 1, 0, 0}), new Move(MOVE_ROOT, BOWL_B, true));

        lib.put(Move.key(new int[] {2, 7, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {2, 6, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {2, 5, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {2, 4, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {2, 3, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {2, 2, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));

        lib.put(Move.key(new int[] {1, 2, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {1, 3, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {1, 4, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {1, 5, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {1, 6, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
        lib.put(Move.key(new int[] {1, 7, 0, 0}), new Move(MOVE_ROOT, BOWL_A, true));
    }

   /**
    * This returns the stored move count, which is displayed on 
    * the game board.  In this way the user can see the move tree
    * grow as moves are made, and shrink when the tree is pruned
    * back.
    */
    int storedMoves() {
        return lib.size();
    }

   /**
    * This has proven useful in debugging and thus, has been left in.
    */
    public String toString() {
        return "MoveLib has " + this.storedMoves() + " moves stored";
    }

    void resetParent() {
        for (int i=0; i<4; i++) parent[i] = 0;
    }

   /**
    * getMove attempts to look up a move in the library, based on the
    * current state of the board.  If none is found, a new random move is
    * created, added to the library, and returned as if a move was found.
    */
    int getMove(int[] board) {
        String key = Move.key(board);      // create a key for lookup
        Move m = (Move) lib.get(key);      // attempt to lookup move

        // if there is no move in library, create one
        if (m == null) {
            if (board[BOWL_A] == 0)             // the cases where we only
                m = new Move(parent, BOWL_B, true);   // have one choice
            else {
                if (board[BOWL_B] == 0)
                    m = new Move(parent, BOWL_A, true);
                else
                    // create a random move
                    m = new Move(parent, BOWL_A + (int) (ran.nextFloat() * 2));
            }

            // save the move
            lib.put(Move.key(board), m);
        }

        // store this board so it can be linked to the next
        for(int i=0; i<4; i++) parent[i] = board[i];
        return m.move();
    }

   /**
    * lostGame trims back the move tree to the last guess made.  We flip it
    * to force exploration of the untried branch.
    */
    void lostGame() {
        String parentKey;
        String key = Move.key(parent);

        // purge tried losing moves
        Move m = (Move) lib.get(key);
        while (m.flipped() && m.parent_key() != "0000") {
            parentKey = m.parent_key();
            lib.remove(key);
            key = parentKey;
            m = (Move) lib.get(key);
        }

        // store flipped move
        lib.remove(key);
        m.flip();
        lib.put(key, m);

        // reset parent for the next game
        resetParent();
    }
}

MoveLib.java