package game.gamesheet;
import game.dice.Dice;
import game.slot.AbstractScoreSlot;
import game.slot.Bonus;
import game.slot.Chance;
import game.slot.House;
import game.slot.LargeStraight;
import game.slot.Pair;
import game.slot.SmallStraight;
import game.slot.Sum;
import game.slot.Tuple;
import game.slot.TwoPair;
import game.slot.XOAK;
import game.slot.Yahtzee;
import general.statics.function.ErrorPumper;
import java.io.Serializable;
import java.util.ArrayList;
import org.quickserver.net.server.ClientHandler;
import client.Client;
import server.protocol.ClientInfo;
/**
* The <code>GameSheet</code> class contains variables and methods for preserving the score-wise state of a Yahtzee game.
* <br/>An object of type <code>GameSheet</code> holds the names of the participating players as well as a set of all 18
* score combinations for each and every player. Provides methods for reading and manipulating with their values.
*
* @author Priidu Neemre
*/
public class GameSheet implements Serializable {
private static final long serialVersionUID = 00000001L;
public static final int UPPERSECTION_END = 6;
public static final int LOWER_SECTION_END = 17;
public static final int UPPERSECTION_BONUS = 6;
public static final int INTERMEDIATE_SUM = 7;
public static final int TOTAL_SUM = 17;
public ArrayList<ArrayList<AbstractScoreSlot>> gameSheetContents = new ArrayList<ArrayList<AbstractScoreSlot>>();
private ArrayList<String> colHeadings = new ArrayList<String>();
private int noOfPlayers;
public GameSheet(int noOfPlayers){
if(noOfPlayers <= Client.MAX_NO_OF_PLAYERS){
this.noOfPlayers = noOfPlayers;
}
}
/**
* A method used when instantiating a new <code>GameSheet</code>. Constructs all 18 personal score slots for each
* of the players (the no. of players should have been specified already in the constructor).
*/
public void instantiateGameSheet(){
for(int i = 0; i < noOfPlayers; i++){
ArrayList<AbstractScoreSlot> playerXStats = new ArrayList<AbstractScoreSlot>();
//Add the upper section slots
Tuple ones = new Tuple(1, "Ones", 5);
playerXStats.add(ones);
Tuple twos = new Tuple(2, "Twos", 10);
playerXStats.add(twos);
Tuple threes = new Tuple(3, "Threes", 15);
playerXStats.add(threes);
Tuple fours = new Tuple(4, "Fours", 20);
playerXStats.add(fours);
Tuple fives = new Tuple(5, "Fives", 25);
playerXStats.add(fives);
Tuple sixes = new Tuple(6, "Sixes", 30);
playerXStats.add(sixes);
Bonus bonus = new Bonus(ones, twos, threes, fours, fives, sixes, "Upper bonus", 50);
playerXStats.add(bonus);
//Add the intermediate summing slot
Sum intermediate = new Sum(1, "Intermediate sum", 105);
playerXStats.add(intermediate);
//Add the lower section score slots
Pair onePair = new Pair("One pair", 12);
playerXStats.add(onePair);
TwoPair twoPair = new TwoPair("Two pairs", 22);
playerXStats.add(twoPair);
XOAK threeOfAKind = new XOAK(3, "Three of a kind", 18);
playerXStats.add(threeOfAKind);
XOAK fourOfAKind = new XOAK(4, "Four of a kind", 24);
playerXStats.add(fourOfAKind);
SmallStraight small = new SmallStraight("Small straight", 15);
playerXStats.add(small);
LargeStraight large = new LargeStraight("Large Straight", 20);
playerXStats.add(large);
House house = new House("House", 28);
playerXStats.add(house);
Chance chance = new Chance("Chance", 30);
playerXStats.add(chance);
Yahtzee yahtzee = new Yahtzee("Yahtzee", 50);
playerXStats.add(yahtzee);
//Add the final summing slot
Sum total = new Sum(2, "Final sum", 374);
playerXStats.add(total);
gameSheetContents.add(playerXStats);
}
}
/**
* Inserts a score into the <code>GameSheet</code> according to the parameters provided.
*
* @param clientName the name of the client declaring the score
* @param slot the index of the slot the client has picked for his/her score
* @param curGameDice the dice of type <code>Dice</code> to be used as a basis for score calculations
*/
public void insertScore(String clientName, int slot, Dice curGameDice){
Integer clientIndex = getClientIndex(clientName);
gameSheetContents.get(clientIndex).get(slot).addScoreToSlot(curGameDice.getDiceValues());
updateSums();
}
/**
* Refreshes the current <code>GameSheet</code> speciman accordingly to the values of the provided <code>GameSheet</code>.
* Used on client-side to update the fields of the local gamesheet to their newest available values.
*
* @param refreshedGameSheet a gamesheet containing the newest available values (provided by the gameserver)
*/
public void refreshGameSheet(GameSheet refreshedGameSheet){
//Refresh'i headereid(m�ngijate nimed)
ArrayList<String> refHeadings = refreshedGameSheet.getColHeadings();
for(int i = 0; i < colHeadings.size(); i++){
colHeadings.set(i, refHeadings.get(i));
}
//Refresh'i skoorslotte
ArrayList<ArrayList<AbstractScoreSlot>> refSheet = refreshedGameSheet.getGameSheetContents();
for(int i = 0; i < refSheet.size(); i++){
for(int j = 0; j < refSheet.get(0).size(); j++){
gameSheetContents.get(i).set(j, refSheet.get(i).get(j));
//gameSheetContents.get(i).get(j).setSlotScore(refSheet.get(i).get(j).getSlotScore());
}
}
updateSums();
}
/**
* Updates the "Intermediate sum" score slot and the "Total sum" score slot according to the contents of the other slots
* in the sheet. <br />After updating the "Intermediate sum" slot, the method also checks whether an uppersection bonus
* should be applied.
*/
public void updateSums(){
for(ArrayList<AbstractScoreSlot> playerXStats : gameSheetContents){
Sum intermediate = (Sum)playerXStats.get(INTERMEDIATE_SUM);
Sum total = (Sum)playerXStats.get(TOTAL_SUM);
Bonus bonus = (Bonus)playerXStats.get(UPPERSECTION_BONUS);
intermediate.resetScoreSum();
total.resetScoreSum();
for(int i = 0; i < UPPERSECTION_END; i++){
intermediate.addScoreToSlot(playerXStats.get(i).getSlotScore());
}
bonus.addScoreToSlot(null); //Check whether a bonus should be applied after updating the intermediate sum slot
for(int i = 0; i < LOWER_SECTION_END; i++){
if(i == INTERMEDIATE_SUM)continue;
total.addScoreToSlot(playerXStats.get(i).getSlotScore());
}
// printGameSheetContents(); V�tsin maha, kuna v�ljastas serveris.
}
}
/**
* This methods provides a way of adding players to the gamesheet before calling the <code>instantiateGamesheet()</code>
* method on it.
*
* @param handler the client being added to the gamesheet
*/
public void addPlayer(ClientHandler handler){
ClientInfo data = (ClientInfo) handler.getClientData();
colHeadings.add(data.getName());
}
/**
* A method for declaring the removal of a player from the gamesheet. Disables all of the players' score slots, sets their
* scores to zero and adds a String marker next to the players name.
*
* @param handler the client being removed from the gamesheet
*/
public void removePlayer(ClientHandler handler){
ClientInfo data = (ClientInfo) handler.getClientData();
Integer clientIndex = getClientIndex(data.getName());
for(int j = 0; j < gameSheetContents.get(clientIndex).size(); j++){
gameSheetContents.get(clientIndex).get(j).setSlotScore(0);
gameSheetContents.get(clientIndex).get(j).setSlotUsed();
}
colHeadings.set(clientIndex, colHeadings.get(clientIndex) + " (LEFT)");
}
/**
* Returns the score from the "Total sum" score slot of a player given his/her name.
*
* @param clientName the name of the player whose score is being queried
* @return the total score of the player
*/
public int findPlayerScore(String clientName){
int clientIndex = getClientIndex(clientName);
return gameSheetContents.get(clientIndex).get(TOTAL_SUM).getSlotScore();
}
/**
* Applies a penalty to one of the offending players' score slots (enters a 0). Normally called when a player
* skips a turn or his/her timeout timer runs out.
*
* @param clientName the name of the client being penalized
*/
public void applyPenalty(String clientName){
Integer clientIndex = getClientIndex(clientName);
for(int i = 0; i < gameSheetContents.get(clientIndex).size(); i++){
if(gameSheetContents.get(clientIndex).get(i).getSlotStatus() == true){ //Find an empty score slot
if(i != UPPERSECTION_BONUS && i != INTERMEDIATE_SUM && i != TOTAL_SUM){ //Check, whether it
//is a slot into which scores can be inserted
gameSheetContents.get(clientIndex).get(i).setSlotScore(0);
gameSheetContents.get(clientIndex).get(i).setSlotUsed();
break;
}
}
}
updateSums();
}
/**
* Generates a sorted collection (sorted descendingly by the value in "Total sum" slot) of the game's results
* and returns the current players' final position and total score in a list.
*
* @param playerName the name of the player whose results are being queried
* @return a <code>String</code> list containing the final position, name and total score of the current player
*/
public ArrayList<String> getSortedResult(String playerName){
String[] playerNameArr = new String[noOfPlayers];
Integer[] playerTotalScoreArr = new Integer[noOfPlayers];
ArrayList<String> retList = new ArrayList<String>();
//Populate the arrays with the appropriate data of form "User - Score"
for(int i = 0; i < gameSheetContents.size(); i++){
playerNameArr[i] = colHeadings.get(i);
playerTotalScoreArr[i] = gameSheetContents.get(i).get(GameSheet.TOTAL_SUM).getSlotScore();
}
//Sort the arrays using the "selection sort" algorithm (hea ja lihtne :))
int minIndex;
String tempName;
int tempScore;
for(int i = 0; i < playerNameArr.length; i++){
minIndex = i;
for(int j = i; j < playerNameArr.length; j++){
if(playerTotalScoreArr[j] > playerTotalScoreArr[minIndex]) minIndex = j;
}
tempName = playerNameArr[i];
tempScore = playerTotalScoreArr[i];
playerNameArr[i] = playerNameArr[minIndex];
playerTotalScoreArr[i] = playerTotalScoreArr[minIndex];
playerNameArr[minIndex] = tempName;
playerTotalScoreArr[minIndex] = tempScore;
}
//Find the index of the current client
int playerIndex = 0;
for(int i = 0; i < playerNameArr.length; i++)if(playerNameArr[i].equals(playerName))playerIndex = i;
//Prepare the results into a list with the player name as the 0-th element and score as the 1-st element
//and the position as the 3-rd element
retList.add(playerNameArr[playerIndex]);
retList.add(String.valueOf(playerTotalScoreArr[playerIndex]));
retList.add(String.valueOf(playerIndex + 1)); //+1 to account for the fact that the array starts with value 0
return retList;
}
/**
* A method for checking whether the game has finished according to the gamesheet (eg. if all the score slots are full).
*
* @return TRUE, if all the slots in the sheet have been used and FALSE, if there is at least 1 unused slot left
*/
public boolean isGameFinished(){
boolean finIndicator = true;
for(int i = 0; i < gameSheetContents.size(); i++){
for(int j = 0; j < gameSheetContents.get(0).size(); j++){
if(gameSheetContents.get(i).get(j).getSlotStatus() == true){
finIndicator = false;
}
}
}
return finIndicator;
}
//[START]GETTERS AND SETTERS
/**
* Returns the index number of the client and his/her scores, given his/her name as <code>String</code>.
*
* @param clientName the name of the client whose index is being looked up
* @return the index number of the client specified by <code>clientName</code>
*/
public int getClientIndex(String clientName){
Integer clientIndex = null;
for(int i = 0; i < colHeadings.size(); i++){
if(clientName.equals(colHeadings.get(i)))clientIndex = i;
}
if(clientIndex == null)throw new RuntimeException(ErrorPumper.ERR011_NO_CLIENT_SPECIFIC_DATA);
return clientIndex;
}
public ArrayList<ArrayList<AbstractScoreSlot>> getGameSheetContents(){
return this.gameSheetContents;
}
public ArrayList<String> getColHeadings(){
return this.colHeadings;
}
public int getNoOfPlayers(){
return noOfPlayers;
}
//[END]GETTERS AND SETTERS
//[START]DEBUG METHODS
/**
* Prints the contents of the gamesheet to System.out (the names of the players and their scores) (DEBUG ONLY)
*/
public void printGameSheetContents(){
for(int i = 0; i < gameSheetContents.size(); i++){
System.out.println("\n" + colHeadings.get(i));
for(int j = 0; j < gameSheetContents.get(0).size(); j++){
System.out.println(gameSheetContents.get(i).get(j));
}
}
}
//[END]DEBUG METHODS
}