cryptopunks 1.0.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+
2
+ ROBOT_COLORS = [
3
+ '000000', # color 1 - BLACK
4
+ '535353', # color 2 - BASE 2 (DARKER)
5
+ 'A4A4A4', # color 3 - BASE 1
6
+ 'A9F7FF', # color 4 - BASE 3 (LIGHTER) - eyes
7
+ ]
8
+
9
+ VAMPIRE_COLORS = [
10
+ '000000', # color 1 - BLACK
11
+ '131313', # color 2 - BASE 4 (DARKEST)
12
+ '535353', # color 3 - BASE 3 (DARKERER) - eyes
13
+ 'A4A4A4', # color 4 - BASE 2 (DARKER) - eyes
14
+ 'E0E0E0', # color 5 - BASE 1
15
+ 'F6000B', # color 6 - BASE 5 - teeth - red
16
+ ]
17
+
18
+ MUMMY_COLORS = [
19
+ '000000', # color 1 - BLACK
20
+ '1F1A15', # color 2 - BASE 5 (DARKEST)
21
+ '2A231C', # color 3 - BASE 4 (DARKERERER)
22
+ '5F5147', # color 4 - BASE 3 (DARKERER)
23
+ '927B6A', # color 5 - BASE 2 (DARKER)
24
+ 'D9B599', # color 6 - BASE 1
25
+ 'F6000B', # color 7 - BASE 6 - eyes - red
26
+ ]
27
+
28
+ ORC_COLORS = [
29
+ '000000', # color 1 - BLACK
30
+ '171a08', # color 2 - BASE 3 (DARKEST)
31
+ '333F0C', # color 3 - BASE 2 (DARKER)
32
+ '50650E', # color 4 - BASE 1
33
+ 'FFFFFF', # color 5 - BASE 4 - white
34
+ ]
35
+
36
+ SKELETON_COLORS = [
37
+ '000000', # color 1 - BLACK
38
+ 'e0e0e0', # color 2 - BASE 1
39
+ ]
40
+
41
+ DEMON_COLORS = [
42
+ '000000', # color 1 - BLACK
43
+ '390102', # color 2 - BASE 3 (DARKEST) - eyes
44
+ '630006', # color 3 - BASE 2 (DARKER) - eyes
45
+ '850008', # color 4 - BASE 1
46
+ ]
47
+
48
+ ZOMBIE_COLORS = [
49
+ '000000', # color 1 - BLACK
50
+ '5e7253', # color 2 - BASE 2 (DARKER)
51
+ '7da269', # color 3 - BASE 1
52
+ 'ff0000', # color 4 - BASE 3 - red eye
53
+ '9bbc88', # color 5 - BASE 4 (LIGHTER)
54
+ '4a010d', # color 6 - BASE 5 - mouth (female only)
55
+ ]
56
+
57
+ APE_COLORS = [
58
+ '000000', # color 1 - BLACK
59
+ '352410', # color 2 - BASE 3 (DARKEST)
60
+ '6a563f', # color 3 - BASE 2 (DARKER) - eyes
61
+ '856f56', # color 4 - BASE 1
62
+ 'a98c6b', # color 5 - BASE 4 (LIGHTER) - eyes
63
+ ]
64
+
65
+ ALIEN_COLORS = [
66
+ '000000', # color 1 - BLACK
67
+ '75bdbd', # color 2 - BASE 3 (DARKEST)
68
+ '9be0e0', # color 3 - BASE 2 (DARKER)
69
+ 'c8fbfb', # color 4 - BASE 1
70
+ ]
71
+
72
+
73
+ HUMAN_LIGHTER_BASE1 = 'ead9d9' # rgb(234 217 217) - hsl( 0° 29% 88%)
74
+ HUMAN_LIGHTER_BASE2 = 'c9b2b2' # rgb(201 178 178) - hsl( 0° 18% 74%) - eyes
75
+ HUMAN_LIGHTER_BASE3 = 'a58d8d' # rgb(165 141 141) - hsl( 0° 12% 60%) - eyes
76
+ HUMAN_LIGHTER_BASE4 = 'ffffff' # rgb(255 255 255) - hsl( 0° 0% 100%) -- white
77
+ HUMAN_LIGHTER_BASE5 = '711010' # rgb(113 16 16) - hsl( 0° 75% 25%) - mouth
78
+
79
+ HUMAN_LIGHT_BASE1 = 'dbb180' # rgb(219 177 128) - hsl( 32° 56% 68%)
80
+ HUMAN_LIGHT_BASE2 = 'd29d60' # rgb(210 157 96) - hsl( 32° 56% 60%) - eyes
81
+ HUMAN_LIGHT_BASE3 = 'a66e2c' # rgb(166 110 44) - hsl( 32° 58% 41%) - eyes
82
+ HUMAN_LIGHT_BASE4 = 'e7cba9' # rgb(231 203 169) - hsl( 33° 56% 78%)
83
+ HUMAN_LIGHT_BASE5 = '711010' # rgb(113 16 16) - hsl( 0° 75% 25%) - mouth
84
+
85
+ HUMAN_DARK_BASE1 = 'ae8b61' # rgb(174 139 97) - hsl( 33° 32% 53%)
86
+ HUMAN_DARK_BASE2 = 'a77c47' # rgb(167 124 71) - hsl( 33° 40% 47%) - eyes
87
+ HUMAN_DARK_BASE3 = '86581e' # rgb(134 88 30) - hsl( 33° 63% 32%) - eyes
88
+ HUMAN_DARK_BASE4 = 'b69f82' # rgb(182 159 130) - hsl( 33° 26% 61%)
89
+ HUMAN_DARK_BASE5 = '5f1d09' # rgb( 95 29 9) - hsl( 14° 83% 20%) - mouth
90
+
91
+ HUMAN_DARKER_BASE1 = '713f1d' # rgb(113 63 29) - hsl( 24° 59% 28%)
92
+ HUMAN_DARKER_BASE2 = '723709' # rgb(114 55 9) - hsl( 26° 85% 24%) - eyes
93
+ HUMAN_DARKER_BASE3 = '562600' # rgb( 86 38 0) - hsl( 27° 100% 17%) - eyes
94
+ HUMAN_DARKER_BASE4 = '8b532c' # rgb(139 83 44) - hsl( 25° 52% 36%)
95
+ HUMAN_DARKER_BASE5 = '4a1201' # rgb( 74 18 1) - hsl( 14° 97% 15%) - mouth
96
+
97
+
98
+ HUMAN_COLORS_LIGHT = [ ## todo/check: change to HUMAN_LIGHT_COLORS???
99
+ '000000', # color 1 - BLACK
100
+ HUMAN_LIGHT_BASE3, # color 2 - BASE 3 (DARKEST) - eyes
101
+ HUMAN_LIGHT_BASE2, # color 3 - BASE 2 (DARKER) - eyes
102
+ HUMAN_LIGHT_BASE1, # color 4 - BASE 1
103
+ HUMAN_LIGHT_BASE4, # color 5 - BASE 4
104
+ HUMAN_LIGHT_BASE5, # color 6 - BASE 5 - mouth (femaly only)
105
+ ]
106
+
107
+ HUMAN_COLORS_LIGHTER = [ ## todo/check: change to HUMAN_LIGHTER_COLORS?? or add alias - why? why not?
108
+ '000000', # color 1 - BLACK
109
+ HUMAN_LIGHTER_BASE3,
110
+ HUMAN_LIGHTER_BASE2,
111
+ HUMAN_LIGHTER_BASE1,
112
+ HUMAN_LIGHTER_BASE4,
113
+ HUMAN_LIGHTER_BASE5,
114
+ ]
115
+
116
+ HUMAN_COLORS_DARK = [
117
+ '000000', # color 1 - BLACK
118
+ HUMAN_DARK_BASE3,
119
+ HUMAN_DARK_BASE2,
120
+ HUMAN_DARK_BASE1,
121
+ HUMAN_DARK_BASE4,
122
+ HUMAN_DARK_BASE5,
123
+ ]
124
+
125
+ HUMAN_COLORS_DARKER = [
126
+ '000000', # color 1 - BLACK
127
+ HUMAN_DARKER_BASE3,
128
+ HUMAN_DARKER_BASE2,
129
+ HUMAN_DARKER_BASE1,
130
+ HUMAN_DARKER_BASE4,
131
+ HUMAN_DARKER_BASE5,
132
+ ]
133
+
134
+
135
+ HUMAN_COLORS = {
136
+ 'light' => HUMAN_COLORS_LIGHT,
137
+ 'lighter' => HUMAN_COLORS_LIGHTER,
138
+ 'dark' => HUMAN_COLORS_DARK,
139
+ 'darker' => HUMAN_COLORS_DARKER,
140
+ }
141
+
142
+
143
+
144
+ ### todo/check:
145
+ ## use a different name other than the "generic" COLORS - why? why not?
146
+
147
+ COLORS = {
148
+ ## original series
149
+ 'human' => HUMAN_COLORS,
150
+ 'zombie' => ZOMBIE_COLORS,
151
+ 'ape' => APE_COLORS,
152
+ 'alien' => ALIEN_COLORS,
153
+ ## more series
154
+ 'vampire' => VAMPIRE_COLORS,
155
+ 'mummy' => MUMMY_COLORS,
156
+ 'orc' => ORC_COLORS,
157
+ 'skeleton' => SKELETON_COLORS,
158
+ 'demon' => DEMON_COLORS,
159
+ 'robot' => ROBOT_COLORS,
160
+ }
161
+
162
+
@@ -0,0 +1,38 @@
1
+ module Cryptopunks
2
+ class Image ## nest Composite inside Image - why? why not?
3
+ class Composite < Pixelart::ImageComposite
4
+
5
+ PUNK_HASH = 'ac39af4793119ee46bbff351d8cb6b5f23da60222126add4268e261199a2921b'
6
+
7
+ def self.sha256( data )
8
+ ## todo/check: or just use Digest::SHA256.hexdigest - why? why not?
9
+ Digest::SHA256.digest( data ).unpack( 'H*' )[0]
10
+ end
11
+
12
+ def self.read( path='./punks.png' )
13
+ data = File.open( path, 'rb' ) { |f| f.read }
14
+
15
+ hexdigest = sha256( data ) ## check sha256 checksum
16
+ if hexdigest == PUNK_HASH
17
+ puts " >#{hexdigest}< SHA256 hash matching"
18
+ puts " ✓ True Official Genuine CryptoPunks™ verified"
19
+ else
20
+ puts " ✓ True Official Genuine Yes, You Can! Punks Not Dead™ verified"
21
+ end
22
+
23
+ img = ChunkyPNG::Image.from_blob( data )
24
+ new( img )
25
+ end
26
+
27
+
28
+ PUNK_HEIGHT = 24
29
+ PUNK_WIDTH = 24
30
+
31
+ def initialize( *args, width: PUNK_WIDTH,
32
+ height: PUNK_HEIGHT )
33
+ super
34
+ end
35
+
36
+ end ## class Composite
37
+ end ## class Image
38
+ end ## module Cryptopunks
@@ -0,0 +1,67 @@
1
+
2
+
3
+ module Cryptopunks
4
+ module Dataset
5
+
6
+ def self.read( path='./datasets/punks/*.csv' )
7
+
8
+ datasets = Dir.glob( path )
9
+ #=> ["./datasets/punks/0-999.csv",
10
+ # "./datasets/punks/1000-1999.csv",
11
+ # "./datasets/punks/2000-2999.csv",
12
+ # "./datasets/punks/3000-3999.csv",
13
+ # "./datasets/punks/4000-4999.csv",
14
+ # "./datasets/punks/5000-5999.csv",
15
+ # "./datasets/punks/6000-6999.csv",
16
+ # "./datasets/punks/7000-7999.csv",
17
+ # "./datasets/punks/8000-8999.csv",
18
+ # "./datasets/punks/9000-9999.csv"]
19
+
20
+ rows = []
21
+ datasets.each do |dataset|
22
+ rows += CsvHash.read( dataset )
23
+ end
24
+
25
+ # puts " #{rows.size} rows(s)"
26
+ #=> 10000 rows(s)
27
+
28
+ ### wrap in punk struct for easier access
29
+ punks = []
30
+ rows.each do |row|
31
+ id = row['id'].to_i
32
+ type_q = row['type']
33
+ count = row['count'].to_i
34
+ accessories_q = row['accessories'].split( %r{[ ]*/[ ]*} )
35
+
36
+ if count != accessories_q.size
37
+ puts "!! ERROR - punk data assertion failed - expected accessories count #{count}; got #{accessories_q.size}"
38
+ pp row
39
+ exit 1
40
+ end
41
+
42
+ type = Metadata::Type.find( type_q )
43
+ if type.nil?
44
+ puts "!! ERROR - punk data assertion failed - unknown punk type >#{type_q}<"
45
+ pp row
46
+ exit 1
47
+ end
48
+
49
+ accessories = []
50
+ accessories_q.each do |acc_q|
51
+ acc = Metadata::Accessory.find( acc_q )
52
+ if acc.nil?
53
+ puts "!! ERROR - punk data assertion failed - unknown punk accessory type >#{acc_q}<"
54
+ pp row
55
+ exit 1
56
+ end
57
+ accessories << acc
58
+ end
59
+
60
+ punks << Metadata.new( id, type, accessories )
61
+ end
62
+ punks
63
+ end
64
+ end # module Dataset
65
+ end # module Cryptopunks
66
+
67
+
@@ -0,0 +1,121 @@
1
+ module Cryptopunks
2
+
3
+
4
+ class Design ## todo/fix - move to its own file!!!
5
+
6
+ end # class Design
7
+
8
+
9
+
10
+ ##############
11
+ ## todo/check:
12
+ ## find a better way to (auto?) include more designs?
13
+ class DesignSeries ## find a better name for class (just use Series?) - why? why not?
14
+ def self.build( dir )
15
+ data = {}
16
+ paths = Dir.glob( "#{dir}/**.txt" )
17
+ paths.each do |path|
18
+ basename = File.basename( path, File.extname( path ) )
19
+ text = File.open( path, 'r:utf-8' ) { |f| f.read }
20
+ ## todo/check: auto-parse "ahead of time" here
21
+ ## or keep "raw" text - why? why not?
22
+ data[ basename ] = text
23
+ end
24
+ data
25
+ end
26
+
27
+ def initialize( dir )
28
+ @dir = dir # e.g. "#{Cryptopunks.root}/config/more"
29
+ end
30
+
31
+ def data
32
+ ## note: lazy load / build on first demand only
33
+ @data ||= self.class.build( @dir )
34
+ end
35
+
36
+ def [](key) data[ key ]; end
37
+ def size() data.size; end
38
+ def keys() data.keys; end
39
+ def to_h() data; end ## todo/check: use to_hash() - why? why not?
40
+ end # class DesignSeries
41
+
42
+
43
+
44
+ class Image
45
+
46
+ def self.read( path ) ## convenience helper
47
+ img = ChunkyPNG::Image.from_file( path )
48
+ new( img )
49
+ end
50
+
51
+
52
+ def initialize( initial=nil, design: nil,
53
+ colors: nil )
54
+ if initial
55
+ ## pass image through as-is
56
+ img = inital
57
+ else
58
+
59
+ ## todo/fix:
60
+ ## move design code into design class!!!
61
+ ## for now assume design is a string
62
+ ## split into parts
63
+ ## original/alien-male or original@alien-male
64
+ ## more/alien-female or more@alien-female
65
+ ## original/human-male+darker or original@human-male!darker ????
66
+ ## human-male!darker ?????
67
+ ## keep @ as separator too - why? why not?
68
+ parts = design.split( %r{[@/]} )
69
+ parts.unshift( '*' ) if parts.size == 1 ## assume "all-in-one" series (use * as name/id/placeholder)
70
+
71
+ series_key = parts[0]
72
+ design_composite = parts[1]
73
+
74
+ ## todo/check - find a way for unambigious (color) variant key
75
+ ## use unique char e.g. +*!# or such
76
+ more_parts = design_composite.split( %r{[!+]} )
77
+ design_key = more_parts[0]
78
+ variant_key = more_parts[1] ## color variant for now (for humans) e.g. lighter/light/dark/darker
79
+
80
+ series = if ['*','**','_','__'].include?( series_key )
81
+ DESIGNS ## use all-series-in-one collection
82
+ else
83
+ case series_key
84
+ when 'original' then DESIGNS_ORIGINAL
85
+ when 'more' then DESIGNS_MORE
86
+ else raise ArgumentError, "unknown design series >#{series_key}<; sorry"
87
+ end
88
+ end
89
+
90
+ design = series[ design_key ]
91
+ raise ArgumentError, "unknow design >#{design_key}< in series >#{series_key}<; sorry" if design.nil?
92
+
93
+ if colors.nil? ## try to auto-fill in colors
94
+ ## note: (auto-)remove _male,_female qualifier if exist
95
+ colors_key = design_key.sub( '-male', '' ).sub( '-female', '' )
96
+ colors = COLORS[ colors_key ]
97
+
98
+ ## allow / support color scheme variants (e.g. lighter/light/dark/darker) etc.
99
+ if colors.is_a?(Hash)
100
+ if variant_key
101
+ colors = colors[ variant_key ]
102
+ raise ArgumentError, "no colors defined for variant >#{variant_key}< for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
103
+ else ## note: use (fallback to) first color scheme if no variant key present
104
+ colors = colors[ colors.keys[0] ]
105
+ end
106
+ end
107
+
108
+ raise ArgumentError, "no (default) colors defined for design >#{design_key}< in series >#{series_key}<; sorry" if colors.nil?
109
+ end
110
+
111
+ ## note: unwrap inner image before passing on to super c'tor
112
+ img = Pixelart::Image.parse( design, colors: colors ).image
113
+ end
114
+
115
+ super( img.width, img.height, img )
116
+ end
117
+
118
+
119
+
120
+ end # class Image
121
+ end # module Cryptopunks
@@ -0,0 +1,161 @@
1
+
2
+ module Cryptopunks
3
+ ### wrap metadata (e.g. punk types, accessories, etc.
4
+ ## in structs for easy/easier access)
5
+
6
+
7
+
8
+ class Metadata
9
+
10
+ ## nested class
11
+ class Type ## todo/check: use alias family or such?
12
+ attr_reader :name,
13
+ :limit
14
+ def initialize( name, limit )
15
+ @name = name
16
+ @limit = limit
17
+ end
18
+ ## def to_s() @name; end
19
+
20
+ def inspect
21
+ %Q{<Type "#{@name}", limit: #{@limit}>}
22
+ end
23
+
24
+
25
+
26
+ def self.build
27
+ TYPES.reduce( {} ) do |h, rec|
28
+ type = Type.new( rec[:name], rec[:limit ] )
29
+ h[ rec[:name].downcase ] = type
30
+ h
31
+ end
32
+ end
33
+
34
+ def self.registry
35
+ ## auto-build registry (hash table) lookup (by name)
36
+ @@types ||= build
37
+ @@types
38
+ end
39
+
40
+ def self.all() registry.values; end
41
+
42
+ def self.find( q ) registry[ q.to_s.downcase ]; end
43
+ end ## (nested) class Type
44
+
45
+
46
+
47
+ ## nested class
48
+ class AccessoryType
49
+ attr_reader :name,
50
+ :accessories
51
+ def initialize( name, accessories=[] )
52
+ @name = name
53
+ @accessories = accessories
54
+ end
55
+
56
+
57
+
58
+ def self.build
59
+ ACCESSORY_TYPES.reduce( {} ) do |h, rec|
60
+ type = AccessoryType.new( rec[:name] )
61
+ ## add all accessories
62
+ rec[:accessories].each do |rec_acc|
63
+ type.accessories << Accessory.new( rec_acc[:name],
64
+ type,
65
+ rec_acc[:limit].to_i )
66
+ end
67
+ h[ rec[:name].downcase ] = type
68
+ h
69
+ end
70
+ end
71
+
72
+ def self.registry
73
+ ## auto-build registry (hash table) lookup (by name)
74
+ @@types ||= build
75
+ @@types
76
+ end
77
+
78
+ def self.all() registry.values; end
79
+
80
+ def self.find( q ) registry[ q.to_s.downcase ]; end
81
+ end ## (nested) class AccessoryType
82
+
83
+
84
+ ## nested class
85
+ class Accessory
86
+
87
+ attr_reader :name,
88
+ :type,
89
+ :limit
90
+ def initialize( name, type, limit )
91
+ @name = name
92
+ @type = type
93
+ @limit = limit
94
+ end
95
+
96
+
97
+ def inspect
98
+ %Q{<Accessory "#{@name}", type: "#{@type.name}", limit: #{@limit}>}
99
+ end
100
+
101
+
102
+
103
+ def self.build
104
+ AccessoryType.all.reduce( {} ) do |h, type|
105
+ type.accessories.each do |acc|
106
+ h[ acc.name.downcase ] = acc
107
+ end
108
+ h
109
+ end
110
+ end
111
+
112
+ def self.registry
113
+ ## auto-build registry (hash table) lookup (by name)
114
+ @@types ||= build
115
+ @@types
116
+ end
117
+
118
+ def self.all() registry.values; end
119
+
120
+ def self.find( q ) registry[ q.to_s.downcase ]; end
121
+ end ## (nested) class Accessory
122
+
123
+
124
+
125
+
126
+
127
+
128
+ attr_reader :id,
129
+ :type,
130
+ :accessories,
131
+ :birthday ## todo/check: use minted or such?
132
+
133
+ def initialize( id, type, accessories )
134
+ @id = id
135
+ @type = type
136
+ @accessories = accessories
137
+ @birthday = Date.new( 2017, 6, 23) ## all 10,000 minted on June 23, 2017
138
+ end
139
+
140
+ def is_type?( name ) @type.name == name; end
141
+ alias_method :is?, :is_type?
142
+
143
+ ## convenience helpers for "classic" (5) types
144
+ def alien?() is_type?( 'Alien'); end
145
+ def ape?() is_type?( 'Ape' ); end
146
+ def zombie?() is_type?( 'Zombie' ); end
147
+ def female?() is_type?( 'Female' ); end
148
+ def male?() is_type?( 'Male' ); end
149
+
150
+ ## convenience helpers to lookup attributes
151
+ def has_attribute?( name )
152
+ accessories.each do |acc|
153
+ return true if acc.name == name
154
+ end
155
+ false
156
+ end
157
+ alias_method :has?, :has_attribute?
158
+ alias_method :include?, :has_attribute?
159
+ end # class Metadata
160
+
161
+ end # module Cryptopunks