Chuck-a-luck Answers

/** Play variations of Chuck-a-luck. (Answers to Exercises 1-4.)
 *
 *  AUTHORS
 *
 *    Original: John P. Spurgeon with support from Akil, Jonathan, Kai, Katherine, and Leon.
 *    This version: John P. Spurgeon.
 *
 *  VERSION LINEAGE
 *
 *    0. https://apcsafreeresponsequestions.blogspot.com/2019/02/original-chuck-a-luck.html. (ORIGINAL)
 *    1. https://apcsafreeresponsequestions.blogspot.com/2019/02/chuck-a-luck.html. (EXERCISES)
 *    2. https://apcsafreeresponsequestions.blogspot.com/2019/02/chuck-a-luck-answers.html. (ANSWERS TO EXERCISES 1-4)
 *    3. https://apcsafreeresponsequestions.blogspot.com/2019/02/chuck-a-luck-or-risk.html. (ANSWERS TO EXERCISES 1-5)
 *
 *  DESCRIPTION
 *
 *    This version of Chuck-a-luck demonstrates possible answers to Exercises 1-4, which
 *    were introduced in the previous version.
 */
import java.util.*;

/********/
/* DICE */
/********/

interface Die
{
    Die roll();
    Integer valueOf();
}

class AsciiDie3x7 {
    public static final String[] sides = {
        "---------\n" +
        "|       |\n" +
        "|   *   |\n" +
        "|       |\n" +
        "---------"
        ,
        "---------\n" +
        "| *     |\n" +
        "|       |\n" +
        "|     * |\n" +
        "---------"
        ,
        "---------\n" +
        "| *     |\n" +
        "|   *   |\n" +
        "|     * |\n" +
        "---------"
        ,
        "---------\n" +
        "| *   * |\n" +
        "|       |\n" +
        "| *   * |\n" +
        "---------"
        ,
        "---------\n" +
        "| *   * |\n" +
        "|   *   |\n" +
        "| *   * |\n" +
        "---------"
        ,
        "---------\n" +
        "| *   * |\n" +
        "| *   * |\n" +
        "| *   * |\n" +
        "---------"
        ,
        "---------\n" +
        "| *   * |\n" +
        "| * * * |\n" +
        "| *   * |\n" +
        "---------"
        ,
        "---------\n" +
        "| * * * |\n" +
        "| *   * |\n" +
        "| * * * |\n" +
        "---------"
        ,
        "---------\n" +
        "| * * * |\n" +
        "| * * * |\n" +
        "| * * * |\n" +
        "---------"
        ,
        "---------\n" +
        "| ***** |\n" +
        "|       |\n" +
        "| ***** |\n" +
        "---------"
        ,
        "---------\n" +
        "| ***** |\n" +
        "|   *   |\n" +
        "| ***** |\n" +
        "---------"
        ,
        "---------\n" +
        "| ***** |\n" +
        "| *   * |\n" +
        "| ***** |\n" +
        "---------"
        ,
        "---------\n" +
        "| ***** |\n" +
        "| * * * |\n" +
        "| ***** |\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|       |\n" +
        "|*******|\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|   *   |\n" +
        "|*******|\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|*     *|\n" +
        "|*******|\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|*  *  *|\n" +
        "|*******|\n" +
        "---------"
        ,
        "---------\n" +
        "|*** ***|\n" +
        "|*** ***|\n" +
        "|*** ***|\n" +
        "---------"
        ,
        "---------\n" +
        "|*** ***|\n" +
        "|*******|\n" +
        "|*** ***|\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|*** ***|\n" +
        "|*******|\n" +
        "---------"
        ,
        "---------\n" +
        "|*******|\n" +
        "|*******|\n" +
        "|*******|\n" +
        "---------"
    };
}

abstract class AbstractDie implements Die
{
    private Integer value;

    protected AbstractDie() {
        value = nextValue();
    }
    public abstract int numberOfSides();
    public final Die roll() {
        value = nextValue();
        return this;
    }
    public String toString() {
        return value.toString();
    }
    public final Integer valueOf() {
        return value;
    }
    protected abstract int nextValue();
}

abstract class AbstractFairDie extends AbstractDie
{
    protected final int nextValue() {
        return (int)(1 + Math.random() * numberOfSides());
    }
}

class FairSixSidedDie extends AbstractFairDie
{
    public int numberOfSides() { return 6; }
}

class FairSixSidedAsciiDie extends FairSixSidedDie
{
    public String toString() {
        return AsciiDie3x7.sides[valueOf() - 1];
    }
}

class Dodecahedron extends AbstractFairDie
{
    public int numberOfSides() { return 12; }
    public String toString() {
        return AsciiDie3x7.sides[valueOf() - 1];
    }
}

/******************/
/* CARNIVAL GAMES */
/******************/

interface CarnivalGame
{
    CarnivalGame recordBets(String[] bets);
    void announceWinners();
}

interface DiceGame
{
    DiceGame analyzeDice();
    DiceGame rollDice();
    DiceGame showDice();
}

interface CarnivalDiceGame extends CarnivalGame, DiceGame
{
}

/** Chuck-a-luck, also known as birdcage, is a game of chance played with three
 *  dice. It is derived from grand hazard and both can be considered a variant
 *  of sic bo, which is a popular casino game, although chuck-a-luck is more of
 *  a carnival game than a true casino game. The game is sometimes used as a
 *  fundraiser for charity.
 *
 *  Chuck-a-luck is played with three standard dice that are kept in a device
 *  shaped somewhat like an hourglass that resembles a wire-frame bird cage
 *  and pivots about its centre. The dealer rotates the cage end over end, with
 *  the dice landing on the bottom. Wagers are placed based on possible
 *  combinations that can appear on the three dice.
 *
 *  WAGER   DESCRIPTION
 *  n       A specific number will appear. (Known as "Single".)
 *  Triple  Any of the triples (all three dice show the same number) will appear.
 *  Big     The total score will be 11 (alternatively 12) or higher and not a triple.
 *  Small   The total score will be 10 (alternatively 9) or lower and not a triple.
 *  Field   The total score will be outside the range of 8 to 12 (inclusive).
 *
 *  Source: https://en.wikipedia.org/wiki/Chuck-a-luck
 */
abstract class ChuckALuckGame implements CarnivalDiceGame
{
    public static final String
        Triple = "Triple", Big = "Big", Small = "Small", Field = "Field";

    protected final Die[] dice;
    protected final List<String> results = new LinkedList<String>();
    private String[] bets;

    public ChuckALuckGame(Die[] dice) {
        if (dice.length != 3) {
            throw new IllegalArgumentException("Wrong number of dice.");
        }
        this.dice = dice;
    }
    public abstract ChuckALuckGame analyzeDice();
    public void announceWinners() {
        for (int i = 1; i < bets.length; i++) {
            String bet = bets[i];
            if (results.indexOf(bet) >= 0) {
                System.out.println("Bet " + i + " (" + bet + ") wins!");
            }
        }
    }
    public ChuckALuckGame recordBets(String[] bets) {
        this.bets = bets;
        return this;
    }
    public ChuckALuckGame rollDice() {
        for (Die die : dice) die.roll();
        return this;
    }
    public ChuckALuckGame showDice() {
        for (Die die : dice) System.out.println(die);
        return this;
    }
}

/** Chuck-a-luck game that uses typical rules. */
class ChuckALuck extends ChuckALuckGame {
    public ChuckALuck(Die[] dice) {
        super(dice);
    }
    public ChuckALuckGame analyzeDice() {
        final Integer
          v1 = dice[0].valueOf(),
          v2 = dice[1].valueOf(),
          v3 = dice[2].valueOf();

        final int sum = v1 + v2 + v3;
        results.clear();

        // For "Single" wagers.
        results.add(v1.toString());
        results.add(v2.toString());
        results.add(v3.toString());

        if (v1 == v2 && v2 == v3) {
            results.add(Triple);
        }
        else if (sum >= 11) {
            results.add(Big);
        }
        else if (sum <= 10 ) {
            results.add(Small);
        }
        if (sum < 8 || sum > 12) {
            results.add(Field);
        }
        return this;
    }
    public String toString() {
        return "Chuck-a-Luck";
    }
}

/** Chuck-a-luck game that uses alternate rules. */
class AlternateChuckALuck extends ChuckALuckGame {
    public AlternateChuckALuck(Die[] dice) {
        super(dice);
    }
    public ChuckALuckGame analyzeDice() {
        final Integer
          v1 = dice[0].valueOf(),
          v2 = dice[1].valueOf(),
          v3 = dice[2].valueOf();

        final int sum = v1 + v2 + v3;
        results.clear();

        // For "Single" wagers.
        results.add(v1.toString());
        results.add(v2.toString());
        results.add(v3.toString());

        if (v1 == v2 && v2 == v3) {
            results.add(Triple);
        }
        else if (sum >= 12) { // ALTERNATE RULE
            results.add(Big);
        }
        else if (sum <= 9 ) { // ALTERNATE RULE
            results.add(Small);
        }
        if (sum < 8 || sum > 12) {
            results.add(Field);
        }
        return this;
    }
    public String toString() {
        return "Alternate Chuck-a-Luck";
    }
}

/********************/
/* CARNIVAL WORKERS */
/********************/

/** A croupier is someone appointed at a gambling table to assist in the conduct of the game,
 *  especially in the distribution of bets and payouts. Croupiers are typically employed by
 *  casinos. Originally a "croupier" meant one who stood behind a gambler, with extra reserves
 *  of cash to back him up during a gambling session. The word derived from "croup" (the rump
 *  of a horse) and was by way of analogy to one who rode behind on horseback. It later came to
 *  refer to one who was employed to collect the money from a gaming-table.
 *
 *  Source: https://en.wikipedia.org/wiki/Croupier
 */
final class Croupier
{
    private CarnivalDiceGame game;
    public Croupier(CarnivalDiceGame game) {
        this.game = game;
    }
    public void acceptBets(String[] bets) {
        game.recordBets(bets);
    }
    public void payWinners() {
        game.announceWinners();
    }
    public void conductGame() {
        game.rollDice();
        game.showDice();
        game.analyzeDice();
    }
}

/***************/
/* THE PROGRAM */
/***************/

/** In North America "joint" refers to any kind of carnival stand. In other parts of
 *  the world, it is a slang term for a gathering place. On the fairground it is used
 *  as a generic term for any form of portable sideshow of the ground booth variety.
 *  Walkup shows, which have raised platforms for the performers, are not so described.
 *
 *  Source: https://conklinshows.com/info/dictionary/j.html
 */
public class CarnivalJoint
{
    private static String help =
      "Play a single round of a dice game that accepts wagers.\n" +
      "\n" +
      "COMMAND LINE ARGUMENTS\n" +
      "\n" +
      "args[0]: Game key; selects a game; default is Chuck-a-luck.\n" +
      "args[i] (i > 0): Zero or more game-specific wagers.\n" +
      "\n" +
      "GAME KEY EXAMPLES\n" +
      "\n" +
      "CAL, CAL6, CAL-6: Typical chuck-a-luck using regular six-sided dice.\n" +
      "ACAL, ACAL-6: Alternate chuck-a-luck (six-sided dice).\n" +
      "CAL12, ACAL-12: Chuck-a-luck using 12-sided dice (dodecahedrons).\n" +
      "\n" +
      "COMMAND LINE ARGUMENTS EXAMPLE\n" +
      "\n" +
      "CAL-6 Big Triple 12 24\n" +
      "\n" +
      "Typical chuck-a-luck using six-sided dice, wagering that the sum of the\n" +
      "values shown on each die will be a 'Big' value or a 'Triple' or 12 or 24.\n";

    private static final Die[] twelveSidedDiceCage = {
        new Dodecahedron(),
        new Dodecahedron(),
        new Dodecahedron()
    };
    private static final Die[] sixSidedDiceCage = {
        new FairSixSidedAsciiDie(),
        new FairSixSidedAsciiDie(),
        new FairSixSidedAsciiDie()
    };
    private static CarnivalDiceGame getDiceGame(String key) {
        final Die[] diceCage = (key.indexOf("12") >= 0) ?
            twelveSidedDiceCage : sixSidedDiceCage;
        if (key.indexOf("ACAL") >= 0)
            return new AlternateChuckALuck(diceCage);
        else
            return new ChuckALuck(diceCage);
    }
    public static void main(String args[]) {

        if (args.length == 0) {
            System.out.println(help);
            return;
        }

        final String gameKey = args[0];
        final CarnivalDiceGame game = getDiceGame(gameKey);

        /* Carny, also spelled carnie, is an informal term used in North America
         * for a traveling carnival employee, and the language they use, particularly
         * when the employee plays a game ("joint"), food stand ("grab" or "popper"),
         * or ride at a carnival. The term "showie" is used synonymously in Australia.
         * Carny is thought to have become popularized around 1931 in North America,
         * when it was first colloquially used to describe one who works at a carnival.
         * The word carnival, originally meaning a "time of merrymaking before Lent,"
         * came into use circa 1549.
         *
         *  Source: https://en.wikipedia.org/wiki/Carny
         */
        final Croupier carny = new Croupier(game);

        System.out.println(gameKey + ": " + game);
        carny.acceptBets(args);
        carny.conductGame();
        carny.payWinners();
    }
}