diff --git a/anki/decks.py b/anki/decks.py
index 6d3a58b59..fe1c980bb 100644
--- a/anki/decks.py
+++ b/anki/decks.py
@@ -76,6 +76,7 @@ defaultConf = {
'maxIvl': 36500,
# may not be set on old decks
'bury': False,
+ 'hardFactor': 1.2,
},
'maxTaken': 60,
'timer': 0,
diff --git a/anki/schedv2.py b/anki/schedv2.py
index 2fc9dda49..d310117ec 100644
--- a/anki/schedv2.py
+++ b/anki/schedv2.py
@@ -898,7 +898,12 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
delay = self._daysLate(card)
conf = self._revConf(card)
fct = card.factor / 1000
- ivl2 = self._constrainedIvl(card.ivl * 1.2, conf, card.ivl, fuzz)
+ hardFactor = conf.get("hardFactor", 1.2)
+ if hardFactor > 1:
+ hardMin = card.ivl
+ else:
+ hardMin = 0
+ ivl2 = self._constrainedIvl(card.ivl * hardFactor, conf, hardMin, fuzz)
if ease == 2:
return ivl2
@@ -963,9 +968,10 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
minNewIvl = 1
if ease == 2:
- factor = 1.2
+ factor = conf.get("hardFactor", 1.2)
# hard cards shouldn't have their interval decreased by more than 50%
- minNewIvl = 0.5
+ # of the normal factor
+ minNewIvl = factor / 2
elif ease == 3:
factor = card.factor / 1000
else: # ease == 4:
diff --git a/aqt/deckconf.py b/aqt/deckconf.py
index c176b5fdf..e26a40187 100644
--- a/aqt/deckconf.py
+++ b/aqt/deckconf.py
@@ -185,6 +185,10 @@ class DeckConf(QDialog):
f.maxIvl.setValue(c['maxIvl'])
f.revplim.setText(self.parentLimText('rev'))
f.buryRev.setChecked(c.get("bury", True))
+ f.hardFactor.setValue(int(c.get("hardFactor", 1.2)*100))
+ if self.mw.col.schedVer() == 1:
+ f.hardFactor.setVisible(False)
+ f.hardFactorLabel.setVisible(False)
# lapse
c = self.conf['lapse']
f.lapSteps.setText(self.listToUser(c['delays']))
@@ -267,6 +271,7 @@ class DeckConf(QDialog):
c['ivlFct'] = f.fi1.value()/100.0
c['maxIvl'] = f.maxIvl.value()
c['bury'] = f.buryRev.isChecked()
+ c['hardFactor'] = f.hardFactor.value()/100.0
# lapse
c = self.conf['lapse']
self.updateList(c, 'delays', f.lapSteps, minSize=0)
diff --git a/designer/dconf.ui b/designer/dconf.ui
index 141b9d1b0..26eb6cc90 100644
--- a/designer/dconf.ui
+++ b/designer/dconf.ui
@@ -77,7 +77,16 @@
New Cards
-
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
12
-
@@ -227,7 +236,16 @@
Reviews
-
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
12
-
@@ -343,13 +361,30 @@
- -
+
-
Bury related reviews until the next day
+ -
+
+
+ Hard interval
+
+
+
+ -
+
+
+ 5
+
+
+ 120
+
+
+
-
@@ -372,7 +407,16 @@
Lapses
-
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
12
-
@@ -521,7 +565,16 @@
General
-
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
12
-
@@ -605,7 +658,16 @@
12
-
+
+ 12
+
+
+ 12
+
+
+ 12
+
+
12
-
@@ -649,6 +711,7 @@
easyBonus
fi1
maxIvl
+ hardFactor
buryRev
lapSteps
lapMult
@@ -659,7 +722,6 @@
showTimer
autoplaySounds
replayQuestion
- buttonBox
desc
diff --git a/tests/test_schedv2.py b/tests/test_schedv2.py
index 5e5f68a9b..96c0d48ee 100644
--- a/tests/test_schedv2.py
+++ b/tests/test_schedv2.py
@@ -451,6 +451,11 @@ def test_button_spacing():
assert ni(c, 3) == "3 days"
assert ni(c, 4) == "4 days"
+ # if hard factor is <= 1, then hard may not increase
+ conf = d.decks.confForDid(1)
+ conf['rev']['hardFactor'] = 1
+ assert ni(c, 2) == "1 day"
+
def test_overdue_lapse():
# disabled in commit 3069729776990980f34c25be66410e947e9d51a2
return
@@ -695,7 +700,7 @@ def test_filt_reviewing_early_normal():
d.reset()
c = d.sched.getCard()
- assert d.sched.nextIvl(c, 2) == 50*86400
+ assert d.sched.nextIvl(c, 2) == 60*86400
assert d.sched.nextIvl(c, 3) == 100*86400
assert d.sched.nextIvl(c, 4) == 101*86400