Usuário:SandroHcBot/Código Fonte/CharmsTask

De RuneScape Wiki
Ir para: navegação, pesquisa

Updates all Charm drops from logs.

/*--------------------------------------------------------
 * AmauriceBot - RuneScape Wikia update task robot
 * Copyright (c) 2009-2012  Maurice Abraham.
 *
 * 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/>.
 *
 * Contributors:
 *   None
 *------------------------------------------------------*/

package amauricebot;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Description(summary = "Updates all Charm drops from logs.")
public class CharmsTask implements BotTask
{
    private WikiSession m_wiki = null;
    private DateFormat m_dateFmt = null;

    private final static int MAX_UPDATE_ITEMS = 10000;
    private final static int LARGE_LOG_CHANGE = 500;  // large change log kills
    private final static int SMALL_GOOD_DROPS = 250;  // small known good kills
    private final static int UNUSUAL_NUM_ENTRIES = 10; // more than usual entries

    // In the following are the suspect and reject levels
    private final static int[] LEARN_LEVELS = { 2 , 20 }; // small good drops
    private final static int[] SMALL_LEVELS = { 5 , 10 }; // small log changes
    private final static int[] LARGE_LEVELS = { 2 , 5 };  // large log changes

    private Pattern m_logPattern = Pattern.compile("\\| *log *= *([^|\\n]*)");
    private Pattern m_multiPattern = Pattern.compile("\\| *charms *= *([0-9,]*)[^|\\n]*");
    private Pattern m_killsPattern = Pattern.compile("\\| *kills *= *([0-9,]*)[^|\\n]*");
    private Pattern[] m_charmsPatterns = {
        Pattern.compile("\\| *gold *= *([0-9,]*)[^|\\n]*"),
        Pattern.compile("\\| *green *= *([0-9,]*)[^|\\n]*"),
        Pattern.compile("\\| *crimson *= *([0-9,]*)[^|\\n]*"),
        Pattern.compile("\\| *blue *= *([0-9,]*)[^|\\n]*")
        };

    private final Pattern m_logPagePattern = Pattern.compile(
            "(?:\\{\\|[^{]*GHORROCK[^{]*\\|\\}\\s*)?" +
            "\\{\\{Charm log header\\}\\}\\s*" +
            "(?:(?:==[^\\n]*==)?\\s*" +
            "\\{\\{Charm log submission[^}]*\\}\\}\\s*)*$");
    private final Pattern m_logEntryPattern = Pattern.compile(
            "(\\{\\{Charm log submission[^}]*\\}\\})");
    private final String m_logStdHeader = "{{Charm log header}}";

    public CharmsTask(WikiSession wiki)
    {
        m_wiki = wiki;

        m_dateFmt = new SimpleDateFormat("HH:mm, MMMM d, yyyy (z)", Locale.ENGLISH);
        m_dateFmt.setTimeZone(TimeZone.getTimeZone("UTC"));
    }

    public void perform()
            throws IOException
    {
        SortedSet<String> allTitles = m_wiki.getAllPages(114, null, MAX_UPDATE_ITEMS);

        long numChecked = 0;
        long numFixed = 0;
        long numFail = 0;
        for (String title : allTitles)
        {
            if (Utils.excessiveLog())
                break;

            if (title.toLowerCase().contains("(historical)"))
                continue;

            numChecked ++;
            try
            {
                if (checkCharmDrops(title))
                    numFixed ++;
            }
            catch (Exception ex)
            {
                if (numFail++ > 5)
                    return;
                Utils.log("ERROR: Problem with " + title + ": " + ex.toString());
                ex.printStackTrace();
            }
        }
        Utils.log("Checked " + numChecked + " charm logs, and updated " + numFixed + " charm drops");
    }

    private boolean checkCharmDrops(String title)
            throws IOException
    {
        // Get good charm drop information
        String goodText = m_wiki.getText(title);
        if (goodText == null)
        {
            Utils.log("ERROR: Could not get content of [[" + title + "]]");
            return false;
        }
        CharmDrops goodDrops = new CharmDrops(goodText);
        String logTitle = goodDrops.getLog();
        if (logTitle == null)
        {
            Utils.log("ERROR: Could not get 'log' from [[" + title + "]]");
            return false;
        }
        if (!logTitle.matches(".*/Charm[ _]log.*"))
        {
            Utils.log("ERROR: Bad value for 'log' in [[" + title + "]]");
            return false;
        }
        if (goodDrops.isBad())
        {
            Utils.log("ERROR: GoodDrops for [[" + title + "]] are BAD !!!");
            return false;
        }

        // Get charm submission log information
        String logText = m_wiki.getText(logTitle);
        if (logText == null)
        {
            Utils.log("ERROR: Could not get content of [[" + logTitle + "]]");
            return false;
        }

        String[] toks = Utils.extract(m_logPagePattern, logText);
        if (toks == null)
        {
            Utils.log("WARNING: Non-standard format in [[" + logTitle + "]]");
        }

        // Extract the good updates
        CharmDrops goodUpdates = new CharmDrops(null);
        goodUpdates.setMulti(goodDrops.getMulti());
        Matcher logMatcher = m_logEntryPattern.matcher(logText);
        int numEntries = 0;
        while (logMatcher.find())
        {
            String logEntryText = logMatcher.group(0);
            CharmDrops logDrops = new CharmDrops(logEntryText);
            logDrops.setMulti(goodDrops.getMulti());

            double mismatch = logDrops.getMismatch(goodDrops);
            if (logDrops.getKills() <= 0 || logDrops.getKills() >= 10000)
                mismatch = 100;

            int[] levels = SMALL_LEVELS;

            if (goodDrops.getKills() < SMALL_GOOD_DROPS)
                levels = LEARN_LEVELS;
            else if (logDrops.getKills() >= LARGE_LOG_CHANGE
                        || logDrops.getKills() >= goodDrops.getKills() / 10)
                levels = LARGE_LEVELS;

            if (mismatch >= levels[0]
                && 20 * logDrops.getKills() >= goodDrops.getKills())
            {
                String msg = (mismatch >= levels[1]
                        ? "WARNING: [[" + logTitle + "]] bad entry"
                        : "NOTE: [[" + logTitle + "]] suspect entry");
                msg += " (score " + (int)mismatch + ") <small>"
                     + logDrops + " vs [[" + title + "|"
                     + goodDrops.toPercentString() +"]]</small>";
                Utils.log(msg);
            }
            if (mismatch < levels[1])
                goodUpdates.add(logDrops);
            numEntries ++;
        }

        if (numEntries == 0)
            return false;

        if (numEntries >= UNUSUAL_NUM_ENTRIES)
            Utils.log("NOTE: [[" + logTitle + "]] had " + numEntries + " log entries");

        // Update log submission page
        String logHeader = m_logStdHeader;
        int oldHeaderPos = logText.indexOf(logHeader);
        if (oldHeaderPos > 0)
        {
            String preHeader = logText.substring(0,oldHeaderPos);
            Matcher preMatcher = m_logEntryPattern.matcher(preHeader);
            if (!preMatcher.find()) // make sure no entries above header
                logHeader = preHeader + logHeader;
        }

        if (!m_wiki.editText(logTitle, logHeader, "Processed log entries", false))
        {
            Utils.log("WARNING: Failed to update [[" + logTitle + "]] (possible conflict)");
            return false;
        }

        if (goodUpdates.getKills() <= 0)
            return false;

        // Now update charm drop page
        goodText = m_wiki.getText(title);
        if (goodText == null)
        {
            Utils.log("ERROR: Could not get content of [[" + title + "]]");
            return false;
        }

        goodDrops.add(goodUpdates);
        long[] charms = goodDrops.getCharms();
        goodText = goodText.replaceAll(m_killsPattern.toString(), "|kills=" + goodDrops.getKills())
                           .replaceAll(m_charmsPatterns[0].toString(), "|gold=" + charms[0])
                           .replaceAll(m_charmsPatterns[1].toString(), "|green=" + charms[1])
                           .replaceAll(m_charmsPatterns[2].toString(), "|crimson=" + charms[2])
                           .replaceAll(m_charmsPatterns[3].toString(), "|blue=" + charms[3]);

        if (!m_wiki.editText(title, goodText, "Updating from log", false))
        {
            Utils.log("WARNING: Failed to update [[" + title + "]] (possible conflict)");
            return false;
        }

        return true;
    }


    private class CharmDrops
    {
        private String m_log = null;
        private long m_multi = 0;
        private long m_kills = 0;
        private long[] m_charms = new long[4];

        public CharmDrops(String text)
        {
            if (text == null) return;

            // extract data from page text
            String[] toks = Utils.extract(m_logPattern, text);
            m_log = (toks != null ? toks[1] : null);
            toks = Utils.extract(m_multiPattern, text);
            m_multi = (toks != null ? Utils.parseNum(toks[1], null, 0) : 0);
            toks = Utils.extract(m_killsPattern, text);
            m_kills = (toks != null ? Utils.parseNum(toks[1], null, 0) : 0);

            for (int i = 0; i < 4; i ++)
            {
                toks = Utils.extract(m_charmsPatterns[i], text);
                m_charms[i] = (toks != null ? Utils.parseNum(toks[1], null, 0) : 0);
            }
        }

        public String getLog()
        {
            return m_log;
        }

        public long getMulti()
        {
            return m_multi;
        }

        public long getKills()
        {
            return m_kills;
        }

        public long[] getCharms()
        {
            return m_charms;
        }

        public void setMulti(long multi)
        {
            m_multi = multi;
        }

        public void add(CharmDrops drops)
        {
            m_multi = drops.m_multi;
            m_kills += drops.m_kills;
            for (int i = 0; i < 4; i ++)
            {
                m_charms[i] += drops.m_charms[i];
            }
        }

        public boolean equals(CharmDrops drops)
        {
            if (drops.m_multi != m_multi)
                return false;
            if (drops.m_kills != m_kills)
                return false;
            for (int i = 0; i < 4; i ++)
            {
                if (drops.m_charms[i] != m_charms[i])
                    return false;
            }
            return true;
        }

        public boolean isBad()
        {
            if (m_multi < 1 || m_multi > 30)
                return true;
            if (m_kills < 0)
                return true;

            long total = 0;
            for (int i = 0; i < 4; i ++)
            {
                if (m_charms[i] != 0 && m_kills == 0)
                    return true;
                if (m_charms[i] < 0)
                    return true;
                if (m_charms[i] % m_multi != 0)
                    return true;

                total += m_charms[i];
            }
            if (total > m_kills * m_multi)
                return true;

            // passed the obvious checks
            return false;
        }

        public double getMismatch(CharmDrops goodDrops)
        {
            if (isBad())
                return 100;

            double mismatch = 0;
            for (int i = 0; i < 4; i ++)
            {
                mismatch += Math.abs(charmPercent(i) - goodDrops.charmPercent(i))
                               * (i == 3 ? 2 : 1);  // count blue twice
                if (goodDrops.charmPercent(i) > 10) // if high drop rate
                    mismatch /= (goodDrops.charmPercent(i)/10); // scale down
                int tenfold = (m_multi % 10 == 0 ? 100 : 10);
                if (m_charms[i] > 0 && m_charms[i] % tenfold == 0)
                    mismatch += 2; // extra 2 points if multiple of 10
            }
            return mismatch / 5;
        }

        private double charmPercent(int id)
        {
            if (m_kills <= 0 || m_multi <= 0)
                return 0;

            return 100.0 * m_charms[id] / m_kills / m_multi;
        }

        public String toPercentString()
        {
            return "" + (int)charmPercent(0) + "% " + (int)charmPercent(1) + "% "
                      + (int)charmPercent(2) + "% " + (int)charmPercent(3) + "%";
        }

        @Override
        public String toString()
        {
            return "" + m_kills + " => ( " + m_charms[0] + " " + m_charms[1] + " "
                         + m_charms[2] + " " + m_charms[3] + " )*" + m_multi + " => "
                         + toPercentString();
        }
    }
}