typing_trainer 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5b3afbdba090c7a2a268af3a7329fa7e030819b33a2b0a201b65b62b7f453475
4
+ data.tar.gz: 0425d685fda5e33b89618aebc0f8ca2c52cc99e3f6e394520d7663cc91420240
5
+ SHA512:
6
+ metadata.gz: 6ab033cdd3268b4ba6c51932e4c76a35a989e0313e0c0cc4488d18259a7549e86db6693299a302bf30a9459a749e7ee1c70f63bc8b4a5e1428fb091580d7dd9f
7
+ data.tar.gz: cd32d9e32af941911bc3d67a1404e1dfea2be0c198bce04347c7a81e6c9a4d646bddee8c7cd152d238c629542c53da2de3e24e99a31bd94ff07cce1af2d2455f
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1 @@
1
+ 2.6.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in typing_trainer.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Juan Germán Castañeda Echevarría
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ # TypingTrainer
2
+
3
+ Learn to touch type from your command line!
4
+
5
+ ## Gameplay screenshots
6
+
7
+ ![Screenshot](screenshots/playing.png)
8
+
9
+
10
+ ## Installation
11
+
12
+ $ gem install 'typing_trainer'
13
+
14
+ ## Starting the game
15
+
16
+ $ typing_trainer
17
+
18
+ To exit during game, use `Ctrl-C` or wait until asked if you want to continue playing.
19
+
20
+ ## Usage
21
+
22
+ $ typing_trainer --help
23
+
24
+ Usage: typing_trainer [options]
25
+ -l, --level LEVEL The starting level to play
26
+ -f, --file FILEPATH Path to file containing custom text to use
27
+ -a, --advanced Hide finger help (default show)
28
+ -d, --debug Show debugging messages (default hide)
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
37
+
38
+ ## Architecture Overview
39
+
40
+ This project is comprised by the following elements:
41
+
42
+ 1. `typing_trainer` - The binary to start the trainer
43
+ 2. `TypingTrainer` - The high level runner
44
+ 3. `TypingTrainer::Game` - Abstracts game mechanics and modes and screen
45
+ 4. `TypingTrainer::LevelGenerator` - Generates levels based on a layout
46
+ 5. `TypingTrainer::Level` - Represents a level: the text to use, instructions and settings
47
+ 6. `TypingTrainer::KeyboardLayout` - Defines finger mappings and letter progressions
48
+
49
+
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby -r rubygems
2
+ require 'typing_trainer'
3
+ require 'optparse'
4
+
5
+ options = {}
6
+
7
+ OptionParser.new do |opts|
8
+ opts.accept(Symbol) do |string|
9
+ string.to_sym
10
+ end
11
+
12
+ opts.banner = 'Usage: typing_trainer [options]'
13
+
14
+ opts.on('-t', '--letter LETTER', String, "A single letter to practice")
15
+
16
+ opts.on('-l', '--level LEVEL', Integer, "The starting level to play")
17
+
18
+ opts.on('-y', '--layout LAYOUT', Symbol, "The layout to use for finger hints and level progression")
19
+
20
+ opts.on('-f', '--file FILEPATH', String, "Path to file containing custom text to use")
21
+
22
+ opts.on('-a', '--advanced', TrueClass, "Hide finger help (default show)")
23
+
24
+ opts.on('-d', '--debug', TrueClass, "Show debugging messages (default hide)")
25
+ end.parse!(into: options)
26
+
27
+
28
+
29
+ TypingTrainer.run(options)
@@ -0,0 +1,32 @@
1
+ require "typing_trainer/version"
2
+
3
+ require "highline"
4
+ require "highline/system_extensions"
5
+ require "stty"
6
+ require "ffi"
7
+ require "termios"
8
+ require "typing_trainer/game"
9
+
10
+ module TypingTrainer
11
+
12
+ COLORS = HighLine::ColorScheme.new do |cs|
13
+ cs[:error] = [ :bold, :white, :on_red ]
14
+ cs[:correct] = [ :bold, :white, :on_green ]
15
+ end
16
+
17
+ HighLine::Style.new(:name=>:return, :builtin=>true, :code=>"\r")
18
+ HighLine::Style.new(:name=>:newline, :builtin=>true, :code=>"\n")
19
+ HighLine::Style.new(:name=>:up, :builtin=>true, :code=>"\e[A")
20
+ HighLine::Style.new(:name=>:down, :builtin=>true, :code=>"\e[B")
21
+ HighLine::Style.new(:name=>:forward, :builtin=>true, :code=>"\e[C")
22
+ HighLine::Style.new(:name=>:backward, :builtin=>true, :code=>"\e[D")
23
+
24
+ HighLine.color_scheme = COLORS
25
+
26
+ def self.run(options)
27
+ @game = Game.new(options)
28
+
29
+ @game.play!
30
+ end
31
+
32
+ end
@@ -0,0 +1,318 @@
1
+ require "typing_trainer/level_generator"
2
+ require "typing_trainer/level"
3
+
4
+ #
5
+ # Game
6
+ #
7
+ # Manages the game and internal state, checks if input is correct, prints information and handles
8
+ # screen cleanup
9
+ #
10
+ class TypingTrainer::Game
11
+ include HighLine::SystemExtensions
12
+
13
+ # Creates a new game
14
+ #
15
+ # @param [String] letter only words with that letter are shown and finger is shown.
16
+ # @param [Integer] level the level of the game, this is used to generate sentences. Overrides `letter`
17
+ # @param [String] file a file path to a text file for the game, overrides `level`
18
+ # @param [Array<String>] sentences the list of sentences to use for this game
19
+ # @param [Bool] advanced whether to show the finger hints or not
20
+ # @param [Symbol] layout the name of the layout to use. See TypingTrainer::KeyboardLayout
21
+ def initialize(level: nil, file: nil, sentences: nil, layout: :QWERTY, debug: false, advanced: false, letter: nil)
22
+ @layout = layout
23
+ @level_number = level
24
+ if !level && !letter
25
+ @level_number = 1
26
+ end
27
+ @file = file
28
+ @debug = debug
29
+ @advanced = advanced
30
+ @override_sentences = sentences
31
+ @introduce_letter = letter
32
+ @h = HighLine.new
33
+ end
34
+
35
+ def play!
36
+ @total_errors = 0
37
+ @errors = {}
38
+ @level = generate_level
39
+ @sentences = get_sentences
40
+ prepare_screen
41
+ before = Time.now
42
+ @sentences.each do |sentence|
43
+ print_level_header
44
+ show_sentence(sentence)
45
+ clean_screen
46
+ end
47
+ show_result(before)
48
+ rescue SystemExit, Interrupt
49
+ reset_terminal
50
+ rescue Exception => e
51
+ reset_terminal
52
+ raise e
53
+ end
54
+
55
+ private
56
+
57
+ def reset_terminal
58
+ clean_screen
59
+ @h.restore_mode
60
+ puts "Thank you for playing."
61
+ puts "Good bye!"
62
+ print :clear
63
+ print :erase_line
64
+ end
65
+
66
+ def generate_level
67
+ @level = if @override_sentences
68
+ TypingTrainer::Level.new(sentences: @override_sentences, layout: @layout)
69
+ elsif @file
70
+ TypingTrainer::Level.new(
71
+ sentences: open(@file).readlines.map {|l| l.strip!; l == "" ? nil : l;}.compact,
72
+ filename: @file,
73
+ layout: @layout
74
+ )
75
+ elsif @introduce_letter
76
+ TypingTrainer::LevelGenerator.generate(letters: [@introduce_letter], layout: @layout)
77
+ else
78
+ TypingTrainer::LevelGenerator.generate(level: @level_number, layout: @layout)
79
+ end
80
+ end
81
+
82
+ def get_sentences
83
+ @level.sentences
84
+ end
85
+
86
+ def get_character
87
+ @h.get_character
88
+ end
89
+
90
+ def show_sentence(sentence)
91
+ @cursor = 0
92
+ original = sentence
93
+ @typed = ""
94
+
95
+ print original
96
+ print :newline
97
+ print_hands(finger: @level.finger(original[@cursor])) if @introduce_letter
98
+ $stdout.flush
99
+
100
+ while c = get_character.chr do
101
+ # puts "CHAR: #{c}"
102
+ typed_error = false
103
+ case c
104
+ when "\x7F" # backspace
105
+ delete_char
106
+ print_hands(finger: @level.finger(original[@cursor]))
107
+ @cursor.times do
108
+ print :forward
109
+ end
110
+ next
111
+ when original[@cursor] # good
112
+ print c, :correct
113
+ else # error
114
+ log_error(c)
115
+ typed_error = true
116
+ end
117
+
118
+
119
+ @typed += c
120
+ @cursor += 1
121
+ finished = original == @typed
122
+ should_show_hands = !finished && original[@cursor] && (@introduce_letter || typed_error)
123
+ if (should_show_hands)
124
+ print_hands(finger: @level.finger(original[@cursor]))
125
+ @cursor.times do
126
+ print :forward
127
+ end
128
+ elsif !finished
129
+ hide_hands
130
+ @cursor.times do
131
+ print :forward
132
+ end
133
+ end
134
+
135
+ typed_error = false
136
+
137
+ if original == @typed
138
+ break
139
+ end
140
+ end
141
+ end
142
+
143
+ # Prints hands with finger hints
144
+ def print_hands(finger: nil)
145
+ print :newline
146
+ print HANDS
147
+ (HANDS.lines.count + 1).times do
148
+ print :up
149
+ end
150
+ print_finger(finger)
151
+ end
152
+
153
+ # Erases the lines showing finger hints
154
+ def hide_hands
155
+ print :newline
156
+ (HANDS.lines.count + 1).times do
157
+ print :erase_line
158
+ puts
159
+ end
160
+ (HANDS.lines.count + 1).times do
161
+ print :up
162
+ end
163
+ print :up
164
+ end
165
+
166
+ # Finds which finger has to be highlighted and prints
167
+ # a white block inside it.
168
+ def print_finger(finger)
169
+ finger_character = '█'
170
+ finger_coords = {
171
+ LEFT_P: [2,1],
172
+ LEFT_R: [1,3],
173
+ LEFT_M: [1,5],
174
+ LEFT_I: [1,7],
175
+ LEFT_T: [4,10],
176
+ RIGHT_P: [2,28],
177
+ RIGHT_R: [1,26],
178
+ RIGHT_M: [1,24],
179
+ RIGHT_I: [1,22],
180
+ RIGHT_T: [4,19]
181
+ }
182
+
183
+ coords = finger_coords[finger]
184
+ if (coords)
185
+ print :down
186
+ coords[0].times { print :down }
187
+ coords[1].times { print :forward }
188
+ print finger_character
189
+ print :backward
190
+ coords[1].times { print :backward }
191
+ coords[0].times { print :up }
192
+ print :up
193
+ end
194
+ end
195
+
196
+ # Print results of last played game.
197
+ def show_result(started_at)
198
+ @h.restore_mode
199
+ total_chars = @sentences.join.size
200
+ accuracy = "#{((total_chars - @total_errors) * 100.0 / total_chars).round(2)}%"
201
+ elapsed_minutes = (Time.now - started_at)/60
202
+ total_words = @sentences.join.split(' ').size
203
+ # wpm = "#{total_words/elapsed_minutes.to_f} WPM"
204
+ ccpm = "#{((total_chars)/elapsed_minutes.to_f).floor/5} WPM / #{((total_chars)/elapsed_minutes.to_f).floor} CPM"
205
+
206
+ print "These are your results:\n", :bold
207
+ print "Total time: #{elapsed_minutes} minutes\n", :bold
208
+ print "Character count: #{total_chars}\n", :bold
209
+ print 'Speed: ', :blue
210
+ print ccpm, :bold
211
+ print ' '
212
+ print 'Accuracy: ', :green
213
+ print accuracy, :bold
214
+ print ' '
215
+ print 'Errors: ', :red
216
+ print "#{@total_errors}/#{total_chars}", :bold
217
+ if @h.ask("\n\nDo you want to play again? [y,n]", ["y", "n"]) == "y"
218
+ # The logic ahead allows us to support starting from a letter or a level indistictively and
219
+ # continue with the next levels and letters as expected
220
+ if @introduce_letter
221
+ @level_number = @level.next_level(@introduce_letter)
222
+ if @level_number == 1
223
+ # If they only know one letter, don't make them repeat it. Introduce one more instead
224
+ @introduce_letter = @level.next_letter(1)
225
+ else
226
+ @introduce_letter = nil
227
+ end
228
+ elsif @level_number
229
+ @introduce_letter = @level.next_letter(@level_number)
230
+ end
231
+ play!
232
+ end
233
+ end
234
+
235
+ # Our own print to override the system one.
236
+ # This uses HighLine to output special characters
237
+ # If the parameter is a symbol, it will use
238
+ # HighLine.Style() and use its code
239
+ def print(str_or_sym, color = :none)
240
+ s = case str_or_sym
241
+ when String
242
+ @h.color(str_or_sym, color)
243
+ when Symbol
244
+ HighLine.Style(str_or_sym).code
245
+ end
246
+ $stdout.print(s)
247
+ end
248
+
249
+ # Ensures the screen has all the lines that the terminal has
250
+ # to move freely without needing to create more lines.
251
+ def prepare_screen
252
+ # Ensure we restore mode bedore setting to no_echo
253
+ # otherwise we lose context and cannot go back
254
+ @h.restore_mode rescue
255
+ print :erase_line
256
+ @h.output_rows.times do
257
+ print :newline
258
+ end
259
+ @h.output_rows.times do
260
+ print :up
261
+ end
262
+ @h.raw_no_echo_mode
263
+ end
264
+
265
+ # Uses the level information to print
266
+ # a header while a game is underway.
267
+ def print_level_header
268
+ print @level.header(@h.output_cols)
269
+ if @debug
270
+ puts({
271
+ layout: @layout,
272
+ level: @level_number,
273
+ letter: @introduce_letter,
274
+ advanced: @advanced,
275
+ cursor: @cursor
276
+ }).inspect
277
+ end
278
+ end
279
+
280
+ # Uses a clear screen escape sequence
281
+ # and prepares the screen
282
+ def clean_screen
283
+ print "\e[2J"
284
+ prepare_screen
285
+ end
286
+
287
+ # Deletes a character in the input "bar"
288
+ def delete_char
289
+ print :backward
290
+ print :erase_char
291
+ @cursor -= 1 if @cursor > 0
292
+ @typed = @typed[0..-2] if @typed.size > 0
293
+ end
294
+
295
+ # Changes the color of the input "bar" to show
296
+ # the user didn't type the right character
297
+ def log_error(c)
298
+ print c, :error
299
+ @total_errors += 1
300
+ @errors[c] ||= 0
301
+ @errors[c] += 1
302
+ end
303
+
304
+ # ASCII Hands. We need to escape \ because it is
305
+ # a special character for strings.
306
+ HANDS = <<~HANDS
307
+ _.-._ _.-._
308
+ _| | | | | | | |_
309
+ | | | | | | | | | |
310
+ | | | | | /\\ /\\ | | | | |
311
+ | |/ / \\ \\| |
312
+ | / / \\ \\ |
313
+ | / \\ |
314
+ \\ / \\ /
315
+ | | | |
316
+ HANDS
317
+
318
+ end
@@ -0,0 +1,7 @@
1
+ require 'typing_trainer/keyboard_layout/qwerty'
2
+
3
+ module TypingTrainer::KeyboardLayout
4
+ LAYOUTS = {
5
+ QWERTY: Qwerty
6
+ }
7
+ end
@@ -0,0 +1,37 @@
1
+ #require "typing_trainer/keyboard_layout"
2
+
3
+ module TypingTrainer::KeyboardLayout
4
+ class Qwerty
5
+ LETTER_PROGRESSION = "asdfjklghruvmbntyeiwoqpxz"
6
+
7
+ FINGER = {
8
+ 'a' => :LEFT_P,
9
+ 'b' => :LEFT_I,
10
+ 'c' => :LEFT_M,
11
+ 'd' => :LEFT_M,
12
+ 'e' => :LEFT_M,
13
+ 'f' => :LEFT_I,
14
+ 'g' => :LEFT_I,
15
+ 'h' => :RIGHT_I,
16
+ 'i' => :RIGHT_M,
17
+ 'j' => :RIGHT_I,
18
+ 'k' => :RIGHT_M,
19
+ 'l' => :RIGHT_R,
20
+ 'm' => :RIGHT_I,
21
+ 'n' => :RIGHT_I,
22
+ 'o' => :RIGHT_R,
23
+ 'p' => :RIGHT_P,
24
+ 'q' => :LEFT_P,
25
+ 'r' => :LEFT_I,
26
+ 's' => :LEFT_R,
27
+ 't' => :LEFT_I,
28
+ 'u' => :RIGHT_I,
29
+ 'v' => :LEFT_I,
30
+ 'w' => :LEFT_R,
31
+ 'x' => :LEFT_R,
32
+ 'y' => :RIGHT_I,
33
+ 'z' => :LEFT_P,
34
+ ' ' => :LEFT_T
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require "typing_trainer/keyboard_layout"
2
+
3
+ class TypingTrainer::Level
4
+ attr_reader :sentences
5
+ attr_reader :number
6
+ attr_reader :letters
7
+
8
+ def initialize(number: nil, letters: nil, sentences: [], filename: nil, layout: nil)
9
+ throw "Missing layout!" unless layout
10
+ throw "Missing sentences!" unless sentences.count > 0
11
+ @number = number
12
+ @letters = letters
13
+ @sentences = sentences
14
+ @layout_id = layout
15
+ layouts = TypingTrainer::KeyboardLayout::LAYOUTS
16
+ @layout = layouts[layout]
17
+ end
18
+
19
+ def header(width)
20
+ if @number
21
+ puts "Playing Level #{@number}"
22
+ puts "-"*width
23
+ puts
24
+ elsif @letters && @letters.size == 1
25
+ puts "New letter: #{@letters[0].upcase}"
26
+ puts "-"*width
27
+ puts
28
+ end
29
+ end
30
+
31
+ def finger(letter)
32
+ @layout::FINGER[letter.downcase]
33
+ end
34
+
35
+ # What is the next level if you know up to this letter?
36
+ def next_level(letter)
37
+ @layout::LETTER_PROGRESSION.index(letter.downcase) + 1
38
+ end
39
+
40
+ # What is the next letter to learn after beating this level?
41
+ def next_letter(level)
42
+ @layout::LETTER_PROGRESSION[level]
43
+ end
44
+ end
@@ -0,0 +1,51 @@
1
+ require 'contemporary_words'
2
+ require "typing_trainer/level"
3
+ require "typing_trainer/keyboard_layout"
4
+
5
+ class TypingTrainer::LevelGenerator
6
+
7
+ SENCENCES_PER_LEVEL = 3
8
+ WORDS_PER_SENTENCE = 8
9
+
10
+ def self.generate(level: nil, letters: nil, layout: nil)
11
+ throw "You need to specify a layout" unless layout
12
+ throw "No level or letters were provided to generate a Game level :(" unless level || letters
13
+
14
+ layouts = TypingTrainer::KeyboardLayout::LAYOUTS
15
+
16
+ layout_letters = layouts[layout]::LETTER_PROGRESSION
17
+ if letters
18
+ letters = letters.join('').downcase
19
+ end
20
+ letters ||= layout_letters.slice(0, level)
21
+ sentences = self.new(letters).generate_sentences(SENCENCES_PER_LEVEL)
22
+ TypingTrainer::Level.new(number: level, letters: letters, sentences: sentences, layout: layout)
23
+ end
24
+
25
+ def initialize(letters)
26
+ @letters = letters.split(//)
27
+ @level_pattern = /^[#{letters}]+$/
28
+ @words = initialize_words
29
+ end
30
+
31
+ def initialize_words
32
+ words = ContemporaryWords.all.filter { |word| @level_pattern.match?(word) }
33
+ while words.size < WORDS_PER_SENTENCE
34
+ words << self.generate_word
35
+ end
36
+ words
37
+ end
38
+
39
+ def generate_sentences(count)
40
+ sentences = []
41
+ count.times {
42
+ sentences << @words.sample(WORDS_PER_SENTENCE).join(' ') + ' '
43
+ }
44
+ sentences
45
+ end
46
+
47
+ def generate_word
48
+ (@letters * 10).sample(rand(5) + 1).join('')
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ module TypingTrainer
2
+ VERSION = "0.1.0"
3
+ end
Binary file
@@ -0,0 +1,41 @@
1
+ require 'typing_trainer'
2
+ require 'typing_trainer/game'
3
+
4
+ describe TypingTrainer::Game, '#play!' do
5
+
6
+ before :each do
7
+ game.stub(:prepare_screen)
8
+ game.stub(:show_result)
9
+ end
10
+
11
+ let(:game) { TypingTrainer::Game.new(sentences: ["first", "second"], layout: :QWERTY) }
12
+
13
+ it "shows each sentence" do
14
+ game.should_receive(:show_sentence).exactly(2)
15
+ game.should_receive(:clean_screen).exactly(2)
16
+ game.should_receive(:show_result)
17
+ game.play!
18
+ end
19
+
20
+ it "reads the characters" do
21
+ game.should_receive(:get_character).and_return(*%w{f i r s t s e c o n d})
22
+ game.play!
23
+ end
24
+
25
+ it "handles delete" do
26
+ input = %w{f i r s x} + [[0x7F].pack('U')] + %w{t s e c o n d}
27
+
28
+ game.should_receive(:get_character).and_return(*input)
29
+ game.should_receive(:show_result)
30
+ game.play!
31
+ end
32
+
33
+ it "handles delete at beginning" do
34
+ input = [[0x7F].pack('U')]*3 + %w{f i r s t} + %w{s e c o n d}
35
+
36
+ game.should_receive(:get_character).and_return(*input)
37
+ game.should_receive(:show_result)
38
+ game.play!
39
+ end
40
+
41
+ end
@@ -0,0 +1,16 @@
1
+ require 'typing_trainer'
2
+ require 'typing_trainer/level_generator'
3
+
4
+ describe TypingTrainer::LevelGenerator, '#generate' do
5
+
6
+ it "should generate 5 sentences per level" do
7
+ level = TypingTrainer::LevelGenerator.generate(level: 1, layout: :QWERTY);
8
+ level.sentences.size.should be 4
9
+ end
10
+
11
+ it "should generate 8 words per sentence" do
12
+ level = TypingTrainer::LevelGenerator.generate(level: 1, layout: :QWERTY);
13
+ level.sentences[0].split(' ').size.should be 8
14
+ end
15
+
16
+ end
@@ -0,0 +1,4 @@
1
+ teas to the toad
2
+ To the inn that is not haunted
3
+ this is not a house
4
+ In a neat toasted seed has it needed as a hot
@@ -0,0 +1,5 @@
1
+ 44444 77777 4444 7777 444 777 47474
2
+ 74747 4477 7744 4747 7474 4 7 47 74
3
+ 33333 88888 3333 8888 333 888 38383
4
+ 83838 3388 8833 3838 8383 3 8 38 83
5
+ 384738 37483748 73487
@@ -0,0 +1,8 @@
1
+ A Crow having stolen a bit of meat, perched in a tree and held it in her beak.
2
+ A Fox, seeing this, longed to possess the meat himself, and by a wily stratagem succeeded.
3
+ 'How handsome is the Crow,' he exclaimed, in the beauty of her shape and in the fairness of her complexion!
4
+ Oh, if her voice were only equal to her beauty, she would deservedly be considered the Queen of Birds!'
5
+ This he said deceitfully but the Crow, anxious to refute the reflection cast upon her voice,
6
+ set up a loud caw and dropped the flesh.
7
+ The Fox quickly picked it up, and thus addressed the Crow:
8
+ 'My good Crow, your voice is right enough, but your wit is wanting.'
@@ -0,0 +1,5 @@
1
+ A Hound started a Hare from his lair, but after a long run, gave up the chase.
2
+ A goat-herd seeing him stop, mocked him, saying
3
+ 'The little one is the best runner of the two.'
4
+ The Hound replied, 'You do not see the difference between us:
5
+ I was only running for a dinner, but he for his life.'
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/typing_trainer/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Juan Germán Castañeda Echevarría']
6
+ gem.email = ['juanger@gmail.com']
7
+ gem.description = %q{Ruby typing trainer}
8
+ gem.summary = %q{Improve your typing skills by completing the tutorial or using your own text or code!}
9
+ gem.homepage = ''
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = 'typing_trainer'
15
+ gem.require_paths = ['lib']
16
+ gem.version = TypingTrainer::VERSION
17
+ gem.add_dependency 'highline', '~> 1.7.3'
18
+ gem.add_dependency 'ffi-ncurses'
19
+ gem.add_dependency 'ruby-termios'
20
+ gem.add_dependency 'stty'
21
+ gem.add_dependency 'contemporary_words'
22
+ gem.add_development_dependency 'rspec'
23
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typing_trainer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Juan Germán Castañeda Echevarría
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: ffi-ncurses
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-termios
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: stty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: contemporary_words
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Ruby typing trainer
98
+ email:
99
+ - juanger@gmail.com
100
+ executables:
101
+ - typing_trainer
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".ruby-version"
107
+ - Gemfile
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - bin/typing_trainer
112
+ - lib/typing_trainer.rb
113
+ - lib/typing_trainer/game.rb
114
+ - lib/typing_trainer/keyboard_layout.rb
115
+ - lib/typing_trainer/keyboard_layout/qwerty.rb
116
+ - lib/typing_trainer/level.rb
117
+ - lib/typing_trainer/level_generator.rb
118
+ - lib/typing_trainer/version.rb
119
+ - screenshots/playing.png
120
+ - spec/game_spec.rb
121
+ - spec/level_generator_spec.rb
122
+ - text/dvorak/home.txt
123
+ - text/dvorak/numbers.txt
124
+ - text/fox_and_crow.txt
125
+ - text/hare_and_hound.txt
126
+ - typing_trainer.gemspec
127
+ homepage: ''
128
+ licenses: []
129
+ metadata: {}
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubygems_version: 3.0.3
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Improve your typing skills by completing the tutorial or using your own text
149
+ or code!
150
+ test_files:
151
+ - spec/game_spec.rb
152
+ - spec/level_generator_spec.rb