The complete specification of assignment #5 can be found as part of the stream at iTunes.
Beef up your Yahtzee to the variant called “Triple Yahtzee.”
In this variant, each player manages three simultaneous scorecard columns, as if they were playing for three players at once. The player can assign a roll to any one of their three columns. All entries are scored normally with respect to categories and validity, but the values in the second column are doubled, and the third column values are tripled. The player’s score consists of the sum of all three columns. This would make for a three- dimensional array (an array of players who have any array of columns which are an array of score entries)—pretty tricky! Game play continues for 3*13 rounds, until all players have filled in all entries in all three columns. The player with the highest total score is the winner
The display needs to additional cols with suiting names to be able to hold the additional scores:
private void initDisplay() { String[] colNames = new String[nPlayers * COLS_PER_PLAYER]; for (int i = 0; i < nPlayers; i++) { for (int j = 0; j < COLS_PER_PLAYER; j++) { colNames[i * COLS_PER_PLAYER + j] = playerNames[i] + "*" + (j + 1); } } display = new YahtzeeDisplay(getGCanvas(), colNames); }
The arrays need an additional dimension and have to be instantiated accordingly. The number of rounds increases:
private boolean[][][] usedCategories; private int[][] upperScore; private int[][] lowerScore; private int[][] totalScore; private boolean[][] allowAdditionalYahtzees; private void playGame() { usedCategories = new boolean[nPlayers][COLS_PER_PLAYER][N_CATEGORIES]; upperScore = new int[nPlayers][COLS_PER_PLAYER]; lowerScore = new int[nPlayers][COLS_PER_PLAYER]; totalScore = new int[nPlayers][COLS_PER_PLAYER]; allowAdditionalYahtzees = new boolean[nPlayers][COLS_PER_PLAYER]; for (int player = 0; player < nPlayers; player++) { for (int col = 0; col < COLS_PER_PLAYER; col++) { allowAdditionalYahtzees[player][col] = true; } } for (int round = 0; round < N_SCORING_CATEGORIES * COLS_PER_PLAYER; round++) { .... }
When the dice are rolled the first time, the column of the current user is marked. Now mark the first column of the current user instead:
private void firstRoll(int player) { .... display.waitForPlayerToClickRoll(player * COLS_PER_PLAYER); .... }
To know which column should be used the horizontal position of the mouse has to be checked when a category is selected. This then can be used to select the additional dimension of the arrays, and to find the correct column to fill in the scores – which also need adjustment depending on the column. Instead of setting only the chosen score column, the total of all columns is updated, because this removes the mark which indicates the current user.
private int xMouseClick; public void mousePressed(MouseEvent e) { xMouseClick = e.getX(); } private void updateScore(int player) { int category; int col; addMouseListeners(); while (true) { display.printMessage("Select a category for this roll."); category = display.waitForPlayerToSelectCategory(); col = COLS_PER_PLAYER - 1; for(int i = 0; i < COLS_PER_PLAYER; i++) { if (xMouseClick <= X_FIRST_COL + X_SIZE_COL * (1 + i + COLS_PER_PLAYER * player)) { col = i; break; } } if (!usedCategories[player][col][category]) break; } usedCategories[player][col][category] = true; int score = calculateScore(category) * (col + 1); if (category == YAHTZEE) { if (score == 0) { allowAdditionalYahtzees[player][col] = false; } } else { if (allowAdditionalYahtzees[player][col] && usedCategories[player][col][YAHTZEE] && (calculateOfAKindValues(5) > 0)) { score += SCORE_ADDITONAL_YAHTZEES; } } int scoreIndex = col + player * COLS_PER_PLAYER; display.updateScorecard(category, scoreIndex, score); if (category < UPPER_SCORE) { upperScore[player][col] += score; display.updateScorecard(UPPER_SCORE, scoreIndex, upperScore[player][col]); } else { lowerScore[player][col] += score; display.updateScorecard(LOWER_SCORE, scoreIndex, lowerScore[player][col]); } totalScore[player][col] = upperScore[player][col] + lowerScore[player][col]; if (upperScore[player][col] >= SCORE_UPPER_BONUS_LIMIT * (col + 1)) { display.updateScorecard(UPPER_BONUS, scoreIndex, SCORE_UPPER_BONUS * (col + 1)); totalScore[player][col] += SCORE_UPPER_BONUS * (col + 1); } for (col = 0; col < COLS_PER_PLAYER; col++) { display.updateScorecard(TOTAL, col + player * COLS_PER_PLAYER, totalScore[player][col]); } }
Finding the winner needs to use the total of all columns of player:
private void findWinner() { ... for (int player = 0; player < nPlayers; player++) { int total = 0; for (int col = 0; col < COLS_PER_PLAYER; col++) { total += totalScore[player][col]; } if (total == winningScore) { winner += next + playerNames[player]; } else if (total > winningScore) { winner = playerNames[player]; winningScore = total; } ... }
… same procedure for checking for a new high score:
private boolean newHighScore() { ... for (int player = 0; player < nPlayers; player++) { boolean newHighScore = false; int total = 0; for (int col = 0; col < COLS_PER_PLAYER; col++) { total += totalScore[player][col]; } for (int i = highScoreNames.size() - 1; i >= 0; i--) { if (highScoreValues.get(i).intValue() <= total) { newHighScore = true; fileNeedsUpdate = true; if (i == 0) { addHighScore(i, player, total); } } else { if (newHighScore) { addHighScore(i + 1, player, total); } break; } } } if (fileNeedsUpdate) saveHighScores(); return fileNeedsUpdate; } private void addHighScore(int i, int player, int total) { highScoreNames.add(i, playerNames[player]); highScoreValues.add(i, new Integer(total)); if (highScoreNames.size() > N_HIGHSCORES) { highScoreNames.remove(N_HIGHSCORES); highScoreValues.remove(N_HIGHSCORES); } }
Constants allow to quick adjustments. Especially as the display class is not available as source and the dimensions for checking which column has been selected are not provided. Note that this class also allows only a maximum of four columns, and crashes already for 2 players having 3 columns each.
private static final int COLS_PER_PLAYER = 3; private static final int X_FIRST_COL = 252; private static final int X_SIZE_COL = 65;
The code for this assignment is available on github.