cryptopunks 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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