spaced_repetition 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +35 -0
- data/lib/spaced_repetition/sm2.rb +63 -0
- data/lib/spaced_repetition/sm2_mod.rb +69 -0
- data/lib/spaced_repetition.rb +2 -0
- data/spaced_repetition.gemspec +18 -0
- data/test/test_sm2.rb +54 -0
- metadata +87 -0
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Spaced Repetition algorithm for Ruby.
|
2
|
+
|
3
|
+
## Install
|
4
|
+
Add to your Gemfile:
|
5
|
+
|
6
|
+
gem 'spaced_repetition', :git => 'git://github.com/espresse/spaced_repetition.git'
|
7
|
+
|
8
|
+
and run
|
9
|
+
|
10
|
+
bundle install
|
11
|
+
|
12
|
+
## Typical use-case:
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'spaced_repetition'
|
16
|
+
|
17
|
+
#user's quality_response is 5, no previous repetition had been done yet
|
18
|
+
sm2 = SpacedRepetition::Sm2.new(5)
|
19
|
+
|
20
|
+
#user's quality response is 3, his/her prevoius interval was 3 days and easiness factor was 2.1
|
21
|
+
sm2 = SpacedRepetition::Sm2.new(3,3,2.1)
|
22
|
+
|
23
|
+
You can fetch results:
|
24
|
+
|
25
|
+
#new interval
|
26
|
+
new_interval = sm2.interval
|
27
|
+
|
28
|
+
#new easiness_factor
|
29
|
+
new_ef = sm2.easniness_factor
|
30
|
+
|
31
|
+
#new repetition date
|
32
|
+
new_date = sm2.next_repetition_date
|
33
|
+
|
34
|
+
By default, SM2, uses 6 possible answers (0-5), where 0 is very bad and 5 is perfect. If you want to use 4 answers (0 /very bad/ - 3 /perfect/) you can choose SM2Mod. It works in the same manner as SM2 does.
|
35
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#http://www.supermemo.com/english/ol/sm2.htm
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module SpacedRepetition
|
5
|
+
class Sm2
|
6
|
+
def initialize(quality_response, prev_interval=0, prev_ef=2.5)
|
7
|
+
@prev_ef = prev_ef
|
8
|
+
@prev_interval = prev_interval
|
9
|
+
@quality_response = quality_response
|
10
|
+
|
11
|
+
@calculated_interval = nil
|
12
|
+
@calculated_ef = nil
|
13
|
+
@repetition_date = nil
|
14
|
+
|
15
|
+
#if quality_response is below 3 start repetition from the begining, but without changing easiness_factor
|
16
|
+
if @quality_response < 3
|
17
|
+
@prev_interval=0
|
18
|
+
@calculated_ef = @prev_ef
|
19
|
+
else
|
20
|
+
calculate_easiness_factor
|
21
|
+
end
|
22
|
+
calculate_interval
|
23
|
+
|
24
|
+
calculate_date
|
25
|
+
end
|
26
|
+
|
27
|
+
def interval
|
28
|
+
@calculated_interval
|
29
|
+
end
|
30
|
+
|
31
|
+
def easiness_factor
|
32
|
+
@calculated_ef
|
33
|
+
end
|
34
|
+
|
35
|
+
def next_repetition_date
|
36
|
+
@repetition_date
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def calculate_interval
|
42
|
+
if @prev_interval == 0
|
43
|
+
@calculated_interval = 1
|
44
|
+
elsif @prev_interval == 1
|
45
|
+
@calculated_interval = 6
|
46
|
+
else
|
47
|
+
@calculated_interval = (@prev_interval*@prev_ef).to_i
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def calculate_easiness_factor
|
52
|
+
@calculated_ef = @prev_ef+(0.1-(5-@quality_response)*(0.08+(5-@quality_response)*0.02))
|
53
|
+
if @calculated_ef < 1.3
|
54
|
+
@calculated_ef = 1.3
|
55
|
+
end
|
56
|
+
@calculated_ef
|
57
|
+
end
|
58
|
+
|
59
|
+
def calculate_date
|
60
|
+
@repetition_date = Date.today + @calculated_interval
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#modified sm2 algorithm
|
2
|
+
#changes:
|
3
|
+
# - initial interval depends on given quality_response
|
4
|
+
# - states (easiness_factor is calulated using that states, sm2 for ok uses 4)
|
5
|
+
# - 0 - bad (0, -0.8), 1 - so so(2, -0.3), 2 - ok(4, 0), 3 - more than better(5, 0.1)
|
6
|
+
require 'date'
|
7
|
+
|
8
|
+
module SpacedRepetition
|
9
|
+
class Sm2Mod
|
10
|
+
def initialize(quality_response, prev_interval=0, prev_ef=2.5)
|
11
|
+
@prev_ef = prev_ef
|
12
|
+
@prev_interval = prev_interval
|
13
|
+
@quality_response = quality_response
|
14
|
+
|
15
|
+
@calculated_interval = nil
|
16
|
+
@calculated_ef = nil
|
17
|
+
@repetition_date = nil
|
18
|
+
|
19
|
+
#if quality_response is below 3 start repetition from the begining, but without changing easiness_factor
|
20
|
+
if @quality_response < 2
|
21
|
+
@prev_interval=0
|
22
|
+
@calculated_ef = @prev_ef
|
23
|
+
else
|
24
|
+
calculate_easiness_factor
|
25
|
+
end
|
26
|
+
calculate_interval
|
27
|
+
calculate_date
|
28
|
+
end
|
29
|
+
|
30
|
+
def interval
|
31
|
+
@calculated_interval
|
32
|
+
end
|
33
|
+
|
34
|
+
def easiness_factor
|
35
|
+
@calculated_ef
|
36
|
+
end
|
37
|
+
|
38
|
+
def next_repetition_date
|
39
|
+
@repetition_date
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def calculate_interval
|
45
|
+
if @prev_interval == 0
|
46
|
+
@calculated_interval = 1
|
47
|
+
if @quality_response == 3
|
48
|
+
@calculated_interval = 6
|
49
|
+
end
|
50
|
+
elsif @prev_interval == 1
|
51
|
+
@calculated_interval = 6
|
52
|
+
else
|
53
|
+
@calculated_interval = (@prev_interval*@prev_ef).to_i
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def calculate_easiness_factor
|
58
|
+
@calculated_ef = @prev_ef+(0.1-(3-@quality_response)*((3-@quality_response)*0.1))
|
59
|
+
if @calculated_ef < 1.3
|
60
|
+
@calculated_ef = 1.3
|
61
|
+
end
|
62
|
+
@calculated_ef
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_date
|
66
|
+
@repetition_date = Date.today + @calculated_interval
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "spaced_repetition"
|
5
|
+
s.version = "1.0.0"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Michał Ostrowski"]
|
8
|
+
s.email = ["michol@linuxcsb.org"]
|
9
|
+
s.summary = %q{Ruby gem for calculating spaced repetition intervals}
|
10
|
+
s.description = %q{Ruby gem for calculating spaced repetition intervals.}
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.add_dependency("bundler", ["~> 1.0"])
|
18
|
+
end
|
data/test/test_sm2.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spaced_repetition"
|
2
|
+
require "test/unit"
|
3
|
+
|
4
|
+
class TestSm2 < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_scenario_user_starts_learning_new_element
|
7
|
+
sm2 = SpacedRepetition::Sm2.new(5)
|
8
|
+
assert_equal(1, sm2.interval)
|
9
|
+
assert_in_delta 2.6, sm2.easiness_factor, 0.001
|
10
|
+
|
11
|
+
sm2 = SpacedRepetition::Sm2.new(4)
|
12
|
+
assert_equal(1, sm2.interval)
|
13
|
+
assert_in_delta 2.5, sm2.easiness_factor, 0.001
|
14
|
+
|
15
|
+
sm2 = SpacedRepetition::Sm2.new(3)
|
16
|
+
assert_equal(1, sm2.interval)
|
17
|
+
assert_in_delta 2.36, sm2.easiness_factor, 0.001
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_scenario_user_is_doing_good
|
21
|
+
sm2 = SpacedRepetition::Sm2.new(4, 6, 2.1)
|
22
|
+
assert_equal(12, sm2.interval)
|
23
|
+
assert_in_delta 2.1, sm2.easiness_factor, 0.001
|
24
|
+
|
25
|
+
sm2 = SpacedRepetition::Sm2.new(5, 12, 2.1)
|
26
|
+
assert_equal(25, sm2.interval)
|
27
|
+
assert_in_delta 2.2, sm2.easiness_factor, 0.001
|
28
|
+
|
29
|
+
sm2 = SpacedRepetition::Sm2.new(5, 25, 2.1)
|
30
|
+
assert_equal(52, sm2.interval)
|
31
|
+
assert_in_delta 2.2, sm2.easiness_factor, 0.001
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_scenario_user_is_doing_good_but_later_is_going_bad
|
35
|
+
sm2 = SpacedRepetition::Sm2.new(3, 6, 2.1)
|
36
|
+
assert_equal(12, sm2.interval)
|
37
|
+
assert_in_delta 1.96, sm2.easiness_factor, 0.001
|
38
|
+
|
39
|
+
sm2 = SpacedRepetition::Sm2.new(0, 12, 1.96)
|
40
|
+
assert_equal(1, sm2.interval)
|
41
|
+
assert_in_delta 1.96, sm2.easiness_factor, 0.001
|
42
|
+
|
43
|
+
sm2 = SpacedRepetition::Sm2.new(0, 1, 1.96)
|
44
|
+
assert_equal(1, sm2.interval)
|
45
|
+
assert_in_delta 1.96, sm2.easiness_factor, 0.001
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_scenario_user_passed_char_instead_of_number
|
49
|
+
assert_raise( ArgumentError ) { SpacedRepetition::Sm2.new("a", 6, 2.1) }
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spaced_repetition
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Micha\xC5\x82 Ostrowski"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-16 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: Ruby gem for calculating spaced repetition intervals.
|
37
|
+
email:
|
38
|
+
- michol@linuxcsb.org
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- README.md
|
47
|
+
- lib/spaced_repetition.rb
|
48
|
+
- lib/spaced_repetition/sm2.rb
|
49
|
+
- lib/spaced_repetition/sm2_mod.rb
|
50
|
+
- spaced_repetition.gemspec
|
51
|
+
- test/test_sm2.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage:
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 1.3.7
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: Ruby gem for calculating spaced repetition intervals
|
86
|
+
test_files: []
|
87
|
+
|