Package lpa.model

Source Code of lpa.model.Grid

package lpa.model;

import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import lpa.command.CompoundCommand;
import lpa.command.EdgeCommand;
import lpa.model.Edge.EdgeState;

/**
* A rectangular grid of cells with integers. Some members (instance variable,
* inner class) are protected instead of private to allow access from inherited
* classes.
*
* @author Dimitry Leonov
*/
public final class Grid {

    private final List<GridElement> items = new ArrayList<>();
    private boolean isInEditMode = false;
    private int width = 0, height = 0;

    /**
     * Constructs the grid using the scanner as an input source.
     *
     * @param scanner {@link Scanner} linked with the file with puzzle data.
     */
    public Grid(Scanner scanner) {
        int i = 0, j = 0;
        while (scanner.hasNext()) {
            char[] chars = scanner.nextLine().toCharArray();
            for (char ch : chars) {
                boolean is_cell = i % 2 == 1 && j % 2 == 1;
                if (is_cell) {
                    if ((ch < '0' || ch > '3') && !Character.isWhitespace(ch)) {
                        throw new IllegalArgumentException("Grid contains invalid symbols.");
                    }
                    items.add(new Cell(i, j, Character.isWhitespace(ch) ? 4 : ch - '0'));
                } else {
                    switch (ch) {
                        case '+':
                            items.add(new Vertex(i, j));
                            break;
                        case '.':
                        case ' ':
                            items.add(new Edge(i, j, EdgeState.MAYBE));
                            break;
                        case '-':
                        case '|':
                            items.add(new Edge(i, j, EdgeState.YES));
                            break;
                    }
                }
                ++j;
            }
            ++i;
            width = j;
            j = 0;

        }
        height = i;

        GridElement.setParentGrid(this);
    }

    /**
     * Constructs empty grid using given width and height.
     *
     * @param _width Width of the grid.
     * @param _height Height of the grid.
     */
    public Grid(int _width, int _height) {
        this.width = _width * 2 + 1;
        this.height = _height * 2 + 1;

        for (int i = 0; i < height; ++i) {
            for (int j = 0; j < width; ++j) {
                if (i % 2 == 1 && j % 2 == 1) {
                    items.add(new Cell(i, j, 4));
                } else if (i % 2 == 0 && j % 2 == 0) {
                    items.add(new Vertex(i, j));
                } else {
                    items.add(new Edge(i, j, EdgeState.MAYBE));
                }
            }
        }

        GridElement.setParentGrid(this);
    }

    /**
     * Returns list of elements in the grid.
     *
     * @return List of elements in the grid.
     */
    public List<GridElement> getGridElements() {
        return items;
    }

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder();
        int i = 0;
        for (GridElement e : items) {
            str.append(e.toString());
            if (i >= width() - 1) {
                i = 0;
                str.append("\n");
            } else {
                ++i;
            }
        }

        return str.toString();
    }

    /**
     * Performs hit test using provided point.
     *
     * @param p {@link Point} to test.
     * @param parentSize {@link Dimension} of GUI component.
     * @return Element which was hitted, or null if no element was hitted.
     */
    public GridElement getElementByPoint(Point p, Dimension parentSize) {
        for (GridElement e : items) {
            if (e.isHit(p, parentSize)) {
                return e;
            }
        }
        return null;
    }

    /**
     * Returns total number of elements in grid.
     *
     * @return Total number of elements in grid.
     */
    protected int itemCount() {
        return items.size();
    }

    /**
     * Returns width of the grid, in elements.
     *
     * @return Width of the grid.
     */
    protected int width() {
        return width;
    }

    /**
     * Returns height of the grid, in elements.
     *
     * @return Height of the grid.
     */
    protected int height() {
        return height;
    }

    /**
     * Calculates value of the edge's minimal dimension.
     *
     * @param size {@link Dimension} of the caller GUI component.
     * @return Value of the edge's minimal dimension.
     */
    protected int getElementSize(Dimension size) {
        int unit_width = width / 2 * 6 + 1;
        int unit_height = height / 2 * 6 + 1;
        int unit_size = Math.min(size.width / unit_width, size.height / unit_height);
        return unit_size;
    }

    /**
     * Checks the validity of the grid.
     *
     * @return String with the error or message about completing the puzzle.
     */
    public String checkGrid() {
        String error = "";
        for (GridElement e : items) {
            if (e instanceof Cell) {
                if (!((Cell) e).isEdgesValid() && "".equals(error)) {
                    error = "Invalid edge count at cell " + e.coordsToString();
                }
            }

            if (e instanceof Vertex) {
                if (!((Vertex) e).isEdgesValid() && "".equals(error)) {
                    error = "Invalid edge count at vertex " + e.coordsToString();
                }
            }
        }

        if ("".equals(error)) {
            List<Edge> edges = new ArrayList<>();

            for (GridElement e : items) {
                if (e instanceof Edge && ((Edge) e).getState() == EdgeState.YES) {
                    edges.add((Edge) e);
                }
            }

            if (edges.size() > 0) {

                boolean[] flags = new boolean[edges.size()];
                Arrays.fill(flags, true);
                Edge current_edge = edges.get(0);
                flags[0] = false;
                while (true) {
                    List<Edge> list = current_edge.getAdjacentEdges();
                    if (list.isEmpty()) {
                        break;
                    }
                    int i1 = edges.indexOf(list.get(0));
                    int i2 = edges.indexOf(list.get(1));
                    if (flags[i1]) {
                        current_edge = list.get(0);
                        flags[i1] = false;
                    } else if (flags[i2]) {
                        current_edge = list.get(1);
                        flags[i2] = false;
                    } else {
                        break;
                    }
                }

                for (boolean i : flags) {
                    if (i) {
                        error = "Multiple loops found.";
                        break;
                    }
                }
            }
        }

        if ("".equals(error)) {
            error = "Congratulations. Puzzle solved.";
        }

        return error;
    }

    /**
     * Turns edit mode on and off.
     *
     * @param is_on True for enabling edit mode, false otherwise.
     */
    public void setEditMode(boolean is_on) {
        isInEditMode = is_on;
    }

    /**
     * Returns true if edit mode is enabled, false otherwise.
     *
     * @return True if edit mode is enabled, false otherwise.
     */
    public boolean editMode() {
        return isInEditMode;
    }

    private static int getEdgeCount(List<Edge> edges, EdgeState state) {
        int count = 0;
        for (Edge i : edges) {
            if (i.getState() == state) {
                ++count;
            }
        }

        return count;
    }

    private static boolean applyStrategy(List<Edge> edges, EdgeState condition_state,
            EdgeState target_state, CompoundCommand command) {

        //int count = getEdgeCount(edges, target_state);
        int action_count = 0;
        for (Edge i : edges) {
            if (i.getState() != condition_state && i.getState() != target_state) {
                command.add(new EdgeCommand(i, target_state));
                i.setState(target_state);
                ++action_count;
            }
        }
        return action_count != 0;
    }

    private boolean iterateStrategies(CompoundCommand command) {
        for (GridElement e : items) {

            if (e instanceof Vertex) {
                Vertex v = (Vertex) e;
                List<Edge> edges = v.surroundingEdges(false);

                /* strategy 2
                 * If a vertex has two yes-edges,
                 * then its other edges can be marked as no-edges
                 * */
                int yes_edges = getEdgeCount(edges, EdgeState.YES);
                if (yes_edges == 2) {
                    if (applyStrategy(edges, EdgeState.YES, EdgeState.NO, command)) {
                        return true;
                    }
                }

                /* strategy 4
                 * If all but one edge at a vertex are no-edges,
                 * then the remaining edge can be marked as no-edge as well.
                 * */
                int no_edges = getEdgeCount(edges, EdgeState.NO);
                if (no_edges == edges.size() - 1) {
                    if (applyStrategy(edges, EdgeState.NO, EdgeState.NO, command)) {
                        return true;
                    }
                }
            }

            if (e instanceof Cell) {
                Cell c = (Cell) e;
                List<Edge> edges = c.surroundingEdges(false);

                /* strategy 1
                 * If a cell containing digit d is surrounded by d yes-edges,
                 * then all its other edges can be marked as no-edges.
                 * */
                int yes_edges = getEdgeCount(edges, EdgeState.YES);
                if (yes_edges == c.getValue() && !c.isEmpty()) {
                    if (applyStrategy(edges, EdgeState.YES, EdgeState.NO, command)) {
                        return true;
                    }
                }

                /* strategy 3
                 * If a cell containing digit d is surrounded by 4 - d no-edges,
                 * then the remaining edges can be marked as yes-edges.
                 * */
                int no_edges = getEdgeCount(edges, EdgeState.NO);
                if (no_edges == 4 - c.getValue() && !c.isEmpty()) {
                    if (applyStrategy(edges, EdgeState.NO, EdgeState.YES, command)) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Applies all the strategies until no strategy is suitable.
     *
     * @return {@link CompoundCommand} with all the actions performed while
     * applying strategies.
     */
    public CompoundCommand applyStrategies() {
        CompoundCommand command = new CompoundCommand();

        while (iterateStrategies(command)) {
        }

        /* strategies 5 and 6 (corner cells)
         * If corner cell contains digit 1,
         * then its two outer edges can be marked as no-edges.
         * If corner cell contains digit 3,
         * then its two outer edges can be marked as yes-edges.
         */

        //upper-left cell
        Cell c11 = (Cell) items.get(width() + 1);
        if (c11.getValue() == 1) {
            ((Edge) items.get(1)).setState(EdgeState.NO);
            ((Edge) items.get(width())).setState(EdgeState.NO);
        } else if (c11.getValue() == 3) {
            ((Edge) items.get(1)).setState(EdgeState.YES);
            ((Edge) items.get(width())).setState(EdgeState.YES);
        }

        //upper-right cell
        Cell c12 = (Cell) items.get(width() * 2 - 2);
        if (c12.getValue() == 1) {
            ((Edge) items.get(width() - 2)).setState(EdgeState.NO);
            ((Edge) items.get(width() * 2 - 1)).setState(EdgeState.NO);
        } else if (c12.getValue() == 3) {
            ((Edge) items.get(width() - 2)).setState(EdgeState.YES);
            ((Edge) items.get(width() * 2 - 1)).setState(EdgeState.YES);
        }

        //bottom-left cell
        Cell c21 = (Cell) items.get(itemCount() - width() * 2 + 1);
        if (c21.getValue() == 1) {
            ((Edge) items.get(itemCount() - width() * 2)).setState(EdgeState.NO);
            ((Edge) items.get(itemCount() - width() + 1)).setState(EdgeState.NO);
        } else if (c21.getValue() == 3) {
            ((Edge) items.get(itemCount() - width() * 2)).setState(EdgeState.YES);
            ((Edge) items.get(itemCount() - width() + 1)).setState(EdgeState.YES);
        }

        //bottom-right cell
        Cell c22 = (Cell) items.get(itemCount() - width() - 2);
        if (c22.getValue() == 1) {
            ((Edge) items.get(itemCount() - width() - 1)).setState(EdgeState.NO);
            ((Edge) items.get(itemCount() - 2)).setState(EdgeState.NO);
        } else if (c22.getValue() == 3) {
            ((Edge) items.get(itemCount() - width() - 1)).setState(EdgeState.YES);
            ((Edge) items.get(itemCount() - 2)).setState(EdgeState.YES);
        }


        return command;
    }
}
TOP

Related Classes of lpa.model.Grid

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.