kittyverse 0.4.3 → 1.0.1
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 +5 -5
- data/CHANGELOG.md +3 -3
- data/Manifest.txt +5 -5
- data/README.md +416 -240
- data/Rakefile +30 -30
- data/lib/kittyverse.rb +39 -44
- data/lib/kittyverse/cattributes.rb +123 -131
- data/lib/kittyverse/config/colors.rb +146 -132
- data/lib/kittyverse/config/exclusives.rb +197 -0
- data/lib/kittyverse/config/fancies.rb +379 -174
- data/lib/kittyverse/config/purrstiges.rb +392 -200
- data/lib/kittyverse/config/special_editions.rb +135 -0
- data/lib/kittyverse/config/traits.rb +12 -0
- data/lib/kittyverse/config/traits_timeline.rb +288 -294
- data/lib/kittyverse/fancies.rb +206 -205
- data/lib/kittyverse/gene.rb +53 -0
- data/lib/kittyverse/genome.rb +176 -0
- data/lib/kittyverse/mewtations.rb +118 -120
- data/lib/kittyverse/recipes.rb +0 -2
- data/lib/kittyverse/traits.rb +0 -2
- data/lib/kittyverse/version.rb +20 -23
- data/test/helper.rb +10 -10
- data/test/test_cattributes.rb +0 -2
- data/test/test_fancies.rb +16 -2
- data/test/test_genome.rb +65 -0
- data/test/test_traits.rb +172 -174
- metadata +21 -17
- data/LICENSE.md +0 -116
- data/lib/kittyverse/api/client.rb +0 -149
- data/lib/kittyverse/api/versions.rb +0 -90
- data/lib/kittyverse/links.rb +0 -57
- data/lib/kittyverse/pages/genes.rb +0 -101
data/lib/kittyverse/fancies.rb
CHANGED
@@ -1,205 +1,206 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def self.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
## note: allow
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
f
|
36
|
-
|
37
|
-
f
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
def
|
102
|
-
|
103
|
-
def
|
104
|
-
|
105
|
-
def
|
106
|
-
def
|
107
|
-
|
108
|
-
def
|
109
|
-
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
date_str = h[:
|
149
|
-
date_str = h[:
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
recipe
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
fancies_by_name[
|
203
|
-
|
204
|
-
|
205
|
-
|
1
|
+
|
2
|
+
class Fancy
|
3
|
+
|
4
|
+
def self.fancies_by_key() @@fancies_by_key ||= {}; end
|
5
|
+
def self.fancies_by_name() @@fancies_by_name ||= {}; end
|
6
|
+
|
7
|
+
def self.find_by_key( key )
|
8
|
+
## note: use (always) a **symbol** for key lookup for now
|
9
|
+
@@fancies_by_key[ key.downcase.to_sym ]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_by_name( name )
|
13
|
+
## note: allow string AND symbols (thus, use .to_s !!!)
|
14
|
+
## allow spaces e.g. Bug Cat is the same as BugCat
|
15
|
+
## note: downcase name e.g. allow BugCat too (not just Bug Cat)
|
16
|
+
@@fancies_by_name[ name.gsub( / /, '' ).downcase.to_s ]
|
17
|
+
end
|
18
|
+
|
19
|
+
## add "generic" convenience find helper
|
20
|
+
def self.find_by( **kwargs )
|
21
|
+
if kwargs[ :key ]
|
22
|
+
find_by_key( kwargs[ :key ] )
|
23
|
+
elsif kwargs[ :name ]
|
24
|
+
find_by_name( kwargs[ :name] )
|
25
|
+
else
|
26
|
+
## todo/fix: throw argument except!!!
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.[]( key )
|
32
|
+
if key.is_a? Symbol ## e.g. :genesis, :bugcat, etc.
|
33
|
+
f = find_by_key( key )
|
34
|
+
f = find_by_name( key ) if f.nil? ## try fancy name next - why? why not?
|
35
|
+
f
|
36
|
+
else ## assume string
|
37
|
+
f = find_by_name( key ) ## search by key next - why? why not?
|
38
|
+
f
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def self.each
|
44
|
+
@@fancies_by_key.each do |(key,fancy)|
|
45
|
+
yield( fancy )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.special_editions # special edition fancies
|
51
|
+
@@fancies_by_key.values.select { |fancy| fancy.special_edition? }
|
52
|
+
end
|
53
|
+
def self.exclusives # exclusive fancies
|
54
|
+
@@fancies_by_key.values.select { |fancy| fancy.exclusive? }
|
55
|
+
end
|
56
|
+
def self.fancies # "normal" fancies
|
57
|
+
@@fancies_by_key.values.select { |fancy| fancy.recipe? }
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.breedable ## todo: find a better name (or add alias) e.g. use unlocked why? why not?
|
61
|
+
today = Date.today
|
62
|
+
@@fancies_by_key.values.select { |fancy| fancy.breedable?( today ) }
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def self.size() @@fancies_by_key.size; end ## todo: add length alias too? why? why not?
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
attr_accessor :key,
|
73
|
+
:name,
|
74
|
+
:name_cn,
|
75
|
+
:date,
|
76
|
+
:desc,
|
77
|
+
:exclusive,
|
78
|
+
:specialedition,
|
79
|
+
:recipe,
|
80
|
+
:count,
|
81
|
+
:limit,
|
82
|
+
:ids,
|
83
|
+
:time_start,
|
84
|
+
:time_end
|
85
|
+
|
86
|
+
def initialize( **kwargs )
|
87
|
+
@exclusive = @specialedition = @recipe = nil
|
88
|
+
update( kwargs )
|
89
|
+
end
|
90
|
+
|
91
|
+
def update( **kwargs )
|
92
|
+
kwargs.each do |name,value|
|
93
|
+
send( "#{name}=", value ) ## use "regular" plain/classic attribute setter
|
94
|
+
end
|
95
|
+
self ## return self for chaining
|
96
|
+
end
|
97
|
+
|
98
|
+
def exclusive?() @exclusive.nil? == false; end
|
99
|
+
def specialedition?() @specialedition.nil? == false; end
|
100
|
+
alias_method :special_edition?, :specialedition?
|
101
|
+
def recipe?() @recipe.nil? == false; end
|
102
|
+
|
103
|
+
def overflow?() @count && @limit && @count > @limit; end
|
104
|
+
def overflow() @count - @limit; end ## todo: check for count limit set - why? why not?
|
105
|
+
def limit?() @limit; end
|
106
|
+
def count?() @count; end
|
107
|
+
|
108
|
+
def time?() @time_start && @time_end; end ## is fancy(recipe,specialedition) time windowed? true/false
|
109
|
+
|
110
|
+
def time_days() (@time_end.jd - @time_start.jd) + 1; end
|
111
|
+
|
112
|
+
|
113
|
+
def unlocked?( today=Date.today )
|
114
|
+
if @recipe
|
115
|
+
if @recipe.time? ## time windowed recipe
|
116
|
+
if @recipe.time_end >= today
|
117
|
+
true
|
118
|
+
else
|
119
|
+
false
|
120
|
+
end
|
121
|
+
else ## assume limit
|
122
|
+
if @count && @count < @limit
|
123
|
+
true
|
124
|
+
else
|
125
|
+
false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
alias_method :breedable?, :unlocked?
|
134
|
+
|
135
|
+
def locked?( today=Date.today ) !unlocked?( today ); end
|
136
|
+
|
137
|
+
|
138
|
+
###########################################
|
139
|
+
## auto-fill fancies
|
140
|
+
FANCIES.each do |key,h|
|
141
|
+
|
142
|
+
puts "fancy:"
|
143
|
+
pp h
|
144
|
+
|
145
|
+
name = h[:name]
|
146
|
+
name_cn = h[:name_cn] # add chinese name if present
|
147
|
+
|
148
|
+
date_str = h[:date]
|
149
|
+
date_str = h[:recipe][:time][:start] if date_str.nil? && h[:recipe]
|
150
|
+
date_str = h[:specialedition][:time][:start] if date_str.nil? && h[:specialedition]
|
151
|
+
|
152
|
+
date = Date.strptime( date_str, '%Y-%m-%d' )
|
153
|
+
|
154
|
+
attribs = {
|
155
|
+
key: key,
|
156
|
+
name: name,
|
157
|
+
name_cn: name_cn,
|
158
|
+
date: date,
|
159
|
+
desc: h[:desc]
|
160
|
+
}
|
161
|
+
|
162
|
+
attribs = if h[:exclusive]
|
163
|
+
attribs.merge( exclusive: true,
|
164
|
+
limit: h[:exclusive][:limit],
|
165
|
+
ids: h[:exclusive][:ids] )
|
166
|
+
elsif h[:specialedition]
|
167
|
+
attribs.merge( specialedition: true,
|
168
|
+
limit: h[:specialedition][:limit],
|
169
|
+
time_start: h[:specialedition][:time] && h[:specialedition][:time][:start] ? Date.strptime( h[:specialedition][:time][:start], '%Y-%m-%d' ) : nil,
|
170
|
+
time_end: h[:specialedition][:time] && h[:specialedition][:time][:end] ? Date.strptime( h[:specialedition][:time][:end], '%Y-%m-%d' ) : nil )
|
171
|
+
else ## assume "normal/regular" fancy with recipes
|
172
|
+
pp h[:recipe]
|
173
|
+
recipe = Recipe.new(
|
174
|
+
traits: h[:recipe][:traits], ## todo/fix: turn strings into trait objs!!!!
|
175
|
+
variants: h[:recipe][:variants], ## todo/fix: turn variant hash into variant ??? - why? why not?
|
176
|
+
limit: h[:recipe][:limit],
|
177
|
+
time_start: h[:recipe][:time] && h[:recipe][:time][:start] ? Date.strptime( h[:recipe][:time][:start], '%Y-%m-%d' ) : nil,
|
178
|
+
time_end: h[:recipe][:time] && h[:recipe][:time][:end] && h[:recipe][:time][:end] != '?' ? Date.strptime( h[:recipe][:time][:end], '%Y-%m-%d' ) : nil )
|
179
|
+
|
180
|
+
## note: support overflow "shortcut" - overflow+limit => count
|
181
|
+
count = if h[:recipe][:overflow]
|
182
|
+
recipe.limit + h[:recipe][:overflow]
|
183
|
+
else
|
184
|
+
h[:recipe][:count]
|
185
|
+
end
|
186
|
+
|
187
|
+
attribs.merge( recipe: recipe,
|
188
|
+
limit: recipe.limit,
|
189
|
+
time_start: recipe.time_start,
|
190
|
+
time_end: recipe.time_end,
|
191
|
+
count: count )
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
fancy = Fancy.new( **attribs )
|
196
|
+
## pp fancy
|
197
|
+
|
198
|
+
## note: key MUST be a symbol (NOT a string)
|
199
|
+
fancies_by_key[key] = fancy
|
200
|
+
|
201
|
+
## note: downcase name and remove all spaces e.g. Bug Cat => bugcat
|
202
|
+
fancies_by_name[name.gsub( / /, '' ).downcase] = fancy
|
203
|
+
fancies_by_name[name_cn] = fancy if name_cn ## add chinese name too if present
|
204
|
+
end
|
205
|
+
|
206
|
+
end # class Fancy
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
class Gene
|
3
|
+
|
4
|
+
### todo/check:
|
5
|
+
## find a better name for Slice(incl.4 genes)
|
6
|
+
## e.g. GeneFour, Gene4, GeneGroup, GeneSlice,TraitGenes,... - why? why not?
|
7
|
+
|
8
|
+
class Slice ## Gene::Slice (nested class)
|
9
|
+
|
10
|
+
attr_reader :type # trait type (tt)
|
11
|
+
attr_reader :d, :r1, :r2, :r3
|
12
|
+
# d (dominant gene) -- todo/check: rename to just d instead of d0 - why? why not?
|
13
|
+
# r1 (1st order recessive gene)
|
14
|
+
# r2 (2nd order recessive gene)
|
15
|
+
# r3 (3rd order recessive gene)
|
16
|
+
alias_method :d0, :d # allow "classic" alias for d too
|
17
|
+
|
18
|
+
## compat: add alias for ("new/modern") p, h1, h2, h3
|
19
|
+
## p(rimary), h(idden) 1, h(idden) 2, h(idden) 3
|
20
|
+
alias_method :p, :d
|
21
|
+
alias_method :h1, :r1
|
22
|
+
alias_method :h2, :r2
|
23
|
+
alias_method :h3, :r3
|
24
|
+
|
25
|
+
|
26
|
+
def initialize( type, d, r1, r2, r3 )
|
27
|
+
@type = TraitType[type] ## lookup trait type by key (e.g. :body, :pattern, etc.)
|
28
|
+
@d = @type[d] ## lookup trait (from trait type) by kai code (e.g. "1", "a", etc.)
|
29
|
+
@r1 = @type[r1]
|
30
|
+
@r2 = @type[r2]
|
31
|
+
@r3 = @type[r3]
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](index)
|
35
|
+
case index
|
36
|
+
when 0 then @d
|
37
|
+
when 1 then @r1
|
38
|
+
when 2 then @r2
|
39
|
+
when 3 then @r3
|
40
|
+
else nil ## return nil for unknown index for now (raise except - why? why not?)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def purebred?() @d == @r1 && @d == @r2 && @d == @r3; end
|
45
|
+
alias_method :pure?, :purebred?
|
46
|
+
|
47
|
+
|
48
|
+
def to_kai
|
49
|
+
@r3.kai + @r2.kai + @r1.kai + @d.kai
|
50
|
+
end ## return a string in kai/base32 notation
|
51
|
+
|
52
|
+
end # class Slice
|
53
|
+
end # class Gene
|