Search in sources :

Example 1 with INTERVAL

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

the class SchedV2 method _nextRevIvl.

/**
 * Interval management ******************************************************
 * *****************************************
 */
/**
 * Next interval for CARD, given EASE.
 */
protected int _nextRevIvl(@NonNull Card card, @Consts.BUTTON_TYPE int ease, boolean fuzz) {
    long delay = _daysLate(card);
    JSONObject conf = _revConf(card);
    double fct = card.getFactor() / 1000.0;
    double hardFactor = conf.optDouble("hardFactor", 1.2);
    int hardMin;
    if (hardFactor > 1) {
        hardMin = card.getIvl();
    } else {
        hardMin = 0;
    }
    int ivl2 = _constrainedIvl(card.getIvl() * hardFactor, conf, hardMin, fuzz);
    if (ease == Consts.BUTTON_TWO) {
        return ivl2;
    }
    int ivl3 = _constrainedIvl((card.getIvl() + delay / 2) * fct, conf, ivl2, fuzz);
    if (ease == Consts.BUTTON_THREE) {
        return ivl3;
    }
    int ivl4 = _constrainedIvl(((card.getIvl() + delay) * fct * conf.getDouble("ease4")), conf, ivl3, fuzz);
    return ivl4;
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Example 2 with INTERVAL

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

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.getODid() == 0 || 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)

Example 3 with INTERVAL

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

the class SchedTest method test_reviewsV1.

@Test
public void test_reviewsV1() throws Exception {
    Collection col = getColV1();
    // 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();
    // failing it should put it in the learn queue with the default options
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    // different delay to new
    col.reset();
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("lapse").put("delays", new JSONArray(new double[] { 2, 20 }));
    col.getDecks().save(conf);
    col.getSched().answerCard(c, 1);
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // it should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getODue());
    assertEquals(1, c.getIvl());
    // but because it's in the learn queue, its current due time should be in
    // the future
    assertThat(c.getDue(), is(greaterThanOrEqualTo(col.getTime().intTime())));
    assertThat(c.getDue() - col.getTime().intTime(), is(greaterThan(118L)));
    // factor should have been decremented
    assertEquals(2300, c.getFactor());
    // check counters
    assertEquals(2, c.getLapses());
    assertEquals(4, c.getReps());
    // check ests.
    assertEquals(120, col.getSched().nextIvl(c, 1));
    assertEquals(20 * 60, col.getSched().nextIvl(c, 2));
    // try again with an ease of 2 instead
    // //////////////////////////////////////////////////////////////////////////////////////////////////
    c = cardcopy.clone();
    c.flush();
    col.getSched().answerCard(c, 2);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // the new interval should be (100 + 8/4) * 1.2 = 122
    assertTrue(checkRevIvl(col, c, 122));
    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());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) 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 4 with INTERVAL

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

the class SchedTest method test_learnV1.

@Test
public void test_learnV1() throws Exception {
    Collection col = getColV1();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set as a learn card and rebuild queues
    col.getDb().execute("update cards set queue=0, type=0");
    col.reset();
    // sched.getCard should return it, since it's due in the past
    Card c = col.getSched().getCard();
    assertNotNull(c);
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("new").put("delays", new JSONArray(new double[] { 0.5, 3, 10 }));
    col.getDecks().save(conf);
    // fail it
    col.getSched().answerCard(c, 1);
    // it should have three reps left to graduation
    assertEquals(3, c.getLeft() % 1000);
    assertEquals(3, c.getLeft() / 1000);
    // it should be due in 30 seconds
    long t = Math.round(c.getDue() - col.getTime().intTime());
    assertThat(t, is(greaterThanOrEqualTo(25L)));
    assertThat(t, is(lessThanOrEqualTo(40L)));
    // pass it once
    col.getSched().answerCard(c, 2);
    // it should be due in 3 minutes
    assertEquals(Math.round(c.getDue() - col.getTime().intTime()), 179, 1);
    assertEquals(2, c.getLeft() % 1000);
    assertEquals(2, c.getLeft() / 1000);
    // check log is accurate
    Cursor log = col.getDb().getDatabase().query("select * from revlog order by id desc");
    assertTrue(log.moveToFirst());
    assertEquals(2, log.getInt(3));
    assertEquals(-180, log.getInt(4));
    assertEquals(-30, log.getInt(5));
    // pass again
    col.getSched().answerCard(c, 2);
    // it should be due in 10 minutes
    assertEquals(c.getDue() - col.getTime().intTime(), 599, 1);
    assertEquals(1, c.getLeft() % 1000);
    assertEquals(1, c.getLeft() / 1000);
    // the next pass should graduate the card
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    assertEquals(CARD_TYPE_LRN, c.getType());
    col.getSched().answerCard(c, 2);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(CARD_TYPE_REV, c.getType());
    // should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    assertEquals(1, c.getIvl());
    // or normal removal
    c.setType(CARD_TYPE_NEW);
    c.setQueue(QUEUE_TYPE_LRN);
    col.getSched().answerCard(c, 3);
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertTrue(checkRevIvl(col, c, 4));
    // revlog should have been updated each time
    assertEquals(5, col.getDb().queryScalar("select count() from revlog where type = 0"));
    // now failed card handling
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_LRN);
    c.setODue(123);
    col.getSched().answerCard(c, 3);
    assertEquals(123, c.getDue());
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // we should be able to remove manually, too
    c.setType(CARD_TYPE_REV);
    c.setQueue(QUEUE_TYPE_LRN);
    c.setODue(321);
    c.flush();
    ((Sched) col.getSched()).removeLrn();
    c.load();
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(321, c.getDue());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Cursor(android.database.Cursor) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 5 with INTERVAL

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

the class SchedV2Test method test_learnV2.

@Test
public void test_learnV2() throws Exception {
    Collection col = getColV2();
    // add a note
    Note note = col.newNote();
    note.setItem("Front", "one");
    note.setItem("Back", "two");
    col.addNote(note);
    // set as a learn card and rebuild queues
    col.getDb().execute("update cards set queue=0, type=0");
    col.reset();
    // sched.getCard should return it, since it's due in the past
    Card c = col.getSched().getCard();
    assertNotNull(c);
    DeckConfig conf = col.getSched()._cardConf(c);
    conf.getJSONObject("new").put("delays", new JSONArray(new double[] { 0.5, 3, 10 }));
    col.getDecks().save(conf);
    // fail it
    col.getSched().answerCard(c, 1);
    // it should have three reps left to graduation
    assertEquals(3, c.getLeft() % 1000);
    assertEquals(3, c.getLeft() / 1000);
    // it should be due in 30 seconds
    long t = Math.round(c.getDue() - col.getTime().intTime());
    assertThat(t, is(greaterThanOrEqualTo(25L)));
    assertThat(t, is(lessThanOrEqualTo(40L)));
    // pass it once
    col.getSched().answerCard(c, 3);
    // it should be due in 3 minutes
    long dueIn = c.getDue() - col.getTime().intTime();
    assertThat(dueIn, is(greaterThanOrEqualTo(178L)));
    assertThat(dueIn, is(lessThanOrEqualTo((long) (180 * 1.25))));
    assertEquals(2, c.getLeft() % 1000);
    assertEquals(2, c.getLeft() / 1000);
    // check log is accurate
    Cursor log = col.getDb().getDatabase().query("select * from revlog order by id desc");
    assertTrue(log.moveToFirst());
    assertEquals(3, log.getInt(3));
    assertEquals(-180, log.getInt(4));
    assertEquals(-30, log.getInt(5));
    // pass again
    col.getSched().answerCard(c, 3);
    // it should be due in 10 minutes
    dueIn = c.getDue() - col.getTime().intTime();
    assertThat(dueIn, is(greaterThanOrEqualTo(599L)));
    assertThat(dueIn, is(lessThanOrEqualTo((long) (600 * 1.25))));
    assertEquals(1, c.getLeft() % 1000);
    assertEquals(1, c.getLeft() / 1000);
    // the next pass should graduate the card
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    assertEquals(CARD_TYPE_LRN, c.getType());
    col.getSched().answerCard(c, 3);
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertEquals(CARD_TYPE_REV, c.getType());
    // should be due tomorrow, with an interval of 1
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    assertEquals(1, c.getIvl());
    // or normal removal
    c.setType(CARD_TYPE_NEW);
    c.setQueue(QUEUE_TYPE_LRN);
    col.getSched().answerCard(c, 4);
    assertEquals(CARD_TYPE_REV, c.getType());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    assertTrue(checkRevIvl(col, c, 4));
    // revlog should have been updated each time
    assertEquals(5, col.getDb().queryScalar("select count() from revlog where type = 0"));
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Cursor(android.database.Cursor) DeckConfig(com.ichi2.libanki.DeckConfig) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Aggregations

RobolectricTest (com.ichi2.anki.RobolectricTest)15 Test (org.junit.Test)15 Card (com.ichi2.libanki.Card)12 Collection (com.ichi2.libanki.Collection)12 DeckConfig (com.ichi2.libanki.DeckConfig)12 Note (com.ichi2.libanki.Note)12 JSONArray (com.ichi2.utils.JSONArray)8 JSONObject (com.ichi2.utils.JSONObject)8 Cursor (android.database.Cursor)4 WebView (android.webkit.WebView)3 Config (org.robolectric.annotation.Config)3 NonNull (androidx.annotation.NonNull)2 SQLException (android.database.SQLException)1 AnkiDb (com.ichi2.anki.AnkiDb)1