/**
 * The Hammurabi game, as an Example for CIT590 in Spring 2009.
 */
package hammurabi;

import java.util.Random;
import java.util.Scanner;

/**
 * The Hammurabi game.
 * 
 * @author David Matuszek
 */
public class Hammurabi {
    private int year;
    private int population;
    private int grain;
    private int acres;
    private int landValue;

    private int starved;
    private int percentStarved;
    private int plagueVictims;
    private int immigrants;
    private int grainHarvested;
    private int harvestPerAcre;
    private int amountEatenByRats;

    private int grainFedToPeople;
    private int acresPlanted;

    private Random rand = new Random();
    private Scanner scanner = new Scanner(System.in);
    private String OGH = "O Great Hammurabi";

    /**
     * Starts Hammurabi.
     * @param args Unused.
     */
    public static void main(String[] args) {
        new Hammurabi().run();
    }

    /**
     * Plays Hammurabi.
     */
    private void run() {
        boolean playAgain = true;
        printIntroductoryParagraph();
        while (playAgain) {
            playGame();
            playAgain = getYesOrNo("Would you like to play again?");
        }
        System.out.println("Goodbye!");
    }

    /**
     * Prints the introductory paragraph.
     */
    private void printIntroductoryParagraph() {
        System.out.println(
                "Congratulations! You are the newest ruler of ancient Samaria,\n"
                + "elected for a ten year term of office. Your duties are to\n"
                + "dispense food, direct farming, and buy and sell land as\n"
                + "needed to support your people. Watch out for rat infestations\n"
                + "and the plague! Grain is the general currency, measured in\n"
                + "bushels. The following will help you in your decisions:\n"
                + "\n"
                + "   * Each person needs at least 20 bushels of grain per year to survive\n"
                + "   * Each person can farm at most 10 acres of land\n"
                + "   * It takes 2 bushels of grain to farm an acre of land\n"
                + "   * The market price for land fluctuates yearly\n"
                + "\n"
                + "Rule wisely and you will be showered with appreciation at the\n"
                + "end of your term. Rule poorly and you will be kicked out of office!\n");
    }

    /**
     * Allows the user to play one or more games.
     */
    private void playGame() {
        boolean stillInOffice = true;

        initializeVariables();
        printSummary();
        while (year <= 10 && stillInOffice) {
            buyLand();
            sellLand();
            feedPeople();
            plantGrain();

            checkForPlague();
            countStarvedPeople();
            if (percentStarved >= 45) {
                stillInOffice = false;
            }
            countImmigrants();
            takeInHarvest();
            checkForRats();
            updateLandValue();
            printSummary();
            year = year + 1;
        }
        printFinalScore();
    }

    /**
     * Initialize all instance variables for start of game.
     */
    private void initializeVariables() {
        year = 1;
        population = 100;
        grain = 2800;
        acres = 1000;
        landValue = 19;

        starved = 0;
        plagueVictims = 0;
        immigrants = 5;
        grainHarvested = 3000;
        harvestPerAcre = 3;
        amountEatenByRats = 200;
    }

    /**
     * Prints the year-end summary.
     */
    private void printSummary() {
        System.out
                .println("___________________________________________________________________");
        System.out.println("\n" + OGH + "!");
        System.out.println("You are in year " + year + " of your ten year rule.");
        if (plagueVictims > 0) {
            System.out.println("A horrible plague killed " + plagueVictims
                    + " people.");
        }
        System.out.println("In the previous year " + starved
                + " people starved to death.");
        System.out.println("In the previous year " + immigrants
                + " people entered the kingdom.");
        System.out.println("The population is now " + population);
        System.out.println("We harvested " + grainHarvested + " bushels at "
                + harvestPerAcre + " bushels per acre.");
        if (amountEatenByRats > 0) {
            System.out.println("*** Rats destroyed " + amountEatenByRats
                    + " bushels, leaving " + grain + " bushels in storage.");
        } else {
            System.out.println("We have " + grain + " bushels of grain in storage.");
        }
        System.out.println("The city owns " + acres + " acres of land.");
        System.out.println("Land is currently worth " + landValue
                + " bushels per acre.");
        System.out.println();
    }

    /**
     * Allows the user to buy land.
     */
    private void buyLand() {
        int acresToBuy;
        String question = "How many acres of land will you buy? ";

        acresToBuy = getNumber(question);
        int cost = landValue * acresToBuy;
        while (cost > grain) {
            jest("We have but " + grain + " bushels of grain, not "
                    + cost + "!");
            acresToBuy = getNumber(question);
            cost = landValue * acresToBuy;
        }
        grain = grain - cost;
        acres = acres + acresToBuy;
        System.out.println(OGH + ", you now have " + acres
                + " acres of land");
        System.out.println("and " + grain + " bushels of grain.");
    }

    /**
     * Tells user that the request cannot be fulfilled.
     * 
     * @param message The reason the request cannot be fulfilled.
     */
    private void jest(String message) {
        System.out.println(OGH + ", surely you jest!");
        System.out.println(message);
    }

    /**
     * Allows the user to sell land.
     */
    private void sellLand() {
        String question = "How many acres of land will you sell? ";
        int acresToSell = getNumber(question);

        while (acresToSell > acres) {
            jest("We have but " + acres + " acres!");
            acresToSell = getNumber(question);
        }
        grain = grain + landValue * acresToSell;
        acres = acres - acresToSell;
        System.out.println(OGH + ", you now have " + acres
                + " acres of land");
        System.out.println("and " + grain + " bushels of grain.");
    }

    /**
     * Allows the user to decide how much grain to use to feed people.
     */
    private void feedPeople() {
        String question = "How much grain will you feed to the people? ";
        grainFedToPeople = getNumber(question);

        while (grainFedToPeople > grain) {
            jest("We have but " + grain + " bushels!");
            grainFedToPeople = getNumber(question);
        }
        grain = grain - grainFedToPeople;
        System.out.println(OGH + ", " + grain
                + " bushels of grain remain.");
    }

    /**
     * Allows the user to choose how much grain to plant.
     */
    private void plantGrain() {
        String question = "How many bushels will you plant? ";
        int amountToPlant = 0;
        boolean haveGoodAnswer = false;

        while (!haveGoodAnswer) {
            amountToPlant = getNumber(question);
            if (amountToPlant > grain) {
                jest("We have but " + grain + " bushels left!");
            } else if (amountToPlant > 2 * acres) {
                jest("We have but " + acres + " acres available for planting!");
            } else if (amountToPlant > 20 * population) {
                jest("We have but " + population
                        + " people to do the planting!");
            } else {
                haveGoodAnswer = true;
            }
        }
        acresPlanted = amountToPlant / 2;
        grain = grain - amountToPlant;
        System.out.println(OGH + ", we now have " + grain
                + " bushels of grain in storage.");
    }

    /**
     * Checks for plague, and counts the victims.
     */
    private void checkForPlague() {
        if (rand.nextDouble() < 0.15) {
            System.out
                    .println("*** A horrible plague kills half your people! ***");
            plagueVictims = population / 2;
            population = population - plagueVictims;
        } else {
            plagueVictims = 0;
        }
    }

    /**
     * Counts how many people starved, and removes them from the population.
     */
    private void countStarvedPeople() {
        int peopleFed = grainFedToPeople / 20;
        if (peopleFed >= population) {
            starved = 0;
            percentStarved = 0;
            System.out.println("Your people are well fed and happy.");
        } else {
            starved = population - peopleFed;
            System.out.println(starved + " people starved to death.");
            percentStarved = (100 * starved) / population;
            population = population - starved;
        }
    }

    /**
     * Counts how many people immigrated.
     */
    private void countImmigrants() {
        if (starved > 0) {
            immigrants = 0;
        } else {
            immigrants = (20 * acres + grain) / (100 * population) + 1;
            population += immigrants;
        }
    }

    /**
     * Determines the harvest, and collects the new grain.
     */
    private void takeInHarvest() {
        harvestPerAcre = rand.nextInt(5) + 1;
        grainHarvested = harvestPerAcre * acresPlanted;
        grain = grain + grainHarvested;
    }

    /**
     * Checks if rats get into the grain, and determines how much they eat.
     */
    private void checkForRats() {
        if (rand.nextInt(100) < 40) {
            int percentEatenByRats = 10 + rand.nextInt(21);
            System.out.println("*** Rats eat " + percentEatenByRats
                    + " percent of your grain! ***");
            amountEatenByRats = (percentEatenByRats * grain) / 100;
            grain = grain - amountEatenByRats;
        } else {
            amountEatenByRats = 0;
        }
    }

    /**
     * Randomly sets the new price of land.
     */
    private void updateLandValue() {
        landValue = 17 + rand.nextInt(7);
    }

    /**
     * Prints an evaluation at the end of a game.
     */
    private void printFinalScore() {
        if (starved >= (45 * population) / 100) {
            System.out
                    .println("O Once-Great Hammurabi,\n"
                            + starved
                            + " of your people starved during the last year of your\n"
                            + "incompetent reign! The few who remain have stormed the palace\n"
                            + "and bodily evicted you!\n"
                            + "\nYour final rating: TERRIBLE.");
            return;
        }
        int plantableAcres = acres;
        if (20 * population < plantableAcres) {
            plantableAcres = 20 * population;
        }

        if (plantableAcres < 600) {
            System.out
                    .println("Congratulations, "
                            + OGH
                            + " You have ruled wisely but not\n"
                            + "well; you have led your people through ten difficult years, but\n"
                            + "your kingdom has shrunk to a mere " + acres
                            + " acres.\n" + "\nYour final rating: ADEQUATE.");
        } else if (plantableAcres < 800) {
            System.out
                    .println("Congratulations, \" + OGH + \" You  have ruled wisely, and\n"
                            + "shown the ancient world that a stable economy is possible.\n"
                            + "\nYour final rating: GOOD.");
        } else {
            System.out
                    .println("Congratulations, \" + OGH + \" You  have ruled wisely and well, and\n"
                            + "expanded your holdings while keeping your people happy.\n"
                            + "Altogether, a most impressive job!\n"
                            + "\nYour final rating: SUPERB.");
        }
    }

    /**
     * Prints the given message (which should ask the user for some integral
     * quantity), and returns the number entered by the user. If the user's
     * response isn't an integer, the question is repeated until the user does
     * give a integer response.
     * 
     * @param message
     *            The request to present to the user.
     * @return The user's numeric response.
     */
    int getNumber(String message) {
        while (true) {
            System.out.print(message);
            try {
                return scanner.nextInt();
            } catch (Exception e) {
                System.out
                        .println("\"" + scanner.next() + "\" isn't a number!");
            }
        }
    }

    /**
     * Returns a boolean response to a yes-no question. (At this point my
     * students only know about reading in integers, but I couldn't bring myself
     * to do the ugly "1 = yes" kind of thing.)
     * 
     * @param string
     *            The question to be asked.
     * @return The answer to the question.
     */
    private boolean getYesOrNo(String question) {
        String answer;
        while (true) {
            System.out.print(question + "  ");
            answer = scanner.next();
            if (answer.toLowerCase().charAt(0) == 'y')
                return true;
            if (answer.toLowerCase().charAt(0) == 'n')
                return false;
        }
    }

}
