engine 0.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.
- checksums.yaml +7 -0
 - data/.gitignore +1 -0
 - data/README.md +1 -0
 - data/Rakefile +12 -0
 - data/engine.gemspec +24 -0
 - data/lib/engine/card.rb +23 -0
 - data/lib/engine/card_state.rb +58 -0
 - data/lib/engine/cli.rb +36 -0
 - data/lib/engine/deck.rb +23 -0
 - data/lib/engine/fixtures.rb +15 -0
 - data/lib/engine/rating.rb +18 -0
 - data/lib/engine/strategies.rb +66 -0
 - data/lib/engine/version.rb +3 -0
 - data/lib/engine.rb +20 -0
 - metadata +100 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA1:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 668d7179c24a01408a2a5a9662ef97e34f541438
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: af96c0c16a180cfc759f17659486a4272df85bc9
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8a05758d9e2d029ed925ab197b4f8c30ed29d2b0a42348ef58e2c7b84f79bcf3ad5f770e7c7102924fb7756f658eb69efd25dda8b474d7bc2d339c66a4bd178a
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a83d69cef23e40c28762d4633083082b7116968759923bf508ba00aee8c532b2d694902f385cedfc41c4e818e8d16d71cef571a25c2b7c9a172a8858f083c1fb
         
     | 
    
        data/.gitignore
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            pkg
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Flashcard Engine with Spaced Repetition
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems/package_task'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            spec = Gem::Specification.load(File.expand_path('../engine.gemspec', __FILE__))
         
     | 
| 
      
 4 
     | 
    
         
            +
            gem = Gem::PackageTask.new(spec)
         
     | 
| 
      
 5 
     | 
    
         
            +
            gem.define
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            desc "Push gem to rubygems.org"
         
     | 
| 
      
 8 
     | 
    
         
            +
            task :push => :gem do
         
     | 
| 
      
 9 
     | 
    
         
            +
              #sh "git tag v#{Engine::VERSION}"
         
     | 
| 
      
 10 
     | 
    
         
            +
              sh "git push --tags"
         
     | 
| 
      
 11 
     | 
    
         
            +
              sh "gem push pkg/engine-#{Engine::VERSION}.gem"
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
    
        data/engine.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require File.expand_path('../lib/engine/version', __FILE__)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |gem|
         
     | 
| 
      
 6 
     | 
    
         
            +
              gem.name        = 'engine'
         
     | 
| 
      
 7 
     | 
    
         
            +
              gem.version     = Engine::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              gem.authors     = [ 'Arne Brasseur' ]
         
     | 
| 
      
 9 
     | 
    
         
            +
              gem.email       = [ 'arne@arnebrasseur.net' ]
         
     | 
| 
      
 10 
     | 
    
         
            +
              gem.description = 'Flashcard Engine with Spaced Repetition.'
         
     | 
| 
      
 11 
     | 
    
         
            +
              gem.summary     = gem.description
         
     | 
| 
      
 12 
     | 
    
         
            +
              gem.homepage    = 'https://github.com/plexus/engine'
         
     | 
| 
      
 13 
     | 
    
         
            +
              gem.license     = 'MIT'
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              gem.require_paths    = %w[lib]
         
     | 
| 
      
 16 
     | 
    
         
            +
              gem.files            = `git ls-files`.split($/)
         
     | 
| 
      
 17 
     | 
    
         
            +
              gem.test_files       = `git ls-files -- spec`.split($/)
         
     | 
| 
      
 18 
     | 
    
         
            +
              gem.extra_rdoc_files = %w[README.md]
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              gem.add_runtime_dependency 'hexp', '~> 0.2.0'
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              gem.add_development_dependency 'rake', '~> 10.1'
         
     | 
| 
      
 23 
     | 
    
         
            +
              gem.add_development_dependency 'rspec', '~> 2.14'
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engine/card.rb
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Card < Struct.new(:data, :card_state)
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 4 
     | 
    
         
            +
                def_delegators :card_state, :factor, :iteration, :streak, :interval, :expired?, :expired_for_seconds, :last_shown
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def new?
         
     | 
| 
      
 7 
     | 
    
         
            +
                  card_state.empty?
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def rate(rating)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  next_card_state = card_state << Rating.new(Time.now, rating)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  self.class.new(data, next_card_state)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def lapsed?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  streak == 0
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "<Card #{data.inspect} #{card_state.inspect}>"
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # CardStates form a linked list, each pointing to the previous state plus
         
     | 
| 
      
 4 
     | 
    
         
            +
              # the new data point. The actual factor/interval, streak are calculated
         
     | 
| 
      
 5 
     | 
    
         
            +
              # recursively based on the previous value, and the new data point.
         
     | 
| 
      
 6 
     | 
    
         
            +
              class CardState < Struct.new(:parent, :data_point)
         
     | 
| 
      
 7 
     | 
    
         
            +
                STRATEGY = AnkiStrategy
         
     | 
| 
      
 8 
     | 
    
         
            +
                EMPTY = CardState.new
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                START = {
         
     | 
| 
      
 11 
     | 
    
         
            +
                  iteration: 0,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  streak: 0,
         
     | 
| 
      
 13 
     | 
    
         
            +
                  factor: 2.5,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  interval: 1
         
     | 
| 
      
 15 
     | 
    
         
            +
                }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def << data_point
         
     | 
| 
      
 18 
     | 
    
         
            +
                  self.class.new(self, data_point)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def strategy
         
     | 
| 
      
 22 
     | 
    
         
            +
                  STRATEGY.new(parent, data_point)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def empty?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  parent.nil?
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def data_points(&blk)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return to_enum(__method__) unless block_given?
         
     | 
| 
      
 31 
     | 
    
         
            +
                  unless empty?
         
     | 
| 
      
 32 
     | 
    
         
            +
                    parent.data_points(&blk)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    yield data_point
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def last_shown
         
     | 
| 
      
 38 
     | 
    
         
            +
                  data_points.to_a.last.timestamp
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def expired?(time)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  (!empty? && data_point.fail?) || expired_for_seconds(time) > 0
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def expired_for_seconds(time)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  empty? ? 0 : [time - last_shown - interval * 60, 0].max
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def iteration ; empty? ? START[:iteration] : parent.iteration + 1   end
         
     | 
| 
      
 50 
     | 
    
         
            +
                def streak    ; empty? ? START[:streak]    : strategy.next_streak   end
         
     | 
| 
      
 51 
     | 
    
         
            +
                def factor    ; empty? ? START[:factor]    : strategy.next_factor   end
         
     | 
| 
      
 52 
     | 
    
         
            +
                def interval  ; empty? ? START[:interval]  : strategy.next_interval end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "<#{self.class} iteration: %d, streak: %d, factor: %.2f, interval: %.2f>" % [iteration, streak, factor, interval]
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engine/cli.rb
    ADDED
    
    | 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class CLI
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :card, :cards
         
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(cards)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @cards = Deck.new(cards)
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def next_card
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @card = cards.sample
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def rate_card(score)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  puts "Before : " + card.inspect
         
     | 
| 
      
 14 
     | 
    
         
            +
                  card.rate(score).tap do |new_card|
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @cards = @cards.update_card(card, new_card)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @card  = new_card
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  puts "After  : " + card.inspect
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def readline_loop
         
     | 
| 
      
 22 
     | 
    
         
            +
                  next_card
         
     | 
| 
      
 23 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    input = Readline.readline("#{card.data.first} > ", true)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    exit unless input
         
     | 
| 
      
 26 
     | 
    
         
            +
                    case input
         
     | 
| 
      
 27 
     | 
    
         
            +
                    when /s/, ""
         
     | 
| 
      
 28 
     | 
    
         
            +
                      puts card.data.last
         
     | 
| 
      
 29 
     | 
    
         
            +
                    when /[0-5]/
         
     | 
| 
      
 30 
     | 
    
         
            +
                      rate_card(input.to_i)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      next_card
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engine/deck.rb
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Deck < DelegateClass(Array)
         
     | 
| 
      
 3 
     | 
    
         
            +
                %w[select map reject grep reverse sort_by sort].each do |array_method|
         
     | 
| 
      
 4 
     | 
    
         
            +
                  define_method array_method do |*args, &blk|
         
     | 
| 
      
 5 
     | 
    
         
            +
                    self.class.new(super(*args, &blk))
         
     | 
| 
      
 6 
     | 
    
         
            +
                  end
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def expired_cards(time)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  select {|card| card.expired?(time) }.sort_by do |card|
         
     | 
| 
      
 11 
     | 
    
         
            +
                    [card.expired_for_seconds(time), -card.last_shown.to_i]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end.reverse
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def new_cards
         
     | 
| 
      
 16 
     | 
    
         
            +
                  select(&:new?)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def update_card(old, new)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  self.class.new(take(index(old)) + [new] + drop(index(old) + 1))
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- coding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 3 
     | 
    
         
            +
              FIXTURES = [
         
     | 
| 
      
 4 
     | 
    
         
            +
                [ '入選', "(入选) [ru4 xuan3] /to be chosen/to be elected as/" ],
         
     | 
| 
      
 5 
     | 
    
         
            +
                [ '報名', "(报名) [bao4 ming2] /to sign up/to enter one's name/to apply/to register/to enroll/to enlist/" ],
         
     | 
| 
      
 6 
     | 
    
         
            +
                [ '訊息', "(讯息) [xun4 xi1] /information/news/message/text message or SMS/" ],
         
     | 
| 
      
 7 
     | 
    
         
            +
                [ '詢問', "(询问) [xun2 wen4] /to inquire/" ],
         
     | 
| 
      
 8 
     | 
    
         
            +
                [ '導致', "(导致) [dao3 zhi4] /to lead to/to create/to cause/to bring about/" ],
         
     | 
| 
      
 9 
     | 
    
         
            +
                [ '琴', "[qin2] /guqin or zither, cf 古琴[gu3 qin2]/musical instrument in general/" ],
         
     | 
| 
      
 10 
     | 
    
         
            +
                [ '提供', "[ti2 gong1] /to offer/to supply/to provide/to furnish/" ],
         
     | 
| 
      
 11 
     | 
    
         
            +
                [ '更新', "[geng1 xin1] /to replace the old with new/to renew/to renovate/to upgrade/to update/to regenerate/" ],
         
     | 
| 
      
 12 
     | 
    
         
            +
                [ '出力', "[chu1 li4] /to exert oneself/" ],
         
     | 
| 
      
 13 
     | 
    
         
            +
                [ '拌蒜', "[ban4 suan4] /to stagger (walk unsteadily)/" ],
         
     | 
| 
      
 14 
     | 
    
         
            +
              ].map{|args| Card.new(args, CardState::EMPTY)}
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Rating < Struct.new(:timestamp, :rating)
         
     | 
| 
      
 3 
     | 
    
         
            +
                MAX_RATING = 3
         
     | 
| 
      
 4 
     | 
    
         
            +
                FAIL = 0
         
     | 
| 
      
 5 
     | 
    
         
            +
                HARD = 1
         
     | 
| 
      
 6 
     | 
    
         
            +
                GOOD = 2
         
     | 
| 
      
 7 
     | 
    
         
            +
                EASY = 3
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def neg_rating
         
     | 
| 
      
 10 
     | 
    
         
            +
                  MAX_RATING - rating
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def fail? ; rating == FAIL end
         
     | 
| 
      
 14 
     | 
    
         
            +
                def hard? ; rating == HARD end
         
     | 
| 
      
 15 
     | 
    
         
            +
                def good? ; rating == GOOD end
         
     | 
| 
      
 16 
     | 
    
         
            +
                def easy? ; rating == EASY end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module XFlash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class BaseStrategy < Struct.new(:card_state, :data_point)
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 4 
     | 
    
         
            +
                def_delegators :card_state, :data_points, :iteration, :streak, :factor, :interval
         
     | 
| 
      
 5 
     | 
    
         
            +
                def_delegators :data_point, :fail?, :rating, :neg_rating
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class SuperMemoStrategy < BaseStrategy
         
     | 
| 
      
 9 
     | 
    
         
            +
                INITIAL_INTERVALS = [1, 6]
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def next_streak
         
     | 
| 
      
 12 
     | 
    
         
            +
                  fail? ? 0 : streak + 1
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def next_factor
         
     | 
| 
      
 16 
     | 
    
         
            +
                  [factor + (0.1 - neg_rating * (0.28 + neg_rating * 0.02)), 1.3].max
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def next_interval
         
     | 
| 
      
 20 
     | 
    
         
            +
                  INITIAL_INTERVALS.fetch(next_streak) do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    interval * next_factor
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              class AnkiStrategy < BaseStrategy
         
     | 
| 
      
 27 
     | 
    
         
            +
                LEARNING_INTERVALS = [0, 1, 10, 25].map(&:to_f)
         
     | 
| 
      
 28 
     | 
    
         
            +
                INITIAL_INTERVALS  = [1, 2].map {|min| min*60*24 }.map(&:to_f)
         
     | 
| 
      
 29 
     | 
    
         
            +
                LEARNING_STEPS     = LEARNING_INTERVALS.length - 1
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def learning?
         
     | 
| 
      
 32 
     | 
    
         
            +
                  steps_to_graduation > 0
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def steps_to_graduation
         
     | 
| 
      
 36 
     | 
    
         
            +
                  data_points.map(&:rating).inject(LEARNING_STEPS, :-)
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def next_streak
         
     | 
| 
      
 40 
     | 
    
         
            +
                  fail? || learning? ? 0 : streak + 1
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def next_factor
         
     | 
| 
      
 44 
     | 
    
         
            +
                  if learning?
         
     | 
| 
      
 45 
     | 
    
         
            +
                    factor
         
     | 
| 
      
 46 
     | 
    
         
            +
                  else
         
     | 
| 
      
 47 
     | 
    
         
            +
                    [factor + [0.15, 0, -0.15, -0.3].fetch(neg_rating) {0}, 1.3].max
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                # Good enough for our purposes, and stable for a given card-state
         
     | 
| 
      
 52 
     | 
    
         
            +
                def pseudo_rand
         
     | 
| 
      
 53 
     | 
    
         
            +
                  data_point.timestamp.to_f % 1
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def next_interval
         
     | 
| 
      
 57 
     | 
    
         
            +
                  if learning?
         
     | 
| 
      
 58 
     | 
    
         
            +
                    LEARNING_INTERVALS.fetch(-steps_to_graduation)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  else
         
     | 
| 
      
 60 
     | 
    
         
            +
                    INITIAL_INTERVALS.fetch(next_streak) do
         
     | 
| 
      
 61 
     | 
    
         
            +
                      interval * next_factor
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end * (0.8 + pseudo_rand * 0.4) # 0.8 - 1.2
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engine.rb
    ADDED
    
    | 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- coding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 5 
     | 
    
         
            +
            #require 'readline'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'delegate'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative 'engine/rating'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative 'engine/strategies'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative 'engine/card_state'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require_relative 'engine/card'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require_relative 'engine/fixtures'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require_relative 'engine/deck'
         
     | 
| 
      
 15 
     | 
    
         
            +
            #require_relative 'engine/cli'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            module Engine
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            #Engine::CLI.new(Engine::FIXTURES).readline_loop
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,100 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: engine
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Arne Brasseur
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2014-04-27 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: hexp
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: 0.2.0
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: 0.2.0
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '10.1'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '10.1'
         
     | 
| 
      
 41 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 42 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 43 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 44 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 45 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '2.14'
         
     | 
| 
      
 48 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 49 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '2.14'
         
     | 
| 
      
 55 
     | 
    
         
            +
            description: Flashcard Engine with Spaced Repetition.
         
     | 
| 
      
 56 
     | 
    
         
            +
            email:
         
     | 
| 
      
 57 
     | 
    
         
            +
            - arne@arnebrasseur.net
         
     | 
| 
      
 58 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 59 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 60 
     | 
    
         
            +
            extra_rdoc_files:
         
     | 
| 
      
 61 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 62 
     | 
    
         
            +
            files:
         
     | 
| 
      
 63 
     | 
    
         
            +
            - ".gitignore"
         
     | 
| 
      
 64 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 65 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 66 
     | 
    
         
            +
            - engine.gemspec
         
     | 
| 
      
 67 
     | 
    
         
            +
            - lib/engine.rb
         
     | 
| 
      
 68 
     | 
    
         
            +
            - lib/engine/card.rb
         
     | 
| 
      
 69 
     | 
    
         
            +
            - lib/engine/card_state.rb
         
     | 
| 
      
 70 
     | 
    
         
            +
            - lib/engine/cli.rb
         
     | 
| 
      
 71 
     | 
    
         
            +
            - lib/engine/deck.rb
         
     | 
| 
      
 72 
     | 
    
         
            +
            - lib/engine/fixtures.rb
         
     | 
| 
      
 73 
     | 
    
         
            +
            - lib/engine/rating.rb
         
     | 
| 
      
 74 
     | 
    
         
            +
            - lib/engine/strategies.rb
         
     | 
| 
      
 75 
     | 
    
         
            +
            - lib/engine/version.rb
         
     | 
| 
      
 76 
     | 
    
         
            +
            homepage: https://github.com/plexus/engine
         
     | 
| 
      
 77 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 78 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 79 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 80 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 81 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 82 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 83 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 84 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 85 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 86 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 87 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 88 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 89 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 90 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 91 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 92 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 93 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 94 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 95 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 96 
     | 
    
         
            +
            rubygems_version: 2.2.2
         
     | 
| 
      
 97 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 98 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 99 
     | 
    
         
            +
            summary: Flashcard Engine with Spaced Repetition.
         
     | 
| 
      
 100 
     | 
    
         
            +
            test_files: []
         
     |