language_cards 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/README.md +5 -24
- data/cards/japanese-hiragana.yml +2 -18
- data/cards/japanese-katakana.yml +3 -27
- data/language_cards.gemspec +2 -1
- data/lib/language_cards.rb +1 -0
- data/lib/language_cards/controllers/game.rb +19 -10
- data/lib/language_cards/controllers/main_menu.rb +6 -3
- data/lib/language_cards/grapheme_builder.rb +12 -0
- data/lib/language_cards/helpers/view_helper.rb +4 -0
- data/lib/language_cards/language_cards.rb +37 -17
- data/lib/language_cards/menu_node.rb +38 -0
- data/lib/language_cards/models/card_set.rb +38 -0
- data/lib/language_cards/models/grapheme.rb +18 -0
- data/lib/language_cards/modes/game.rb +29 -0
- data/lib/language_cards/modes/translate.rb +14 -0
- data/lib/language_cards/modes/typing_practice.rb +14 -0
- data/lib/language_cards/user_interface.rb +26 -30
- data/lib/language_cards/version.rb +1 -1
- data/lib/language_cards/view/main_menu.erb +2 -2
- metadata +26 -9
- data/lib/language_cards/card_collection.rb +0 -97
- data/lib/language_cards/comp_bitz.rb +0 -12
- data/lib/language_cards/comparator.rb +0 -67
- data/lib/language_cards/mappings.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 18f1ce1cc9252361375183fcf05ff8f0c53af01f61c8c9b4a361e58265be245f
|
4
|
+
data.tar.gz: 6930a65377a3bb2dc4e0d29b897e99e1f85aa8301a352eaeb49771142cbfcc1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f418b1fc7567256199684e241d93d76ade1987fdaec463e80ed8105a11bb58f4de9e5a408ddcaa59837c825bea73dc873e1f8bbedcb6fca298eb0c3daa7727a5
|
7
|
+
data.tar.gz: 2a6f47d07d483b3401840a840fbba080e21801bd596ad55fb653afa3947d93a4f69b04132ac0d5f04d71e3e7d7c64ac0d8e95ca520cf4a961b0978546739575c
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -32,32 +32,21 @@ Or install it yourself as:
|
|
32
32
|
## Usage
|
33
33
|
|
34
34
|
After installing the gem you can run the executable `language_cards`. If you clone the repo then use
|
35
|
-
`bin/language_cards`.
|
35
|
+
`bundle exec bin/language_cards`.
|
36
36
|
|
37
37
|
# Card Format
|
38
38
|
|
39
39
|
The cards are stored in YAML format. You can look in the `cards` directory for existing examples to follow.
|
40
|
-
The first entry is a
|
40
|
+
The first entry is a language name and it's okay if that already exists in another file. The entries below that
|
41
41
|
must be unique for that language (eg: you can't have two Hiragana sub entries on Japanese). The next step in
|
42
|
-
will have a mapping hash
|
43
|
-
|
44
|
-
mapped; the how is for whether you want to do a translation mapping key to value, or keyboard practice mapping
|
45
|
-
value to value. This part of the mapping must be with the symbol entries of :k or :v. Along with the mapping
|
46
|
-
entry you may puts all the keys and values for your cards. Just follow the below outline for a working example.
|
42
|
+
will have a mapping hash on how the language is being mapped in the form of key to value (eg "Romaji" => "Hiragana").
|
43
|
+
Just follow the below outline for a working example.
|
47
44
|
|
48
45
|
```yaml
|
49
46
|
---
|
50
47
|
Japanese:
|
51
48
|
Hiragana:
|
52
|
-
mapping:
|
53
|
-
- Romaji: Hiragana
|
54
|
-
index:
|
55
|
-
- :k
|
56
|
-
- :v
|
57
|
-
- Hiragana: Hiragana
|
58
|
-
index:
|
59
|
-
- :v
|
60
|
-
- :v
|
49
|
+
mapping: { Romaji: Hiragana }
|
61
50
|
a: あ
|
62
51
|
i: い
|
63
52
|
u: う
|
@@ -65,14 +54,6 @@ Japanese:
|
|
65
54
|
o: お
|
66
55
|
```
|
67
56
|
|
68
|
-
In the example above we allow two mappings (game modes as-it-were). The first mapping is a translation mapping
|
69
|
-
for people to write romaji to solve the Hiragana character, and the second is to actually type the Hiragana
|
70
|
-
character in. As you can see the entries for the cards are bellow at the same level as mapping.
|
71
|
-
|
72
|
-
The first entry Japanese can be in any other cards file. The next level in where "Hiragana" is must be unique to
|
73
|
-
the language Japanese and only in one file. If you mess up the mapping the error messages will be very clear
|
74
|
-
about it. You may enter either one mapping, or two.
|
75
|
-
|
76
57
|
## Development
|
77
58
|
|
78
59
|
*Tests required moving forward with this project unless it's translation files.*
|
data/cards/japanese-hiragana.yml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
--- # https://en.wikibooks.org/wiki/Japanese/Kana_chart
|
2
2
|
Japanese:
|
3
3
|
Hiragana:
|
4
|
-
mapping:
|
5
|
-
- Romaji: Hiragana
|
6
|
-
index:
|
7
|
-
- :k
|
8
|
-
- :v
|
9
|
-
- Hiragana: Hiragana
|
10
|
-
index:
|
11
|
-
- :v
|
12
|
-
- :v
|
4
|
+
mapping: { Romaji: Hiragana }
|
13
5
|
a: あ # ✓
|
14
6
|
i: い # ✓
|
15
7
|
u: う # ✓
|
@@ -112,15 +104,7 @@ Japanese:
|
|
112
104
|
ryu: りゅ # ✓
|
113
105
|
ryo: りょ # ✓
|
114
106
|
Hiragana Diacritics: # https://en.wikipedia.org/wiki/Hiragana
|
115
|
-
mapping:
|
116
|
-
- Romaji: Hiragana
|
117
|
-
index:
|
118
|
-
- :k
|
119
|
-
- :v
|
120
|
-
- Hiragana: Hiragana
|
121
|
-
index:
|
122
|
-
- :v
|
123
|
-
- :v
|
107
|
+
mapping: { Romaji: Hiragana }
|
124
108
|
ga: が
|
125
109
|
gi: ぎ
|
126
110
|
gu: ぐ
|
data/cards/japanese-katakana.yml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
--- # https://en.wikibooks.org/wiki/Japanese/Kana_chart
|
2
2
|
Japanese:
|
3
3
|
Katakana:
|
4
|
-
mapping:
|
5
|
-
- Romaji: Katakana
|
6
|
-
index:
|
7
|
-
- :k
|
8
|
-
- :v
|
9
|
-
- Katakana: Katakana
|
10
|
-
index:
|
11
|
-
- :v
|
12
|
-
- :v
|
4
|
+
mapping: { Romaji: Katakana }
|
13
5
|
a: ア # ✓
|
14
6
|
i: イ # ✓
|
15
7
|
u: ウ # ✓
|
@@ -111,15 +103,7 @@ Japanese:
|
|
111
103
|
ryu: リュ # ✓
|
112
104
|
ryo: リョ # ✓
|
113
105
|
Katakana Diacritics: # https://en.wikipedia.org/wiki/Katakana
|
114
|
-
mapping:
|
115
|
-
- Romaji: Katakana
|
116
|
-
index:
|
117
|
-
- :k
|
118
|
-
- :v
|
119
|
-
- Katakana: Katakana
|
120
|
-
index:
|
121
|
-
- :v
|
122
|
-
- :v
|
106
|
+
mapping: { Romaji: Katakana }
|
123
107
|
ga: ガ
|
124
108
|
gi: ギ
|
125
109
|
gu: グ
|
@@ -161,15 +145,7 @@ Japanese:
|
|
161
145
|
pyu: ピュ
|
162
146
|
pyo: ピョ
|
163
147
|
Katakana Keyboard Mappings:
|
164
|
-
mapping:
|
165
|
-
- Romaji: Katakana
|
166
|
-
index:
|
167
|
-
- :k
|
168
|
-
- :v
|
169
|
-
- Katakana: Katakana
|
170
|
-
index:
|
171
|
-
- :v
|
172
|
-
- :v
|
148
|
+
mapping: { Romaji: Katakana }
|
173
149
|
a: ア
|
174
150
|
i: イ
|
175
151
|
u: ウ
|
data/language_cards.gemspec
CHANGED
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.add_dependency "highline", "~> 1.7"
|
25
25
|
spec.add_dependency "i18n", "~> 0.7"
|
26
|
+
spec.add_dependency "elixirize", "~> 0.3"
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.13"
|
27
|
-
spec.add_development_dependency "rake", "~> 12.
|
28
|
+
spec.add_development_dependency "rake", "~> 12.3"
|
28
29
|
spec.add_development_dependency "minitest", "~> 5.10"
|
29
30
|
end
|
data/lib/language_cards.rb
CHANGED
@@ -6,16 +6,18 @@ module LanguageCards
|
|
6
6
|
include Helpers::GameHelper
|
7
7
|
|
8
8
|
def render(correct:, incorrect:, title:, timer:, last:)
|
9
|
-
_score = t('Game.ScoreMenu.Score') + ": %0.2d
|
9
|
+
_score = t('Game.ScoreMenu.Score') + ": %0.2d%%" % calc_score(correct, incorrect)
|
10
10
|
_timer = [((t('Timer.Timer') + ": " + timer.ha) if timer.time?), nil, timer.h]
|
11
11
|
_mexit = t 'Menu.Exit'
|
12
12
|
|
13
|
-
view =
|
13
|
+
view = File.expand_path('../view/game.erb', __dir__).
|
14
|
+
ᐅ( IO.method :read ).
|
15
|
+
ᐅ ERB.method :new
|
14
16
|
view.result(binding)
|
15
17
|
end
|
16
18
|
|
17
|
-
def process(
|
18
|
-
ic = struct_data.new(
|
19
|
+
def process(cards, mode)
|
20
|
+
ic = struct_data.new(cards, mode)
|
19
21
|
ic.get_input
|
20
22
|
{
|
21
23
|
correct: ic.valid?,
|
@@ -30,19 +32,26 @@ module LanguageCards
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def get_input
|
33
|
-
@input ||= CLI.ask("#{I18n.t('Game.TypeThis')}
|
35
|
+
@input ||= CLI.ask("#{I18n.t('Game.TypeThis')}: #{display}")
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
@
|
38
|
+
def card
|
39
|
+
@card ||= collection.sample
|
38
40
|
end
|
39
41
|
|
40
42
|
def display
|
41
|
-
|
43
|
+
"#{card}"
|
42
44
|
end
|
43
45
|
|
44
46
|
def expected
|
45
|
-
|
47
|
+
case mode
|
48
|
+
when :translate
|
49
|
+
card.translation
|
50
|
+
when :typing_practice
|
51
|
+
"#{card}"
|
52
|
+
else
|
53
|
+
raise "Invalid mode in Game Controller!"
|
54
|
+
end
|
46
55
|
end
|
47
56
|
|
48
57
|
def correct_msg
|
@@ -56,7 +65,7 @@ module LanguageCards
|
|
56
65
|
end
|
57
66
|
|
58
67
|
def valid?
|
59
|
-
|
68
|
+
!!(expected == input)
|
60
69
|
end
|
61
70
|
end
|
62
71
|
end
|
@@ -7,14 +7,17 @@ module LanguageCards
|
|
7
7
|
def render(courses:, mode:)
|
8
8
|
_title = t 'Menu.Title'
|
9
9
|
_select = t 'Menu.Choose'
|
10
|
-
_mode = case mode.peek
|
10
|
+
_mode = t('Menu.GameMode') + case mode.peek
|
11
11
|
when :translate then t 'Menu.ModeTranslate'
|
12
|
-
when :
|
12
|
+
when :typing_practice then t 'Menu.ModeTyping'
|
13
13
|
end
|
14
|
+
_toggle = "m: " + t('Menu.ToggleGameMode')
|
14
15
|
_courses = courses.each.with_index.map {|item,index| "#{index + 1}: #{item}" }
|
15
16
|
_mexit = t 'Menu.Exit'
|
16
17
|
|
17
|
-
view =
|
18
|
+
view = File.expand_path('../view/main_menu.erb', __dir__).
|
19
|
+
ᐅ( IO.method :read ).
|
20
|
+
ᐅ ERB.method :new
|
18
21
|
view.result(binding)
|
19
22
|
end
|
20
23
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'language_cards/models/grapheme'
|
2
|
+
|
3
|
+
module LanguageCards
|
4
|
+
module GraphemeBuilder
|
5
|
+
# Hash {translation => grapheme}
|
6
|
+
def self.call(translation_graphemes = {})
|
7
|
+
translation_graphemes.each_with_object([]) do |(key, value), memo|
|
8
|
+
memo << Grapheme.new(value, key)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'language_cards/menu_node'
|
2
2
|
require_relative 'user_interface'
|
3
3
|
|
4
4
|
module LanguageCards
|
@@ -6,30 +6,50 @@ module LanguageCards
|
|
6
6
|
def initialize
|
7
7
|
@CARDS = {}
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
if @CARDS.has_key? language
|
20
|
-
@CARDS[language] = Hash(@CARDS[language]).merge(Hash(yaml_data[language]))
|
9
|
+
# TODO: Extract out YAML file loading behavior to methods via SRP.
|
10
|
+
File.join('..','..').
|
11
|
+
ᐅ(File.method(:expand_path), __dir__ ).
|
12
|
+
ᐅ(File.method(:join), 'cards', '*.yml').
|
13
|
+
ᐅ(Dir.method :[] ).
|
14
|
+
+(
|
15
|
+
if ENV['HOME']
|
16
|
+
File.expand_path(ENV['HOME']).
|
17
|
+
ᐅ(File.method(:join), '.language_cards', 'cards', '*.yml').
|
18
|
+
ᐅ Dir.method :[]
|
21
19
|
else
|
22
|
-
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
).
|
23
|
+
each do |c|
|
24
|
+
next unless yaml_data = c.ᐅ(File.method :open).ᐅ(~:read).ᐅ(YAML.method :load)
|
25
|
+
for language in yaml_data.keys do
|
26
|
+
# Merges sub-items for languages
|
27
|
+
if @CARDS.has_key? language
|
28
|
+
@CARDS[language] = \
|
29
|
+
yaml_data[language].
|
30
|
+
ᐅ(method :Hash).
|
31
|
+
ᐅ @CARDS[language].
|
32
|
+
ᐅ(method :Hash).
|
33
|
+
method(:merge)
|
34
|
+
else
|
35
|
+
# Merges top scope languages
|
36
|
+
{ language => yaml_data[language] }.
|
37
|
+
ᐅ @CARDS.method :merge!
|
38
|
+
end
|
23
39
|
end
|
40
|
+
|
24
41
|
end
|
25
42
|
|
43
|
+
# Builder
|
44
|
+
@CARDS = @CARDS.each_with_object([]) do |(language, values), memo|
|
45
|
+
values.each do |category_with_card_set|
|
46
|
+
memo << MenuNode.new(language, category_with_card_set)
|
47
|
+
end
|
26
48
|
end
|
27
|
-
# Recursive Builder
|
28
|
-
@CARDS = CardCollection.new @CARDS
|
29
49
|
end
|
30
50
|
|
31
51
|
def start
|
32
|
-
UserInterface.new
|
52
|
+
@CARDS.ᐅ(UserInterface.method :new).ᐅ ~:start
|
33
53
|
end
|
34
54
|
end
|
35
55
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'language_cards/models/card_set'
|
2
|
+
module LanguageCards
|
3
|
+
class MenuNode
|
4
|
+
def initialize name, child
|
5
|
+
@name = name
|
6
|
+
|
7
|
+
if child.is_a?(Hash) and child.has_key?("mapping")
|
8
|
+
@mapping = child.delete("mapping") # Extra unused data for the moment
|
9
|
+
@child = CardSet.new(child)
|
10
|
+
else
|
11
|
+
@child = MenuNode.new(*child)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def title(fmt = ' - ', rng = 0..-1)
|
16
|
+
label[rng].delete_if(&:empty?).join(fmt)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return <Mode<CardSet> < Game>
|
20
|
+
def mode(game_mode)
|
21
|
+
child.mode(game_mode)
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is the preferred method for the view as this object shouldn't
|
25
|
+
# care about how it should be displayed in the view.
|
26
|
+
# @return Array<String>
|
27
|
+
def label
|
28
|
+
[@name].push(*child.label)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
label.delete_if(&:empty?).join(' - ')
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
attr_reader :name, :child
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'language_cards/models/grapheme'
|
2
|
+
require 'language_cards/grapheme_builder'
|
3
|
+
require 'language_cards/modes/typing_practice'
|
4
|
+
require 'language_cards/modes/translate'
|
5
|
+
|
6
|
+
module LanguageCards
|
7
|
+
class CardSet
|
8
|
+
attr_reader :graphemes
|
9
|
+
def initialize(grapheme_hash)
|
10
|
+
@graphemes = GraphemeBuilder.(grapheme_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def sample
|
14
|
+
@graphemes.sample
|
15
|
+
end
|
16
|
+
|
17
|
+
def mode(mode)
|
18
|
+
case mode
|
19
|
+
when :translate
|
20
|
+
Modes::Translate.new(self)
|
21
|
+
when :typing_practice
|
22
|
+
Modes::TypingPractice.new(self)
|
23
|
+
else
|
24
|
+
raise "Invalid Game Mode!"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# So as to not interfere with menu naming as this is not meant to
|
29
|
+
# be displayed as a string.
|
30
|
+
def to_s
|
31
|
+
""
|
32
|
+
end
|
33
|
+
|
34
|
+
def label
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module LanguageCards
|
3
|
+
class Grapheme
|
4
|
+
attr_reader :translation
|
5
|
+
def initialize grapheme, translation
|
6
|
+
@grapheme = grapheme
|
7
|
+
@translation = translation
|
8
|
+
end
|
9
|
+
|
10
|
+
def display
|
11
|
+
@grapheme
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@grapheme
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module LanguageCards
|
3
|
+
module Modes
|
4
|
+
class Game
|
5
|
+
def initialize card_set
|
6
|
+
@card_set = card_set
|
7
|
+
@index = 0
|
8
|
+
@current = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def current
|
12
|
+
@current or raise "Current grapheme not yet set!"
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return Grapheme Returns a random grapheme
|
16
|
+
def sample
|
17
|
+
@current = @card_set.sample
|
18
|
+
end
|
19
|
+
|
20
|
+
# Iterator for cycling through all translations sequentially.
|
21
|
+
# @return Grapheme Returns a random grapheme
|
22
|
+
def next
|
23
|
+
value = @card_set[@index % @card_set.length]
|
24
|
+
@index += 1
|
25
|
+
@current = value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require 'language_cards/timer'
|
2
|
+
require 'language_cards/helpers/view_helper'
|
3
|
+
require 'language_cards/helpers/game_helper'
|
4
|
+
require 'language_cards/controllers/main_menu'
|
5
|
+
require 'language_cards/controllers/game'
|
6
6
|
require 'erb'
|
7
7
|
|
8
8
|
module LanguageCards
|
@@ -11,15 +11,16 @@ module LanguageCards
|
|
11
11
|
include Controllers
|
12
12
|
def initialize cards
|
13
13
|
@cards = cards
|
14
|
-
@courses = cards.
|
15
|
-
@mode = [:translate, :
|
14
|
+
@courses = cards.flat_map {|i| i.label.join(' - ') }
|
15
|
+
@mode = [:translate, :typing_practice].cycle
|
16
16
|
end
|
17
17
|
|
18
18
|
def start
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
unless ENV['SKIP_SPLASH']
|
20
|
+
clear
|
21
|
+
CLI.say SPLASH_SCREEN
|
22
|
+
sleep 2
|
23
|
+
end
|
23
24
|
|
24
25
|
begin
|
25
26
|
loop do
|
@@ -34,19 +35,23 @@ module LanguageCards
|
|
34
35
|
|
35
36
|
last = nil
|
36
37
|
if (0..courses.length-1).include? value
|
37
|
-
|
38
|
+
|
39
|
+
collection = cards[value] # MenuNode
|
40
|
+
title = "#{collection.title} (#{humanize mode.peek})"
|
41
|
+
collection = collection.mode(mode.peek) # Mode<CardSet> < Game
|
42
|
+
|
38
43
|
timer = Timer.new
|
39
|
-
begin
|
44
|
+
begin # Game Loop
|
40
45
|
loop do
|
41
46
|
clear
|
42
47
|
timer.mark
|
43
|
-
CLI.say Game.render correct:
|
44
|
-
incorrect:
|
45
|
-
title:
|
48
|
+
CLI.say Game.render correct: correct,
|
49
|
+
incorrect: incorrect,
|
50
|
+
title: title,
|
46
51
|
timer: timer,
|
47
52
|
last: last
|
48
|
-
result = Game.process(collection, mode)
|
49
|
-
result[:correct] ? correct : incorrect
|
53
|
+
result = Game.process(collection, collection.mode)
|
54
|
+
result[:correct] ? correct! : incorrect!
|
50
55
|
last = result[:last]
|
51
56
|
end
|
52
57
|
rescue SystemExit, Interrupt
|
@@ -59,23 +64,14 @@ module LanguageCards
|
|
59
64
|
end
|
60
65
|
|
61
66
|
private
|
62
|
-
attr_reader :mode, :cards
|
63
|
-
def correct
|
67
|
+
attr_reader :mode, :cards, :correct, :incorrect, :courses
|
68
|
+
def correct!
|
64
69
|
@correct = @correct.to_i + 1
|
65
70
|
end
|
66
71
|
|
67
|
-
def incorrect
|
72
|
+
def incorrect!
|
68
73
|
@incorrect = @incorrect.to_i + 1
|
69
74
|
end
|
70
|
-
|
71
|
-
def courses(value = nil)
|
72
|
-
courses = @courses.select {|c| detect_course_mode(c) == mode.peek }
|
73
|
-
value ? courses[value] : courses
|
74
|
-
end
|
75
|
-
|
76
|
-
def detect_course_mode str
|
77
|
-
str.split(JOIN).last.split(" => ").inject(:==) ? :typing : :translate
|
78
|
-
end
|
79
75
|
end
|
80
76
|
end
|
81
77
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<%= divider %>
|
2
2
|
<%= draw(_title, nil, 'v' + VERSION) %>
|
3
3
|
<%= divider %>
|
4
|
-
<%= draw(_select, nil,
|
4
|
+
<%= draw(_select, nil, _mode) %>
|
5
5
|
<% _courses.each do |course| %>
|
6
6
|
<%= course.chomp %><% end %>
|
7
7
|
|
8
|
-
<%= draw(_mexit, nil,
|
8
|
+
<%= draw(_mexit, nil, _toggle) %>
|
9
9
|
<%= divider %>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: language_cards
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel P. Clark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: elixirize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.3'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,14 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '12.
|
75
|
+
version: '12.3'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '12.
|
82
|
+
version: '12.3'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: minitest
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,15 +117,18 @@ files:
|
|
103
117
|
- cards/japanese-katakana.yml
|
104
118
|
- language_cards.gemspec
|
105
119
|
- lib/language_cards.rb
|
106
|
-
- lib/language_cards/card_collection.rb
|
107
|
-
- lib/language_cards/comp_bitz.rb
|
108
|
-
- lib/language_cards/comparator.rb
|
109
120
|
- lib/language_cards/controllers/game.rb
|
110
121
|
- lib/language_cards/controllers/main_menu.rb
|
122
|
+
- lib/language_cards/grapheme_builder.rb
|
111
123
|
- lib/language_cards/helpers/game_helper.rb
|
112
124
|
- lib/language_cards/helpers/view_helper.rb
|
113
125
|
- lib/language_cards/language_cards.rb
|
114
|
-
- lib/language_cards/
|
126
|
+
- lib/language_cards/menu_node.rb
|
127
|
+
- lib/language_cards/models/card_set.rb
|
128
|
+
- lib/language_cards/models/grapheme.rb
|
129
|
+
- lib/language_cards/modes/game.rb
|
130
|
+
- lib/language_cards/modes/translate.rb
|
131
|
+
- lib/language_cards/modes/typing_practice.rb
|
115
132
|
- lib/language_cards/timer.rb
|
116
133
|
- lib/language_cards/user_interface.rb
|
117
134
|
- lib/language_cards/version.rb
|
@@ -139,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
156
|
version: '0'
|
140
157
|
requirements: []
|
141
158
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.6
|
159
|
+
rubygems_version: 2.7.6
|
143
160
|
signing_key:
|
144
161
|
specification_version: 4
|
145
162
|
summary: Flashcard game for language learning.
|
@@ -1,97 +0,0 @@
|
|
1
|
-
require_relative 'comparator'
|
2
|
-
require_relative 'mappings'
|
3
|
-
|
4
|
-
module LanguageCards
|
5
|
-
class CardCollection
|
6
|
-
attr_reader :name
|
7
|
-
##
|
8
|
-
# RULE: Always compare by value regardless of key value ordering in mapping.
|
9
|
-
# (This allows for duplicate spellings of the same character translation)
|
10
|
-
def initialize hsh, name=nil
|
11
|
-
@hsh = hsh
|
12
|
-
@name = name ? I18n.t("LanguageName.#{name}") : nil
|
13
|
-
send :_build
|
14
|
-
@mappings = @hsh["mapping"]
|
15
|
-
if @mappings
|
16
|
-
@cards = @hsh.select {|k,v| !v.is_a? Mappings}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
##
|
21
|
-
# Recursively build class course names
|
22
|
-
def classes s=nil
|
23
|
-
s = @name.nil? ? nil : [s, @name].compact.join(JOIN)
|
24
|
-
@hsh.select do |k,v|
|
25
|
-
[CardCollection, Mappings].include? v.class
|
26
|
-
end.flat_map do |k,v|
|
27
|
-
v.send :classes, s
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def [](value)
|
32
|
-
raise EmptyCollection unless collection?
|
33
|
-
@cards[value]
|
34
|
-
end
|
35
|
-
|
36
|
-
def rand
|
37
|
-
raise EmptyCollection unless collection?
|
38
|
-
v = @cards.keys.sample
|
39
|
-
@comparator.given(v, @cards[v])
|
40
|
-
end
|
41
|
-
|
42
|
-
def correct? input, comp_bitz
|
43
|
-
raise EmptyCollection unless collection?
|
44
|
-
@comparator.match? input, comp_bitz
|
45
|
-
end
|
46
|
-
|
47
|
-
def mapped_as
|
48
|
-
raise EmptyCollection unless collection?
|
49
|
-
@comparator.mapped_as
|
50
|
-
end
|
51
|
-
|
52
|
-
def children
|
53
|
-
@hsh.select {|k,v| v.is_a? CardCollection}.values.map do |cc|
|
54
|
-
{cc.name => cc}
|
55
|
-
end.inject(:merge)
|
56
|
-
end
|
57
|
-
|
58
|
-
def select_collection string
|
59
|
-
first, tail = string.split(JOIN, 2)
|
60
|
-
if collection?
|
61
|
-
@comparator = @mappings.select_mapping(first)
|
62
|
-
return self
|
63
|
-
else
|
64
|
-
children[first].select_collection tail
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def collection?
|
69
|
-
!!(@cards ||= nil)
|
70
|
-
end
|
71
|
-
|
72
|
-
def cards
|
73
|
-
@cards || raise(I18n.t 'Errors.UpperCollection')
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
##
|
78
|
-
# Replace respective raw ruby object with our objects
|
79
|
-
def _build
|
80
|
-
if @hsh.has_key? "mapping"
|
81
|
-
@hsh["mapping"] = Mappings.new(@hsh["mapping"], self)
|
82
|
-
else
|
83
|
-
@hsh.tap do |h|
|
84
|
-
h.select {|k,v| v.is_a? Hash }.each do |k,v|
|
85
|
-
h[k] = self.class.new(v, k)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class EmptyCollection < StandardError
|
92
|
-
def initialize
|
93
|
-
super(I18n.t 'Errors.EmptyCollection')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
module LanguageCards
|
2
|
-
class CompBitz
|
3
|
-
attr_reader :display, :collection, :value, :mapping, :expected
|
4
|
-
def initialize options
|
5
|
-
@display = options.fetch(:display)
|
6
|
-
@collection = options.fetch(:collection)
|
7
|
-
@expected = options.fetch(:expected)
|
8
|
-
@value = options.fetch(:value)
|
9
|
-
@mapping = options.fetch(:mapping)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require_relative 'comp_bitz'
|
2
|
-
|
3
|
-
module LanguageCards
|
4
|
-
class Comparator
|
5
|
-
def initialize mapping_key, mapping, collection
|
6
|
-
##
|
7
|
-
# @title should be a hash like {"Romaji"=>"Katakana"}
|
8
|
-
# @mapping should be an array like [:k, :v]
|
9
|
-
raise I18n.t('Error.MappingNotFound') unless mapping.has_key? mapping_key
|
10
|
-
begin
|
11
|
-
@mapped_as, @mapping = mapping[mapping_key].reduce
|
12
|
-
rescue LocalJumpError
|
13
|
-
raise I18n.t('Errors.InvalidMapping')
|
14
|
-
end
|
15
|
-
|
16
|
-
@collection = collection
|
17
|
-
end
|
18
|
-
|
19
|
-
def mapped_as
|
20
|
-
case @mapping.first
|
21
|
-
when :k
|
22
|
-
@mapped_as.keys.first
|
23
|
-
else
|
24
|
-
@mapped_as.values.first
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def given key, value
|
29
|
-
CompBitz.new(
|
30
|
-
display: choose_display(key, value),
|
31
|
-
collection: @collection,
|
32
|
-
expected: choose_expected(key, value),
|
33
|
-
value: value,
|
34
|
-
mapping: @mapping.first
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
def match? input, comp_bitz
|
39
|
-
case comp_bitz.mapping
|
40
|
-
when :k
|
41
|
-
comp_bitz.value == comp_bitz.collection[input]
|
42
|
-
when :v
|
43
|
-
comp_bitz.value == input
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
def choose_display key, value
|
49
|
-
case @mapping.last
|
50
|
-
when :k
|
51
|
-
key
|
52
|
-
when :v
|
53
|
-
value
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def choose_expected key, value
|
58
|
-
case @mapping.first
|
59
|
-
when :k
|
60
|
-
key
|
61
|
-
when :v
|
62
|
-
value
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
@@ -1,51 +0,0 @@
|
|
1
|
-
module LanguageCards
|
2
|
-
class Mappings
|
3
|
-
def initialize mapping, collection=nil
|
4
|
-
@collection = collection
|
5
|
-
@mappings = {}
|
6
|
-
mapping.each do |h|
|
7
|
-
index = h.delete("index")
|
8
|
-
begin
|
9
|
-
a,b = h.reduce
|
10
|
-
rescue LocalJumpError
|
11
|
-
raise InvalidMapping
|
12
|
-
end
|
13
|
-
a = I18n.t "LanguageName.#{a}"
|
14
|
-
b = I18n.t "LanguageName.#{b}"
|
15
|
-
|
16
|
-
@mappings["#{a} => #{b}"] = {
|
17
|
-
h => index
|
18
|
-
}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def select_mapping string
|
23
|
-
Comparator.new string, self, @collection
|
24
|
-
end
|
25
|
-
|
26
|
-
def classes s=nil
|
27
|
-
keys.map do |names|
|
28
|
-
[s, names].compact.join(JOIN)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def [] key
|
33
|
-
@mappings[key]
|
34
|
-
end
|
35
|
-
|
36
|
-
def has_key? key
|
37
|
-
@mappings.has_key? key
|
38
|
-
end
|
39
|
-
|
40
|
-
def keys
|
41
|
-
@mappings.keys
|
42
|
-
end
|
43
|
-
|
44
|
-
class InvalidMapping < StandardError
|
45
|
-
def initialize
|
46
|
-
super(I18n.t 'Errors.InvalidMapping')
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|