timequiz 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Binary file
Binary file
@@ -0,0 +1,156 @@
1
+ =======================
2
+ TIMEQUIZ
3
+ =======================
4
+ ----------------------------------------
5
+ play a history quiz game
6
+ ----------------------------------------
7
+
8
+ SYNOPSIS
9
+ =======================
10
+ timequiz [options]
11
+
12
+ Called without any option, the game is started with the events that are included
13
+ in the installed program package. There are not many.
14
+
15
+ DESCRIPTION
16
+ =======================
17
+ The general idea is to put historical events in the right order. In the
18
+ beginning, a limited number of events (3 at the time of this writing) is
19
+ presented and you have to indicate the right order of these events. All the
20
+ following rounds will confront you with just 1 additional event and you must
21
+ find for the new event the right spot in the previously ordered list, i.e.
22
+ after one of the listed events or earlier than any of the known events. After
23
+ each round, the first one inclusive, your performance is evaluated. You can
24
+ then choose to continue with a new event or, if you like, read a short
25
+ description of any of the events, already in the list.
26
+
27
+ The game continues in this way until you push the 'q' button to quit or until
28
+ all the events that are known to the game, are listed.
29
+
30
+ You do not need to know it all
31
+ --------------------------------
32
+ Beware that to win a round, it is oftentimes not necessary to really know the
33
+ date of an event. Pure logic and reasonable guesses may be sufficient to find
34
+ the right spot within a historical context or to exclude events as too far in
35
+ the past or future of the currently handled event. Reading the descriptive
36
+ comments to an event may later help you get different events into context.
37
+
38
+ OPTIONS
39
+ =======================
40
+ **-a, --add** Add a new event
41
+
42
+ **-e, --event** [EVENT] Name of the new event
43
+
44
+ **-y, --year** [YEAR] Year of the new event
45
+
46
+ **-b, --background** [INFO] Background information for the new event
47
+
48
+ **-f, --file** [FILE] The file, where events are read from or written to
49
+
50
+ **-d, --debug** Be verbose
51
+
52
+ **-v, --version** Show program version
53
+
54
+ **-h, --help** Show a help text
55
+
56
+ The Options in detail:
57
+ ----------------------
58
+ -a --add
59
+ This option imposes that -e, -y, and -f be given, too. In consequence, it
60
+ will be considered superfluous and removed in future versions of the program.
61
+
62
+ -e --event
63
+ Name the event to add to the events-file. For this option to take effect, -a,
64
+ -y and -f are needed, too. Beware to enclose the value with quotes, like in, e.g.
65
+ --event "Some guy was born and something broke"
66
+
67
+ -y --year
68
+ Is followed by a year, like -y 1244
69
+
70
+ -b --background
71
+ Should be used to provide background-information to an event. While this
72
+ option is ... optional, the information will help solving quizzes and may be
73
+ educative to the player... do not abuse, though.
74
+ Example: -b "The phenomenon was bearing no significance whatsoever"
75
+
76
+ -f --file
77
+ Name the “events-file” which will be used for either creating the quizzes
78
+ randomly or adding events to an existing list of events. The file must be
79
+ readable to serve for a quiz and writable to accept new events. If it does
80
+ not exist, it is created and some exemplary events are included on top.
81
+ Example: --file ~/my_history_quiz
82
+
83
+ -d --debug
84
+ Be verbose. This switches on the output of debug-messages that have been left
85
+ in the program code. Probably useless.
86
+
87
+ -v --version
88
+ Shows the program version
89
+
90
+ -h --help
91
+ Shows the option-summary.
92
+
93
+
94
+ GAME EXAMPLE
95
+ =======================
96
+ Calling timequiz without arguments on the command-line, you are confronted with
97
+ three arbitrary, historical events, like in the following screen:
98
+
99
+ +---------------------------------------------------------------------------+
100
+ |Put the following events into the right chronological order (e.g.: 2 3 1). |
101
+ | |
102
+ |1) The Roman emperor Diocletian devides the empire in two |
103
+ |2) End of the "War of the mercenaries" |
104
+ |3) Destruction of Nimrod and Ninive by the Medians and Neo-Babylonians |
105
+ | |
106
+ |Available: 1, 2, 3) |
107
+ +---------------------------------------------------------------------------+
108
+
109
+ You type in the three numbers in the order that you consider chronologically
110
+ correct, like "321" (you do not have to push return after each number). The
111
+ game will respond by showing you the correct order of events with the
112
+ corresponding years and an evaluation of your input. You are also prompted to
113
+ decide if you want to play with a new arbitrary event or first be shown some
114
+ information about the listed events:
115
+
116
+ +------------------------------------------------------------------------------------------------------+
117
+ |Enter number for more information about an event, 'a' to play with one new, random event, 'q' to quit.|
118
+ +------------------------------------------------------------------------------------------------------+
119
+
120
+ If you push 'a', a new event is shown:
121
+
122
+ +----------------------------------------------------------------------------------------------------------+
123
+ |Indicate the event from the previous list, which precedes the following, or '0' (zero) to put it in front:|
124
+ | |
125
+ |The Italian painter Lorenzo Lotto dies. |
126
+ +----------------------------------------------------------------------------------------------------------+
127
+
128
+ You can now decide, which of the three previous events precedes the death of
129
+ the Italian painter and either put in the (current) number of that previous
130
+ event or the number 0 (zero) if you consider that Lorenzo Lotto died well
131
+ before any of the other events (which would, of course, be false in the
132
+ example). You have to push the return-button after your input, as it can
133
+ contain an arbitrary number of ciphers, later in the game.
134
+
135
+ Other Information
136
+ ====================
137
+ Development and source code
138
+ timequiz has been written in Ruby. As Ruby is an interpreted programming
139
+ language, the executable file and all those that it may refer to at one point
140
+ in time, are themselves the source-files of the current program-version. You
141
+ can open them in any text-editor to scrutinize the source-code. If you have
142
+ received the program as a Ruby-gem, you can also decompress a copy of the
143
+ gem-file with *tar -x*, then *tar -xzf*.
144
+
145
+ Bugs
146
+ At one time during the game, the list of already known events is incorrectly
147
+ wrapped and continues to be.
148
+
149
+ License
150
+ timequiz is distributed under the conditions of the GNU General Public
151
+ License, version 3.
152
+
153
+ Author
154
+ timequiz has been developed by Michael Uplawski
155
+ <michael.uplawski@uplawski.eu>
156
+
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ #encoding: UTF-8
3
+
4
+ =begin
5
+ /******************************************************************************
6
+ * Copyright © 2017-2017, Michael Uplawski <michael.uplawski@uplawski.eu> *
7
+ * *
8
+ * This program is free software; you can redistribute it and/or modify *
9
+ * it under the terms of the GNU General Public License as published by *
10
+ * the Free Software Foundation; either version 3 of the License, or *
11
+ * (at your option) any later version. *
12
+ * *
13
+ * This program is distributed in the hope that it will be useful, *
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16
+ * GNU General Public License for more details. *
17
+ * *
18
+ * You should have received a copy of the GNU General Public License *
19
+ * along with this program; if not, write to the *
20
+ * Free Software Foundation, Inc., *
21
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22
+ ******************************************************************************/
23
+ =end
24
+
25
+ require_relative 'constants'
26
+ require_relative 'logging'
27
+
28
+ class Adder
29
+ #@@events_file = File.dirname(__FILE__) << File::Separator << 'events.rb'
30
+ #@@events_backup = File.dirname(__FILE__) << File::Separator << 'bak_events.rb'
31
+ self::extend(Logging)
32
+ @@log = self::init_logger
33
+ # Do not present as 'Adder' on the command-line
34
+ self::log_label=$APPNAME
35
+
36
+ def self::add(options)
37
+ @log.debug('options are ' << options.to_s)
38
+ if options.file
39
+ @@events_file = options.file
40
+ @@events_backup = File.dirname(@@events_file) << File::Separator << 'bak_' << File.basename(@@events_file)
41
+ else
42
+ @log.error('To add events, you must name a file on the command-line. Aborting')
43
+ exit false
44
+ end
45
+ if(!options.event || !options.year)
46
+ @log.error("To add events, you must provide them on the command-line.\n\tPSE start the program with option -h or --help to see an option-overview.")
47
+ exit false
48
+ end
49
+
50
+ @@log.level = $LOG_LEVEL if $LOG_LEVEL
51
+ fields = [:event, :year, :background]
52
+ @log.debug('options are ' << options.to_s)
53
+ if options[:event] && options[:year]
54
+ if !options[:background]
55
+ @log.warn('Optional background information is missing')
56
+ bg = ''
57
+ else
58
+ bg = options.background
59
+ end
60
+
61
+ ev_def = "$events << [\"" << options.event << "\", " << options.year << ", \"" << bg << "\"]"
62
+ @log.debug('writing events-backup to ' << @@events_backup)
63
+ File.open(@@events_backup, 'w+') {|bak| bak.write( File.read(@@events_file))}
64
+ @log.debug('adding new event to the list: ' << ev_def)
65
+ File.open(@@events_file, 'a') {|evf| evf.puts(ev_def)}
66
+ @log.info('A new event has been added to ' << @@events_file << '.')
67
+ else
68
+ opts = options.to_h.keys
69
+ @log.error('Option missing: ' << fields.each {|f| opts.delete(f) }.join(', '))
70
+ exit false
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,85 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * ©2016-2017 Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * *
6
+ * This program is free software; you can redistribute it and/or modify *
7
+ * it under the terms of the GNU General Public License as published by *
8
+ * the Free Software Foundation; either version 3 of the License, or *
9
+ * (at your option) any later version. *
10
+ * *
11
+ * This program is distributed in the hope that it will be useful, *
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
+ * GNU General Public License for more details. *
15
+ * *
16
+ * You should have received a copy of the GNU General Public License *
17
+ * along with this program; if not, write to the *
18
+ * Free Software Foundation, Inc., *
19
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20
+ ***************************************************************************/
21
+ =end
22
+
23
+
24
+ require 'optparse'
25
+ require 'optparse/time'
26
+ require 'ostruct'
27
+ require_relative 'logging'
28
+ # require_relative 'translating'
29
+ require_relative 'constants'
30
+
31
+ class ArgParser
32
+ # Class level logger. This is a static class.
33
+ self.extend(Logging)
34
+ # self.extend(Translating)
35
+ @@log = init_logger()
36
+
37
+ # Returns a structure describing the options.
38
+ #
39
+ def self.parse(args)
40
+ # The options specified on the command line will be collected in
41
+ # <b>options</b>. No defaults. Most options are optional and do not
42
+ # have to be set at all.
43
+ # The others must be named for each transformation or be set in the
44
+ # configuration-file.
45
+ options = OpenStruct.new
46
+
47
+ op = OptionParser.new do |opts|
48
+ opts.on("-a", "--add", 'Add new event') do
49
+ options.add = 'add'
50
+ end
51
+
52
+ opts.on("-eEVENT", "--event=EVENT", 'Name of the event') do |ev|
53
+ options.event = ev
54
+ end
55
+ opts.on("-yYEAR", "--year=YEAR", 'Year of the event') do |year|
56
+ options.year = year
57
+ end
58
+ opts.on("-bINFO", "--background=INFO", 'Background information') do |info|
59
+ options.background = info
60
+ end
61
+ opts.on("-fFILE", "--file=FILE", 'The file, where events are read from') do |file|
62
+ options.file = file
63
+ end
64
+ opts.on("-d", "--debug", 'Be verbose') do
65
+ options.debug = 'debug'
66
+ end
67
+ opts.on("-v", "--version", 'Show program version') do
68
+ puts $APPNAME.dup << ", version " << $VERSION
69
+ exit true
70
+ end
71
+ end
72
+ begin
73
+ op.parse!(args)
74
+ rescue OptionParser::ParseError => er
75
+ msg = "ERROR! Unsuitable or incomplete program-arguments" << ": %s" %er.message
76
+ puts msg
77
+ puts "Start this program with parameter -h or --help to see the usage-message."
78
+ exit false
79
+ end
80
+
81
+ options
82
+ end # parse()
83
+
84
+ end
85
+
@@ -0,0 +1,104 @@
1
+ #encoding: UTF-8
2
+
3
+ =begin
4
+ /******************************************************************************
5
+ * Copyright © 2014-2017, Michael Uplawski <michael.uplawski@uplawski.eu> *
6
+ * *
7
+ * This program is free software; you can redistribute it and/or modify *
8
+ * it under the terms of the GNU General Public License as published by *
9
+ * the Free Software Foundation; either version 3 of the License, or *
10
+ * (at your option) any later version. *
11
+ * *
12
+ * This program is distributed in the hope that it will be useful, *
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
+ * GNU General Public License for more details. *
16
+ * *
17
+ * You should have received a copy of the GNU General Public License *
18
+ * along with this program; if not, write to the *
19
+ * Free Software Foundation, Inc., *
20
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21
+ ******************************************************************************/
22
+ =end
23
+
24
+ # BusyIndicator provides a way to show 'activity' in a terminal-window, while
25
+ # the main thread does something else. For a usage example see the test-code at
26
+ # the bottom of this file and execute the file (e.g. ruby ./busy_indicator.rb)
27
+ class BusyIndicator
28
+ attr_writer :width
29
+ def initialize(start = true, width = nil)
30
+ # defaults
31
+ @width = width && width >= 3 ? width : 3
32
+ @sequence = %w"OOO ooo ___ ooo"
33
+ run if start
34
+ end
35
+
36
+ # change sequence for the following run().
37
+ def sequence=(seq)
38
+ @sequence = seq
39
+ @width = seq[0].length
40
+ end
41
+
42
+ # start busy-indicator with the current settings for width and sequence.
43
+ def run()
44
+ @thr = busy_indicator(@width) if !@thr || !@thr.alive?
45
+ end
46
+
47
+ # stop busy-indicator and show comment-string if given.
48
+ def stop(comment = nil)
49
+ @thr.terminate
50
+ @thr.join(0.1)
51
+ print ("\b" * @width)
52
+ print ("%+#{@width}s\n" %comment) if comment
53
+ end
54
+
55
+ # returns true or false, so that !stopped? == running?.
56
+ def running?
57
+ @thr.alive?
58
+ end
59
+
60
+ # returns true or false, so that !stopped? == running?.
61
+ def stopped?
62
+ !running?
63
+ end
64
+
65
+ private
66
+
67
+ # does it.
68
+ def busy_indicator(width)
69
+ tobj = Thread.new() do
70
+ loop do
71
+ @sequence.each do |s|
72
+ print "%+#{width}s" %s
73
+ sleep 0.1
74
+ print ("\b" * width)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # ========== TEST =============
82
+ if($0 == __FILE__)
83
+ width = 20
84
+ 2.times do |i|
85
+ puts "I am busy, pse wait..."
86
+ thr = BusyIndicator.new(true, width)
87
+ sleep 3
88
+ # effect of comment != nil and comment == nil
89
+ thr.stop("Okay") if i == 0
90
+ thr.stop() if i != 0
91
+ end
92
+ bi = BusyIndicator.new(false)
93
+ # change sequence
94
+ bi.sequence = %w"E [ ( | ¦ ; , . o O D P F"
95
+ bi.run
96
+ sleep 3
97
+ bi.stop("Stopped")
98
+ # change width
99
+ bi.width = 8
100
+ bi.run
101
+ sleep 3
102
+ bi.stop("stopped again")
103
+ end
104
+
@@ -0,0 +1,73 @@
1
+ #encoding: UTF-8
2
+
3
+ =begin
4
+ /******************************************************************************
5
+ * Copyright © 2014-2017, Michael Uplawski <michael.uplawski@uplawski.eu> *
6
+ * *
7
+ * This program is free software; you can redistribute it and/or modify *
8
+ * it under the terms of the GNU General Public License as published by *
9
+ * the Free Software Foundation; either version 3 of the License, or *
10
+ * (at your option) any later version. *
11
+ * *
12
+ * This program is distributed in the hope that it will be useful, *
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
+ * GNU General Public License for more details. *
16
+ * *
17
+ * You should have received a copy of the GNU General Public License *
18
+ * along with this program; if not, write to the *
19
+ * Free Software Foundation, Inc., *
20
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21
+ ******************************************************************************/
22
+ =end
23
+
24
+ # Functions to apply colors to terminal output.
25
+ # This is stolen from the Internet and I have lost track of my own additions
26
+ # and modifications.
27
+
28
+ COLORS = {:default => 9, :black => 0, :red => 1, :green => 2, :yellow => 3, :blue => 4, :purple => 5, :cyan => 6, :white => 7 }
29
+
30
+ BG = 4
31
+ FG = 3
32
+ REGULAR = 0
33
+ BOLD = 1
34
+ UNDERLINE = 4
35
+ BLINK = 5
36
+ SWAP = 7
37
+ NEUTRAL = 0
38
+
39
+ STYLES = {:regular => REGULAR, :bold => BOLD, :underline => UNDERLINE, :blink => BLINK, :swap => SWAP, :neutral => NEUTRAL}
40
+
41
+ # Colorizes the given text. Color-code is either an escape-sequence or one of
42
+ # the symbols representing color-names in the COLORS hash.
43
+ def colorize(text, color_code)
44
+ if (COLORS.keys.include?(color_code) )
45
+ "\033[3#{COLORS[color_code]}m#{text}\033[0m"
46
+ else
47
+ "#{color_code}#{text}\033[0m"
48
+ end
49
+ end
50
+
51
+ def style(text, style_code)
52
+ "#{style_code}#{text}\033[0m"
53
+ end
54
+ # a function which allows to manipulate every known aspect of the ansi-output.
55
+ def colored_output(output_text, fg_color = :default, bg_color = :default, style = :regular , mode = :neutral )
56
+ "\033[%i;%i;%i%i;%i%im%s\033[0m" %[STYLES[mode.to_sym], STYLES[style.to_sym], FG, COLORS[fg_color.to_sym], BG, COLORS[bg_color.to_sym], output_text]
57
+ end
58
+
59
+ # convenience functions
60
+ def red(text); colorize(text, "\033[31m"); end
61
+ def green(text); colorize(text, "\033[32m"); end
62
+ def yellow(text); colorize(text, "\033[33m"); end
63
+ def purple(text); colorize(text, "\033[35m"); end
64
+ def cyan(text); colorize(text, "\033[36m"); end
65
+ def blue(text); colorize(text, "\033[34m"); end
66
+ def white(text); colorize(text, "\033[37m"); end
67
+
68
+ def black_on_white(text); colorize(colorize(text, "\033[30m"), "\033[47m");end
69
+ def white_on_black(text); colorize(colorize(text, "\033[37m"), "\033[40m");end
70
+
71
+ def bold(text); style(text, "\033[01m");end
72
+ def underline(text); style(text, "\033[04m");end
73
+