Package silvertrout.plugins.quizmaster

Source Code of silvertrout.plugins.quizmaster.Quizmaster

/*   _______ __ __                    _______                    __
*  |     __|__|  |.--.--.-----.----.|_     _|.----.-----.--.--.|  |_
*  |__     |  |  ||  |  |  -__|   _|  |   |  |   _|  _  |  |  ||   _|
*  |_______|__|__| \___/|_____|__|    |___|  |__| |_____|_____||____|
*
*  Copyright 2008 - Gustav Tiger, Henrik Steen and Gustav "Gussoh" Sohtell
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/
package silvertrout.plugins.quizmaster;

import java.util.Collection;
import java.util.Map;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Random;
import java.util.Calendar;
import java.io.File;
import java.net.URISyntaxException;

import java.net.URL;
import java.util.Collections;

import silvertrout.Channel;
import silvertrout.User;
import silvertrout.Modes;

import silvertrout.commons.game.ScoreManager;
import silvertrout.commons.game.Trophy;
import silvertrout.commons.game.TrophyManager;

/**
*
**
*/
public class Quizmaster extends silvertrout.Plugin {

    private enum State { RUNNING, RUNNING_QUESTION, NOT_RUNNING };

    // Settings:
    private final int                  voiceInterval        = 60;
    private final int                  hintTime             = 7;
    private final int                  waitTime             = 3;
    private final int                  rankInterval         = 500;

    // Variables:

    private final LinkedList<Question> questions = new LinkedList<Question>();
    private final Random               rand      = new Random();
    private ScoreManager               scoreManager;
    private TrophyManager              trophyManager;
    private String                     channelName;

    private Question                   question;
    //private String                     currentAnswerString;
    private int                        currentHint = 0;

    private String[]                   grad =
            {
            "\"Tought\"", "Cell", "Egg", "Embryo", "Fetus", "Neonate",
            "Toddler", "Child",

            "Preschooler", "Lower Primary School Student",
            "Upper Primary School Student", "Lower Secondary School Student",
            "Upper Secondary School Student", "Bachelor Student",
            "Master Student",

            "Volunteer", "Intern", "Receptionist", "Personal Secretary",
            "Personal Assistant", "Clerk", "Executive Secretary",
            "Executive Assistant", "Foreman", "Supervisor", "Manager",
            "Superintendent",

            "Associate Vice President", "Senior Vice President",
            "Executive Vice President", "Chief Officer",
            "Chief Executive Officer", "Chairman of the Board",

            "Apprentice", "Apprentice-Companion", "Brother",
            "Commander", "Master", "Grand Master"
            };



    private int                        startTime;
    private int                        currentTime;
    private int                        endTime;

    private int                        statTime             = 0;

    private int                        unanswerdQuestions   = 0;

    private int                        answerStreak         = 0;
    private String                     answerStreakNickname = new String();

    private long                       startMiliTime;

    private State                      state                = State.NOT_RUNNING;

    /**
     *
     */
    public Quizmaster() {

        String scoresPath   = "Scores/Scores";
        String trophiesPath = "Trophies";

        try {
            scoreManager  = new ScoreManager(new File(this.getClass().getResource(scoresPath).toURI()));
            trophyManager = new TrophyManager(new File(this.getClass().getResource(trophiesPath).toURI()));
        } catch(URISyntaxException e) {
            e.printStackTrace();
        }
    }

    /** Award trophy to user.
     *
     * @param t
     * @param nick
     */
    public void awardTrophy(Trophy t, String nick) {
        if(!trophyManager.haveTrophy(t, nick)) {
            trophyManager.addTrophy(t, nick);
            getNetwork().getConnection().sendPrivmsg(channelName, nick
                + ": Du har fått en trofé - " + t.getName());
        }
    }

    /**
     *
     * @param score
     */
    public String getRang(int score) {
        int rang = score / rankInterval;
        if(rang < grad.length) {
            return grad[rang];
        } else {
            int umlevel = rang - grad.length + 2;
            return "Über master (level " + umlevel + ")";
        }
    }

    /**
     *
     * @param nick
     * @param score
     */
    public void awardScore(String nick, int score) {
        // Calculate answer time, in seconds:
        long miliSec = Calendar.getInstance().getTimeInMillis() - startMiliTime;
        double time  = ((double)miliSec / 1000.0);

        // Calculate winning streak
        if(answerStreakNickname.equals(nick)) {
            answerStreak++;
        } else {
            answerStreakNickname = nick;
            answerStreak         = 1;
        }

        // Update scores:
        int oldScore = scoreManager.getTotalScore(nick);
        int oldPos   = scoreManager.getPosition(nick);
        scoreManager.addScore(nick, question.category, score);
        int newScore = scoreManager.getTotalScore(nick);
        int newPos   = scoreManager.getPosition(nick);

        // New rank
        if(oldScore / rankInterval < newScore / rankInterval)
        {
            getNetwork().getConnection().sendPrivmsg(channelName,
                    "Utmärkt jobbat! Din nya rank är: "
                    + getRang(newScore));
        }

        // Print message
        String msg = "Rätt svar var \"" + question.hintLine + "\". ";
        if(answerStreak >= 3)    msg += "(" + answerStreak + " i rad) ";
        if(oldPos == -1)         msg += "(In på listan på placering " + newPos + ") ";
        else if(oldPos < newPos) msg += "(Upp " + (newPos-oldPos) + " placering(ar)) ";
        msg += nick + " (" + time +" sek) fick " + score + "p och har nu " + newScore +"p.";
        getNetwork().getConnection().sendPrivmsg(channelName, msg);

        // Check for trophies won
        int year  = Calendar.getInstance().get(Calendar.YEAR);
        int month = Calendar.getInstance().get(Calendar.MONTH) + 1;
        int day   = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);

        int hour  = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
        int min   = Calendar.getInstance().get(Calendar.MINUTE);
        // First blood trophy
        if(oldScore == 0 && newScore >= 1)
            awardTrophy(trophyManager.getTrophy("First Blood"), nick);
        // Speedster trophy
        if(time < 3.0 && question.hintLine.length() > 5)
            awardTrophy(trophyManager.getTrophy("Speedster"), nick);
        // Chain Reaction
        if(answerStreak >= 5)
            awardTrophy(trophyManager.getTrophy("Chain Reaction"), nick);
        // Chain Overload
        if(answerStreak >= 10)
            awardTrophy(trophyManager.getTrophy("Chain Overload"), nick);
        // Chain Overdose
        if(answerStreak >= 30)
            awardTrophy(trophyManager.getTrophy("Chain Overdose"), nick);
        // Elite!
        if(oldScore < 1337 && newScore >= 1337)
            awardTrophy(trophyManager.getTrophy("Elite!"), nick);
        // Top Ten
        if(newScore > 100 && newPos <= 10)
            awardTrophy(trophyManager.getTrophy("Top Ten"), nick);
        // Top Three
        if(newScore > 300 && newPos <= 3)
            awardTrophy(trophyManager.getTrophy("Top Three"), nick);
        // Top Dog
        if(newScore > 1000 && newPos == 1)
            awardTrophy(trophyManager.getTrophy("Top Dog"), nick);
        // Säg ett datum, vilket som helst!
        if(month == 5 && day == 29)
            awardTrophy(trophyManager.getTrophy("Säg ett datum, vilket som helst!"), nick);
        // Endurance Master
        if(question.hintLine.length() >= 30)
            awardTrophy(trophyManager.getTrophy("Endurance Master"), nick);
    }

    /**
     *
     * @param categories
     */
    public void newRound(java.util.Collection<String> categories) {
        try{
            File qdir = new File(this.getClass().getResource("Questions").toURI());

            for(File d: qdir.listFiles()) {
                //System.out.println("Begin checking directory: " +d.getName());
                if(categories == null || categories.contains(d.getName())) {
                    if(d.isDirectory()) {
                        //System.out.println("Checking directory: " +d.getName());
                        for(File f: d.listFiles()) {
                            if(f.getName().endsWith(".xml")) {
                                System.out.println("Loading questions from " + f.getName());
                                Collection<Question> qss = QuestionReader.load(f);
                                questions.addAll(qss);
                                // TODO: FIX CRASH ON GRAMMAR ERROR: INSTEAD
                                // REPORT ERROR!!
                                //System.out.println("Added file: " + f.getName());
                            }
                        }
                    }
                }
            }
            // Suffle the questions
            Collections.shuffle(questions);

            getNetwork().getConnection().sendPrivmsg(channelName, "En ny omgång"
                    + " startas. Totalt finns " + questions.size() + " frågor.");

            unanswerdQuestions = 0;
            state              = State.RUNNING;
            newQuestion();
        } catch(URISyntaxException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     */
    public void endRound() {
        getNetwork().getConnection().sendPrivmsg(channelName, "Omgången är"
                + " slut. Skriv !start för att starta en ny omgång.");
        state = State.NOT_RUNNING;
    }

    /**
     *
     */
    public void newQuestion() {
        // Pop of question from queue
        try {
            question = questions.removeFirst();
        } catch(java.util.NoSuchElementException e) {
            endRound();
            return;
        }
        // Construct hint line from answers (if there is none)
        if(question.hintLine == null) {
            int requiredAnswers = 0;
            question.hintLine = "";
            for(Question.Answer answer: question.answers) {
                if(answer.required)requiredAnswers++;
            }
            for(Question.Answer answer: question.answers) {
                if(answer.required) {
                    question.hintLine += answer.answer + " ";
                } else if(requiredAnswers >= 0) {
                    requiredAnswers--;
                    question.hintLine += answer.answer + " ";
                }
            }
            question.hintLine = question.hintLine.trim();
        }

        // Construct hints from hint line (if there are hints missing)
        // TODO, improve
        int generate = question.hintCount - question.hints.size();
        //System.out.println("Generating " + generate + " questions (-1, all dots)");
        if(generate > 0) {
            int chars = 0;
            String base = new String();

            for(int i = 0; i < question.hintLine.length(); i++) {
                if(Character.isLetterOrDigit(question.hintLine.charAt(i))) {
                    chars++;
                    base += '.';
                } else {
                    base += question.hintLine.charAt(i);
                }
            }
            //System.out.println("hintline contains " + chars + " chars");
            // Add base (only dots)
            Question.Hint h = question.new Hint();
            h.hint = base;
            question.hints.add(h);
            generate--;

            final double percentage = 0.70;

            double reveal     = ((double)chars * percentage) / (double)generate;
            double revealLeft = 0;
            //System.out.println("revealing " + reveal + ", r+rl: " + (reveal + revealLeft));
            for(int g = 0; g < generate; g++) {
                for(int r = 1, b = 0; r < reveal + revealLeft && b < 100;) {
                    int index = rand.nextInt(question.hintLine.length());
                    if(base.charAt(index) == '.') {
                        base = base.substring(0, index) + question.hintLine.charAt(index) + base.substring(index + 1);
                        r++;
                    }
                    b++;
                }

                if(reveal + revealLeft > 1) {
                    Question.Hint hi = question.new Hint();
                    hi.hint     = base;
                    question.hints.add(hi);
                    //System.out.print("(Added) ");
                }
                //System.out.print(base + " - old left " + revealLeft + ", reveal " + reveal);
                revealLeft = reveal + revealLeft - Math.floor(reveal + revealLeft);
                //System.out.println(" - new left " + revealLeft);
            }
            //System.out.println("y");
        }
        //System.out.println("x");
        currentHint   = 0;
        startTime     = currentTime;
        startMiliTime = Calendar.getInstance().getTimeInMillis();
        getNetwork().getConnection().sendPrivmsg(channelName, "" + "["
                + question.category + "] " + question.questionLine);
        state   = State.RUNNING_QUESTION;
    }


    /**
     *
     * @param answered - true iff the question was correctly answered
     */
    public void endQuestion(boolean answered) {
        if(!answered) {
            getNetwork().getConnection().sendPrivmsg(channelName, "Rätt svar"
                    + " var \"" + question.hintLine + "\". Ingen"
                    + " lyckades svara rätt.");
            unanswerdQuestions++;
            answerStreak = 0;
        } else {
            unanswerdQuestions = 0;
        }
        endTime = currentTime;
        state   = State.RUNNING;
    }


    public void printTopTen(String sender) {
        ScoreManager.Score[] topList      = scoreManager.getTop(10);
        String               toptenString = new String();

        if(topList.length == 0) {
            toptenString  = "Ingen är på listan än. Quizza hårdare!";
        }

        for(int i = 0; i < topList.length; i++) {
            toptenString += "#" + (i+1) + " " + topList[i].nick + " "
                + topList[i].getTotalScore() + "p";
            if(i != topList.length - 1)toptenString += "  -  ";
        }

        getNetwork().getConnection().sendPrivmsg(channelName,
                "Top 10: " + toptenString);
    }

    /**
     *
     * @param sender
     */
    public void printStats(String sender, String lookup) {

        int          scorePos   = scoreManager.getPosition(lookup);
        int          score      = scoreManager.getTotalScore(lookup);

        int          trophyTot  = trophyManager.getTrophies().size();
        List<Trophy> trophyList = trophyManager.getTrophies(lookup);
        int          trophy     = trophyList.size();

        String       msg        = sender + ", ";

        boolean      nickSame   = sender.equalsIgnoreCase(lookup);
        String       nickSing   = nickSame ? "Du":  lookup;
        String       nickPlur   = nickSame ? "Din": lookup.endsWith("s") ? lookup: lookup + "s";

        // Score part
        if(scorePos == -1) {
            msg += nickSing + " har inga poäng, och  är inte med på topplistan.  ";
        } else {
            msg += nickSing + " ligger på plats " + scorePos + ", med " + score + "p.  ";
        }

        // Trophy part
        if(trophy == 0) {
            msg += nickSing + " har inga troféer. ";
        } else {
            msg += nickSing + " har " + trophy + " av " + trophyTot + " troféer: ";

            for(int i = 0; i < trophy; i++) {
                msg += trophyList.get(i).getName();
                if(i == trophy - 2) {
                    msg += " och ";
                } else if(i < trophy - 2) {
                    msg += ", ";
                }
            }
        }

        // Rank part
        int nextrank = rankInterval - score % rankInterval;
        msg += ". " + nickPlur + " rank är: " + getRang(score) + " ("
                + nextrank + "p kvar till nästa rank).";

        getNetwork().getConnection().sendPrivmsg(channelName, msg);
    }

    void checkAnswer(User user, Channel channel, String message) {
        // Answer to question
        String uanswer      = message.toLowerCase().trim();
        int    uanswerCount = 0;
        int    score        = 0;
        for(Question.Answer answer: question.answers) {

            String cans = answer.answer.toLowerCase();
            int pos = uanswer.indexOf(cans);
            char before = pos <= 0 ? ' ': uanswer.charAt(pos - 1);
            char after  = pos + cans.length() >= uanswer.length() ? ' ': uanswer.charAt(pos + cans.length());

            System.out.println(cans + "/" + uanswer + " = " + pos + ", " + before + ", " + after);

            if(pos >= 0 && !Character.isLetterOrDigit(before) &&
                        !Character.isLetterOrDigit(after)) {
                score += answer.score;
                uanswerCount++;

            } else {
                if(answer.required) {
                    //getNetwork().getConnection().sendPrivmsg(channelName,
                    //      "missing req answer: " + answer.answer);
                    return;
                }
            }
        }
        if(uanswerCount >= question.required) {
            for(int i = 0; i < currentHint; i++)
               score -= question.hints.get(i).scoredec;
            awardScore(user.getNickname(), Math.max(1, score));
            endQuestion(true);

        } else {
            //getNetwork().getConnection().sendPrivmsg(channelName,
            //      "req answer " + question.required + " > " + uanswerCount);
        }

    }

    @Override
    public void onPrivmsg(User user, Channel channel, String message) {


        if(channel != null && channel.getName().equalsIgnoreCase(channelName)) {

            if(state == State.RUNNING_QUESTION) {
                checkAnswer(user, channel, message);
            } else if(state == State.NOT_RUNNING) {
                // Start new round
                // TODO: what about categories?
                if(message.startsWith("!start")) {
                    String[] cat = message.substring(6).split("\\s");
                    newRound(null);
                }
            }

            if(message.startsWith("!stats")) {
                String[] parts = message.substring(1).split("\\s");
                if(currentTime - statTime > 20) {
                    if(parts.length == 2) {
                        printStats(user.getNickname(), parts[1]);
                    } else {
                        printStats(user.getNickname(), user.getNickname());
                    }
                    statTime = currentTime;
                }
            }
            if(message.equals("!toplist")) {
                if(currentTime - statTime > 20) {
                    printTopTen(user.getNickname());
                    statTime = currentTime;
                }
            }
            else if(message.equals("!help")) {
                if(currentTime - statTime > 20)
                    getNetwork().getConnection().sendPrivmsg(channelName,
                              user.getNickname()
                            + ", Skriv !start för att starta frågesproten och "
                            + "!toplist för att se tio i topp-listan. För att "
                            + "se dina egna poäng och titta på dina troféer, "
                            + "skriv !stats. Du kan rapportera felaktiga "
                            + "frågor genom att skriva !report num [reason]."
                            + "Om du vill visa denna hjälp igen, skriv !help.");
            }
            else if(message.startsWith("!suggest")) {
                // TODO!
            }
            else if(message.startsWith("!report")) {
                getNetwork().getConnection().sendPrivmsg(channelName,
                    user.getNickname() + ", Not implemented yet");
                // TODO!
            }
            else if(message.equals("!trophies")) {

                int tot    = trophyManager.getTrophies().size();
                String msg = user.getNickname() + ", Följande troféer finns: ";
                for(Trophy t: trophyManager.getTrophies()) {
                    msg += t.getName() + ", ";
                }
                msg += tot + " stycken - samla alla!";
                getNetwork().getConnection().sendPrivmsg(channelName, msg);
            }
        }
    }

    /**
     *
     */
    public void giveHint() {

        if(currentHint == question.hints.size()) {
            //getNetwork().getConnection().sendPrivmsg(channelName, "err, to few hints");
            endQuestion(false);
        } else {
            Question.Hint currentHintObj = question.hints.get(currentHint);
            getNetwork().getConnection().sendPrivmsg(channelName, currentHintObj.hint);
            currentHint++;
        }

    }

    @Override
    public void onTick(int ticks) {
        currentTime = ticks;
        //System.out.println(currentTime + ": " + state);
        if(state == State.RUNNING_QUESTION) {
            // If we have a question that no one have answered in a while
            if(currentTime > startTime + hintTime * question.hintCount) {
                endQuestion(false);
                if(unanswerdQuestions >= 5) {
                    endRound();
                } else {
                    newQuestion();
                }
            // Or if it is time to give a hint
            } else if(currentTime - startTime == hintTime * question.hintCount) {
                endQuestion(false);
            } else if((currentTime - startTime) % hintTime == 0) {
                giveHint();
            }

        } else if(state == State.RUNNING) {
            // Time for a new question
            if(currentTime - endTime == waitTime) {
                newQuestion();
            }
        }

        // Do every minute
        if(ticks % voiceInterval == 0) {
            // Only voice if we are in the channel and are an operator
            if(getNetwork().isInChannel(channelName)) {

                Channel channel  = getNetwork().getChannel(channelName);
                User    myUser   = getNetwork().getMyUser();
                boolean operator = channel.getUsers().get(myUser).haveMode('o');

                if(operator) {

                    LinkedList<String> voice   = new LinkedList<String>();
                    LinkedList<String> devoice = new LinkedList<String>();
                    Map<User, Modes>   users   = channel.getUsers();
                    System.out.println("\n\n\nVoicing people!:");
                    for(User u: users.keySet()) {

                        System.out.println(u);
                        System.out.println(users.get(u));

                        if(users.get(u).haveMode('v')){
                            // the user do have voice
                            if(!scoreManager.isTop(10, u.getNickname()))
                                devoice.add(u.getNickname());
                        } else {
                            // the user do not have voice
                            if(scoreManager.isTop(10, u.getNickname()))
                                voice.add(u.getNickname());
                        }
                    }

                    // TODO: combine or something. Move to the silvertrout
                    // mode thingy?
                    while(!voice.isEmpty()) {
                        String mode  = "+";
                        String usrs = "";
                        for(int i = 0; !voice.isEmpty() && i < 4; i++) {
                            mode  += "v";
                            usrs += voice.pop() + " ";
                        }
                        getNetwork().getConnection().sendRaw("MODE " +
                                channelName + " " + mode + " " + usrs);
                        System.out.println("MODE " +
                                channelName + " " + mode + " " + usrs);
                    }

                    while(!devoice.isEmpty()) {
                        String mode  = "-";
                        String usrs = "";
                        for(int i = 0; !devoice.isEmpty() && i < 4; i++) {
                            mode  += "v";
                            usrs += devoice.pop() + " ";
                        }
                        getNetwork().getConnection().sendRaw("MODE " +
                                channelName + " " + mode + " " + usrs);
                        System.out.println("MODE " +
                                channelName + " " + mode + " " + usrs);
                    }

                }
            }
        }
    }

    private String printNick(String nick){

        int s = scoreManager.getTotalScore(nick) / rankInterval;
        if(s > 0) {
            if(s > grad.length)
                return grad[grad.length-1] + " " + nick;
            else if(s > 0)
                return grad[s-1] + " " + nick;
        }
        return nick;
    }

    @Override
    public void onConnected() {
        // Join quiz channel:
        if(!getNetwork().isInChannel(channelName)) {
            getNetwork().getConnection().join(channelName);
        }
    }

    @Override
    public void onDisconnected() {
        state = State.NOT_RUNNING;
    }

    @Override
    public void onLoad(Map<String,String> settings){
        channelName = settings.get("channel");
        if(channelName == null || !channelName.startsWith("#")) channelName = "#superquiz";
    }
}
TOP

Related Classes of silvertrout.plugins.quizmaster.Quizmaster

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.