Playboard.java :  » Game » shortyz » com » totsp » crossword » puz » Java Open Source

Java Open Source » Game » shortyz 
shortyz » com » totsp » crossword » puz » Playboard.java
package com.totsp.crossword.puz;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;



public class Playboard implements Serializable {
   
  private HashMap<Integer, Position> acrossWordStarts = new HashMap<Integer, Position>();
    private HashMap<Integer, Position> downWordStarts = new HashMap<Integer, Position>();
    private Position highlightLetter = new Position(0, 0);
    private Puzzle puzzle;
    private String responder;
    private Box[][] boxes;
    private boolean across = true;
    private boolean showErrors;
    private boolean skipCompletedLetters;
    private MovementStrategy movementStrategy = MovementStrategy.MOVE_NEXT_ON_AXIS;

    public Playboard(Puzzle puzzle, MovementStrategy movementStrategy){
      this(puzzle);
      this.movementStrategy = movementStrategy;
    }
    
    public Playboard(Puzzle puzzle) {
        this.puzzle = puzzle;
        this.highlightLetter = new Position(0, 0);
        this.boxes = new Box[puzzle.getBoxes()[0].length][puzzle.getBoxes().length];

        for (int x = 0; x < puzzle.getBoxes().length; x++) {
            for (int y = 0; y < puzzle.getBoxes()[x].length; y++) {
                boxes[y][x] = puzzle.getBoxes()[x][y];

                if ((boxes[y][x] != null) && boxes[y][x].isAcross()) {
                    acrossWordStarts.put(boxes[y][x].getClueNumber(),
                        new Position(y, x));
                }

                if ((boxes[y][x] != null) && boxes[y][x].isDown()) {
                    downWordStarts.put(boxes[y][x].getClueNumber(),
                        new Position(y, x));
                }
            }
        }
        
        if(this.boxes[0][0] == null){
          this.moveRight(false);
        }
    }

    public void setAcross(boolean across) {
        this.across = across;
    }

    public boolean isAcross() {
        return across;
    }

    public void toggleShowErrors(){
      this.showErrors = !this.showErrors;
    }
    
    public Clue[] getAcrossClues() {
        Clue[] clues = new Clue[puzzle.getAcrossClues().length];

        for (int i = 0; i < clues.length; i++) {
            clues[i] = new Clue();
            clues[i].hint = puzzle.getAcrossClues()[i];
            clues[i].number = puzzle.getAcrossCluesLookup()[i];
        }

        return clues;
    }

    public Box[][] getBoxes() {
        return this.boxes;
    }

    public Clue getClue() {
        Clue c = new Clue();

        try {
            Position start = this.getCurrentWordStart();
            c.number = this.getBoxes()[start.across][start.down].getClueNumber();
            c.hint = this.across ? this.puzzle.findAcrossClue(c.number)
                                 : this.puzzle.findDownClue(c.number);
        } catch (Exception e) {
        }

        return c;
    }
    
    /** Returns the 0 based index of the current clue based on the current across or down state
     * 
     * @return index of the across or down clue based on the current state
     */
    public int getCurrentClueIndex(){
      Clue c = this.getClue();
      if(across){
        return Arrays.binarySearch(this.puzzle.getAcrossCluesLookup(), c.number);
      } else {
        return Arrays.binarySearch(this.puzzle.getDownCluesLookup(), c.number);
      }
      
    }

    public Box getCurrentBox() {
        return this.boxes[this.highlightLetter.across][this.highlightLetter.down];
    }

    public Word getCurrentWord() {
        Word w = new Word();
        w.start = this.getCurrentWordStart();
        w.across = this.across;
        w.length = this.getWordRange();

        return w;
    }

    public Box[] getCurrentWordBoxes() {
        Word currentWord = this.getCurrentWord();
        Box[] result = new Box[currentWord.length];

        int across = currentWord.start.across;
        int down = currentWord.start.down;
        for (int i = 0; i < result.length; i++) {
          int newAcross = across;
          int newDown = down;
            if (currentWord.across) {
               newAcross += i;
            } else {
               newDown += i;
            }

            result[i] = this.boxes[newAcross][newDown];
        }
        return result;
    }
    
    public Position[] getCurrentWordPositions() {
        Word currentWord = this.getCurrentWord();
        Position[] result = new Position[currentWord.length];
        int across = currentWord.start.across;
        int down = currentWord.start.down;
        for (int i = 0; i < result.length; i++) {
          int newAcross = across;
          int newDown = down;
            if (currentWord.across) {
               newAcross += i;
            } else {
               newDown += i;
            }

            result[i] =  new Position(newAcross, newDown);
        }

        return result;
    }
    

    public Position getCurrentWordStart() {
        if (this.isAcross()) {
            int col = this.highlightLetter.across;
            Box b = null;

            while (b == null) {
                try {
                    if ((boxes[col][this.highlightLetter.down] != null) &&
                            boxes[col][this.highlightLetter.down].isAcross()) {
                        b = boxes[col][this.highlightLetter.down];
                    } else {
                        col--;
                    }
                } catch (Exception e) {
                    break;
                }
            }

            return new Position(col, this.highlightLetter.down);
        } else {
            int row = this.highlightLetter.down;
            Box b = null;

            while (b == null) {
                try {
                    if ((boxes[this.highlightLetter.across][row] != null) &&
                            boxes[this.highlightLetter.across][row].isDown()) {
                        b = boxes[this.highlightLetter.across][row];
                    } else {
                        row--;
                    }
                } catch (Exception e) {
                    break;
                }
            }

            return new Position(this.highlightLetter.across, row);
        }
    }

    public Clue[] getDownClues() {
        Clue[] clues = new Clue[puzzle.getDownClues().length];

        for (int i = 0; i < clues.length; i++) {
            clues[i] = new Clue();
            clues[i].hint = puzzle.getDownClues()[i];
            clues[i].number = puzzle.getDownCluesLookup()[i];
        }

        return clues;
    }

    public Word setHighlightLetter(Position highlightLetter) {
        Word w = this.getCurrentWord();

        if (highlightLetter.equals(this.highlightLetter)) {
            this.toggleDirection();
        } else {
            if ((this.boxes.length > highlightLetter.across) && (highlightLetter.across >= 0) &&
                    (this.boxes[highlightLetter.across].length > highlightLetter.down) &&
                    (highlightLetter.down >= 0) &&
                    (this.boxes[highlightLetter.across][highlightLetter.down] != null)) {
                this.highlightLetter = highlightLetter;
            }
        }

        return w;
    }

    public Position getHighlightLetter() {
        return highlightLetter;
    }

    public Puzzle getPuzzle() {
        return this.puzzle;
    }

    /**
     * @param responder the responder to set
     */
    public void setResponder(String responder) {
        this.responder = responder;
    }

    /**
     * @return the responder
     */
    public String getResponder() {
        return responder;
    }

    public boolean isShowErrors() {
        return this.showErrors;
    }

    public void setSkipCompletedLetters(boolean skipCompletedLetters) {
        this.skipCompletedLetters = skipCompletedLetters;
    }

    public boolean isSkipCompletedLetters() {
        return skipCompletedLetters;
    }
    
    public Box[] getWordBoxes(int number, boolean isAcross){
      Position start = isAcross ? this.acrossWordStarts.get(number) : this.downWordStarts.get(number);
      int range = this.getWordRange(start, isAcross);
      int across = start.across;
        int down = start.down;
        Box[] result = new Box[range];
        for (int i = 0; i < result.length; i++) {
          int newAcross = across;
          int newDown = down;
            if (isAcross) {
               newAcross += i;
            } else {
               newDown += i;
            }

            result[i] = this.boxes[newAcross][newDown];
        }
        return result;
    }
    
    public int getWordRange(Position start, boolean across){
      if (across) {
            int col = start.across;
            Box b = null;

            do {
                b = null;

                int checkCol = col + 1;

                try {
                    col++;
                    b = this.getBoxes()[checkCol][start.down];
                } catch (RuntimeException e) {
                }
            } while (b != null);

            return col - start.across;
        } else {
            int row = start.down;
            Box b = null;

            do {
                b = null;

                int checkRow = row + 1;

                try {
                    row++;
                    b = this.getBoxes()[start.across][checkRow];
                } catch (RuntimeException e) {
                }
            } while (b != null);

            return row - start.down;
        }
    }

    public int getWordRange() {
        return getWordRange(this.getCurrentWordStart(), this.isAcross());
    }

    /**
     * Handler for the backspace key.  Uses the following algorithm:
     * -If current box is empty, move back one character.  If not, stay still.
     * -Delete the letter in the current box.
     */
    public Word deleteLetter() {
      Box currentBox = this.boxes[this.highlightLetter.across][this.highlightLetter.down];
      Word wordToReturn = this.getCurrentWord();
        if(currentBox.getResponse() == ' ') {
          wordToReturn = this.previousLetter();
          currentBox = this.boxes[this.highlightLetter.across][this.highlightLetter.down];
        }
        currentBox.setResponse(' ');
        return wordToReturn;
    }
    
    public void setMovementStrategy(MovementStrategy movementStrategy){
      this.movementStrategy = movementStrategy;
    }
    
    

    public void jumpTo(int clueIndex, boolean across) {
      try {
          if (across) {
              this.highlightLetter = (this.acrossWordStarts.get(this.puzzle.getAcrossCluesLookup()[clueIndex]));
          } else {
              this.highlightLetter = (this.downWordStarts.get(this.puzzle.getDownCluesLookup()[clueIndex]));
          }
          this.across = across;
      } catch (Exception e) {
      }
    }

    public Word moveDown() {
        return this.moveDown(false);
    }

    public Position moveDown(Position original, boolean skipCompleted) {
        Position next = new Position(original.across, original.down + 1);
        Box value = this.getBoxes()[next.across][next.down];

        if ((value == null) || skipCurrentBox(value, skipCompleted)) {
            try {
                next = moveDown(next, skipCompleted);
            } catch (ArrayIndexOutOfBoundsException e) {
            }
        }

        return next;
    }

    public Word moveDown(boolean skipCompleted) {
        Word w = this.getCurrentWord();

        try {
            Position newPos = this.moveDown(this.getHighlightLetter(),
                    skipCompleted);
            this.setHighlightLetter(newPos);
        } catch (ArrayIndexOutOfBoundsException e) {
        }

        return w;
    }

    public Position moveLeft(Position original, boolean skipCompleted) {
        Position next = new Position(original.across - 1, original.down);
        Box value = this.getBoxes()[next.across][next.down];

        if ((value == null) || skipCurrentBox(value, skipCompleted)) {
            try {
                next = moveLeft(next, skipCompleted);
            } catch (ArrayIndexOutOfBoundsException e) {
            }
        }

        return next;
    }

    public Word moveLeft(boolean skipCompleted) {
        Word w = this.getCurrentWord();

        try {
            Position newPos = this.moveLeft(this.getHighlightLetter(),
                    skipCompleted);
            this.setHighlightLetter(newPos);
        } catch (ArrayIndexOutOfBoundsException e) {
        }

        return w;
    }

    public Word moveLeft() {
        return moveLeft(false);
    }

    public Word moveRight() {
        return moveRight(false);
    }

    public Word nextWord(){
      Word previous = this.getCurrentWord();

        Position p = this.getHighlightLetter();

        int newAcross = p.across;
        int newDown = p.down;
        if (previous.across) {
            newAcross = (previous.start.across + previous.length) - 1;
        } else {
            newDown = (previous.start.down + previous.length) - 1;
        }
        
        Position newPos = new Position(newAcross, newDown);
        if (!newPos.equals(p)) {
          this.setHighlightLetter(newPos);
        }
        this.nextLetter();
        return previous;
    }
    
    public Word previousWord() {
      Word previous = this.getCurrentWord();

        Position p = this.getHighlightLetter();

        int newAcross = p.across;
        int newDown = p.down;
        if (previous.across) {
            newAcross = previous.start.across - 1;
        } else {
            newDown = previous.start.down - 1;
        }
        
        this.setHighlightLetter(new Position(newAcross, newDown));
        this.previousLetter();
        Word current = this.getCurrentWord();
        this.setHighlightLetter(new Position(current.start.across, current.start.down));
        return previous;
    }
    
    public Position moveRight(Position original, boolean skipCompleted) {
        Position next = new Position(original.across + 1, original.down);
        Box value = this.getBoxes()[next.across][next.down];

        if ((value == null) || skipCurrentBox(value, skipCompleted)) {
            try {
                next = moveRight(next, skipCompleted);
            } catch (ArrayIndexOutOfBoundsException e) {
            }
        }

        return next;
    }

    public Word moveRight(boolean skipCompleted) {
        Word w = this.getCurrentWord();

        try {
            Position newPos = this.moveRight(this.getHighlightLetter(),
                    skipCompleted);
            this.setHighlightLetter(newPos);
        } catch (ArrayIndexOutOfBoundsException e) {
        }

        return w;
    }

    public Position moveUp(Position original, boolean skipCompleted) {
        Position next = new Position(original.across, original.down - 1);
        Box value = this.getBoxes()[next.across][next.down];

        if ((value == null) || skipCurrentBox(value, skipCompleted)) {
            try {
                next = moveUp(next, skipCompleted);
            } catch (ArrayIndexOutOfBoundsException e) {
            }
        }

        return next;
    }

    public Word moveUp() {
        return moveUp(false);
    }

    public Word moveUp(boolean skipCompleted) {
        Word w = this.getCurrentWord();

        try {
            Position newPos = this.moveUp(this.getHighlightLetter(),
                    skipCompleted);
            this.setHighlightLetter(newPos);
        } catch (ArrayIndexOutOfBoundsException e) {
        }

        return w;
    }
    
    public boolean skipCurrentBox(Box b, boolean skipCompleted) {
    return skipCompleted && b.getResponse() != ' ' && 
         (!this.isShowErrors() || b.getResponse() == b.getSolution());
  }
    
    public Word nextLetter(boolean skipCompletedLetters){
      return this.movementStrategy.move(this, skipCompletedLetters);
    }

    public Word nextLetter() {
        return nextLetter(this.skipCompletedLetters);
    }

    public Word playLetter(char letter) {
        Box b = this.boxes[this.highlightLetter.across][this.highlightLetter.down];

        if (b == null) {
            return null;
        }
        b.setResponse(letter);
        b.setResponder(this.responder);
        return this.nextLetter();
    }

    public Word previousLetter() {
        return this.movementStrategy.back(this);
    }

    public Position revealLetter() {
        Box b = this.boxes[this.highlightLetter.across][this.highlightLetter.down];

        if ((b != null) && (b.getSolution() != b.getResponse())) {
            b.setCheated(true);
            b.setResponse(b.getSolution());

            return this.highlightLetter;
        }

        return null;
    }

    public List<Position> revealPuzzle() {
        ArrayList<Position> changes = new ArrayList<Position>();

        for (int across = 0; across < this.boxes.length; across++) {
            for (int down = 0; down < this.boxes[across].length; down++) {
                Box b = this.boxes[across][down];

                if ((b != null) && (b.getSolution() != b.getResponse())) {
                    b.setCheated(true);
                    b.setResponse(b.getSolution());
                    changes.add(new Position(across, down));
                }
            }
        }

        return changes;
    }

    public List<Position> revealWord() {
        ArrayList<Position> changes = new ArrayList<Position>();
        Position oldHighlight = this.highlightLetter;
        Word w = this.getCurrentWord();
        this.highlightLetter = w.start;

        for (int i = 0; i < w.length; i++) {
            Position p = revealLetter();

            if (p != null) {
                changes.add(p);
            }

            nextLetter(false);
        }

        this.highlightLetter = oldHighlight;

        return changes;
    }

    public Word toggleDirection() {
        Word w = this.getCurrentWord();
        this.across = !across;

        return w;
    }

    public void setShowErrors(boolean showErrors) {
        this.showErrors = showErrors;
    }

    public static class Clue {
        public String hint;
        public int number;

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }

            if (getClass() != obj.getClass()) {
                return false;
            }

            final Clue other = (Clue) obj;

            if ((this.hint == null) ? (other.hint != null)
                                        : (!this.hint.equals(other.hint))) {
                return false;
            }

            if (this.number != other.number) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            return this.number;
        }

        @Override
        public String toString() {
            return number + ". " + hint;
        }
    }

    public static class Position {
        public final int across;
        public final int down;

        public Position(int across, int down) {
            this.down = down;
            this.across = across;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || o.getClass() != this.getClass()) {
                return false;
            }

            Position p = (Position) o;

            return ((p.down == this.down) && (p.across == this.across));
        }

        @Override
        public int hashCode() {
            return this.across ^ this.down;
        }

        @Override
        public String toString() {
            return "[" + this.across + " x " + this.down + "]";
        }
    }

    public static class Word {
        public Position start;
        public boolean across;
        public int length;

        public boolean checkInWord(int across, int down) {
            int ranging = this.across ? across : down;
            boolean offRanging = this.across ? (down == start.down)
                                             : (across == start.across);

            int startPos = this.across ? start.across : start.down;

            return (offRanging && (startPos <= ranging) &&
            ((startPos + length) > ranging));
        }

        @Override
        public boolean equals(Object o) {
            if (o.getClass() != Word.class) {
                return false;
            }

            Word check = (Word) o;

            return check.start.equals(this.start) &&
            (check.across == this.across) && (check.length == this.length);
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = (29 * hash) +
                ((this.start != null) ? this.start.hashCode() : 0);
            hash = (29 * hash) + (this.across ? 1 : 0);
            hash = (29 * hash) + this.length;

            return hash;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.