com.mischivous.wormysharpyloggy.wsl.model.Tile.java Source code

Java tutorial

Introduction

Here is the source code for com.mischivous.wormysharpyloggy.wsl.model.Tile.java

Source

/*
 * Copyright (C) 2015 Jeremy Brown. Released under the Non-Profit Open Software License version 3.0 (NPOSL-3.0)
 */

package com.mischivous.wormysharpyloggy.wsl.model;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Size;
import android.support.v4.content.res.ResourcesCompat;
import com.mischivous.wormysharpyloggy.wsl.util.SetHelper;
import org.paukov.combinatorics.Factory;
import org.paukov.combinatorics.Generator;
import org.paukov.combinatorics.ICombinatoricsVector;

import java.util.*;

/**
 * Provides a representation of an individual tile used in the game.
 *
 * @author Jeremy Brown
 * @version 1.0
 * @since June 28, 2015
 */
public class Tile implements Comparable<Tile> {
    private static final List<Tile> allTiles;
    static {
        allTiles = new ArrayList<>(81);
        for (Shape sh : Shape.values()) {
            for (Fill fi : Fill.values()) {
                for (Color co : Color.values())
                    for (int i = 1; i < 4; i++) {
                        int index = (i - 1) + (3 * (co.GetNumVal() - 1)) + (9 * (fi.GetNumVal() - 1))
                                + (27 * (sh.GetNumVal() - 1));
                        allTiles.add(index, new Tile(i, co, fi, sh));
                    }
            }
        }
    }

    static Random r = new Random();
    private int num;
    private Color col;
    private Fill fil;
    private Shape sha;

    public Tile(@IntRange(from = 1, to = 3) int count, @NonNull Color color, @NonNull Fill fill,
            @NonNull Shape shape) {
        if (count < 1 || count > 3) {
            throw new IllegalArgumentException("Tiles have between one and three shapes.");
        } else if (color == null) {
            throw new NullPointerException("The Color of a Tile cannot be null.");
        } else if (fill == null) {
            throw new NullPointerException("The Fill of a Tile cannot be null.");
        } else if (shape == null) {
            throw new NullPointerException("The Shape of a Tile cannot be null.");
        }

        num = count;
        col = color;
        fil = fill;
        sha = shape;
    }

    /**
     * Return a previously-generated Tiles instead of creating a new one
     *
     * @param count The number of items on the Tile
     * @param color The Color of the items on the Tile
     * @param fill The Fill of the items on the Tile
     * @param shape the Shape of the items on the Tile
     * @return The Tile conforming to the provided attributes
     */
    @NonNull
    public static Tile GetTile(@IntRange(from = 1, to = 3) int count, @NonNull Color color, @NonNull Fill fill,
            @NonNull Shape shape) {
        if (count < 1 || count > 3) {
            throw new IllegalArgumentException("Tiles have between one and three shapes.");
        } else if (color == null) {
            throw new NullPointerException("The Color of a Tile cannot be null.");
        } else if (fill == null) {
            throw new NullPointerException("The Fill of a Tile cannot be null.");
        } else if (shape == null) {
            throw new NullPointerException("The Shape of a Tile cannot be null.");
        }

        int index = (count - 1) + (3 * (color.GetNumVal() - 1)) + (9 * (fill.GetNumVal() - 1))
                + (27 * (shape.GetNumVal() - 1));
        return allTiles.get(index);
    }

    /**
     * Return the specified number of random Tiles.
     *
     * @param count The number of Tiles to return
     * @return An array containing the specified number of Tiles
     */
    @NonNull
    public static Tile[] GetRandomTiles(@IntRange(from = 1, to = 81) int count) {
        if (count < 1 || count > 81) {
            throw new IllegalArgumentException("There are only 81 Tiles in the deck");
        }
        Set<Tile> tiles = new HashSet<>(count);
        Tile t;
        while (tiles.size() != count) {
            t = allTiles.get(r.nextInt(81));
            if (!tiles.contains(t)) {
                tiles.add(t);
            }
        }

        return tiles.toArray(new Tile[count]);
    }

    /**
     * Return the specified amount of Tiles using the specified amount of random Tiles
     * <p>
     * Create a Set board with the requested amount of Tiles using the
     * requested amount of random Tiles. The difference will be made up
     * for by manual selection.
     *
     * @param boardSize The size of the board to return
     * @param randomStarters The number of random Tiles to use when creating the board
     * @return A Tile array conforming to the given inputs
     */
    @NonNull
    public static Tile[] GetTilesForBoard(int boardSize, int randomStarters) {
        if (boardSize != 9) {
            throw new IllegalArgumentException("Boards must have nine Tiles.");
        } else if (randomStarters > boardSize) {
            throw new IllegalArgumentException(String.format("Cannot use %d random starters for Board of size %d.",
                    randomStarters, boardSize));
        } else if (randomStarters == 0) {
            return GetRandomTiles(boardSize);
        }

        Set<Tile> maybeBoard = new HashSet<>(boardSize);
        Set<Tile> starters;

        do {
            // Starting with six random Tiles and building the Board
            // to nine yields a more optimal distribution for normal
            // and time attack games, which is why I do it here.
            maybeBoard.clear();
            starters = new HashSet<>(Arrays.asList(Tile.GetRandomTiles(randomStarters)));
            maybeBoard.addAll(starters);

            // Check for Powerset games so they don't have to go
            // through the loop.
            if (maybeBoard.size() == boardSize) {
                break;
            }

            ICombinatoricsVector<Tile> tileVector = Factory.createVector(maybeBoard);
            Generator<Tile> gen = Factory.createSimpleCombinationGenerator(tileVector, 2);
            for (ICombinatoricsVector<Tile> v : gen) {
                Tile t1 = v.getValue(0);
                Tile t2 = v.getValue(1);
                Tile t = SetHelper.GetLastTile(t1, t2);

                if (!maybeBoard.contains(t)) {
                    maybeBoard.add(t);
                }
                if (maybeBoard.size() == boardSize) {
                    break;
                }
            }
            // It's possible there is no way to create a Board of
            // size nine with the selected starters, hence the
            // redundant checks.
        } while (maybeBoard.size() != boardSize);

        return maybeBoard.toArray(new Tile[boardSize]);
    }

    /**
     * Returns the Drawable representing the Tile.
     *
     * @param context The context to retrieve the Tile from
     * @return A Drawable representing the Tile
     */
    @NonNull
    public Drawable GetDrawable(@NonNull Context context) {
        if (context == null) {
            throw new NullPointerException("Context cannot be null.");
        }

        int id = context.getResources().getIdentifier(
                String.format("tile_%d%d%d%d", GetNum(), GetShapeVal(), GetColorVal(), GetFillVal()), "mipmap",
                context.getPackageName());

        return ResourcesCompat.getDrawable(context.getResources(), id, null);
    }

    /**
     * Returns a miniature version of the Drawable representing the Tile.
     *
     * @param context The context to retrieve the Tile from
     * @return A Drawable representing the Tile
     */
    @NonNull
    public Drawable GetSmallDrawable(@NonNull Context context) {
        if (context == null) {
            throw new NullPointerException("Context cannot be null.");
        }

        int id = context.getResources().getIdentifier(
                String.format("tile_small_%d%d%d%d", GetNum(), GetShapeVal(), GetColorVal(), GetFillVal()),
                "mipmap", context.getPackageName());

        return ResourcesCompat.getDrawable(context.getResources(), id, null);
    }

    /**
     * Get the number of items on the Tile
     *
     * @return The number of items on the Tile
     */
    public int GetNum() {
        return num;
    }

    /**
     * Get the Color value of the Tile
     *
     * @return The Color value of the Tile
     */
    @NonNull
    public Color GetColor() {
        return col;
    }

    /**
     * Get the integer value relating to the Tile's Color
     *
     * @return The integer value relating to the Color of the Tile
     */
    public int GetColorVal() {
        return col.GetNumVal();
    }

    /**
     * Get the Fill value of the Tile
     *
     * @return The Fill value of the Tile
     */
    @NonNull
    public Fill GetFill() {
        return fil;
    }

    /**
     * Get the integer value of the Tile's Fill
     *
     * @return The integer value relating to the Fill of the Tile
     */
    public int GetFillVal() {
        return fil.GetNumVal();
    }

    /**
     * Get the Shape value of the Tile
     *
     * @return The Shape value of the Tile
     */
    @NonNull
    public Shape GetShape() {
        return sha;
    }

    /**
     * Get the integer value of the Tile's Shape
     *
     * @return The integer value relating to the Shape of the Tile
     */
    public int GetShapeVal() {
        return sha.GetNumVal();
    }

    /**
     * Given a Tile's string, retrieve the original Tile
     *
     * @param str The String representation of the Tile
     * @return The Tile represented by the string
     */
    @NonNull
    public static Tile fromString(@NonNull @Size(min = 1) String str) {
        if (str == null) {
            throw new NullPointerException("Tile String cannot be null.");
        } else if (str.isEmpty()) {
            throw new IllegalArgumentException("Tile String cannot be empty.");
        } else if (!str.matches("[123] (Red|Green|Purple) (Filled|Dashed|Hollow) (Oval|Diamond|Squiggle)")) {
            throw new IllegalArgumentException("Entered string not valid Tile");
        }

        String[] parts = str.split(" ");
        return Tile.GetTile(Integer.parseInt(parts[0]), Color.valueOf(parts[1]), Fill.valueOf(parts[2]),
                Shape.valueOf(parts[3]));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return String.format(Locale.US, "%d %s %s %s", num, col.toString(), fil.toString(), sha.toString());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(@NonNull Object o) {
        if (o == null) {
            return false;
        } else if (o == this) {
            return true;
        } else if (!(o instanceof Tile)) {
            throw new IllegalArgumentException("Object entered is not Tile");
        }

        Tile t = (Tile) o;
        if (t.num != num) {
            return false;
        }
        if (t.col != col) {
            return false;
        }
        if (t.fil != fil) {
            return false;
        }
        if (t.sha != sha) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return (num - 1) + (3 * (col.GetNumVal() - 1)) + (9 * (fil.GetNumVal() - 1)) + (27 * (sha.GetNumVal() - 1));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int compareTo(@NonNull Tile t) {
        if (t == null) {
            throw new NullPointerException("Comparison object cannot be null.");
        }

        if (this.hashCode() == t.hashCode()) {
            return 0;
        } else if (this.hashCode() < t.hashCode()) {
            return -1;
        } else {
            return 1;
        }
    }
}