Search in sources :

Example 21 with EASE

use of com.ichi2.anki.CardBrowser.Column.EASE in project AnkiChinaAndroid by ankichinateam.

the class SchedV2Test method test_reviewsV2.

@Test
public void test_reviewsV2() throws Exception {
    Collection col = getColV2();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set the card up as a review card, due 8 days ago
    Card c = note.cards().get(0);
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_REV);
    c.setDue(col.getSched().getToday() - 8);
    c.setFactor(STARTING_FACTOR);
    c.setReps(3);
    c.setLapses(1);
    c.setIvl(100);
    c.startTimer();
    c.flush();
    // save it for later use as well
    Card cardcopy = c.clone();
    // try with an ease of 2
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.reset();
    col.getSched().answerCard(c, 2);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // the new interval should be (100) * 1.2 = 120
    assertTrue(checkRevIvl(col, c, 120));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been decremented
    assertEquals(2350, c.getFactor());
    // check counters
    assertEquals(1, c.getLapses());
    assertEquals(4, c.getReps());
    // ease 3
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 3);
    // the new interval should be (100 + 8/2) * 2.5 = 260
    assertTrue(checkRevIvl(col, c, 260));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been left alone
    assertEquals(STARTING_FACTOR, c.getFactor());
    // ease 4
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 4);
    // the new interval should be (100 + 8) * 2.5 * 1.3 = 351
    assertTrue(checkRevIvl(col, c, 351));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been increased
    assertEquals(2650, c.getFactor());
    // leech handling
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    DeckConfig conf = col.getDecks().getConf(1);
    conf.getJSONObject("lapse").put("leechAction", LEECH_SUSPEND);
    col.getDecks().save(conf);
    c = cardcopy.clone();
    c.setLapses(7);
    c.flush();
/* todo hook
        // steup hook
        hooked = new [] {};

        def onLeech(card):
        hooked.append(1);

        hooks.card_did_leech.append(onLeech);
        col.getSched().answerCard(c, 1);
        assertTrue(hooked);
        assertEquals(QUEUE_TYPE_SUSPENDED, c.getQueue());
        c.load();
        assertEquals(QUEUE_TYPE_SUSPENDED, c.getQueue());
        */
}
Also used : Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 22 with EASE

use of com.ichi2.anki.CardBrowser.Column.EASE in project Anki-Android by ankidroid.

the class SchedV2Test method test_reviewsV2.

@Test
public void test_reviewsV2() throws Exception {
    Collection col = getColV2();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set the card up as a review card, due 8 days ago
    Card c = note.cards().get(0);
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_REV);
    c.setDue(col.getSched().getToday() - 8);
    c.setFactor(STARTING_FACTOR);
    c.setReps(3);
    c.setLapses(1);
    c.setIvl(100);
    c.startTimer();
    c.flush();
    // save it for later use as well
    Card cardcopy = c.clone();
    // try with an ease of 2
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.reset();
    col.getSched().answerCard(c, BUTTON_TWO);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // the new interval should be (100) * 1.2 = 120
    assertTrue(checkRevIvl(col, c, 120));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been decremented
    assertEquals(2350, c.getFactor());
    // check counters
    assertEquals(1, c.getLapses());
    assertEquals(4, c.getReps());
    // ease 3
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, BUTTON_THREE);
    // the new interval should be (100 + 8/2) * 2.5 = 260
    assertTrue(checkRevIvl(col, c, 260));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been left alone
    assertEquals(STARTING_FACTOR, c.getFactor());
    // ease 4
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, BUTTON_FOUR);
    // the new interval should be (100 + 8) * 2.5 * 1.3 = 351
    assertTrue(checkRevIvl(col, c, 351));
    assertEquals(col.getSched().getToday() + c.getIvl(), c.getDue());
    // factor should have been increased
    assertEquals(2650, c.getFactor());
    // leech handling
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    DeckConfig conf = col.getDecks().getConf(1);
    conf.getJSONObject("lapse").put("leechAction", LEECH_SUSPEND);
    col.getDecks().save(conf);
    c = cardcopy.clone();
    c.setLapses(7);
    c.flush();
/* todo hook
        // steup hook
        hooked = new [] {};

        def onLeech(card):
        hooked.append(1);

        hooks.card_did_leech.append(onLeech);
        col.getSched().answerCard(c, BUTTON_ONE);
        assertTrue(hooked);
        assertEquals(QUEUE_TYPE_SUSPENDED, c.getQueue());
        c.load();
        assertEquals(QUEUE_TYPE_SUSPENDED, c.getQueue());
        */
}
Also used : Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 23 with EASE

use of com.ichi2.anki.CardBrowser.Column.EASE in project Anki-Android by ankidroid.

the class Sched method _updateRevIvl.

@Override
protected void _updateRevIvl(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
    try {
        int idealIvl = _nextRevIvl(card, ease);
        JSONObject conf = _revConf(card);
        card.setIvl(Math.min(Math.max(_adjRevIvl(card, idealIvl), card.getIvl() + 1), conf.getInt("maxIvl")));
    } catch (JSONException e) {
        throw new RuntimeException(e);
    }
}
Also used : JSONObject(com.ichi2.utils.JSONObject) JSONException(com.ichi2.utils.JSONException)

Example 24 with EASE

use of com.ichi2.anki.CardBrowser.Column.EASE in project Anki-Android by ankidroid.

the class Sched method _answerLrnCard.

/**
 * @param ease 1=no, 2=yes, 3=remove
 */
@Override
protected void _answerLrnCard(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
    JSONObject conf = _lrnConf(card);
    @Consts.REVLOG_TYPE int type;
    if (card.isInDynamicDeck() && !card.getWasNew()) {
        type = Consts.REVLOG_CRAM;
    } else if (card.getType() == Consts.CARD_TYPE_REV) {
        type = Consts.REVLOG_RELRN;
    } else {
        type = Consts.REVLOG_LRN;
    }
    boolean leaving = false;
    // lrnCount was decremented once when card was fetched
    int lastLeft = card.getLeft();
    // immediate graduate?
    if (ease == Consts.BUTTON_THREE) {
        _rescheduleAsRev(card, conf, true);
        leaving = true;
    // graduation time?
    } else if (ease == Consts.BUTTON_TWO && (card.getLeft() % 1000) - 1 <= 0) {
        _rescheduleAsRev(card, conf, false);
        leaving = true;
    } else {
        // one step towards graduation
        if (ease == Consts.BUTTON_TWO) {
            // decrement real left count and recalculate left today
            int left = (card.getLeft() % 1000) - 1;
            card.setLeft(_leftToday(conf.getJSONArray("delays"), left) * 1000 + left);
        // failed
        } else {
            card.setLeft(_startingLeft(card));
            boolean resched = _resched(card);
            if (conf.has("mult") && resched) {
                // review that's lapsed
                card.setIvl(Math.max(Math.max(1, (int) (card.getIvl() * conf.getDouble("mult"))), conf.getInt("minInt")));
            } else {
            // new card; no ivl adjustment
            // pass
            }
            if (resched && card.isInDynamicDeck()) {
                card.setODue(mToday + 1);
            }
        }
        int delay = _delayForGrade(conf, card.getLeft());
        if (card.getDue() < getTime().intTime()) {
            // not collapsed; add some randomness
            delay *= Utils.randomFloatInRange(1f, 1.25f);
        }
        card.setDue(getTime().intTime() + delay);
        // due today?
        if (card.getDue() < mDayCutoff) {
            mLrnCount += card.getLeft() / 1000;
            // if the queue is not empty and there's nothing else to do, make
            // sure we don't put it at the head of the queue and end up showing
            // it twice in a row
            card.setQueue(Consts.QUEUE_TYPE_LRN);
            if (!mLrnQueue.isEmpty() && revCount() == 0 && newCount() == 0) {
                long smallestDue = mLrnQueue.getFirstDue();
                card.setDue(Math.max(card.getDue(), smallestDue + 1));
            }
            _sortIntoLrn(card.getDue(), card.getId());
        } else {
            // the card is due in one or more days, so we need to use the day learn queue
            long ahead = ((card.getDue() - mDayCutoff) / SECONDS_PER_DAY) + 1;
            card.setDue(mToday + ahead);
            card.setQueue(Consts.QUEUE_TYPE_DAY_LEARN_RELEARN);
        }
    }
    _logLrn(card, ease, conf, leaving, type, lastLeft);
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Example 25 with EASE

use of com.ichi2.anki.CardBrowser.Column.EASE in project Anki-Android by ankidroid.

the class SchedV2 method _earlyReviewIvl.

/**
 * next interval for card when answered early+correctly
 */
private int _earlyReviewIvl(@NonNull Card card, @Consts.BUTTON_TYPE int ease) {
    if (!card.isInDynamicDeck() || card.getType() != Consts.CARD_TYPE_REV || card.getFactor() == 0) {
        throw new RuntimeException("Unexpected card parameters");
    }
    if (ease <= 1) {
        throw new RuntimeException("Ease must be greater than 1");
    }
    long elapsed = card.getIvl() - (card.getODue() - mToday);
    @NonNull JSONObject conf = _revConf(card);
    double easyBonus = 1;
    // early 3/4 reviews shouldn't decrease previous interval
    double minNewIvl = 1;
    double factor;
    if (ease == Consts.BUTTON_TWO) {
        factor = conf.optDouble("hardFactor", 1.2);
        // hard cards shouldn't have their interval decreased by more than 50%
        // of the normal factor
        minNewIvl = factor / 2;
    } else if (ease == 3) {
        factor = card.getFactor() / 1000.0;
    } else {
        // ease == 4
        factor = card.getFactor() / 1000.0;
        double ease4 = conf.getDouble("ease4");
        // 1.3 -> 1.15
        easyBonus = ease4 - (ease4 - 1) / 2;
    }
    double ivl = Math.max(elapsed * factor, 1);
    // cap interval decreases
    ivl = Math.max(card.getIvl() * minNewIvl, ivl) * easyBonus;
    return _constrainedIvl(ivl, conf, 0, false);
}
Also used : JSONObject(com.ichi2.utils.JSONObject) NonNull(androidx.annotation.NonNull)

Aggregations

JSONObject (com.ichi2.utils.JSONObject)16 RobolectricTest (com.ichi2.anki.RobolectricTest)9 Test (org.junit.Test)9 Card (com.ichi2.libanki.Card)8 Collection (com.ichi2.libanki.Collection)7 Note (com.ichi2.libanki.Note)6 DeckConfig (com.ichi2.libanki.DeckConfig)4 JSONArray (com.ichi2.utils.JSONArray)4 JSONException (com.ichi2.utils.JSONException)4 WebView (android.webkit.WebView)3 DB (com.ichi2.libanki.DB)3 Config (org.robolectric.annotation.Config)3 SuppressLint (android.annotation.SuppressLint)2 NonNull (androidx.annotation.NonNull)2 Model (com.ichi2.libanki.Model)2 Map (java.util.Map)2 Set (java.util.Set)2 MimeTypeMap (android.webkit.MimeTypeMap)1 AnkiDb (com.ichi2.anki.AnkiDb)1 SchedulerService (com.ichi2.anki.servicelayer.SchedulerService)1