/** Play variations of Chuck-a-luck or Risk. (Answers to Exercises 1-5.)
*
* 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
*
* Chuck-a-luck or Risk includes the answers to Exercises 1-4 that were part of
* the previous version of the program and adds an answer to Exercise 5.
*
* A PROCEDURE FOR ADDING NEW GAMES
*
* 1. Choose an available (not already in use) game key (a String value) that
* will be used to identify the new game. Game keys currently in use include
* "[Alt-]Chuck-a-luck", "Risk[-Shot|-Shootout]", and "RiskiKo![-Shot|-Shootout]".
* 2. Dice games need one or more sets of dice. Determine what dice your game
* needs and make sure they are available:
* 2.1. Check to see whether the CarnivalJoint class already contains
* sets of dice that can be used by the new game. If the dice you
* need are already available, you might want to use the ones that
* are already there.
* 2.2. If the dice your games needs are not defined yet, then create them.
* If necessary, create new dice types. Then add instances of the dice
* that your game will use to the CarnivalJoint class.
* 3. Modify the CarnivalJoint.getDiceGame method so that it returns an object
* representing an instance of the new game when the game's game key is
* passed as the argument to CarnivalJoint.getDiceGame.
* 4. If you are practicing top-down programming, you will now have a program
* that does not compile, because the changes you made in the previous step
* include the invocation of a constructor method of a class that does not
* yet exist. Create the missing class now.
* 4.1. If you anticipate creating more than one variation of your game,
* then you might want to consider creating an abstract class in which
* you define the methods and values that are common to all instances
* of the game.
* 4.2. The class you create should implement the CarnivalDiceGame interface
* so that the object returned by CarnivalJoint.getDiceGame can be used as
* the argument when the Carny class's constructor method is called i
* CarnivalJoint.main.
* 4.2.1. Create a constructor method (or methods) that are defined in
* terms of the dice set(s) needed by your game, and initialize
* private instance variables using the value(s) passed to the
* constructor method(s).
* 4.2.2. Implement the methods needed to implement the CarnivalDiceGame
* interface, which are the methods needed to implement the
* CarnivalGame and DiceGame interfaces.
* 5. Document your changes.
* 5.1. Update CarnivalJoint.help.
* 5.2. Make sure the source code looks at least as good as how you found it.
* Comment new code judiciously.
* 6. Test your changes.
* 6.1. Test the new game.
* 6.2. Test the previously existing games to make sure you didn't break them in
* the process of adding the new game.
* 7. Show off your new game!
* 7.1. Show off your game to friends and family.
* 7.2. Send a copy of your program's source code to jspurgeon@vcstudent.org.
*/
import java.util.*;
import java.util.concurrent.TimeUnit;
/* 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
{
/** Advertises games or conducts one.
*/
public static void main(String args[]) {
if (args.length == 0)
System.out.println(advert);
else
getCarny(args[0]).setupGame(args).conductGame().rewardWinners();
}
private static String advert =
"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): Game configuration values (e.g. wagers).\n" +
"\n" +
"GAME KEY EXAMPLES\n" +
"\n" +
"Chuck-a-luck: Typical chuck-a-luck using regular six-sided dice.\n" +
"Alt-Chuck-a-luck: Alternate chuck-a-luck (six-sided dice).\n" +
"Chuck-a-luck-12: Chuck-a-luck using 12-sided dice (dodecahedrons).\n" +
"Triple-Threes: Typical Triple Threes game with five dice.\n" +
"\n" +
"COMMAND LINE ARGUMENTS EXAMPLES\n" +
"\n" +
"Chuck-a-luck-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" +
"\n" +
"Risk-Shootout 20 20 -1 No-Dice\n" +
"\n" +
"A game of no-limit, no-dice Risk-Shootout where both sides start with 20 dice.\n" +
" args[0]: Game key (required).\n" +
" args[1]: Attacker's army count (required).\n" +
" args[2]: Defender's army count (required).\n" +
" args[3]: Max number of shots (optional; negative means no-limit).\n" +
" args[args.length -1]: \"No-Dice\" (optional; means do not print dice).\n" +
"\n" +
"RisiKo!-Shot 3 3\n" +
"\n" +
"Attacker rolls 3; defender rolls 3; outputs number of armies lost by whom.\n" +
"\n" +
"Triple-Threes (wager)\n" +
"\n" +
"Roll five dice; player gains one point for each 3 rolled; if no 3s were rolled, player loses;\n" +
"if at least one 3 was rolled, player rolls again; player gains 3 points to win.\n" +
"Special rules: If the player rolls three 3s on the first roll, the reward is tripled\n" +
" If the player rolls five 3s on the first roll, the reward is five times the wager.";
;
/* The joint's dice sets. */
private static final Die[] twelveSidedDiceCage = {
new Dodecahedron(),
new Dodecahedron(),
new Dodecahedron()
};
private static final Die[] sixSidedDiceCage = {
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie()
};
private static final Die[] redRiskDice = {
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie()
};
private static final Die[] whiteRiskDice = {
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie()
};
private static final Die[] fiveDice = {
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie(),
new FairSixSidedAsciiDie()
};
private static Croupier getCarny(String key) {
final CarnivalDiceGame game = getDiceGame(key);
System.out.println(key + ": " + game);
if (key.equals("Risk-Shootout") || key.equals("RisiKo!-Shootout"))
return new RiskyCarny((RiskShot)game);
else if (key.equals("Triple-Threes")) {
return new TripleCarny((TripleThrees)game);
}
else if (key.equals("3s")) {
return new TripleCarny((TripleThrees)game);
}
else
return new Carny(game);
}
/** Given a game key, returns a game or throws an exception. */
private static CarnivalDiceGame getDiceGame(String key) {
// Risk[-Shot] or RisiKo![-Shot]
if (key.indexOf("Risk") >=0)
return new StandardRiskShot(redRiskDice, whiteRiskDice);
if (key.indexOf("RisiKo!") >= 0)
return new RisiKoShot(redRiskDice, whiteRiskDice);
// Chuck-a-luck or Alternate Chuck-a-luck
final Die[] diceCage = (key.indexOf("12") >= 0) ?
twelveSidedDiceCage : sixSidedDiceCage;
if (key.indexOf("Alt-Chuck-a-luck") >= 0)
return new AlternateChuckALuck(diceCage);
if (key.indexOf("Chuck-a-luck") >= 0)
return new ChuckALuck(diceCage);
// New added game of Triple-Threes
if (key.indexOf("Triple-Threes") >= 0)
return new TripleThrees(fiveDice);
if (key.indexOf("3s") >= 0)
return new TripleThrees(fiveDice);
// Bad game key.
throw new IllegalArgumentException("Invalid game key:" + key);
}
}
/**************/
/* INTERFACES */
/**************/
/* 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
*/
interface Croupier
{
Croupier setupGame(String[] inputs);
Croupier conductGame();
Croupier rewardWinners();
}
interface CarnivalGame
{
CarnivalGame processInputs(String[] inputs);
CarnivalGame printResults();
}
interface DiceGame
{
DiceGame analyzeDice();
DiceGame rollDice();
DiceGame showDice();
}
interface CarnivalDiceGame extends CarnivalGame, DiceGame
{
}
interface Die
{
Die roll();
Integer valueOf();
}
/********************/
/* CARNIVAL WORKERS */
/********************/
/* 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
*/
/** A Croupier that oversees a game.
*/
final class Carny implements Croupier
{
private final CarnivalDiceGame game;
public Carny(CarnivalDiceGame game) {
this.game = game;
}
public Carny setupGame(String[] inputs) {
game.processInputs(inputs);
return this;
}
public Carny conductGame() {
game.rollDice().showDice().analyzeDice();
return this;
}
public Carny rewardWinners() {
game.printResults();
return this;
}
}
/** A Croupier that oversees a game of Risk or RisiKo! in which players duel
* until the defender is defeated or the attacker can no longer attack.
*/
final class RiskyCarny implements Croupier
{
private static final String NO_DICE = "No-Dice";
private final RiskShot game;
private int offensiveArmies;
private int defensiveArmies;
private String[] gameInputs = new String[3];
private boolean showDice;
private int remainingShots = -1;
public RiskyCarny(RiskShot riskyGame) {
game = riskyGame;
}
public RiskyCarny setupGame(String[] inputs) {
gameInputs[0] = inputs[0];
offensiveArmies = Integer.parseInt(inputs[1]);
defensiveArmies = Integer.parseInt(inputs[2]);
showDice = !inputs[inputs.length - 1].equals(NO_DICE);
if (inputs.length > 3 && !inputs[3].equals(NO_DICE))
remainingShots = Integer.parseInt(inputs[3]);
return this;
}
public RiskyCarny conductGame() {
while (remainingShots-- != 0 && offensiveArmies > 1 && defensiveArmies > 0) {
prepareGameInputs();
game.processInputs(gameInputs).rollDice();
if (showDice) game.showDice();
game.analyzeDice().printResults();
offensiveArmies -= game.getOffensiveCasualties();
defensiveArmies -= game.getDefensiveCasualties();
System.out.print("Attacker's armies remaining: ");
System.out.println(offensiveArmies);
System.out.print("Defender's armies remaining: ");
System.out.println(defensiveArmies);
}
return this;
}
public RiskyCarny rewardWinners() {
return this;
}
private void prepareGameInputs() {
gameInputs[1] = Integer.toString(Math.min(game.maxRedDice, offensiveArmies - 1));
System.out.println("Attacker rolls " + gameInputs[1]);
gameInputs[2] = Integer.toString(Math.min(game.maxWhiteDice, defensiveArmies));
System.out.println("Defender rolls " + gameInputs[2]);
}
}
final class TripleCarny implements Croupier
{
private final CarnivalDiceGame game;
public TripleCarny(CarnivalDiceGame game) {
this.game = game;
}
public TripleCarny setupGame(String[] inputs) {
game.processInputs(inputs);
return this;
}
public TripleCarny conductGame() {
while (AbstractTripleThrees.rollAgain == true && AbstractTripleThrees.score < 3) {
game.rollDice().showDice().analyzeDice();
}
return this;
}
public TripleCarny rewardWinners() {
game.printResults();
return this;
}
}
/*********/
/* GAMES */
/*********/
/** A game of chance involving a single roll of two sets of dice similar to a roll used to
* determine the units lost in round of a battle in variations of the board game Risk.
* Inputs are the numbers of dice rolled by an attacker and a defender. The output is the
* dice rolled grouped by attacker and defender, sorted by value, and the number of armies
* lost and by either the attacker or the defender or both.
*
* Risk is a strategy board game of diplomacy, conflict and conquest for two to six players.
* The standard version is played on a board depicting a political map of Earth, divided into
* forty-two territories, which are grouped into six continents. Turn rotates among players
* who control armies of playing pieces with which they attempt to capture territories from
* other players, with results determined by dice rolls.
*
* See: https://en.wikipedia.org/wiki/Risk_(game)
*/
abstract class AbstractTripleThrees implements CarnivalDiceGame
{
public static boolean rollAgain = true;
public static int score = 0;
protected int roll = 1;
protected final Die[] dice;
protected final List<String> results = new LinkedList<String>();
private String[] inputs;
public AbstractTripleThrees(Die[] dice) {
if (dice.length != 5) {
throw new IllegalArgumentException("Wrong number of dice.");
}
this.dice = dice;
}
public abstract AbstractTripleThrees analyzeDice();
public AbstractTripleThrees printResults() {
for (int i = 1; i < inputs.length; i++) {
String bet = inputs[i];
if ((score == 5) && (roll == 2)) {
System.out.println("You win $" + (Integer.parseInt(bet) * 5));
}
else if ((score >= 3) && (roll == 2)) {
System.out.println("You win $" + (Integer.parseInt(bet) * 3));
}
else if (score >= 3) {
System.out.println("You win $" + bet);
}
else {
System.out.println("You lose $" + bet);
}
}
return this;
}
public AbstractTripleThrees processInputs(String[] inputs) {
this.inputs = inputs;
return this;
}
public AbstractTripleThrees rollDice() {
for (Die die : dice) die.roll();
return this;
}
public AbstractTripleThrees showDice() {
System.out.println("\nRoll " + roll + ":");
roll++;
for (Die die : dice) System.out.println(die);
return this;
}
}
class TripleThrees extends AbstractTripleThrees
{
public TripleThrees(Die[] dice) {
super(dice);
}
public TripleThrees analyzeDice() {
rollAgain = false;
boolean check = false;
for (int i = 0; i < 5; i++) {
int value = dice[i].valueOf();
if (value == 3) {
score++;
check = true;
}
}
if (check == true) rollAgain = true;
results.clear();
return this;
}
public String toString() {
return "Three threes for the win!";
}
}
abstract class RiskShot implements CarnivalDiceGame
{
public final int maxRedDice;
public final int maxWhiteDice;
private final Die[] redDice;
private final Die[] whiteDice;
protected final List<Die> attackingDice = new LinkedList<Die>();
protected final List<Die> defendingDice = new LinkedList<Die>();
protected int offensiveCasualties = 0;
protected int defensiveCasualties = 0;
protected RiskShot(Die[] redDice, int maxRedDice, Die[] whiteDice, int maxWhiteDice) {
if (redDice.length < maxRedDice) {
throw new IllegalArgumentException("Too few red dice.");
}
if (whiteDice.length < maxWhiteDice) {
throw new IllegalArgumentException("Too few white dice.");
}
this.redDice = redDice;
this.whiteDice = whiteDice;
this.maxRedDice = maxRedDice;
this.maxWhiteDice = maxWhiteDice;
}
public abstract RiskShot analyzeDice();
public RiskShot printResults() {
reportCasualties("Attacker", offensiveCasualties);
reportCasualties("Defender", defensiveCasualties);
return this;
}
public int getOffensiveCasualties() {
return offensiveCasualties;
}
public int getDefensiveCasualties() {
return defensiveCasualties;
}
public RiskShot processInputs(String[] inputs) {
if (inputs.length < 3) {
throw new IllegalArgumentException("Too few inputs.");
}
selectDice(Integer.parseInt(inputs[1]), Integer.parseInt(inputs[2]));
return this;
}
public RiskShot rollDice() {
for (Die redDie : attackingDice) {
redDie.roll();
}
Collections.sort(attackingDice, AbstractDie.descendingComparator);
for (Die whiteDie : defendingDice) {
whiteDie.roll();
}
Collections.sort(defendingDice, AbstractDie.descendingComparator);
return this;
}
public RiskShot showDice() {
System.out.println("Attacking (Red)");
for (Die redDie : attackingDice) {
System.out.println(redDie);
}
System.out.println("Defending (White)");
for (Die whiteDie : defendingDice) {
System.out.println(whiteDie);
}
return this;
}
public String toString() {
return "Shots fired in a game of Risk.";
}
private void selectDice(final int attackWager, final int defendWager) {
verifyWagers(attackWager, defendWager);
attackingDice.clear();
for (int i = 0; i < attackWager; i++) {
attackingDice.add(redDice[i]);
}
defendingDice.clear();
for (int i = 0; i < defendWager; i++) {
defendingDice.add(whiteDice[i]);
}
}
private void verifyWagers(final int attackWager, final int defendWager) {
if (attackWager < 1 || attackWager > maxRedDice) {
throw new IllegalArgumentException("Offensive wager out of range: " + attackWager);
}
if (defendWager < 1 || defendWager > maxWhiteDice) {
throw new IllegalArgumentException("Defensive wager out of range: " + defendWager);
}
}
private static void reportCasualties(final String who, final int count) {
if (count == 0) return;
System.out.print(who + " loses " + count + " arm");
System.out.println(count == 1 ? "y." : "ies.");
}
}
/** An instance of a RiskShot game that adopts rules of a standard game of Risk.
*
* Defenders always win ties when dice are rolled. This gives the defending player the advantage
* in "one-on-one" fights, but the attacker's ability to use more dice offsets this advantage.
* It is always advantageous to roll the maximum number of dice, unless an attacker wishes to
* avoid moving men into a 'dead-end' territory, in which case they may choose to roll fewer than
* three. Thus when rolling three dice against two, three against one, or two against one, the
* attacker has a slight advantage, otherwise the defender has an advantage.
*
* See also: https://en.wikipedia.org/wiki/Risk_(game)
*/
class StandardRiskShot extends RiskShot
{
protected StandardRiskShot(Die[] redDice, int maxRedDice, Die[] whiteDice, int maxWhiteDice) {
super(redDice, maxRedDice, whiteDice, maxWhiteDice);
}
public StandardRiskShot(Die[] redDice, Die[] whiteDice) {
this(redDice, 3, whiteDice, 2);
}
/**
* Precondition: Wagers have been made, dice have been rolled, and the
* attacking and defending dice have been arranged in order from largest
* to smallest value.
*/
public StandardRiskShot analyzeDice() {
defensiveCasualties = 0;
offensiveCasualties = 0;
final int n = attackingDice.size();
final int m = defendingDice.size();
for (int i = 0; i < n && i < m; i++) {
if (attackingDice.get(i).valueOf() > defendingDice.get(i).valueOf()) {
defensiveCasualties++;
}
else {
offensiveCasualties++;
}
}
return this;
}
}
/** An instance of a RiskShot game that adopts rules of RisiKo! (a variation of Risk).
*
* RisiKo! derives from the 1957 French game La ConquĂȘte du Monde, better known worldwide as Risk.
* RisiKo! is a variant of the game released in Italy, in which the defender is allowed to roll up
* to three dice to defend. This variation dramatically shifts the balance of power towards defense.
*
* See: https://en.wikipedia.org/wiki/RisiKo!
* See also: https://en.wikipedia.org/wiki/Risk_(game)
*/
class RisiKoShot extends StandardRiskShot
{
public RisiKoShot(Die[] redDice, Die[] whiteDice) {
super(redDice, 3, whiteDice, 3);
}
public String toString() {
return "Shots fired in a game of RisiKo!";
}
}
/** A partial simulation of the dice game Chuck-a-luck that determines winners
* but does not compute gains or losses based on odds.
*
* 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 AbstractChuckALuck 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[] inputs;
public AbstractChuckALuck(Die[] dice) {
if (dice.length != 3) {
throw new IllegalArgumentException("Wrong number of dice.");
}
this.dice = dice;
}
public abstract AbstractChuckALuck analyzeDice();
public AbstractChuckALuck printResults() {
for (int i = 1; i < inputs.length; i++) {
String bet = inputs[i];
if (results.indexOf(bet) >= 0) {
System.out.println("Number " + i + " wagered " + bet + " and wins!");
}
}
return this;
}
public AbstractChuckALuck processInputs(String[] inputs) {
this.inputs = inputs;
return this;
}
public AbstractChuckALuck rollDice() {
for (Die die : dice) die.roll();
return this;
}
public AbstractChuckALuck showDice() {
for (Die die : dice) System.out.println(die);
return this;
}
}
/** An instance of AbstractChuckALuck that uses typical rules.
*
* WAGER DESCRIPTION
* Big The total score will be 11 or higher and not a triple.
* Small The total score will be 10 or lower and not a triple.
*/
class ChuckALuck extends AbstractChuckALuck {
public ChuckALuck(Die[] dice) {
super(dice);
}
public ChuckALuck 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";
}
}
/** An instance of AbstractChuckALuck that uses alternate rules.
*
* WAGER DESCRIPTION
* Big The total score will be 12 or higher and not a triple.
* Small The total score will be 9 or lower and not a triple.
*/
class AlternateChuckALuck extends AbstractChuckALuck {
public AlternateChuckALuck(Die[] dice) {
super(dice);
}
public AlternateChuckALuck 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";
}
}
/********/
/* DICE */
/********/
class AscendingDiceComparator implements Comparator<Die>
{
public final int compare(Die d1, Die d2) { return d1.valueOf().compareTo(d2.valueOf()); }
}
class DescendingDiceComparator implements Comparator<Die>
{
public final int compare(Die d1, Die d2) { return -1 * d1.valueOf().compareTo(d2.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
{
public static Comparator<Die> ascendingComparator = new AscendingDiceComparator();
public static Comparator<Die> descendingComparator = new DescendingDiceComparator();
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];
}
}