├── bitcat ├── HISTORY.md ├── bitcat.png ├── lib │ ├── bitcat │ │ ├── public │ │ │ └── style.css │ │ ├── views │ │ │ ├── layout.erb │ │ │ ├── index.erb │ │ │ └── kitty.erb │ │ ├── version.rb │ │ └── app.rb │ └── bitcat.rb ├── Manifest.txt ├── bin │ └── bitcat ├── .gitignore ├── Rakefile └── README.md ├── copycats ├── CHANGELOG.md ├── test │ ├── helper.rb │ ├── test_version.rb │ ├── test_models.rb │ ├── test_traits.rb │ ├── test_mixgenes.rb │ └── test_genome.rb ├── bin │ └── kitty ├── script │ ├── kitty.rb │ └── convert.rb ├── lib │ ├── copycats │ │ ├── version.rb │ │ ├── traits.rb │ │ ├── reports │ │ │ ├── kitty.rb │ │ │ ├── mix.rb │ │ │ └── traits.rb │ │ ├── models │ │ │ └── kitty.rb │ │ ├── import │ │ │ ├── setup.rb │ │ │ └── read.rb │ │ ├── schema.rb │ │ ├── tool.rb │ │ ├── gene.rb │ │ └── genome.rb │ └── copycats.rb ├── Manifest.txt ├── Rakefile ├── .gitignore ├── KITTY-100000.md ├── KITTY-99895.md ├── NOTES.md └── README.md ├── copycats-tables.png ├── LICENSE.md └── README.md /bitcat/HISTORY.md: -------------------------------------------------------------------------------- 1 | ### 0.0.1 / 2018-03-24 2 | 3 | * Everything is new. First release 4 | -------------------------------------------------------------------------------- /bitcat/bitcat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptocopycats/copycats/HEAD/bitcat/bitcat.png -------------------------------------------------------------------------------- /copycats/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.1 / 2018-01-10 2 | 3 | * Everything is new. First release 4 | -------------------------------------------------------------------------------- /copycats-tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cryptocopycats/copycats/HEAD/copycats-tables.png -------------------------------------------------------------------------------- /bitcat/lib/bitcat/public/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: arial,sans-serif; 4 | color: #222; 5 | } 6 | -------------------------------------------------------------------------------- /copycats/test/helper.rb: -------------------------------------------------------------------------------- 1 | ## $:.unshift(File.dirname(__FILE__)) 2 | 3 | ## minitest setup 4 | 5 | require 'minitest/autorun' 6 | 7 | 8 | ## our own code 9 | 10 | require 'copycats' 11 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bitcat - Bit Catalog Kitty Browser 5 | 6 | 7 | 8 | 9 | <%= yield %> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /bitcat/Manifest.txt: -------------------------------------------------------------------------------- 1 | HISTORY.md 2 | LICENSE.md 3 | Manifest.txt 4 | README.md 5 | Rakefile 6 | bin/bitcat 7 | lib/bitcat.rb 8 | lib/bitcat/app.rb 9 | lib/bitcat/public/style.css 10 | lib/bitcat/version.rb 11 | lib/bitcat/views/index.erb 12 | lib/bitcat/views/kitty.erb 13 | lib/bitcat/views/layout.erb 14 | -------------------------------------------------------------------------------- /bitcat/bin/bitcat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ################### 4 | # DEV TIPS: 5 | # 6 | # For local testing run like: 7 | # 8 | # ruby -I./lib bin/bitcat 9 | # 10 | # Set the executable bit in Linux. Example: 11 | # 12 | # % chmod a+x bin/bitcat 13 | # 14 | 15 | require 'bitcat' 16 | 17 | Bitcat.main 18 | -------------------------------------------------------------------------------- /copycats/bin/kitty: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ################### 4 | # DEV TIPS: 5 | # 6 | # For local testing run like: 7 | # 8 | # ruby -I./lib bin/kitty 9 | # 10 | # Set the executable bit in Linux. Example: 11 | # 12 | # % chmod a+x bin/kitty 13 | # 14 | 15 | require 'copycats' 16 | 17 | Copycats.main 18 | -------------------------------------------------------------------------------- /copycats/test/test_version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_version.rb 6 | 7 | 8 | require 'helper' 9 | 10 | 11 | class TestVersion < MiniTest::Test 12 | 13 | def test_version 14 | pp Copycats.version 15 | pp Copycats.banner 16 | pp Copycats.root 17 | 18 | assert true ## (for now) everything ok if we get here 19 | end 20 | 21 | end # class TestVersion 22 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat/views/index.erb: -------------------------------------------------------------------------------- 1 |

Bitcat - Bit Catalog Kitty Browser

2 | 3 |
4 |
5 | 6 | 7 |
8 |
9 | 10 | 11 |

12 | Try 13 | <% Copycats::Model::Kitty.limit(10).each do |kitty| %> 14 | #<%= kitty.id %> 15 | <% end %> 16 |

17 | -------------------------------------------------------------------------------- /copycats/script/kitty.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | #### 4 | # use: 5 | # $ ruby -I ./lib script/kitty.rb 6 | 7 | 8 | require 'copycats' 9 | 10 | 11 | KittyReport.new( id: 100_000, 12 | genes: "dadc 557j 4aaa gb9g 1161 383k 7dbe 774b 6667 8438 dd9a cbbd" 13 | ).save( "./KITTY-100000.md" ) 14 | 15 | KittyReport.new( id: 99_895, 16 | genes: "cdad 55r6 7f2f g9bg 1111 3757 d99d 272h 8k78 k884 9da9 fdee" 17 | ).save( "./KITTY-99895.md" ) 18 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | module Bitcat 5 | 6 | MAJOR = 0 7 | MINOR = 1 8 | PATCH = 0 9 | VERSION = [MAJOR,MINOR,PATCH].join('.') 10 | 11 | def self.version 12 | VERSION 13 | end 14 | 15 | def self.banner 16 | "bitcat/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" 17 | end 18 | 19 | def self.root 20 | "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}" 21 | end 22 | 23 | end # module Bitcat 24 | -------------------------------------------------------------------------------- /copycats/lib/copycats/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | module Copycats 5 | 6 | MAJOR = 0 7 | MINOR = 8 8 | PATCH = 1 9 | VERSION = [MAJOR,MINOR,PATCH].join('.') 10 | 11 | def self.version 12 | VERSION 13 | end 14 | 15 | def self.banner 16 | "copycats/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" 17 | end 18 | 19 | def self.root 20 | "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}" 21 | end 22 | 23 | end # module Copycats 24 | -------------------------------------------------------------------------------- /copycats/Manifest.txt: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | LICENSE.md 3 | Manifest.txt 4 | README.md 5 | Rakefile 6 | bin/kitty 7 | lib/copycats.rb 8 | lib/copycats/fancies.rb 9 | lib/copycats/gene.rb 10 | lib/copycats/genome.rb 11 | lib/copycats/import/read.rb 12 | lib/copycats/import/setup.rb 13 | lib/copycats/models/kitty.rb 14 | lib/copycats/reports/genes.rb 15 | lib/copycats/reports/kitty.rb 16 | lib/copycats/reports/mix.rb 17 | lib/copycats/reports/traits.rb 18 | lib/copycats/schema.rb 19 | lib/copycats/tool.rb 20 | lib/copycats/traits.rb 21 | lib/copycats/traits_timeline.rb 22 | lib/copycats/version.rb 23 | test/helper.rb 24 | test/test_genome.rb 25 | test/test_mixgenes.rb 26 | test/test_models.rb 27 | test/test_traits.rb 28 | test/test_version.rb 29 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat/app.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module Bitcat 4 | 5 | class App < Sinatra::Base 6 | 7 | set :root, "#{Bitcat.root}/lib/bitcat" 8 | 9 | get '/' do 10 | erb :index 11 | end 12 | 13 | post '/' do 14 | puts "post '/'" 15 | pp params 16 | 17 | id = params['kitty']['id'] 18 | if id.blank? 19 | ## id required - todo: add flash message 20 | redirect "/" 21 | else 22 | redirect "/kitty/#{id}" 23 | end 24 | end 25 | 26 | 27 | get '/kitty/:id' do 28 | puts "get '/kitty/:id'" 29 | pp params 30 | 31 | kitty = Copycats::Model::Kitty.find( params['id'] ) 32 | pp kitty 33 | 34 | erb :kitty, :locals => { kitty: kitty } 35 | end 36 | 37 | 38 | ## helpers 39 | def h(text) 40 | Rack::Utils.escape_html(text) 41 | end 42 | 43 | end # class App 44 | end # module Bitcat 45 | -------------------------------------------------------------------------------- /bitcat/.gitignore: -------------------------------------------------------------------------------- 1 | #################### 2 | # copycats specific ignores 3 | 4 | kitties.db 5 | dl/ 6 | data/ 7 | data2/ 8 | data3/ 9 | debug/ 10 | 11 | 12 | 13 | *.gem 14 | *.rbc 15 | /.config 16 | /coverage/ 17 | /InstalledFiles 18 | /pkg/ 19 | /spec/reports/ 20 | /test/tmp/ 21 | /test/version_tmp/ 22 | /tmp/ 23 | 24 | ## Specific to RubyMotion: 25 | .dat* 26 | .repl_history 27 | build/ 28 | 29 | ## Documentation cache and generated files: 30 | /.yardoc/ 31 | /_yardoc/ 32 | /doc/ 33 | /rdoc/ 34 | 35 | ## Environment normalisation: 36 | /.bundle/ 37 | /vendor/bundle 38 | /lib/bundler/man/ 39 | 40 | # for a library or gem, you might want to ignore these files since the code is 41 | # intended to run in multiple environments; otherwise, check them in: 42 | # Gemfile.lock 43 | # .ruby-version 44 | # .ruby-gemset 45 | 46 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 47 | .rvmrc 48 | -------------------------------------------------------------------------------- /bitcat/Rakefile: -------------------------------------------------------------------------------- 1 | require 'hoe' 2 | require './lib/bitcat/version.rb' 3 | 4 | Hoe.spec 'bitcat' do 5 | 6 | self.version = Bitcat::VERSION 7 | 8 | self.summary = "bitcat - bit cat(alog) browser - browse your (digital) bit(s) collections; browse your (crypto) kitties and more" 9 | self.description = summary 10 | 11 | self.urls = ['https://github.com/openblockchains/bitcat'] 12 | 13 | self.author = 'Gerald Bauer' 14 | self.email = 'wwwmake@googlegroups.com' 15 | 16 | # switch extension to .markdown for gihub formatting 17 | self.readme_file = 'README.md' 18 | self.history_file = 'HISTORY.md' 19 | 20 | self.extra_deps = [ 21 | ['sinatra', '>=2.0'], 22 | ['sass'], ## used for css style preprocessing (scss) 23 | ['copycats', '>=0.6.1'], 24 | ] 25 | 26 | self.licenses = ['Public Domain'] 27 | 28 | self.spec_extras = { 29 | required_ruby_version: '>= 2.3' 30 | } 31 | 32 | end 33 | -------------------------------------------------------------------------------- /copycats/test/test_models.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_models.rb 6 | 7 | 8 | require 'helper' 9 | 10 | 11 | class TestModels < MiniTest::Test 12 | 13 | 14 | def test_create 15 | ## connect to local kitties.db database 16 | connect( adapter: 'sqlite3', 17 | database: ':memory:' ) 18 | 19 | setup_db 20 | setup_traits # note: also builds TRAIT_IDS_CACHE for read_datafiles for now 21 | 22 | ## print some (record) stats 23 | kitty_count = Copycats::Model::Kitty.count 24 | gene_count = Copycats::Model::Gene.count 25 | trait_count = Copycats::Model::Trait.count 26 | 27 | puts "kitties: #{kitty_count}" 28 | puts "genes: #{gene_count}" 29 | puts "traits: #{trait_count}" 30 | 31 | assert_equal 0, kitty_count 32 | assert_equal 0, gene_count 33 | assert_equal 384, trait_count 34 | end 35 | 36 | end # class TestModels 37 | -------------------------------------------------------------------------------- /copycats/lib/copycats/traits.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | ### build "reverse" lookup tables 5 | 6 | TRAITS_BY_NAME = {} # e.g. savannah, selkirk, chantilly, ... 7 | TRAITS_BY_CODE = {} # e.g. FU00, FU01, FU02, ... 8 | 9 | TRAITS.each do |key,h| 10 | h[:kai].each do |kai, name| 11 | 12 | num = Kai::NUMBER[kai] 13 | code = "#{h[:code]}#{'%02d' % num}" ## e.g. FU00, FU01, etc. 14 | 15 | rec = {} 16 | rec[:name] = name 17 | rec[:kai] = kai 18 | rec[:code] = code 19 | rec[:type] = key ## todo - use trait instead of type (use string not symbol?) - why? why not? 20 | 21 | if TRAITS_BY_NAME[name] 22 | puts "warn: duplicate trait name!! overwritting old #{name}:" 23 | pp TRAITS_BY_NAME[name] 24 | ## add count - why? why not? 25 | ## note: totebasic used three (3) times!!! 26 | ## how to make name "unique" ? - add kai e.g. totebase_g etc. - why? why not? 27 | end 28 | 29 | 30 | TRAITS_BY_NAME[name] = rec unless name.empty? || name == '?' 31 | TRAITS_BY_CODE[code] = rec 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /copycats/Rakefile: -------------------------------------------------------------------------------- 1 | require 'hoe' 2 | require './lib/copycats/version.rb' 3 | 4 | Hoe.spec 'copycats' do 5 | 6 | self.version = Copycats::VERSION 7 | 8 | self.summary = "copycats - command line tool (and core library) crypto cats / kitties collectibles unchained - buy! sell! hodl! sire! - play for free - runs off the blockchain w/ ledger lite - no ether / gas required; run your own peer-to-peer (P2P) network node over HTTP" 9 | self.description = summary 10 | 11 | self.urls = ['https://github.com/cryptocopycats/copycats'] 12 | 13 | self.author = 'Gerald Bauer' 14 | self.email = 'wwwmake@googlegroups.com' 15 | 16 | # switch extension to .markdown for gihub formatting 17 | self.readme_file = 'README.md' 18 | self.history_file = 'CHANGELOG.md' 19 | 20 | self.extra_deps = [ 21 | ['base32-alphabets', '>= 1.0.0'], 22 | ['csvreader', '>= 1.2.3'], 23 | ['activerecord'], 24 | ['sqlite3'], 25 | ] 26 | 27 | self.licenses = ['Public Domain'] 28 | 29 | self.spec_extras = { 30 | required_ruby_version: '>= 2.3' 31 | } 32 | 33 | end 34 | -------------------------------------------------------------------------------- /copycats/lib/copycats/reports/kitty.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class KittyReport 4 | 5 | def initialize( *args, **kwargs ) 6 | if args.empty? # try keyword args 7 | @id = kwargs[:id] 8 | @kai = kwargs[:genes] ## todo/check: also incl. check for kai key - why? why not? 9 | else 10 | # try positional args 11 | ## for now alsways assume ActiveRecord - Kitty::Model 12 | kitty = args[0] 13 | @id = kitty.id 14 | @kai = fmt_kai( kitty.genes_kai ) ## pretty print in groups of four (4) 15 | end 16 | end 17 | 18 | def build 19 | buf = "# Kitty \##{@id}\n\n" 20 | buf << "genes (kai): #{@kai}\n\n" 21 | buf << Genome.new( @kai ).build_tables 22 | 23 | ## puts buf 24 | 25 | buf 26 | end 27 | 28 | def save( path ) 29 | File.open( path, "w" ) do |f| 30 | f.write build 31 | end 32 | end 33 | 34 | 35 | ##### 36 | # helpers 37 | def fmt_kai( kai ) 38 | ## format in groups of four (4) separated by space 39 | ## e.g. ccac7787fa7fafaa16467755f9ee444467667366cccceede 40 | ## : ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede 41 | kai.reverse.gsub( /(.{4})/, '\1 ').reverse.strip 42 | end 43 | 44 | 45 | end # class KittyReport 46 | -------------------------------------------------------------------------------- /copycats/lib/copycats/reports/mix.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class MixReport 4 | 5 | def initialize( matron, sire ) 6 | @matron_id = matron.id 7 | @sire_id = sire.id 8 | @matron_kai = fmt_kai( matron.genes_kai ) ## pretty print in groups of four (4) 9 | @sire_kai = fmt_kai( sire.genes_kai ) 10 | end 11 | 12 | 13 | def build 14 | buf = "# Kitty \##{@matron_id} + \##{@sire_id}\n\n" 15 | buf << "genes (kai) 1: #{@matron_kai}\n" 16 | buf << "genes (kai) 2: #{@sire_kai}\n\n" 17 | 18 | buf << Genome.new( @matron_kai ).build_mix_tables( Genome.new( @sire_kai )) 19 | 20 | ## puts buf 21 | buf 22 | end 23 | 24 | 25 | ## fix/todo: use "generic" report class (inherits save)!!!!! 26 | def save( path ) 27 | File.open( path, "w" ) do |f| 28 | f.write build 29 | end 30 | end 31 | 32 | 33 | 34 | ##### 35 | # helpers 36 | # todo: move to helpers module for (re)use 37 | def fmt_kai( kai ) 38 | ## format in groups of four (4) separated by space 39 | ## e.g. ccac7787fa7fafaa16467755f9ee444467667366cccceede 40 | ## : ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede 41 | kai.reverse.gsub( /(.{4})/, '\1 ').reverse.strip 42 | end 43 | 44 | 45 | end # class KittyReport 46 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat/views/kitty.erb: -------------------------------------------------------------------------------- 1 | 2 | « Index 3 | 4 |

Kitty #<%= kitty.id %>

5 | 6 | Generation: <%= kitty.gen %> 7 |
8 | Birthdate: <%= kitty.birthdate %> (<%= Date.today.jd - kitty.birthdate.to_date.jd %> days ago) 9 |
10 | Day count: <%= kitty.day_count %> 11 |
12 | Genes (Kai): <%= kitty.genes_kai %> 13 | 14 | 15 | 29 | 30 | 31 |

32 | More Profile Pages: 33 |

34 | 35 | 45 | 46 | 47 | 54 | -------------------------------------------------------------------------------- /copycats/.gitignore: -------------------------------------------------------------------------------- 1 | #################### 2 | # copycats specific ignores 3 | 4 | kitties.db 5 | dl/ 6 | data/ 7 | data2/ 8 | data3/ 9 | debug/ 10 | 11 | 12 | ########## 13 | # regular ignores 14 | 15 | *.gem 16 | *.rbc 17 | /.config 18 | /coverage/ 19 | /InstalledFiles 20 | /pkg/ 21 | /spec/reports/ 22 | /spec/examples.txt 23 | /test/tmp/ 24 | /test/version_tmp/ 25 | /tmp/ 26 | 27 | # Used by dotenv library to load environment variables. 28 | # .env 29 | 30 | ## Specific to RubyMotion: 31 | .dat* 32 | .repl_history 33 | build/ 34 | *.bridgesupport 35 | build-iPhoneOS/ 36 | build-iPhoneSimulator/ 37 | 38 | ## Specific to RubyMotion (use of CocoaPods): 39 | # 40 | # We recommend against adding the Pods directory to your .gitignore. However 41 | # you should judge for yourself, the pros and cons are mentioned at: 42 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 43 | # 44 | # vendor/Pods/ 45 | 46 | ## Documentation cache and generated files: 47 | /.yardoc/ 48 | /_yardoc/ 49 | /doc/ 50 | /rdoc/ 51 | 52 | ## Environment normalization: 53 | /.bundle/ 54 | /vendor/bundle 55 | /lib/bundler/man/ 56 | 57 | # for a library or gem, you might want to ignore these files since the code is 58 | # intended to run in multiple environments; otherwise, check them in: 59 | # Gemfile.lock 60 | # .ruby-version 61 | # .ruby-gemset 62 | 63 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 64 | .rvmrc 65 | -------------------------------------------------------------------------------- /bitcat/lib/bitcat.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'pp' 4 | 5 | ## 3rd party 6 | require 'copycats' 7 | require 'sinatra/base' ## note: use "modular" class sinatra style 8 | 9 | 10 | 11 | ## our own code 12 | require 'bitcat/version' # note: let version always go first 13 | require 'bitcat/app' 14 | 15 | 16 | 17 | module Bitcat 18 | 19 | ## convenience method 20 | def self.run! 21 | app = App 22 | port = 4567 23 | Rack::Handler::WEBrick.run( app, Port:port ) do |server| 24 | ## todo: add traps here - why, why not?? 25 | end 26 | end 27 | 28 | def self.main 29 | p banner 30 | ## p root #=> e.g. "C:/Sites/openblockchains/bitcat" 31 | 32 | database = './kitties.db' 33 | 34 | unless File.exist?( database ) 35 | puts "*** error - no SQLite database found - expected >#{database}<" 36 | puts 37 | puts " note: for how to setup a database," 38 | puts " see " 39 | puts 40 | exit 1 41 | end 42 | 43 | 44 | ## connect to local kitties.db database 45 | connect( adapter: 'sqlite3', 46 | database: database ) 47 | 48 | ## print some (record) stats 49 | puts "kitties: #{Copycats::Model::Kitty.count}" 50 | puts "genes: #{Copycats::Model::Gene.count}" 51 | puts "traits: #{Copycats::Model::Trait.count}" 52 | 53 | run! 54 | end 55 | end # module Bitcat 56 | -------------------------------------------------------------------------------- /bitcat/README.md: -------------------------------------------------------------------------------- 1 | # bitcat - Bit Catalog Kitty Browser 2 | 3 | browse your (digital) bit(s) collections - 4 | browse your (crypto) kitties and more 5 | 6 | 7 | * home :: [github.com/openblockchains/bitcat](https://github.com/openblockchains/bitcat) 8 | * bugs :: [github.com/openblockchains/bitcat/issues](https://github.com/openblockchains/bitcat/issues) 9 | * gem :: [rubygems.org/gems/bitcat](https://rubygems.org/gems/bitcat) 10 | * rdoc :: [rubydoc.info/gems/bitcat](http://rubydoc.info/gems/bitcat) 11 | 12 | 13 | ## Usage 14 | 15 | 16 | ### Step 0 - Database Setup 17 | 18 | Note: The bitcat server machinery requires a (local) single-file SQLite database, 19 | that is, `kitties.db`, in your working folder. 20 | 21 | See the [Copycats page on how to setup a (local) single-file SQLite database »](https://github.com/openblockchains/copycats#database-setup) 22 | 23 | 24 | 25 | ### Step 1 - Start the Bitcat Server 26 | 27 | Start up the bitcat server on the command line: 28 | 29 | $ bitcat 30 | 31 | 32 | ### Step 3 - Open Bitcat Page in Browser 33 | 34 | Open up the browser at `localhost:4567`. 35 | 36 | ![](bitcat.png) 37 | 38 | That's it. Enjoy. 39 | 40 | 41 | 42 | 43 | ## Installation - I Can Has Bitcat? 44 | 45 | Use: 46 | 47 | ``` 48 | $ gem install bitcat 49 | ``` 50 | 51 | 52 | ## Questions? Comments? 53 | 54 | Post them on the [cryptokitties reddit](https://www.reddit.com/r/cryptokitties). Thanks. 55 | 56 | 57 | 58 | ## License 59 | 60 | ![](https://publicdomainworks.github.io/buttons/zero88x31.png) 61 | 62 | The `bitcat` scripts are dedicated to the public domain. 63 | Use it as you please with no restrictions whatsoever. 64 | -------------------------------------------------------------------------------- /copycats/test/test_traits.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_traits.rb 6 | 7 | 8 | require 'helper' 9 | 10 | 11 | class TestTraits < MiniTest::Test 12 | 13 | def test_traits 14 | pp TRAITS 15 | assert true ## (for now) everything ok if we get here 16 | 17 | ## todo: assert number of traits use traits.keys.size !!! 18 | end 19 | 20 | def test_fancies 21 | pp FANCIES 22 | assert true ## (for now) everything ok if we get here 23 | 24 | ## todo: assert number of fancies use fancies.keys.size !!! 25 | end 26 | 27 | 28 | def test_kai 29 | 30 | ## Kitty #1001 31 | ## see https://cryptokittydex.com/kitties/1001 32 | 33 | kai = "aaaa 7885 22f2 agff 1661 7755 e979 2441 6667 7664 a9aa cfff".gsub( ' ', '' ) 34 | 35 | puts "kai.length: #{kai.length}" ## 48 36 | puts " first: #{kai[0]}" 37 | puts " last: #{kai[-1]}" 38 | puts " last: #{kai[47]}" 39 | 40 | puts kai.reverse 41 | puts kai.reverse[0,4] 42 | puts kai.reverse[4,4] 43 | puts kai.reverse[8,4] 44 | eyes = kai.reverse[12,4] 45 | puts "eyes:" 46 | puts eyes 47 | 48 | puts TRAITS[:eyes] 49 | pp eyes[0] 50 | puts TRAITS[:eyes][eyes[0]] 51 | puts TRAITS[:eyes][eyes[1]] 52 | puts TRAITS[:eyes][eyes[2]] 53 | puts TRAITS[:eyes][eyes[3]] 54 | 55 | color1 = kai.reverse[16,4] 56 | puts "color1:" 57 | puts color1 58 | pp color1[0] 59 | puts TRAITS[:color1][color1[0]] 60 | puts TRAITS[:color1][color1[1]] 61 | puts TRAITS[:color1][color1[2]] 62 | puts TRAITS[:color1][color1[3]] 63 | 64 | assert true 65 | end 66 | 67 | end # class TestTraits 68 | -------------------------------------------------------------------------------- /copycats/test/test_mixgenes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_mixgenes.rb 6 | 7 | ## note: 8 | ## rand(100) - random number between 0 and 100 inclusively 9 | ## todo/check: use rand(99) - why? why not? e.g. 0 to 99 inclusive (100 values) 10 | 11 | 12 | require 'helper' 13 | 14 | 15 | 16 | class TestMixGenes < MiniTest::Test 17 | 18 | def setup 19 | Lottery.random = Random.new( 1234 ) ## seed == 1234 20 | end 21 | 22 | def mixgenes( matron, sire ) 23 | matron.mix( sire ) 24 | end 25 | 26 | def test_random 27 | assert_equal 47, Lottery.rand(100) 28 | assert_equal 83, Lottery.rand(100) 29 | assert_equal 38, Lottery.rand(100) 30 | assert_equal 53, Lottery.rand(100) 31 | end 32 | 33 | 34 | def test_baby 35 | 36 | kai1 = "dadc 557j 4aaa gb9g 1161 383k 7dbe 774b 6667 8438 dd9a cbbd" # kitty 100000 37 | kai2 = "cdad 55r6 7f2f g9bg 1111 3757 d99d 272h 8k78 k884 9da9 fdee" # kitty 99895 38 | 39 | matron = Genome.new( kai1 ) 40 | sire = Genome.new( kai2 ) 41 | baby = mixgenes( matron, sire ) 42 | pp baby 43 | 44 | puts "body: #{baby.body}" 45 | puts "coloreyes: #{baby.coloreyes}" 46 | puts "eyes: #{baby.eyes}" 47 | puts "pattern: #{baby.pattern}" 48 | puts "mouth: #{baby.mouth}" 49 | puts "color1: #{baby.color1}" 50 | puts "color2: #{baby.color2}" 51 | puts "color3: #{baby.color3}" 52 | 53 | assert_equal "laperm", baby.body 54 | assert_equal "strawberry", baby.coloreyes 55 | assert_equal "simple", baby.eyes 56 | assert_equal "calicool", baby.pattern 57 | assert_equal "soserious", baby.mouth 58 | assert_equal "salmon", baby.color1 59 | assert_equal "coffee", baby.color2 60 | assert_equal "bloodred", baby.color3 61 | end 62 | 63 | end # class TestMixGenes 64 | -------------------------------------------------------------------------------- /copycats/lib/copycats/models/kitty.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ## 4 | # todo: rename file from kitty.rb to models.rb - why? why not? 5 | # keep all models together in a single file? - why? why not? 6 | 7 | 8 | module Copycats 9 | module Model 10 | 11 | class Kitty < ActiveRecord::Base 12 | ## self.table_name = 'kitties' 13 | 14 | has_many :genes 15 | 16 | belongs_to :sire, class_name: 'Kitty', foreign_key: 'sire_id' 17 | belongs_to :matron, class_name: 'Kitty', foreign_key: 'matron_id' 18 | 19 | ## convenience shortcuts for gene (d) traits 20 | belongs_to :body, class_name: 'Trait', foreign_key: 'body_id' 21 | belongs_to :pattern, class_name: 'Trait', foreign_key: 'pattern_id' 22 | belongs_to :coloreyes, class_name: 'Trait', foreign_key: 'coloreyes_id' 23 | belongs_to :eyes, class_name: 'Trait', foreign_key: 'eyes_id' 24 | belongs_to :color1, class_name: 'Trait', foreign_key: 'color1_id' 25 | belongs_to :color2, class_name: 'Trait', foreign_key: 'color2_id' 26 | belongs_to :color3, class_name: 'Trait', foreign_key: 'color3_id' 27 | belongs_to :wild, class_name: 'Trait', foreign_key: 'wild_id' 28 | belongs_to :mouth, class_name: 'Trait', foreign_key: 'mouth_id' 29 | 30 | ## todo: add more genes convenience shortcuts 31 | ## has_many :genes_body 32 | ## has_many :genes_pattern 33 | end # class Kitty 34 | 35 | 36 | class TraitType < ActiveRecord::Base 37 | ## self.table_name = 'trait_types' 38 | 39 | has_many :traits 40 | end # class TraitType 41 | 42 | 43 | class Trait < ActiveRecord::Base 44 | ## self.table_name = 'traits' 45 | 46 | belongs_to :trait_types 47 | end # class Trait 48 | 49 | 50 | class Gene < ActiveRecord::Base 51 | ## self.table_name = 'genes' 52 | 53 | belongs_to :kitty 54 | end # class Gene 55 | 56 | 57 | 58 | end # module Model 59 | end # module Copycats 60 | -------------------------------------------------------------------------------- /copycats/lib/copycats.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'pp' # for pp => pretty printer 4 | 5 | require 'date' 6 | require 'time' 7 | require 'uri' 8 | require 'json' 9 | require 'enumerator' ## needed for each_slice 10 | require 'optparse' # note: used for command line tool (see Tool in tool.rb) 11 | 12 | require 'logger' # note: use for ActiveRecord::Base.logger -- remove/replace later w/ LogUtils::Logger ??? 13 | 14 | ## 3rd party gems 15 | require 'active_record' ## todo: add sqlite3? etc. 16 | 17 | require 'base32-alphabets' ## base32 / kai encoding / decoding for genes 18 | require 'csvreader' 19 | 20 | 21 | 22 | ## our own code 23 | require 'copycats/version' # note: let version always go first 24 | require 'copycats/traits' 25 | require 'copycats/traits_timeline' 26 | 27 | require 'copycats/fancies' 28 | require 'copycats/gene' 29 | require 'copycats/genome' 30 | 31 | require 'copycats/schema' 32 | 33 | require 'copycats/models/kitty' 34 | 35 | require 'copycats/links' 36 | 37 | require 'copycats/reports/kitty' 38 | require 'copycats/reports/mix' 39 | require 'copycats/reports/genes' 40 | require 'copycats/reports/traits' 41 | 42 | require 'copycats/import/setup' 43 | require 'copycats/import/read' 44 | 45 | require 'copycats/tool' 46 | 47 | 48 | ## add wrapper for allowing configuration of random number generator 49 | module Lottery 50 | 51 | def self.random 52 | @random ||= Random.new 53 | end 54 | 55 | def self.random=(value) 56 | @random = value 57 | end 58 | 59 | def self.rand( arg=nil ) 60 | if arg.is_a? Integer 61 | random.rand( arg ) ## max (number) = arg 62 | else 63 | random.rand ## between 0.0 and 1.0 (as floating point number) 64 | end 65 | end 66 | end ## module Lottery 67 | 68 | 69 | module Copycats 70 | ## add command line binary (tool) e.g. $ try kitty -h 71 | def self.main 72 | Tool.new.run( ARGV ) 73 | end 74 | end 75 | 76 | 77 | 78 | # say hello 79 | puts Copycats.banner if defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG 80 | -------------------------------------------------------------------------------- /copycats/KITTY-100000.md: -------------------------------------------------------------------------------- 1 | # Kitty #100000 2 | 3 | genes (kai): dadc 557j 4aaa gb9g 1161 383k 7dbe 774b 6667 8438 dd9a cbbd 4 | 5 | Fur (Genes 0-3) 6 | 7 | |Gene |Binary |Kai |Trait | | 8 | |------|---------|-----|---------|---| 9 | | 0 | 01100 | d | **munchkin** | d | 10 | | 1 | 01010 | b | chartreux | r1 | 11 | | 2 | 01010 | b | chartreux | r2 | 12 | | 3 | 01011 | c | himalayan | r3 | 13 | 14 | d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive 15 | 16 | Pattern (Genes 4-7) 17 | 18 | |Gene |Binary |Kai |Trait | | 19 | |------|---------|-----|---------|---| 20 | | 4 | 01001 | a | **luckystripe** | d | 21 | | 5 | 01000 | 9 | calicool | r1 | 22 | | 6 | 01100 | d | spock | r2 | 23 | | 7 | 01100 | d | spock | r3 | 24 | 25 | Eye Color (Genes 8-11) 26 | 27 | |Gene |Binary |Kai |Trait | | 28 | |------|---------|-----|---------|---| 29 | | 8 | 00111 | 8 | **strawberry** | d | 30 | | 9 | 00010 | 3 | topaz | r1 | 31 | | 10 | 00011 | 4 | mintgreen | r2 | 32 | | 11 | 00111 | 8 | strawberry | r3 | 33 | 34 | Eye Shape (Genes 12-15) 35 | 36 | |Gene |Binary |Kai |Trait | | 37 | |------|---------|-----|---------|---| 38 | | 12 | 00110 | 7 | **crazy** | d | 39 | | 13 | 00101 | 6 | simple | r1 | 40 | | 14 | 00101 | 6 | simple | r2 | 41 | | 15 | 00101 | 6 | simple | r3 | 42 | 43 | Base Color (Genes 16-19) 44 | 45 | |Gene |Binary |Kai |Trait | | 46 | |------|---------|-----|---------|---| 47 | | 16 | 01010 | b | **greymatter** | d | 48 | | 17 | 00011 | 4 | orangesoda | r1 | 49 | | 18 | 00110 | 7 | aquamarine | r2 | 50 | | 19 | 00110 | 7 | aquamarine | r3 | 51 | 52 | Highlight Color (Genes 20-23) 53 | 54 | |Gene |Binary |Kai |Trait | | 55 | |------|---------|-----|---------|---| 56 | | 20 | 01101 | e | **lemonade** | d | 57 | | 21 | 01010 | b | scarlet | r1 | 58 | | 22 | 01100 | d | coffee | r2 | 59 | | 23 | 00110 | 7 | royalpurple | r3 | 60 | 61 | Accent Color (Genes 24-27) 62 | 63 | |Gene |Binary |Kai |Trait | | 64 | |------|---------|-----|---------|---| 65 | | 24 | 10011 | k | **bloodred** | d | 66 | | 25 | 00010 | 3 | peach | r1 | 67 | | 26 | 00111 | 8 | emeraldgreen | r2 | 68 | | 27 | 00010 | 3 | peach | r3 | 69 | 70 | Wild (Genes 28-31) 71 | 72 | |Gene |Binary |Kai |Trait | | 73 | |------|---------|-----|---------|---| 74 | | 28 | 00000 | 1 | **?** | d | 75 | | 29 | 00101 | 6 | ? | r1 | 76 | | 30 | 00000 | 1 | ? | r2 | 77 | | 31 | 00000 | 1 | ? | r3 | 78 | 79 | Mouth (Genes 32-35) 80 | 81 | |Gene |Binary |Kai |Trait | | 82 | |------|---------|-----|---------|---| 83 | | 32 | 01111 | g | **soserious** | d | 84 | | 33 | 01000 | 9 | beard | r1 | 85 | | 34 | 01010 | b | saycheese | r2 | 86 | | 35 | 01111 | g | soserious | r3 | 87 | 88 | -------------------------------------------------------------------------------- /copycats/KITTY-99895.md: -------------------------------------------------------------------------------- 1 | # Kitty #99895 2 | 3 | genes (kai): cdad 55r6 7f2f g9bg 1111 3757 d99d 272h 8k78 k884 9da9 fdee 4 | 5 | Fur (Genes 0-3) 6 | 7 | |Gene |Binary |Kai |Trait | | 8 | |------|---------|-----|---------|---| 9 | | 0 | 01101 | e | **sphynx** | d | 10 | | 1 | 01101 | e | sphynx | r1 | 11 | | 2 | 01100 | d | munchkin | r2 | 12 | | 3 | 01110 | f | ragamuffin | r3 | 13 | 14 | d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive 15 | 16 | Pattern (Genes 4-7) 17 | 18 | |Gene |Binary |Kai |Trait | | 19 | |------|---------|-----|---------|---| 20 | | 4 | 01000 | 9 | **calicool** | d | 21 | | 5 | 01001 | a | luckystripe | r1 | 22 | | 6 | 01100 | d | spock | r2 | 23 | | 7 | 01000 | 9 | calicool | r3 | 24 | 25 | Eye Color (Genes 8-11) 26 | 27 | |Gene |Binary |Kai |Trait | | 28 | |------|---------|-----|---------|---| 29 | | 8 | 00011 | 4 | **mintgreen** | d | 30 | | 9 | 00111 | 8 | strawberry | r1 | 31 | | 10 | 00111 | 8 | strawberry | r2 | 32 | | 11 | 10011 | k | bubblegum | r3 | 33 | 34 | Eye Shape (Genes 12-15) 35 | 36 | |Gene |Binary |Kai |Trait | | 37 | |------|---------|-----|---------|---| 38 | | 12 | 00111 | 8 | **thicccbrowz** | d | 39 | | 13 | 00110 | 7 | crazy | r1 | 40 | | 14 | 10011 | k | raisedbrow | r2 | 41 | | 15 | 00111 | 8 | thicccbrowz | r3 | 42 | 43 | Base Color (Genes 16-19) 44 | 45 | |Gene |Binary |Kai |Trait | | 46 | |------|---------|-----|---------|---| 47 | | 16 | 10000 | h | **cloudwhite** | d | 48 | | 17 | 00001 | 2 | salmon | r1 | 49 | | 18 | 00110 | 7 | aquamarine | r2 | 50 | | 19 | 00001 | 2 | salmon | r3 | 51 | 52 | Highlight Color (Genes 20-23) 53 | 54 | |Gene |Binary |Kai |Trait | | 55 | |------|---------|-----|---------|---| 56 | | 20 | 01100 | d | **coffee** | d | 57 | | 21 | 01000 | 9 | swampgreen | r1 | 58 | | 22 | 01000 | 9 | swampgreen | r2 | 59 | | 23 | 01100 | d | coffee | r3 | 60 | 61 | Accent Color (Genes 24-27) 62 | 63 | |Gene |Binary |Kai |Trait | | 64 | |------|---------|-----|---------|---| 65 | | 24 | 00110 | 7 | **kittencream** | d | 66 | | 25 | 00100 | 5 | granitegrey | r1 | 67 | | 26 | 00110 | 7 | kittencream | r2 | 68 | | 27 | 00010 | 3 | peach | r3 | 69 | 70 | Wild (Genes 28-31) 71 | 72 | |Gene |Binary |Kai |Trait | | 73 | |------|---------|-----|---------|---| 74 | | 28 | 00000 | 1 | **?** | d | 75 | | 29 | 00000 | 1 | ? | r1 | 76 | | 30 | 00000 | 1 | ? | r2 | 77 | | 31 | 00000 | 1 | ? | r3 | 78 | 79 | Mouth (Genes 32-35) 80 | 81 | |Gene |Binary |Kai |Trait | | 82 | |------|---------|-----|---------|---| 83 | | 32 | 01111 | g | **soserious** | d | 84 | | 33 | 01010 | b | saycheese | r1 | 85 | | 34 | 01000 | 9 | beard | r2 | 86 | | 35 | 01111 | g | soserious | r3 | 87 | 88 | -------------------------------------------------------------------------------- /copycats/lib/copycats/import/setup.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | def connect( config={} ) 5 | if config.empty? 6 | puts "ENV['DATBASE_URL'] - >#{ENV['DATABASE_URL']}<" 7 | 8 | ### change default to ./copycats.db ?? why? why not? 9 | db = URI.parse( ENV['DATABASE_URL'] || 'sqlite3:///kitties.db' ) 10 | 11 | if db.scheme == 'postgres' 12 | config = { 13 | adapter: 'postgresql', 14 | host: db.host, 15 | port: db.port, 16 | username: db.user, 17 | password: db.password, 18 | database: db.path[1..-1], 19 | encoding: 'utf8' 20 | } 21 | else # assume sqlite3 22 | config = { 23 | adapter: db.scheme, # sqlite3 24 | database: db.path[1..-1] # world.db (NB: cut off leading /, thus 1..-1) 25 | } 26 | end 27 | end 28 | 29 | 30 | puts "Connecting to db using settings: " 31 | pp config 32 | ActiveRecord::Base.establish_connection( config ) 33 | ActiveRecord::Base.logger = Logger.new( STDOUT ) 34 | ## ActiveRecord::Base.colorize_logging = false - no longer exists - check new api/config setting? 35 | 36 | ## if sqlite3 add (use) some pragmas for speedups 37 | if config[:adapter] == 'sqlite3' 38 | ## check/todo: if in memory e.g. ':memory:' no pragma needed!! 39 | con = ActiveRecord::Base.connection 40 | con.execute( 'PRAGMA synchronous=OFF;' ) 41 | con.execute( 'PRAGMA journal_mode=OFF;' ) 42 | con.execute( 'PRAGMA temp_store=MEMORY;' ) 43 | end 44 | end ## method connect 45 | 46 | 47 | def setup_db 48 | ## build schema 49 | CreateDb.new.up 50 | end 51 | 52 | 53 | 54 | ### todo/fix: 55 | ## make trait_ids_cache more (re)usable - fix global!!!! 56 | TRAIT_IDS_CACHE = {} 57 | 58 | 59 | def setup_traits 60 | ## for speed - turn off logging for info/debug/etc. levels 61 | ActiveRecord::Base.logger.level = :warn 62 | 63 | ### add traits 64 | TRAITS.each do |trait_key, trait_hash| 65 | 66 | trait_t = Copycats::Model::TraitType.new 67 | trait_t.name = trait_hash[:name] 68 | trait_t.key = trait_key.to_s 69 | trait_t.save! 70 | 71 | cache = {} 72 | 73 | Kai::ALPHABET.each_with_index do |kai,n| 74 | name = trait_hash[:kai][kai] 75 | name = '?' if name.nil? || name.empty? 76 | 77 | tier = Gene::TIER[kai] 78 | 79 | puts "Kai: #{kai} (#{n}) /#{tier}, Cattribute: #{name}" 80 | trait = Copycats::Model::Trait.new 81 | trait.name = name 82 | trait.kai = kai 83 | trait.n = n 84 | trait.tier = tier 85 | trait.trait_type_id = trait_t.id 86 | trait.save! 87 | 88 | cache[ kai ] = trait.id 89 | end 90 | 91 | TRAIT_IDS_CACHE[ trait_key ] = { id: trait_t.id, 92 | kai: cache } 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /copycats/test/test_genome.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_genome.rb 6 | 7 | 8 | require 'helper' 9 | 10 | 11 | class TestGenome < MiniTest::Test 12 | 13 | 14 | def test_kitty_100000 15 | ## see https://cryptokittydex.com/kitties/100000 16 | ## https://www.cryptokitties.co/kitty/100000 17 | 18 | kai = "dadc 557j 4aaa gb9g 1161 383k 7dbe 774b 6667 8438 dd9a cbbd" 19 | 20 | genome = Genome.new( kai ) 21 | pp genome 22 | 23 | puts "body: #{genome.body}" 24 | puts "coloreyes: #{genome.coloreyes}" 25 | puts "eyes: #{genome.eyes}" 26 | puts "pattern: #{genome.pattern}" 27 | puts "mouth: #{genome.mouth}" 28 | puts "color1: #{genome.color1}" 29 | puts "color2: #{genome.color2}" 30 | puts "color3: #{genome.color3}" 31 | 32 | assert_equal "munchkin", genome.body 33 | assert_equal "strawberry", genome.coloreyes 34 | assert_equal "crazy", genome.eyes 35 | assert_equal "luckystripe", genome.pattern 36 | assert_equal "soserious", genome.mouth 37 | assert_equal "greymatter", genome.color1 38 | assert_equal "lemonade", genome.color2 39 | assert_equal "bloodred", genome.color3 40 | 41 | 42 | puts genome.genes[:color1].p 43 | puts genome.genes[:color1].h1 44 | puts genome.genes[:color1].h2 45 | puts genome.genes[:color1].h3 46 | 47 | assert_equal genome.genes[:color1].p, genome.genes[:color1].d 48 | assert_equal genome.genes[:color1].h1, genome.genes[:color1].r1 49 | assert_equal genome.genes[:color1].h2, genome.genes[:color1].r2 50 | assert_equal genome.genes[:color1].h3, genome.genes[:color1].r3 51 | 52 | 53 | puts genome.genes[:eyes].p 54 | puts genome.genes[:eyes].h1 55 | puts genome.genes[:eyes].h2 56 | puts genome.genes[:eyes].h3 57 | 58 | puts TRAITS[:eyes][ genome.genes[:eyes].p ] 59 | puts TRAITS[:eyes][ genome.genes[:eyes].h1 ] 60 | puts TRAITS[:eyes][ genome.genes[:eyes].h2 ] 61 | puts TRAITS[:eyes][ genome.genes[:eyes].h3 ] 62 | end 63 | 64 | 65 | def test_kitty_99895 66 | ## see https://cryptokittydex.com/kitties/99895 67 | ## https://www.cryptokitties.co/kitty/99895 68 | 69 | kai = "cdad 55r6 7f2f g9bg 1111 3757 d99d 272h 8k78 k884 9da9 fdee" 70 | 71 | genome = Genome.new( kai ) 72 | pp genome 73 | 74 | puts "body: #{genome.body}" 75 | puts "coloreyes: #{genome.coloreyes}" 76 | puts "eyes: #{genome.eyes}" 77 | puts "pattern: #{genome.pattern}" 78 | puts "mouth: #{genome.mouth}" 79 | puts "color1: #{genome.color1}" 80 | puts "color2: #{genome.color2}" 81 | puts "color3: #{genome.color3}" 82 | 83 | assert_equal "sphynx", genome.body 84 | assert_equal "mintgreen", genome.coloreyes 85 | assert_equal "thicccbrowz", genome.eyes 86 | assert_equal "calicool", genome.pattern 87 | assert_equal "soserious", genome.mouth 88 | assert_equal "cloudwhite", genome.color1 89 | assert_equal "coffee", genome.color2 90 | assert_equal "kittencream", genome.color3 91 | end 92 | 93 | end # class TestGenome 94 | -------------------------------------------------------------------------------- /copycats/lib/copycats/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ####################### 4 | ## yuml.me diagram: 5 | 6 | =begin 7 | [Kitties|id;gen;birthdate;day_count;genes_kai;name]1-<>48 (12x4)[Genes|n;gene;gene_n], 8 | [Genes]0..*<>-1[Traits|n;kai;tier;name], 9 | [Traits]32<>-1[Trait Types|name;key], 10 | [Kitties]<-parents (matron x sire) [Kitties], 11 | =end 12 | 13 | 14 | 15 | 16 | class CreateDb 17 | 18 | def up 19 | 20 | ActiveRecord::Schema.define do 21 | 22 | create_table :kitties do |t| 23 | ### t.integer :ref, null: false ## reference id - todo - find a better name e.g. uid (for unique id?) or gid? 24 | ## note: use built-in id for id!!!! 25 | 26 | t.string :name ## optional name e.g. Genesis, Galaxy Cat, etc. 27 | t.string :genes_kai, null: false ## genes in kai format 28 | t.integer :gen, null: false ## generation e.g. 0,1,2,etc. 29 | t.datetime :birthdate, null: false 30 | t.integer :day_count, null: false ## day 1, day 2, running day counter since 2017-11-23 31 | 32 | t.references :matron ## optional references kitty (with matron id) 33 | t.references :sire ## optional references kitty (with sire id) 34 | 35 | 36 | t.boolean :is_fancy, null: false, default: false 37 | t.boolean :is_exclusive, null: false, default: false 38 | t.boolean :is_founder, null: false, default: false # ids 1 to 100 (in cryptokitties) 39 | 40 | 41 | ## for easy queries add convenience gene-d for all traits 42 | t.references :body, null: false ## gene 0 (d) 43 | t.references :pattern, null: false ## gene 4 (d) 44 | t.references :coloreyes, null: false ## gene 8 (d) 45 | t.references :eyes, null: false ## gene 12 (d) 46 | t.references :color1, null: false ## gene 16 (d) 47 | t.references :color2, null: false ## gene 20 (d) 48 | t.references :color3, null: false ## gene 24 (d) 49 | t.references :wild, null: false ## gene 28 (d) 50 | t.references :mouth, null: false ## gene 32 (d) 51 | end 52 | 53 | 54 | create_table :genes do |t| 55 | t.references :kitty, null: false 56 | t.integer :n, null: false # gene number/pos 0-47 (start w/ 1 why? why not?) 57 | 58 | t.string :gene, null: false # d, r1, r2, r3 59 | t.integer :gene_n, null: false # 0-3 (0=d,1=r1,2=r2,3=r3 - start w/ 1 why? why not?) 60 | 61 | t.references :trait, null: false 62 | end 63 | 64 | 65 | create_table :traits do |t| 66 | t.references :trait_type, null: false 67 | t.string :name, null: false 68 | t.integer :n, null: false # 0-31 (decimal/base10) 69 | t.string :kai, null: false # 1-x (kai/base32) 70 | t.integer :tier # 1,2,3,4 - note: is nil (for x/31) for now (thus, optinal) 71 | end 72 | 73 | create_table :trait_types do |t| 74 | t.string :name, null: false # use "pretty" name e.g. fur, highlight color, etc. 75 | t.string :key, null: false # use key/ lowercase name from "official json api" 76 | end 77 | 78 | 79 | end # block Schema.define 80 | 81 | end # method up 82 | 83 | end # class CreateDb 84 | -------------------------------------------------------------------------------- /copycats/lib/copycats/tool.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module Copycats 4 | 5 | class Tool 6 | 7 | def run( args ) 8 | opts = {} 9 | 10 | parser = OptionParser.new do |cmd| 11 | cmd.banner = "Usage: kitty [options]" 12 | 13 | cmd.separator "" 14 | cmd.separator " Database options:" 15 | 16 | cmd.on("-n", "--dbname=NAME", "Database name (default: kitties.db)") do |name| 17 | opts[:db_name] = name 18 | end 19 | 20 | cmd.on("-d", "--dbpath=PATH", "Database path (default: .)") do |path| 21 | opts[:db_path] = path 22 | end 23 | 24 | cmd.separator "" 25 | cmd.separator " Data options:" 26 | 27 | cmd.on("-i", "--include=DIR", "Data directory (default: ./data for in-memory queries and . for setup)") do |name| 28 | opts[:data_dir] = name 29 | end 30 | 31 | cmd.separator "" 32 | cmd.separator " General options:" 33 | 34 | cmd.on("-h", "--help", "Prints this help") do 35 | puts cmd 36 | exit 37 | end 38 | end 39 | 40 | parser.parse!( args ) 41 | pp opts 42 | pp args 43 | 44 | 45 | db_path = opts[:db_path] || '.' 46 | db_name = opts[:db_name] || 'kitties.db' 47 | database = "#{db_path}/#{db_name}" 48 | 49 | 50 | if args.size == 1 && args[0] == 'setup' ## setup database 51 | connect( adapter: 'sqlite3', 52 | database: database ) 53 | 54 | setup_db 55 | setup_traits # note: also builds TRAIT_IDS_CACHE for read_datafiles for now 56 | 57 | data_dir = opts[:data_dir] || '.' ## for setup assume local dir for data 58 | read_datafiles( data_dir: data_dir ) 59 | else 60 | ## check if (local) database exists? 61 | if File.exist?( database ) ## if yes, use "cached" local database 62 | connect( adapter: 'sqlite3', 63 | database: database ) 64 | else ## otherwise use (new) on-demand in-memory database 65 | puts "note: database >#{database}<; setting up in-memory database" 66 | connect( adapter: 'sqlite3', 67 | database: ':memory:' ) 68 | 69 | setup_db 70 | setup_traits # note: also builds TRAIT_IDS_CACHE for read_datafiles for now 71 | 72 | data_dir = opts[:data_dir] || './data' 73 | read_datafiles( data_dir: data_dir ) 74 | end 75 | 76 | 77 | 78 | if args.size == 1 79 | id = args[0].to_i 80 | kitty = Copycats::Model::Kitty.find( id ) 81 | 82 | ## pp kitty 83 | ## pp kitty.genes 84 | ## pp kitty.body 85 | ## pp kitty.pattern 86 | ## pp kitty.genes.to_a 87 | 88 | buf = KittyReport.new( kitty ).build 89 | 90 | puts buf 91 | elsif args.size == 2 92 | matron_id = args[0].to_i 93 | sire_id = args[1].to_i 94 | 95 | matron = Copycats::Model::Kitty.find( matron_id ) 96 | sire = Copycats::Model::Kitty.find( sire_id ) 97 | ## pp matron 98 | 99 | buf = MixReport.new( matron, sire ).build 100 | 101 | puts buf 102 | else 103 | puts "error: too many arguments (expected setup or one or two kitty ids / names / genes)" 104 | end 105 | end 106 | end ## method run 107 | 108 | 109 | end ## class Tool 110 | 111 | end ## module Copycats 112 | -------------------------------------------------------------------------------- /copycats/lib/copycats/gene.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | class Gene 5 | 6 | 7 | 8 | 9 | ### todo/check: 10 | ## find a better name for Slice(incl.4 genes) 11 | ## e.g. GeneFour, Gene4, GeneGroup, GeneSlice,TraitGenes,... - why? why not? 12 | 13 | class Slice ## Gene::Slice (nested class) 14 | 15 | attr_reader :p, :h1, :h2, :h3 # p(rimary), h(idden) 1, h(idden) 2, h(idden) 3 16 | 17 | ## compat: add alias for ("old/classic") d, r1, r2, r3 18 | # d (dominant gene) -- todo/check: rename to just d instead of d0 - why? why not? 19 | # r1 (1st order recessive gene) 20 | # r2 (2nd order recessive gene) 21 | # r3 (3rd order recessive gene) 22 | def d() p; end # todo: use alias - why? why not? 23 | def r1() h1; end 24 | def r2() h2; end 25 | def r3() h3; end 26 | 27 | 28 | def initialize( arg ) 29 | ## (always) assume String in base32/kai for now 30 | kai = arg 31 | ## puts "Gene.initialize #{kai}" 32 | kai = kai.reverse 33 | @p = kai[0] 34 | @h1 = kai[1] 35 | @h2 = kai[2] 36 | @h3 = kai[3] 37 | end 38 | 39 | def to_kai() @h3 + @h2 + @h1 + @p; end ## return a string in kai/base32 notation 40 | 41 | 42 | def swap 43 | puts "Gene#swap" 44 | kai = to_kai.reverse # note: use reverse kai string (kai[0] is first char/digit/letter) 45 | 46 | 3.downto(1) do |i| 47 | if Lottery.rand(100) < 25 48 | puts " bingo! swap #{i}<>#{i-1}" 49 | kai[i-1], kai[i] = kai[i], kai[i-1] 50 | end 51 | end 52 | Gene.new( kai.reverse ) ## note: do NOT forget to pass in kai (unreversed) 53 | end 54 | 55 | 56 | def mutate( other ) 57 | puts "Gene#mutate" 58 | 59 | gene1 = Kai::NUMBER[p] ## primary/dominant gene1 60 | gene2 = Kai::NUMBER[other.p] ## primary/dominant gene2 61 | if gene1 > gene2 62 | gene1, gene2 = gene2, gene1 ## make sure gene2 is always bigger 63 | end 64 | if (gene2 - gene1) == 1 && gene1.even? 65 | probability = 25 66 | probability /= 2 if gene1 > 23 67 | if Lottery.rand(100) < probability 68 | genex = (gene1 / 2) + 16 ## 16=2^4 69 | puts " bingo! mutation #{gene2}+#{gene1} => #{genex}" 70 | puts " #{Kai::ALPHABET[gene2]}+#{Kai::ALPHABET[gene1]} => #{Kai::ALPHABET[genex]}" 71 | return Kai::ALPHABET[genex] 72 | end 73 | end 74 | nil # no mutation 75 | end 76 | 77 | def mix_inner( other ) 78 | puts "Gene#mix_inner" 79 | 80 | new_p = mutate( other ) 81 | if new_p.nil? ## no mutation of gene.p - use "regular" formula 82 | new_p = Lottery.rand(100) < 50 ? p : other.p 83 | end 84 | 85 | new_h1 = Lottery.rand(100) < 50 ? h1 : other.h1 86 | new_h2 = Lottery.rand(100) < 50 ? h2 : other.h2 87 | new_h3 = Lottery.rand(100) < 50 ? h3 : other.h3 88 | 89 | gene = Gene.new( new_h3 + new_h2 + new_h1 + new_p ) 90 | pp gene 91 | gene 92 | end 93 | 94 | 95 | def mix( other ) 96 | puts "Gene#mix" 97 | self_swapped = swap 98 | other_swapped = other.swap 99 | 100 | gene = self_swapped.mix_inner( other_swapped ) 101 | pp gene 102 | gene 103 | end 104 | 105 | end # class Slice 106 | end # class Gene 107 | -------------------------------------------------------------------------------- /copycats/lib/copycats/reports/traits.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class TraitsReport 4 | 5 | ROMAN = { 6 | 1 => 'I', 7 | 2 => 'II', 8 | 3 => 'III', 9 | 4 => 'IIII', 10 | 5 => 'V' 11 | } 12 | 13 | 14 | def build 15 | buf = "" 16 | buf << "# Traits\n\n" 17 | 18 | buf << "| Tier | Kai |" 19 | TRAITS.values[0,6].each do |trait| 20 | buf << " #{trait[:name]} |" 21 | end 22 | buf << "\n" 23 | buf << "|----|----|----|----|----|----|----|----|\n" 24 | 25 | buf << "| | |" 26 | TRAITS.values[0,6].each do |trait| 27 | buf << " #{trait[:genes]} |" 28 | end 29 | buf << "\n" 30 | 31 | Kai::ALPHABET.each_char do |kai| 32 | tier = ROMAN[ Gene::TIER[kai] ] 33 | tier = '?' if tier.nil? 34 | buf << "| #{tier} | #{kai} |" 35 | TRAITS.values[0,6].each do |trait| 36 | value = trait[:kai][kai] 37 | value = '?' if value.nil? || value.empty? 38 | buf << " #{value} |" 39 | end 40 | buf << "\n" 41 | end 42 | buf << "\n\n" 43 | 44 | ## part ii (split into two tables) 45 | buf << "| Tier | Kai |" 46 | TRAITS.values[6,6].each do |trait| 47 | buf << " #{trait[:name]} |" 48 | end 49 | buf << "\n" 50 | buf << "|----|----|----|----|----|----|----|----|\n" 51 | 52 | buf << "| | |" 53 | TRAITS.values[6,6].each do |trait| 54 | buf << " #{trait[:genes]} |" 55 | end 56 | buf << "\n" 57 | 58 | Kai::ALPHABET.each_char do |kai| 59 | tier = ROMAN[ Gene::TIER[kai] ] 60 | tier = '?' if tier.nil? 61 | buf << "| #{tier} | #{kai} |" 62 | TRAITS.values[6,6].each do |trait| 63 | value = trait[:kai][kai] 64 | value = '?' if value.nil? || value.empty? 65 | buf << " #{value} |" 66 | end 67 | buf << "\n" 68 | end 69 | buf << "\n\n" 70 | 71 | 72 | buf += < '1-99_999', 40 | 1 => '100_000-199_999', 41 | 2 => '200_000_299_999', 42 | 3 => '300_000-399_999', 43 | 4 => '400_000-499_999', 44 | 5 => '500_000-599_999', 45 | 6 => '600_000-699_999', 46 | 7 => '700_000-799_999', 47 | 8 => '800_000-899_999', 48 | 9 => '900_000-999_999', 49 | } 50 | 51 | 52 | def id_to_nos( id ) 53 | dir_no = id / 100_000 ## e.g. 0-9 ++ 1 => 100_000-199_999 ... 54 | file_no = id / 1000 ## e.g. 0-999 ++ 1 => 001 ... 55 | 56 | ## puts "id: #{id} => #{dir_no} / #{file_no}" 57 | 58 | [dir_no, file_no] 59 | end 60 | 61 | 62 | 63 | HEADERS = ['id', 64 | 'gen', 65 | 'matron_id', 66 | 'sire_id', 67 | 'birthdate', 68 | 'genes', 69 | 'name'] 70 | 71 | 72 | 73 | def initialize 74 | @last_no = [-1,-1] 75 | @last_out = nil 76 | end 77 | 78 | 79 | 80 | def out_for( id, out_dir: ) 81 | no = id_to_nos( id ) 82 | print no.inspect 83 | 84 | ## check for new section / batch 85 | ## if yes, close old file and open new file 86 | if @last_no[1] != no[1] 87 | @last_out.close if @last_out 88 | 89 | root = "#{out_dir}/#{DIR[no[0]]}" 90 | num = '%03d' % no[1] ## use 000, 001, 002, 099, 91 | ## 100, 101, 102, ... etc. 92 | filepath = "#{root}/#{num}.csv" 93 | puts "\n** >#{filepath}<:" 94 | 95 | FileUtils.mkdir_p( root ) unless Dir.exist?( root ) 96 | 97 | out = CSV.open( filepath, 'w' ) 98 | out << HEADERS 99 | @last_out = out 100 | end 101 | 102 | @last_no = no 103 | @last_out 104 | end 105 | 106 | 107 | def convert( path, out_dir: './o' ) 108 | i = 0 109 | last_id = 0 110 | out = nil 111 | 112 | CSV.foreach( path, headers: true ) do |row| 113 | if i==0 114 | pp row 115 | pp row.headers 116 | end 117 | 118 | id = row['id'].to_i 119 | print id 120 | print "." 121 | 122 | if id < last_id 123 | puts "!!! kitty id must be greater than last id" 124 | exit 1 125 | end 126 | 127 | last_id = id 128 | 129 | rec = [] 130 | rec << row['id'] 131 | rec << row['gen'] 132 | rec << (row['matron_id'] == '0' ? nil : row['matron_id']) 133 | rec << (row['sire_id'] == '0' ? nil : row['sire_id']) 134 | rec << row['birth_date'] 135 | rec << fmt_kai(row['genes_kai']) 136 | rec << nil ## name 137 | 138 | out = out_for( id, out_dir: out_dir ) 139 | out << rec 140 | 141 | i+=1 142 | ## break if i>=2002 143 | end 144 | 145 | print "\n" 146 | out.close ## close last out(put) file/stream 147 | end 148 | 149 | 150 | ######################### 151 | ### format helpers 152 | 153 | def fmt_kai( kai ) 154 | ## format in groups of four (4) separated by space 155 | ## e.g. ccac7787fa7fafaa16467755f9ee444467667366cccceede 156 | ## : ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede 157 | kai.reverse.gsub( /(.{4})/, '\1 ').reverse.strip 158 | end 159 | 160 | end # class KittyConverter 161 | 162 | 163 | 164 | conv = KittyConverter.new 165 | conv.convert( 'dl/kittydex-20180320.csv', out_dir: '../kitties' ) 166 | -------------------------------------------------------------------------------- /copycats/NOTES.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## Todos 4 | 5 | - [ ] change formula / recipe to traits in traits.rb 6 | 7 | ### How to name the group of four genes? 8 | 9 | use 10 | - Gene 11 | - Genes / G4 / Gene4x / ? 12 | - Gene Slice / Slice / Gene::Slice / Genome::Slice 13 | - Gene Family / Family 14 | - Gene Cluster / Cluster 15 | - Gene Group / Group 16 | - Gene Region / Region 17 | - Gene Seq / Sequence / ? 18 | - Gene Set / Set / ? 19 | - Multigene (?) 20 | - Zone / Unit / ? 21 | 22 | https://en.wikipedia.org/wiki/Gene 23 | 24 | > The term gene was introduced by Danish botanist, plant physiologist and geneticist Wilhelm Johannsen in 1905. 25 | > It is inspired by the ancient Greek: γόνος, gonos, that means offspring and procreation. 26 | 27 | https://en.wikipedia.org/wiki/Gene_family 28 | 29 | 30 | 31 | 32 | 33 | ### print traits with database id - why? why not? 34 | 35 | TRAIT_IDS.md - use / build a new page (report) - why? why not? 36 | 37 | 38 | ### Ruby SQL intersect with ActiveRecord 39 | 40 | 41 | ``` 42 | genes = Gene.where( trait: Trait.find_by( name: 'savannah' )).intersect( 43 | Gene.where( trait: Trait.find_by( name: 'pattern' ))) 44 | genes.map { |gene| gene.kitty } # get kitties (from gene) 45 | ``` 46 | 47 | check if the same as: 48 | 49 | ``` 50 | genes = Gene.find_by( trait: Trait.find_by( name: 'savannah' )) & 51 | Gene.find_by( trait: Trait.find_by( name: 'pattern' )) 52 | genes.map { |gene| gene.kitty } # get kitties (from gene) 53 | ``` 54 | 55 | use to_sql for see generated sql! 56 | 57 | 58 | check if project on kitty_id required!! 59 | otherwise all records are different (has its own id!!!) 60 | 61 | 62 | 63 | ### SQL 64 | 65 | ``` sql 66 | SELECT kitty_id FROM genes WHERE trait_id = 0 67 | INTERSECT 68 | SELECT kitty_id FROM genes WHERE trait_id = 33 69 | ``` 70 | 71 | 72 | check query samples 73 | with article and tags (find article by two tags - more than one - for example) 74 | 75 | 76 | does it work with inner join? 77 | 78 | ``` sql 79 | SELECT id -- ??? to be done (not working) 80 | FROM kitties 81 | INNER JOIN genes 82 | where genes.trait_id in [0,3] 83 | ``` 84 | 85 | or use subselect in where? 86 | 87 | ``` sql 88 | SELECT id -- ??? to be done (not working) 89 | FROM kitties 90 | where select kitty_id from genes where trait_id in [0,3] 91 | ``` 92 | 93 | 94 | 95 | 96 | 97 | 98 | --- 99 | 100 | ## Mutations / Mewtations 101 | 102 | 5-bit Gene = 2^5 = 32 Values / Attributes 103 | 104 | 16 Mutation Pairs (16 x 2 = 32) 105 | 106 | | Tier | Count | Total | 107 | |------------------------|-------|-------| 108 | | Tier 1 (Basic Traits) | 16 | 16 | 109 | | Tier 2 (Mutations) | 8 | 24 | 110 | | Tier 3 (Mutations) | 4 | 28 | 111 | | Tier 4 (Mutations) | 2 | 30 | 112 | | Tier 5 (Mutations) | 1 | 31 | 113 | 114 | 115 | ``` 116 | Tier 1 Tier 2 Tier 3 Tier 4 Tier 5 117 | (0-15) (16-23) (24-27) (28,29) (30) 118 | 0+1 = 16 16+17 = 24 24+25 = 28 28+29 = 30 119 | 2+3 = 17 18+19 = 25 26+27 = 29 120 | 4+5 = 18 20+21 = 26 121 | 6+7 = 19 22+23 = 27 122 | 8+9 = 20 123 | 10+11 = 21 124 | 12+13 = 22 125 | 14+15 = 23 126 | ``` 127 | 128 | Note: It's impossible for a mutation to reach `31` e.g.`30+31 = 31`. 129 | 130 | 131 | in Kai (base32) notation: 132 | 133 | ``` 134 | Tier 1 Tier 2 Tier 3 Tier 4 Tier 5 135 | (1-g) (h-p) (q-t) (u,v) (w) 136 | 1+2 = h h+i = q q+r = u u+v = w 137 | 3+4 = i j+k = r s+t = v 138 | 5+6 = j m+n = s 139 | 7+8 = k o+p = t 140 | 9+a = m 141 | b+c = n 142 | d+e = o 143 | f+g = p 144 | ``` 145 | 146 | Note: It's impossible for a mutation to reach `x` e.g. `w+x = x`. 147 | 148 | 149 | 150 | The formula is `n+(n+1) = n/2+16` if n is an even number (0,2,4,6,...). 151 | Running: 152 | 153 | ``` ruby 154 | (0..31).each do |n| 155 | if n.even? 156 | puts "#{n}+#{n+1} = #{n/2+16}" 157 | end 158 | end 159 | ``` 160 | 161 | results in: 162 | 163 | ``` 164 | 0+1 = 16 165 | 2+3 = 17 166 | 4+5 = 18 167 | 6+7 = 19 168 | 8+9 = 20 169 | 10+11 = 21 170 | 12+13 = 22 171 | 14+15 = 23 172 | 16+17 = 24 173 | 18+19 = 25 174 | 20+21 = 26 175 | 22+23 = 27 176 | 24+25 = 28 177 | 26+27 = 29 178 | 28+29 = 30 179 | 30+31 = 31 180 | ``` 181 | 182 | and in kai (base32) notation. 183 | 184 | ``` ruby 185 | ALPHABET = "123456789abcdefghijkmnopqrstuvwx" 186 | 187 | (0..31).each do |n| 188 | if n.even? 189 | puts "#{ALPHABET[n]}+#{ALPHABET[n+1]} = #{ALPHABET[n/2+16]}" 190 | end 191 | end 192 | ``` 193 | 194 | results in: 195 | 196 | ``` 197 | 1+2 = h 198 | 3+4 = i 199 | 5+6 = j 200 | 7+8 = k 201 | 9+a = m 202 | b+c = n 203 | d+e = o 204 | f+g = p 205 | h+i = q 206 | j+k = r 207 | m+n = s 208 | o+p = t 209 | q+r = u 210 | s+t = v 211 | u+v = w 212 | w+x = x 213 | ``` 214 | 215 | 216 | ## Fancies 217 | 218 | Stephen Curry Kitties 219 | 220 | Three kitties created in the likeness of Stephen Curry, an NBA player. 221 | These three kittis were ChefFurry, StephFurThree, and #30Furry (Kitty ids: 130, 230, and 330 respectively). However, shortly after releasing these kitties, their pages were removed. Currently the CryptoKitties team states: 222 | 223 | We have reason to believe Steph wasn't as involved in the CurryKitties as we thought. 224 | Until we're sure he's an active participant, we're suspending the campaign. 225 | 226 | --- 227 | 228 | - 帝龙喵 (GoldenDragonCat) 888 229 | - 旺财汪 (GoldDogCat) 1802, 1803, 1805, 1806, 1808, 1809, 1812, 1816, 1826, 1827, 1828 230 | 231 | Fancies: 232 | - 汪星爷/DogCat 88 tigerpunk, sweetlemoncakes, barkbrown, periwinkle, yokel 233 | - 咚咚锵/LionDanceCat 888 manx, googly, royalblue, starstruck 234 | - 红包喵/FortuneCat 888 235 | 236 | http://www.cryptokittieswiki.com/Fancies/ 237 | 238 | 239 | http://www.cryptokittieswiki.com/contracts/ 240 | 241 | Trait Releases 242 | 243 | In early April, 2018, trait releases were changed in gen0 kitties. 244 | Specifically, after the Family Jewels update when new traits were released, it was relatively easy to snipe the diamond. This lead to automation that would quickly purchase the kitty. Therefore traits were changed so they would be introduced at H3 and then after some time go up to H2, till they are at the highest level they will achieve. While this did not eliminated the automation, it made the process require more investment and breeding to get the diamond.1 245 | 246 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /copycats/lib/copycats/genome.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | class Genome 5 | attr_reader :genes ## hash of (sliced) genes (key is gene trait type) 6 | 7 | def initialize( arg ) 8 | if arg.is_a? Hash 9 | hash = arg ## assumes (pre-built) hash with genes 10 | @genes = hash 11 | else 12 | if arg.is_a? Integer ## use Integer (Fixnum+Bignum??) - why? why not? 13 | num = arg 14 | kai = Kai.encode( num ) 15 | else # else assume string in kai/base32 format 16 | kai = arg.dup # just in case; make a clean (fresh) copy 17 | kai = kai.gsub( ' ', '' ) ## allow spaces (strip/remove) 18 | end 19 | ## puts "Genome.initialize #{kai}" 20 | build_genes( kai ) 21 | end 22 | end 23 | 24 | 25 | def build_genes( kai ) 26 | kai = kai.reverse ## note: reserve for easy left-to-right access 27 | @genes = {} ## hash of genes (key is gene type) 28 | ## fix/todo: use as_json for "official" api order 29 | ## note: use insert order from "official" api 30 | @genes[:body] = Gene::Slice.new( kai[0,4].reverse ) 31 | @genes[:pattern] = Gene::Slice.new( kai[4,4].reverse ) 32 | @genes[:coloreyes] = Gene::Slice.new( kai[8,4].reverse ) 33 | @genes[:eyes] = Gene::Slice.new( kai[12,4].reverse ) 34 | @genes[:color1] = Gene::Slice.new( kai[16,4].reverse ) ## colorprimary / body color / base color 35 | @genes[:color2] = Gene::Slice.new( kai[20,4].reverse ) ## colorsecondary / sec color / pattern color / hi(light) color 36 | @genes[:color3] = Gene::Slice.new( kai[24,4].reverse ) ## colortertiary / acc(ent) color 37 | @genes[:wild] = Gene::Slice.new( kai[28,4].reverse ) 38 | @genes[:mouth] = Gene::Slice.new( kai[32,4].reverse ) 39 | @genes[:environment] = Gene::Slice.new( kai[36,4].reverse ) 40 | @genes[:secret] = Gene::Slice.new( kai[40,4].reverse ) 41 | @genes[:prestige] = Gene::Slice.new( kai[44,4].reverse ) 42 | end 43 | 44 | def body() TRAITS[:body][:kai][ @genes[:body].p ]; end 45 | def coloreyes() TRAITS[:coloreyes][:kai][ @genes[:coloreyes].p ]; end 46 | def eyes() TRAITS[:eyes][:kai][ @genes[:eyes].p ]; end 47 | def pattern() TRAITS[:pattern][:kai][ @genes[:pattern].p ]; end 48 | def mouth() TRAITS[:mouth][:kai][ @genes[:mouth].p ]; end 49 | def color1() TRAITS[:color1][:kai][ @genes[:color1].p ]; end 50 | def color2() TRAITS[:color2][:kai][ @genes[:color2].p ]; end 51 | def color3() TRAITS[:color3][:kai][ @genes[:color3].p ]; end 52 | 53 | def wild() TRAITS[:wild][:kai][ @genes[:wild].p ]; end 54 | def environment() TRAITS[:environment][:kai][ @genes[:environment].p ]; end 55 | 56 | 57 | 58 | def genes_color1() @genes[:color1]; end ## rename to color1_genes instead - why? why not? 59 | def genes_eyes() @genes[:eyes]; end 60 | ## .... 61 | 62 | ## add cattributes ?? why? why not? 63 | 64 | 65 | def mix( other ) 66 | mgenes = genes ## matron genes 67 | sgenes = other.genes ## sire genes 68 | new_genes = {} 69 | 70 | [:body, ### todo/fix: use TRAITS.keys or something - why? why not? 71 | :pattern, 72 | :coloreyes, 73 | :eyes, 74 | :color1, 75 | :color2, 76 | :color3, 77 | :wild, 78 | :mouth, 79 | :environment, 80 | :secret, 81 | :prestige].each do |key| 82 | mgene = mgenes[key] 83 | sgene = sgenes[key] 84 | 85 | new_gene = mgene.mix( sgene ) 86 | new_genes[key] = new_gene 87 | end 88 | 89 | Genome.new( new_genes ) ## return new genome from (pre-built) hash (with genes) 90 | end 91 | 92 | 93 | def build_tables() GenomeTables.new( self ).build; end 94 | 95 | def build_mix_tables( other ) GenomeMixTables.new( self, other ).build; end 96 | 97 | end # class Genome 98 | 99 | 100 | 101 | class GenomeTables 102 | def initialize( genome ) 103 | @genome = genome 104 | end 105 | 106 | def build 107 | pos = 0 108 | buf = "" 109 | 110 | genes = @genome.genes 111 | 112 | TRAITS.each do |key, trait| 113 | gene = genes[key] 114 | next if gene.nil? ## skip future_1, future_2, etc. 115 | 116 | buf << "#{trait[:name]} (Genes #{trait[:genes]})\n\n" 117 | 118 | ### 119 | ## fix/todo: add stars for purity? 120 | ## **** - all traits the same 121 | ## *** - two same pairs of traits 122 | ## ** - one pair of same traits 123 | 124 | buf << "|Gene |Binary |Kai |Trait | |\n" 125 | buf << "|------|---------|-----|---------|---|\n" 126 | buf << "| #{pos} | #{Kai::BINARY[gene.d]} | #{gene.d} | **#{fmt_trait(trait[:kai][gene.d])}** | d |\n"; pos+=1 127 | buf << "| #{pos} | #{Kai::BINARY[gene.r1]} | #{gene.r1} | #{fmt_trait(trait[:kai][gene.r1])} | r1 |\n"; pos+=1 128 | buf << "| #{pos} | #{Kai::BINARY[gene.r2]} | #{gene.r2} | #{fmt_trait(trait[:kai][gene.r2])} | r2 |\n"; pos+=1 129 | buf << "| #{pos} | #{Kai::BINARY[gene.r3]} | #{gene.r3} | #{fmt_trait(trait[:kai][gene.r3])} | r3 |\n"; pos+=1 130 | buf << "\n" 131 | 132 | if key == :body ## add legend for first entry 133 | buf << "d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive\n\n" 134 | end 135 | end 136 | 137 | buf 138 | end 139 | 140 | #################### 141 | ## helpers 142 | 143 | def fmt_trait( trait ) 144 | (trait.nil? || trait.empty?) ? '?' : trait 145 | end 146 | end # class GenomeTables 147 | 148 | 149 | 150 | 151 | class GenomeMixTables 152 | def initialize( matron, sire ) 153 | @matron = matron 154 | @sire = sire 155 | end 156 | 157 | def build 158 | pos = 0 159 | buf = "" 160 | 161 | mgenes = @matron.genes 162 | sgenes = @sire.genes 163 | 164 | TRAITS.each do |key, trait| 165 | mgene = mgenes[key] 166 | sgene = sgenes[key] 167 | next if mgene.nil? ## skip future_1, future_2, etc. 168 | 169 | buf << "#{trait[:name]} (Genes #{trait[:genes]})\n\n" 170 | 171 | buf << "|Gene |Kai |Trait (Matron)|Kai|Trait (Sire)| |\n" 172 | buf << "|------|-----|---------|-----|---------|---|\n" 173 | buf << "| #{pos} | #{mgene.d} | **#{fmt_trait(trait[:kai][mgene.d])}** | #{sgene.d} | **#{fmt_trait(trait[:kai][sgene.d])}** | d |\n"; pos+=1 174 | buf << "| #{pos} | #{mgene.r1} | #{fmt_trait(trait[:kai][mgene.r1])} | #{sgene.r1} | #{fmt_trait(trait[:kai][sgene.r1])} | r1 |\n"; pos+=1 175 | buf << "| #{pos} | #{mgene.r2} | #{fmt_trait(trait[:kai][mgene.r2])} | #{sgene.r2} | #{fmt_trait(trait[:kai][sgene.r2])} | r2 |\n"; pos+=1 176 | buf << "| #{pos} | #{mgene.r3} | #{fmt_trait(trait[:kai][mgene.r3])} | #{sgene.r3} | #{fmt_trait(trait[:kai][sgene.r3])} | r3 |\n"; pos+=1 177 | buf << "\n" 178 | 179 | if key == :body ## add legend for first entry 180 | buf << "d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive\n\n" 181 | end 182 | end 183 | 184 | buf 185 | end 186 | 187 | #################### 188 | ## helpers 189 | 190 | def fmt_trait( trait ) 191 | (trait.nil? || trait.empty?) ? '?' : trait 192 | end 193 | end # class GenomeMixTables 194 | -------------------------------------------------------------------------------- /copycats/lib/copycats/import/read.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | ## load all *.file in data folder 5 | 6 | def find_datafiles( root='.' ) 7 | files = [] 8 | ## todo/check: include all subfolders - why? why not? 9 | Dir.glob( root + '/**/*.csv' ).each do |file| 10 | files << file 11 | end 12 | files 13 | end 14 | 15 | 16 | def read_datafiles( data_dir: './data' ) 17 | files = find_datafiles( data_dir ) 18 | pp files 19 | 20 | ## todo: check if files found 21 | 22 | 23 | ## for speed - turn off logging for info/debug/etc. levels 24 | ActiveRecord::Base.logger.level = :warn 25 | 26 | 27 | ## pp TRAIT_IDS_CACHE 28 | 29 | ## add / read / load all datafiles 30 | files.each_with_index do |file,i| 31 | 32 | puts "== #{i+1}/#{files.size} reading datafile '#{file}'..." 33 | 34 | 35 | kitties_headers = CsvReader.header( file ) 36 | pp kitties_headers 37 | 38 | ## todo: fix - use field index not name!!!! - why? why not? 39 | ## check format 40 | if kitties_headers.include?( 'id' ) && 41 | kitties_headers.include?( 'gen' ) && 42 | kitties_headers.include?( 'matron_id' ) && 43 | kitties_headers.include?( 'sire_id' ) && 44 | kitties_headers.include?( 'birthdate' ) && 45 | kitties_headers.include?( 'genes' ) && 46 | kitties_headers.include?( 'name' ) 47 | ## "standard" format 48 | ## required headers include: id, gen, matron_id, sire_id, birthdate, genes, name 49 | 50 | ## todo: fix - use field index not name!!!! - why? why not? 51 | headers = { 52 | 'id' => 'id', 53 | 'gen' => 'gen', 54 | 'matron_id' => 'matron_id', 55 | 'sire_id' => 'sire_id', 56 | 'birthdate' => 'birthdate', 57 | 'genes' => 'genes', 58 | 'name' => 'name' 59 | } 60 | elsif kitties_headers.include?( 'id' ) && 61 | kitties_headers.include?( 'matron_id' ) && 62 | kitties_headers.include?( 'sire_id' ) && 63 | kitties_headers.include?( 'gen' ) && 64 | kitties_headers.include?( 'birth_date' ) && 65 | kitties_headers.include?( 'genes_kai' ) 66 | ## "kittydex" format 67 | ## see https://cryptokittydex.com/resources 68 | ## required headers include: id, matron_id, sire_id, gen, birth_date, genes_kai 69 | headers = { 70 | 'id' => 'id', 71 | 'matron_id' => 'matron_id', 72 | 'sire_id' => 'sire_id', 73 | 'gen' => 'gen', 74 | 'birthdate' => 'birth_date', 75 | 'genes' => 'genes_kai', 76 | 'name' => 'name' ## note: will always be nil (is missing in kittydex) 77 | } 78 | else 79 | ## unknown format 80 | puts "!!! unknown datafile format; matching headers NOT found / missing" 81 | exit 1 82 | end 83 | 84 | 85 | ## start of kitties blockchain / genesis 86 | genesisdate = Date.new( 2017, 11, 23) ## 2017-11-23 87 | 88 | ## note: for now use first 5 rows for testing 89 | ## kitties[0..4].each do |row| 90 | 91 | kitties = CsvHash.read( file ) 92 | kitties.each do |row| 93 | ## puts row['id'] + '|' + row['gen'] + '|' + row['genes_kai'] 94 | k = Copycats::Model::Kitty.new 95 | k.id = row[headers['id']].to_i 96 | k.gen = row[headers['gen']].to_i 97 | k.matron_id = row[headers['matron_id']].to_i unless row[headers['matron_id']].blank? || row[headers['matron_id']] == '0' 98 | k.sire_id = row[headers['sire_id']].to_i unless row[headers['sire_id']].blank? || row[headers['sire_id']] == '0' 99 | k.name = row[headers['name']] unless row[headers['name']].blank? 100 | 101 | ## founder cats - first one hundret (1 to 100 - note: includes genesis (1)) 102 | k.is_founder = true if k.id >= 1 && k.id <= 100 103 | 104 | 105 | ## todo: pretty print (format genes !!!!) 106 | k.genes_kai = row[headers['genes']] ### .gsub( ' ', '' ) ## remove all spaces - why? why not? 107 | 108 | ## pp row['birthdate'] 109 | birthdate = DateTime.strptime( row[headers['birthdate']], '%Y-%m-%d %H:%M:%S' ) 110 | k.birthdate = birthdate 111 | k.day_count = (birthdate.to_date.jd - genesisdate.jd)+1 112 | 113 | 114 | genome = Genome.new( k.genes_kai ) 115 | genes = genome.genes 116 | 117 | k.body_id = TRAIT_IDS_CACHE[:body][:kai][genes[:body].d] 118 | k.pattern_id = TRAIT_IDS_CACHE[:pattern][:kai][genes[:pattern].d] 119 | k.coloreyes_id = TRAIT_IDS_CACHE[:coloreyes][:kai][genes[:coloreyes].d] 120 | k.eyes_id = TRAIT_IDS_CACHE[:eyes][:kai][genes[:eyes].d] 121 | k.color1_id = TRAIT_IDS_CACHE[:color1][:kai][genes[:color1].d] 122 | k.color2_id = TRAIT_IDS_CACHE[:color2][:kai][genes[:color2].d] 123 | k.color3_id = TRAIT_IDS_CACHE[:color3][:kai][genes[:color3].d] 124 | k.wild_id = TRAIT_IDS_CACHE[:wild][:kai][genes[:wild].d] 125 | k.mouth_id = TRAIT_IDS_CACHE[:mouth][:kai][genes[:mouth].d] 126 | 127 | ## pp k 128 | 129 | ## print ids for progress report - why? why not? 130 | print "#{k.id}." 131 | k.save! 132 | 133 | ## add genes 134 | TRAITS.each_with_index do |(trait_key, trait_hash),i| 135 | gene = genes[trait_key] 136 | next if gene.nil? ## skip future_1, future_2, etc. for now - add - why? why not? 137 | 138 | ## puts "#{trait_hash[:name]} (Genes #{trait_hash[:genes]})\n\n" 139 | 140 | ## note: start counting for d.n with 1 (NOT 0) 141 | ## use idx for zero-based counting - why? why not? 142 | 143 | d = Copycats::Model::Gene.new 144 | d.kitty_id = k.id 145 | d.n = (i*4) # gene number/pos 0-47 (start w/ 1 why? why not?) 146 | d.gene = 'd' # d (0), r1 (1), r2 (2), r3 (3) 147 | d.gene_n = 0 # 0-3 (0=d,1=r1,2=r2,3=r3) 148 | d.trait_id = TRAIT_IDS_CACHE[trait_key][:kai][gene.d] 149 | d.save! 150 | 151 | r1 = Copycats::Model::Gene.new 152 | r1.kitty_id = k.id 153 | r1.n = (i*4)+1 # gene number/pos 0-47 (start w/ 1 why? why not?) 154 | r1.gene = 'r1' # d (0), r1 (1), r2 (2), r3 (3) 155 | r1.gene_n = 1 # 0-3 (0=d,1=r1,2=r2,3=r3) 156 | r1.trait_id = TRAIT_IDS_CACHE[trait_key][:kai][gene.r1] 157 | r1.save! 158 | 159 | r2 = Copycats::Model::Gene.new 160 | r2.kitty_id = k.id 161 | r2.n = (i*4)+2 # gene number/pos 0-47 (start w/ 1 why? why not?) 162 | r2.gene = 'r2' # d (0), r1 (1), r2 (2), r3 (3) 163 | r2.gene_n = 2 # 0-3 (0=d,1=r1,2=r2,3=r3) 164 | r2.trait_id = TRAIT_IDS_CACHE[trait_key][:kai][gene.r2] 165 | r2.save! 166 | 167 | r3 = Copycats::Model::Gene.new 168 | r3.kitty_id = k.id 169 | r3.n = (i*4)+3 # gene number/pos 0-47 (start w/ 1 why? why not?) 170 | r3.gene = 'r3' # d (0), r1 (1), r2 (2), r3 (3) 171 | r3.gene_n = 3 # 0-3 (0=d,1=r1,2=r2,3=r3) 172 | r3.trait_id = TRAIT_IDS_CACHE[trait_key][:kai][gene.r3] 173 | r3.save! 174 | end 175 | 176 | end 177 | print "\n" 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crypto Copycats Collectibles - Buy! Sell! Hodl! Sire! 2 | 3 | 4 | ## Copycats Database Tools 5 | 6 | - [**copycats**](copycats) - incl. `kitty` command line tool to (auto-)read kitty data records (in comma-separated values (CSV)) into an in-memory SQLite database 7 | and print reports 8 | - [**bitcat**](bitcat) - bit catalog kitty browser; browse your (digital) bit(s) collections 9 | 10 | 11 | 12 | 13 | ## Database Tables 14 | 15 | Table Diagram 16 | 17 | ![](copycats-tables.png) 18 | 19 | 20 | SQL Tables (in SQLite Dialect) 21 | 22 | ``` sql 23 | CREATE TABLE kitties ( 24 | id INTEGER PRIMARY KEY AUTOINCREMENT 25 | NOT NULL, 26 | name VARCHAR, 27 | genes_kai VARCHAR NOT NULL, 28 | gen INTEGER NOT NULL, 29 | birthdate DATETIME NOT NULL, 30 | day_count INTEGER NOT NULL, 31 | matron_id INTEGER, 32 | sire_id INTEGER, 33 | body_id INTEGER NOT NULL, 34 | pattern_id INTEGER NOT NULL, 35 | coloreyes_id INTEGER NOT NULL, 36 | eyes_id INTEGER NOT NULL, 37 | color1_id INTEGER NOT NULL, 38 | color2_id INTEGER NOT NULL, 39 | color3_id INTEGER NOT NULL, 40 | wild_id INTEGER NOT NULL, 41 | mouth_id INTEGER NOT NULL 42 | ); 43 | 44 | 45 | CREATE TABLE genes ( 46 | id INTEGER PRIMARY KEY AUTOINCREMENT 47 | NOT NULL, 48 | kitty_id INTEGER NOT NULL, 49 | n INTEGER NOT NULL, 50 | gene VARCHAR NOT NULL, 51 | gene_n INTEGER NOT NULL, 52 | trait_id INTEGER NOT NULL 53 | ); 54 | 55 | 56 | CREATE TABLE traits ( 57 | id INTEGER PRIMARY KEY AUTOINCREMENT 58 | NOT NULL, 59 | trait_type_id INTEGER NOT NULL, 60 | name VARCHAR NOT NULL, 61 | n INTEGER NOT NULL, 62 | kai VARCHAR NOT NULL, 63 | tier INTEGER 64 | ); 65 | 66 | CREATE TABLE trait_types ( 67 | id INTEGER PRIMARY KEY AUTOINCREMENT 68 | NOT NULL, 69 | name VARCHAR NOT NULL, 70 | [key] VARCHAR NOT NULL 71 | ); 72 | 73 | ``` 74 | 75 | ## Database Setup 76 | 77 | Use the kitty setup command to setup an SQLite database and (auto-)read 78 | all datafiles. Example: 79 | 80 | ``` 81 | $ kitty setup 82 | ``` 83 | 84 | This will create: 85 | 86 | - a single-file SQLite database `kitties.db` 87 | - setup all tables 88 | - add all known traits and trait types (body, pattern, eyes, ...) and 89 | - (auto-)read all datafiles (`**/*.csv`) in the `.` and all subdirectories 90 | 91 | 92 | Note: Use the `-i/--include` option to change the default data directory (that is, `.`) 93 | and use the `-n/--dbname` option to change the default SQLite database name (that is, `kitties.db`) 94 | and use the `-d/--dbpath` option to change the default SQLite database path (that is, `.`). 95 | 96 | 97 | Showtime! Use the sqlite3 command line tool 98 | and try some queries. Example: 99 | 100 | ``` 101 | $ sqlite3 kitties.db 102 | 103 | sqlite> SELECT * FROM kitties WHERE id = 1; 104 | 105 | 1||ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede|0|2017-11-23 06:19:59|... 106 | 107 | sqlite> SELECT * FROM genes WHERE trait_id = 14; -- sphynx (14) 108 | 109 | 1|1|0|d|0|14 110 | 3|1|2|r2|2|14 111 | 4|1|3|r3|3|14 112 | 38|2|1|r1|1|14 113 | 146|5|1|r1|1|14 114 | 181|6|0|d|0|14 115 | 183|6|2|r2|2|14 116 | ... 117 | ``` 118 | 119 | 120 | 121 | ## Database Queries 122 | 123 | ### SQL 124 | 125 | #### Find all kitties with a trait 126 | 127 | Let's use the trait savannah (fur) with the id 0: 128 | 129 | ``` sql 130 | SELECT id FROM kitties WHERE body_id = 0 131 | ``` 132 | 133 | #### Find all kitties with two traits 134 | 135 | Let's use the trait savannah (fur) with the id 0 136 | and the trait tiger (pattern) with the id 33: 137 | 138 | ``` sql 139 | SELECT id FROM kitties 140 | WHERE body_id = 0 AND pattern_id = 33 141 | ``` 142 | 143 | 144 | #### Find all kitties with a trait (in any gene d/r1/r2/r3) 145 | 146 | Note: All traits (12 x 32 = 384) are numbered with ids from 0 to 383 in the traits database table. 147 | Let's use the trait savannah (fur) with the id 0: 148 | 149 | ``` sql 150 | SELECT kitty_id FROM genes WHERE trait_id = 0 151 | ``` 152 | 153 | 154 | #### Find all kitties with a dominant (visible) trait 155 | 156 | Note: Use `gene` column (`d`/`r1`/`r2`/`r3`) or the numeric `gene_n` 157 | column (0/1/2/3): Let's use the trait savannah (fur) with the id 0 158 | and a dominant (d) gene: 159 | 160 | 161 | ``` sql 162 | SELECT kitty_id FROM genes 163 | WHERE trait_id = 0 AND gene='d' 164 | ``` 165 | 166 | 167 | #### Find all kitties with two traits (in any gene d/r1/r2/r3) 168 | 169 | Use two query with "intersect" the result. Let's 170 | use the trait savannah (fur) with the id 0 171 | and the trait tiger (pattern) with the id 33: 172 | 173 | ``` sql 174 | SELECT kitty_id FROM genes WHERE trait_id = 0 175 | INTERSECT 176 | SELECT kitty_id FROM genes WHERE trait_id = 33 177 | ``` 178 | 179 | 180 | ### Using Models w/ ActiveRecord in Ruby 181 | 182 | 183 | #### Find all kitties with a trait 184 | 185 | Let's use the trait savannah (fur) with the id 0: 186 | 187 | ``` ruby 188 | Kitty.find_by( body: Trait.find_by( name: 'savannah' )) 189 | # -or - 190 | Kitty.find_by( body_id: 0) 191 | ``` 192 | 193 | #### Find all kitties with two traits 194 | 195 | Let's use the trait savannah (fur) with the id 0 196 | and the trait tiger (pattern) with the id 33: 197 | 198 | ``` ruby 199 | Kitty.find_by( body: Trait.find_by( name: 'savannah' ), 200 | pattern: Trait.find_by( name: 'tiger' )) 201 | # -or - 202 | Kitty.find_by( body_id: 0, pattern_id: 33 ) 203 | ``` 204 | 205 | 206 | #### Find all kitties with a trait (in any gene d/r1/r2/r3) 207 | 208 | Let's use the trait savannah (fur) with the id 0: 209 | 210 | ``` ruby 211 | genes = Gene.find_by( trait: Trait.find_by( name: 'savannah' )) # query 212 | #-or- 213 | genes = Gene.find_by( trait_id: 0 ) 214 | genes.map { |gene| gene.kitty } # get kitties (from gene) 215 | ``` 216 | 217 | 218 | #### Find all kitties with a dominant (visible) trait 219 | 220 | Let's use the trait savannah (fur) with the id 0 and a dominant (d) gene: 221 | 222 | 223 | ``` ruby 224 | genes = Gene.find_by( trait: Trait.find_by( name: 'savannah' ), 225 | d: 'd' ) #query 226 | #-or- 227 | genes = Gene.find_by( trait_id: 0, d: 'd' ) 228 | genes.map { |gene| gene.kitty } # get kitties (from gene) 229 | ``` 230 | 231 | 232 | #### Find all kitties with two traits 233 | 234 | Use two query with "intersect" the result. Let's 235 | use the trait savannah (fur) 236 | and the trait tiger (pattern): 237 | 238 | ``` ruby 239 | genes = Gene.select('kitty_id').where( trait: Trait.find_by( name: 'savannah' )).intersect( 240 | Gene.select('kitty_id').where( trait: Trait.find_by( name: 'pattern' ))) 241 | genes.map { |gene| gene.kitty } # get kitties (from gene) 242 | ``` 243 | 244 | 245 | ## Datasets 246 | 247 | [(Crypto) Kitties on the Blockchain](https://github.com/cryptocopycats/kitties) - 248 | public dataset in comma-separated values (CSV) format in blocks of a thousand kitties each (e.g. 249 | [`000.csv`](https://github.com/cryptocopycats/kitties/blob/master/1-99_999/000.csv) incl. 1-999, 250 | [`001.csv`](https://github.com/cryptocopycats/kitties/blob/master/1-99_999/001.csv) incl. 1000-1999, 251 | [`002.csv`](https://github.com/cryptocopycats/kitties/blob/master/1-99_999/002.csv) incl. 2000-2999, 252 | and so on). The data records for kitties incl. id, gen(eration), matron+sire ids, birthdate, 48 (12x4) genes in kai (base32) notation, and more. 253 | 254 | 258 | 259 | 260 | Add your dataset here! 261 | 262 | 263 | -------------------------------------------------------------------------------- /copycats/README.md: -------------------------------------------------------------------------------- 1 | # Crypto Copycats Collectibles - Buy! Sell! Hodl! Sire! 2 | 3 | copycats command line tool (and core library) - crypto cats / kitties collectibles unchained - buy! sell! hodl! sire! - play for free - runs off the blockchain w/ ledger lite - no ether / gas required; run your own peer-to-peer (P2P) network node over HTTP 4 | 5 | 6 | 7 | * home :: [github.com/cryptocopycats/copycats](https://github.com/cryptocopycats/copycats) 8 | * bugs :: [github.com/cryptocopycats/copycats/issues](https://github.com/cryptocopycats/copycats/issues) 9 | * gem :: [rubygems.org/gems/copycats](https://rubygems.org/gems/copycats) 10 | * rdoc :: [rubydoc.info/gems/copycats](http://rubydoc.info/gems/copycats) 11 | 12 | 13 | 14 | 61 | 62 | 63 | 64 | 65 | ## kitty Command Line Tool 66 | 67 | Use the `kitty` command line tool to (auto-)read kitty data records 68 | (in comma-separated values (CSV)) into an in-memory SQLite database 69 | and print reports. Example - [`kitties/1-99_999/000.csv`](https://github.com/cryptocopycats/kitties/blob/master/1-99_999/000.csv): 70 | 71 | ``` 72 | id,gen,matron_id,sire_id,birthdate,genes,name 73 | 1,0,,,2017-11-23 06:19:59,ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede, 74 | 2,0,,,2017-11-23 06:19:59,ca9c 7575 f442 af9g 6664 5557 7777 4444 6686 8667 cccc ffec, 75 | 3,0,,,2017-11-23 06:19:59,ac9a 6686 ff7f 99aa 6666 5575 779f 4444 6786 7748 cccc dcfc, 76 | 4,0,,,2017-11-23 06:19:59,ccac 5686 22ff f99g 1616 7555 ffed 4444 8668 4687 cccc dcff, 77 | 5,0,,,2017-11-23 06:19:59,ca9c 8777 747f g99g 4411 7775 f77d 4444 7788 6377 cccc ffef, 78 | ... 79 | ``` 80 | 81 | Note: By default all datafiles (`**/*.csv`) in the `./data` and all subdirectories 82 | get (auto-)read. Use the `-i/--include` option to change the data directory. 83 | 84 | 85 | 86 | ### Kitty Genes Reader / Report 87 | 88 | Pass in the id (e.g. `1`, `43`, etc.) of the kitty to print a genes report. 89 | Example: 90 | 91 | ``` 92 | $ kitty 1 93 | ``` 94 | 95 | prints: 96 | 97 | ``` 98 | # Kitty #1 99 | 100 | genes (kai): ccac 7787 fa7f afaa 1646 7755 f9ee 4444 6766 7366 cccc eede 101 | 102 | Fur (Genes 0-3) 103 | 104 | |Gene| Binary |Kai (Tier)| Trait | | 105 | |----|--------|----------|------------|----| 106 | | 0 | 01101 | e (I) | **sphynx** | d | 107 | | 1 | 01100 | d (I) | munchkin ) | r1 | 108 | | 2 | 01101 | e (I) | sphynx | r2 | 109 | | 3 | 01101 | e (I) | sphynx | r3 | 110 | 111 | d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive 112 | 113 | Pattern (Genes 4-7) 114 | 115 | |Gene| Binary |Kai (Tier)| Trait | | 116 | |----|--------|----------|------------|----| 117 | | 4 | 01011 | c (I) | **jaguar** | d | 118 | | 5 | 01011 | c (I) | jaguar | r1 | 119 | | 6 | 01011 | c (I) | jaguar | r2 | 120 | | 7 | 01011 | c (I) | jaguar | r3 | 121 | 122 | Eye Color (Genes 8-11) 123 | 124 | |Gene| Binary |Kai (Tier)| Trait | | 125 | |----|--------|----------|-------------|----| 126 | | 8 | 00101 | 6 (I) | **sizzurp** | d | 127 | | 9 | 00101 | 6 (I) | sizzurp | r1 | 128 | | 10 | 00010 | 3 (I) | topaz | r2 | 129 | | 11 | 00110 | 7 (I) | chestnut | r3 | 130 | 131 | Eye Shape (Genes 12-15) 132 | 133 | |Gene| Binary |Kai (Tier)| Trait | | 134 | |----|---------|----------|------------|----| 135 | | 12 | 00101 | 6 (I) | **simple** | d | 136 | | 13 | 00101 | 6 (I) | simple | r1 | 137 | | 14 | 00110 | 7 (I) | crazy | r2 | 138 | | 15 | 00101 | 6 (I) | simple | r3 | 139 | 140 | Base Color (Genes 16-19) 141 | 142 | |Gene| Binary |Kai (Tier)| Trait | | 143 | |----|--------|----------|----------------|----| 144 | | 16 | 00011 | 4 (I) | **orangesoda** | d | 145 | | 17 | 00011 | 4 (I) | orangesoda | r1 | 146 | | 18 | 00011 | 4 (I) | orangesoda | r2 | 147 | | 19 | 00011 | 4 (I) | orangesoda | r3 | 148 | 149 | Highlight Color (Genes 20-23) 150 | 151 | |Gene| Binary |Kai (Tier)| Trait | | 152 | |----|--------|----------|--------------|----| 153 | | 20 | 01101 | e (I) | **lemonade** | d | 154 | | 21 | 01101 | e (I) | lemonade | r1 | 155 | | 22 | 01000 | 9 (I) | swampgreen | r2 | 156 | | 23 | 01110 | f (I) | chocolate | r3 | 157 | 158 | Accent Color (Genes 24-27) 159 | 160 | |Gene| Binary |Kai (Tier)| Trait | | 161 | |----|--------|----------|-----------------|----| 162 | | 24 | 00100 | 5 (I) | **granitegrey** | d | 163 | | 25 | 00100 | 5 (I) | granitegrey | r1 | 164 | | 26 | 00110 | 7 (I) | kittencream | r2 | 165 | | 27 | 00110 | 7 (I) | kittencream | r3 | 166 | 167 | Wild (Genes 28-31) 168 | 169 | |Gene| Binary |Kai (Tier)| Trait | | 170 | |----|--------|----------|---------|----| 171 | | 28 | 00101 | 6 (I) | **?** | d | 172 | | 29 | 00011 | 4 (I) | ? | r1 | 173 | | 30 | 00101 | 6 (I) | ? | r2 | 174 | | 31 | 00000 | 1 (I) | ? | r3 | 175 | 176 | Mouth (Genes 32-35) 177 | 178 | |Gene| Binary |Kai (Tier)| Trait | | 179 | |----|--------|----------|--------------|---| 180 | | 32 | 01001 | a (I) | **pouty** | d | 181 | | 33 | 01001 | a (I) | pouty | r1 | 182 | | 34 | 01110 | f (I) | happygokitty | r2 | 183 | | 35 | 01001 | a (I) | pouty | r3 | 184 | ``` 185 | 186 | 187 | ### Kitty Mix Genes (Matron + Sire) Report 188 | 189 | Pass in two ids for the matron and sire kitties to print a mix genes report. 190 | Example: 191 | 192 | 193 | ``` 194 | $ kitty 2 43 195 | ``` 196 | 197 | prints: 198 | 199 | ``` 200 | # Kitty #2 + #43 201 | 202 | genes (kai) 1: ca9c 7575 f442 af9g 6664 5557 7777 4444 6686 8667 cccc ffec 203 | genes (kai) 2: ca9a 7588 72a7 fa9f 4111 5555 dedf 4444 5888 4666 cccc fded 204 | 205 | Fur (Genes 0-3) 206 | 207 | |Gene|Kai|Trait (Matron) |Kai|Trait (Sire) | | 208 | |----|---|---------------|---|--------------|----| 209 | | 0 | c | **himalayan** | d | **munchkin** | d | 210 | | 1 | e | sphynx | e | sphynx | r1 | 211 | | 2 | f | ragamuffin | d | munchkin | r2 | 212 | | 3 | f | ragamuffin | f | ragamuffin | r3 | 213 | 214 | d = dominant, r1 = 1st order recessive, r2 = 2nd order recessive, r3 = 3rd order recessive 215 | 216 | Pattern (Genes 4-7) 217 | 218 | |Gene|Kai|Trait (Matron)|Kai|Trait (Sire)| | 219 | |----|---|--------------|---|------------|----| 220 | | 4 | c | **jaguar** | c | **jaguar** | d | 221 | | 5 | c | jaguar | c | jaguar | r1 | 222 | | 6 | c | jaguar | c | jaguar | r2 | 223 | | 7 | c | jaguar | c | jaguar | r3 | 224 | 225 | Eye Color (Genes 8-11) 226 | 227 | |Gene|Kai|Trait (Matron)|Kai|Trait (Sire) | | 228 | |----|---|--------------|---|-------------|----| 229 | | 8 | 7 | **chestnut** | 6 | **sizzurp** | d | 230 | | 9 | 6 | sizzurp | 6 | sizzurp | r1 | 231 | | 10 | 6 | sizzurp | 6 | sizzurp | r2 | 232 | | 11 | 8 | strawberry | 4 | mintgreen | r3 | 233 | 234 | Eye Shape (Genes 12-15) 235 | 236 | |Gene|Kai|Trait (Matron)|Kai|Trait (Sire) | | 237 | |----|---|--------------|---|-----------------|----| 238 | | 12 | 6 | **simple** | 8 | **thicccbrowz** | d | 239 | | 13 | 8 | thicccbrowz | 8 | thicccbrowz | r1 | 240 | | 14 | 6 | simple | 8 | thicccbrowz | r2 | 241 | | 15 | 6 | simple | 5 | otaku | r3 | 242 | 243 | Base Color (Genes 16-19) 244 | 245 | |Gene|Kai|Trait (Matron) |Kai|Trait (Sire) | | 246 | |----|---|----------------|---|----------------|----| 247 | | 16 | 4 | **orangesoda** | 4 | **orangesoda** | d | 248 | | 17 | 4 | orangesoda | 4 | orangesoda | r1 | 249 | | 18 | 4 | orangesoda | 4 | orangesoda | r2 | 250 | | 19 | 4 | orangesoda | 4 | orangesoda | r3 | 251 | 252 | Highlight Color (Genes 20-23) 253 | 254 | |Gene|Kai|Trait (Matron) |Kai|Trait (Sire) | | 255 | |----|---|-----------------|---|---------------|----| 256 | | 20 | 7 | **royalpurple** | f | **chocolate** | d | 257 | | 21 | 7 | royalpurple | d | coffee | r1 | 258 | | 22 | 7 | royalpurple | e | lemonade | r2 | 259 | | 23 | 7 | royalpurple | d | coffee | r3 | 260 | 261 | Accent Color (Genes 24-27) 262 | 263 | |Gene|Kai|Trait (Matron) |Kai|Trait (Sire) | | 264 | |----|---|-----------------|---|-----------------|----| 265 | | 24 | 7 | **kittencream** | 5 | **granitegrey** | d | 266 | | 25 | 5 | granitegrey | 5 | granitegrey | r1 | 267 | | 26 | 5 | granitegrey | 5 | granitegrey | r2 | 268 | | 27 | 5 | granitegrey | 5 | granitegrey | r3 | 269 | 270 | Wild (Genes 28-31) 271 | 272 | |Gene|Kai|Trait (Matron)|Kai|Trait (Sire)| | 273 | |----|---|--------------|---|------------|----| 274 | | 28 | 4 | **?** | 1 | **?** | d | 275 | | 29 | 6 | ? | 1 | ? | r1 | 276 | | 30 | 6 | ? | 1 | ? | r2 | 277 | | 31 | 6 | ? | 4 | ? | r3 | 278 | 279 | Mouth (Genes 32-35) 280 | 281 | |Gene|Kai|Trait (Matron) |Kai|Trait (Sire) | | 282 | |----|---|---------------|---|------------------|----| 283 | | 32 | g | **soserious** | f | **happygokitty** | d | 284 | | 33 | 9 | beard | 9 | beard | r1 | 285 | | 34 | f | happygokitty | a | pouty | r2 | 286 | | 35 | a | pouty | f | happygokitty | r3 | 287 | ``` 288 | 289 | 290 | 291 | ## Installation - I Can Has Copycats? 292 | 293 | Use: 294 | 295 | ``` 296 | $ gem install copycats 297 | ``` 298 | 299 | 300 | ## License 301 | 302 | ![](https://publicdomainworks.github.io/buttons/zero88x31.png) 303 | 304 | The `copycats` scripts are dedicated to the public domain. 305 | Use it as you please with no restrictions whatsoever. 306 | 307 | 308 | ## Questions? Comments? 309 | 310 | Post them on the [cryptokitties reddit](https://www.reddit.com/r/cryptokitties). Thanks. 311 | --------------------------------------------------------------------------------