cryptopunks 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -0
- data/README.md +1 -36
- data/Rakefile +1 -0
- data/lib/cryptopunks.rb +7 -85
- data/lib/cryptopunks/attributes.rb +147 -0
- data/lib/cryptopunks/dataset.rb +67 -0
- data/lib/cryptopunks/image.rb +84 -0
- data/lib/cryptopunks/structs.rb +148 -0
- data/lib/cryptopunks/version.rb +2 -2
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db4841328c13e863838826b922663aef0e8e28d3f1b2f551e18e95fc4b8cc81b
|
4
|
+
data.tar.gz: 0f1d9bb17816c0e7152c3a4fb403927aa5219528fa41931fb9fbcf23303a11e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c73e7546c178138a62d8b39b4624a85f638ec18c1268537a0b198cf317b6200272c1506f7d0943f6b45594aa81af2fa7d8b628f67c851cabff854a659f65a190
|
7
|
+
data.tar.gz: 513e3f4bfd838d7e57f24c6005e6161e1a4eeecdc46c6493deb06234105678c5ce7db70fac44226c948038257d443642f1c542f2de18e4e02a9bc0309642a157
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -8,44 +8,10 @@ cryptopunks - mint your own 24×24 pixel punk images off chain from the True Off
|
|
8
8
|
* rdoc :: [rubydoc.info/gems/cryptopunks](http://rubydoc.info/gems/cryptopunks)
|
9
9
|
|
10
10
|
|
11
|
-
|
12
|
-
> Someday, owning a CryptoPunk might signify just how early of an
|
13
|
-
> adopter you were into the world of blockchain and its thriving digital
|
14
|
-
> art scene. Or, they could just be a bunch of [24×24 pixel] images.
|
15
|
-
>
|
16
|
-
> -- [June 2017](https://mashable.com/2017/06/16/cryptopunks-ethereum-art-collectibles/)
|
17
|
-
>
|
18
|
-
>
|
19
|
-
> There will be a desire and need to buy expensive [status symbols]
|
20
|
-
> in the digital realm [to "flex" how rich I am].
|
21
|
-
> What could be more desirable than a small [24×24]
|
22
|
-
> pixelated [knitted cap-wearing ape] face?
|
23
|
-
> CryptoPunk artwork [![](i/punk-8219.png) [#8219](https://www.larvalabs.com/cryptopunks/details/8219)] just sold for $176,000.
|
24
|
-
>
|
25
|
-
> -- [January 2021](https://decrypt.co/53519/an-ethereum-based-cryptopunk-artwork-just-sold-for-176000)
|
26
|
-
>
|
27
|
-
>
|
28
|
-
> Ultra-rare alien [24×24 pixel] CryptoPunk
|
29
|
-
> sells for 605 ETH, or $750,000.
|
30
|
-
> The investment thesis. "Aliens are the rarest form of CryptoPunk and
|
31
|
-
> we believe that the acquired Alien [![](i/punk-2890.png) [#2890](https://www.larvalabs.com/cryptopunks/details/2890), one of nine]
|
32
|
-
> will be prized by collectors over
|
33
|
-
> time and mature into an iconic digital art piece."
|
34
|
-
>
|
35
|
-
> -- [January 2021](https://cointelegraph.com/news/ultra-rare-alien-cryptopunk-nft-sells-for-605-eth-or-750-000)
|
36
|
-
>
|
37
|
-
>
|
38
|
-
> The [CryptoPunksMarket] contract now holds 4,095 ETH (~$5.4M USD) in open bids and pending withdrawals.
|
39
|
-
>
|
40
|
-
> -- [January 2021](https://twitter.com/larvalabs/status/1353915659453870080)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
11
|
New to Crypto Punks?
|
45
12
|
See the [**Awesome CryptoPunks Bubble (Anno 2021) - Modern 24×24 Pixel Crypto Art on the Blockchain** »](https://github.com/openblockchains/awesome-cryptopunks-bubble)
|
46
13
|
|
47
14
|
|
48
|
-
|
49
15
|
## Command Line
|
50
16
|
|
51
17
|
Use the `punk` (or `cryptopunk`) command line tool. Try:
|
@@ -196,5 +162,4 @@ Use it as you please with no restrictions whatsoever.
|
|
196
162
|
|
197
163
|
## Questions? Comments?
|
198
164
|
|
199
|
-
|
200
|
-
Thanks!
|
165
|
+
Post them on the [cryptopunk reddit](https://www.reddit.com/r/cryptopunk). Thanks.
|
data/Rakefile
CHANGED
data/lib/cryptopunks.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
## 3rd party
|
2
2
|
require 'crypto-lite'
|
3
3
|
require 'chunky_png'
|
4
|
+
require 'csvreader'
|
5
|
+
|
4
6
|
|
5
7
|
## extra stdlibs
|
6
8
|
require 'fileutils'
|
@@ -10,94 +12,14 @@ require 'optparse'
|
|
10
12
|
|
11
13
|
## our own code
|
12
14
|
require 'cryptopunks/version' # note: let version always go first
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def self.read( path='./punks.png' )
|
18
|
-
data = File.open( path, 'rb' ) { |f| f.read }
|
19
|
-
new( data )
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
attr_accessor :zoom
|
24
|
-
|
25
|
-
PUNK_ROWS = 100
|
26
|
-
PUNK_COLS = 100
|
27
|
-
PUNK_COUNT = PUNK_ROWS * PUNK_COLS ## 10_000 = 100x100 (24000x24000 pixel)
|
28
|
-
|
29
|
-
PUNK_HEIGHT = 24
|
30
|
-
PUNK_WIDTH = 24
|
31
|
-
|
32
|
-
PUNK_HASH = 'ac39af4793119ee46bbff351d8cb6b5f23da60222126add4268e261199a2921b'
|
33
|
-
|
34
|
-
|
35
|
-
def initialize( data )
|
36
|
-
@punks = ChunkyPNG::Image.from_blob( data )
|
37
|
-
puts " #{@punks.height}x#{@punks.width} (height x width)"
|
38
|
-
|
39
|
-
## check sha256 checksum
|
40
|
-
@hexdigest = sha256( data )
|
41
|
-
if original?
|
42
|
-
puts " >#{@hexdigest}< SHA256 hash matching"
|
43
|
-
puts " ✓ True Official Genuine CryptoPunks™ verified"
|
44
|
-
else
|
45
|
-
puts " !! ERROR: >#{hexdigest}< SHA256 hash NOT matching"
|
46
|
-
puts " >#{PUNK_HASH}< expected for True Official Genuine CryptoPunks™."
|
47
|
-
puts ""
|
48
|
-
puts " Sorry, please download the original."
|
49
|
-
exit 1
|
50
|
-
end
|
51
|
-
|
52
|
-
@zoom = 1
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
def hexdigest() @hexdigest end
|
57
|
-
|
58
|
-
def verify?() @hexdigest == PUNK_HASH; end
|
59
|
-
alias_method :genuine?, :verify?
|
60
|
-
alias_method :original?, :verify?
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def size() PUNK_COUNT; end
|
65
|
-
|
66
|
-
def []( index )
|
67
|
-
@zoom == 1 ? crop( index ) : scale( index, @zoom )
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
def crop( index )
|
72
|
-
y, x = index.divmod( PUNK_ROWS )
|
73
|
-
@punks.crop( x*PUNK_WIDTH, y*PUNK_HEIGHT, PUNK_WIDTH, PUNK_HEIGHT )
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
def scale( index, zoom )
|
78
|
-
punk = ChunkyPNG::Image.new( PUNK_WIDTH*zoom, PUNK_HEIGHT*zoom,
|
79
|
-
ChunkyPNG::Color::WHITE )
|
80
|
-
|
81
|
-
## (x,y) offset in big all-in-one punks image
|
82
|
-
y, x = index.divmod( PUNK_ROWS )
|
83
|
-
|
84
|
-
## copy all 24x24 pixels
|
85
|
-
PUNK_WIDTH.times do |i|
|
86
|
-
PUNK_HEIGHT.times do |j|
|
87
|
-
pixel = @punks[i+x*PUNK_WIDTH, j+y*PUNK_HEIGHT]
|
88
|
-
zoom.times do |n|
|
89
|
-
zoom.times do |m|
|
90
|
-
punk[n+zoom*i,m+zoom*j] = pixel
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
punk
|
96
|
-
end
|
97
|
-
end ## class Image
|
15
|
+
require 'cryptopunks/attributes'
|
16
|
+
require 'cryptopunks/structs'
|
17
|
+
require 'cryptopunks/image'
|
18
|
+
require 'cryptopunks/dataset'
|
98
19
|
|
99
20
|
|
100
21
|
|
22
|
+
module Cryptopunks
|
101
23
|
class Tool
|
102
24
|
def run( args )
|
103
25
|
opts = { zoom: 1,
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Cryptopunks
|
2
|
+
|
3
|
+
##################
|
4
|
+
## 5 punk types
|
5
|
+
TYPES = [
|
6
|
+
{ name: 'Alien', limit: 9 },
|
7
|
+
{ name: 'Ape', limit: 24 },
|
8
|
+
{ name: 'Zombie', limit: 88 },
|
9
|
+
{ name: 'Female', limit: 3840 },
|
10
|
+
{ name: 'Male', limit: 6039 },
|
11
|
+
]
|
12
|
+
|
13
|
+
|
14
|
+
###
|
15
|
+
## categories used from:
|
16
|
+
## https://cryptoslam.io/cryptopunks/checklist
|
17
|
+
## see categories in urls e.g.
|
18
|
+
## - cryptopunks/eyes/horned-rim-glasses
|
19
|
+
## - cryptopunks/hair/peak-spike
|
20
|
+
## - cryptopunks/emotion/smile
|
21
|
+
## - etc.
|
22
|
+
##
|
23
|
+
|
24
|
+
#######
|
25
|
+
## 87 attributes / accessories
|
26
|
+
##
|
27
|
+
## note: does NOT include 5 punk types (that is, alien, ape, zombie, female, male)
|
28
|
+
|
29
|
+
|
30
|
+
ACCESSORY_TYPES = [
|
31
|
+
{ name: 'Mouth', accessories: [ { name: 'Cigarette', limit: 961 },
|
32
|
+
{ name: 'Pipe', limit: 317 },
|
33
|
+
{ name: 'Vape', limit: 272 },
|
34
|
+
{ name: 'Medical Mask', limit: 175 }]
|
35
|
+
},
|
36
|
+
{ name: 'Nose', accessories: [ { name: 'Clown Nose', limit: 212 }]
|
37
|
+
},
|
38
|
+
{
|
39
|
+
name: 'Hair',
|
40
|
+
accessories: [ { name: 'Wild Blonde', limit: 144 },
|
41
|
+
{ name: 'Wild Hair', limit: 447 },
|
42
|
+
{ name: 'Dark Hair', limit: 157 },
|
43
|
+
{ name: 'Stringy Hair', limit: 463 },
|
44
|
+
{ name: 'Crazy Hair', limit: 414 },
|
45
|
+
{ name: 'Messy Hair', limit: 460 },
|
46
|
+
{ name: 'Mohawk', limit: 441 },
|
47
|
+
{ name: 'Mohawk Thin', limit: 441 },
|
48
|
+
{ name: 'Mohawk Dark', limit: 429 },
|
49
|
+
{ name: 'Peak Spike', limit: 303 },
|
50
|
+
{ name: 'Frumpy Hair', limit: 442 },
|
51
|
+
{ name: 'Clown Hair Green', limit: 148 },
|
52
|
+
{ name: 'Shaved Head', limit: 300 },
|
53
|
+
{ name: 'Vampire Hair', limit: 147 },
|
54
|
+
{ name: 'Red Mohawk', limit: 147 },
|
55
|
+
{ name: 'Blonde Bob', limit: 147 },
|
56
|
+
{ name: 'Straight Hair Dark', limit: 148 },
|
57
|
+
{ name: 'Straight Hair', limit: 151 },
|
58
|
+
{ name: 'Purple Hair', limit: 165 },
|
59
|
+
{ name: 'Straight Hair Blonde', limit: 144 },
|
60
|
+
{ name: 'Wild White Hair', limit: 136 },
|
61
|
+
{ name: 'Half Shaved', limit: 147 },
|
62
|
+
{ name: 'Pigtails', limit: 94 },
|
63
|
+
{ name: 'Orange Side', limit: 68 },
|
64
|
+
{ name: 'Do-rag', limit: 300 },
|
65
|
+
{ name: 'Tiara', limit: 55 },
|
66
|
+
{ name: 'Blonde Short', limit: 129 },
|
67
|
+
{ name: 'Pink With Hat', limit: 95 },
|
68
|
+
{ name: 'Beanie', limit: 44 },
|
69
|
+
{ name: 'Headband', limit: 406 },
|
70
|
+
{ name: 'Bandana', limit: 481 },
|
71
|
+
{ name: 'Hoodie', limit: 259 },
|
72
|
+
{ name: 'Top Hat', limit: 115 },
|
73
|
+
{ name: 'Tassle Hat', limit: 178 },
|
74
|
+
{ name: 'Cap', limit: 351 },
|
75
|
+
{ name: 'Knitted Cap', limit: 419 },
|
76
|
+
{ name: 'Cap Forward', limit: 254 },
|
77
|
+
{ name: 'Police Cap', limit: 203 },
|
78
|
+
{ name: 'Fedora', limit: 186 },
|
79
|
+
{ name: 'Pilot Helmet', limit: 54 },
|
80
|
+
{ name: 'Cowboy Hat', limit: 142 }]
|
81
|
+
},
|
82
|
+
{
|
83
|
+
name: 'Beard',
|
84
|
+
accessories: [{ name: 'Normal Beard', limit: 292 },
|
85
|
+
{ name: 'Normal Beard Black', limit: 289 },
|
86
|
+
{ name: 'Front Beard Dark', limit: 260 },
|
87
|
+
{ name: 'Front Beard', limit: 273 },
|
88
|
+
{ name: 'Shadow Beard', limit: 526 },
|
89
|
+
{ name: 'Luxurious Beard', limit: 286 },
|
90
|
+
{ name: 'Big Beard', limit: 146 },
|
91
|
+
{ name: 'Chinstrap', limit: 282 },
|
92
|
+
{ name: 'Mustache', limit: 288 },
|
93
|
+
{ name: 'Muttonchops', limit: 303 },
|
94
|
+
{ name: 'Handlebars', limit: 263 },
|
95
|
+
{ name: 'Goat', limit: 295 }]
|
96
|
+
},
|
97
|
+
{
|
98
|
+
name: 'Ears',
|
99
|
+
accessories: [{ name: 'Earring', limit: 2459 }]
|
100
|
+
},
|
101
|
+
{
|
102
|
+
name: 'Eyes',
|
103
|
+
accessories: [{ name: 'Blue Eye Shadow', limit: 266 },
|
104
|
+
{ name: 'Purple Eye Shadow', limit: 262 },
|
105
|
+
{ name: 'Green Eye Shadow', limit: 271 },
|
106
|
+
{ name: 'Welding Goggles', limit: 86 },
|
107
|
+
{ name: 'VR', limit: 332 },
|
108
|
+
{ name: '3D Glasses', limit: 286 },
|
109
|
+
{ name: 'Clown Eyes Blue', limit: 384 },
|
110
|
+
{ name: 'Clown Eyes Green', limit: 382 },
|
111
|
+
{ name: 'Small Shades', limit: 378 },
|
112
|
+
{ name: 'Regular Shades', limit: 527 },
|
113
|
+
{ name: 'Big Shades', limit: 535 },
|
114
|
+
{ name: 'Classic Shades', limit: 502 },
|
115
|
+
{ name: 'Nerd Glasses', limit: 572 },
|
116
|
+
{ name: 'Horned Rim Glasses', limit: 535 },
|
117
|
+
{ name: 'Eye Mask', limit: 293 },
|
118
|
+
{ name: 'Eye Patch', limit: 461 }]
|
119
|
+
},
|
120
|
+
{
|
121
|
+
name: 'Lips',
|
122
|
+
accessories: [{ name: 'Purple Lipstick', limit: 655 },
|
123
|
+
{ name: 'Black Lipstick', limit: 617 },
|
124
|
+
{ name: 'Hot Lipstick', limit: 696 } ]
|
125
|
+
},
|
126
|
+
{
|
127
|
+
name: 'Face',
|
128
|
+
accessories: [{ name: 'Spots', limit: 124 },
|
129
|
+
{ name: 'Mole', limit: 644 }]
|
130
|
+
},
|
131
|
+
{
|
132
|
+
name: 'Neck',
|
133
|
+
accessories: [{ name: 'Choker', limit: 48 },
|
134
|
+
{ name: 'Silver Chain', limit: 156 },
|
135
|
+
{ name: 'Gold Chain', limit: 169 }]
|
136
|
+
},
|
137
|
+
{ name: 'Cheeks', accessories: [{ name: 'Rosy Cheeks', limit: 128 }]
|
138
|
+
},
|
139
|
+
{ name: 'Teeth', accessories: [{ name: 'Buck Teeth', limit: 78 }]
|
140
|
+
},
|
141
|
+
{ name: 'Emotion', accessories: [{ name: 'Frown', limit: 261 },
|
142
|
+
{ name: 'Smile', limit: 238 }]
|
143
|
+
}
|
144
|
+
]
|
145
|
+
|
146
|
+
|
147
|
+
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,84 @@
|
|
1
|
+
module Cryptopunks
|
2
|
+
class Image
|
3
|
+
def self.read( path='./punks.png' )
|
4
|
+
data = File.open( path, 'rb' ) { |f| f.read }
|
5
|
+
new( data )
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
attr_accessor :zoom
|
10
|
+
|
11
|
+
PUNK_ROWS = 100
|
12
|
+
PUNK_COLS = 100
|
13
|
+
PUNK_COUNT = PUNK_ROWS * PUNK_COLS ## 10_000 = 100x100 (24000x24000 pixel)
|
14
|
+
|
15
|
+
PUNK_HEIGHT = 24
|
16
|
+
PUNK_WIDTH = 24
|
17
|
+
|
18
|
+
PUNK_HASH = 'ac39af4793119ee46bbff351d8cb6b5f23da60222126add4268e261199a2921b'
|
19
|
+
|
20
|
+
|
21
|
+
def initialize( data )
|
22
|
+
@punks = ChunkyPNG::Image.from_blob( data )
|
23
|
+
puts " #{@punks.height}x#{@punks.width} (height x width)"
|
24
|
+
|
25
|
+
## check sha256 checksum
|
26
|
+
@hexdigest = sha256( data )
|
27
|
+
if original?
|
28
|
+
puts " >#{@hexdigest}< SHA256 hash matching"
|
29
|
+
puts " ✓ True Official Genuine CryptoPunks™ verified"
|
30
|
+
else
|
31
|
+
puts " !! ERROR: >#{hexdigest}< SHA256 hash NOT matching"
|
32
|
+
puts " >#{PUNK_HASH}< expected for True Official Genuine CryptoPunks™."
|
33
|
+
puts ""
|
34
|
+
puts " Sorry, please download the original."
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
|
38
|
+
@zoom = 1
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def hexdigest() @hexdigest end
|
43
|
+
|
44
|
+
def verify?() @hexdigest == PUNK_HASH; end
|
45
|
+
alias_method :genuine?, :verify?
|
46
|
+
alias_method :original?, :verify?
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
def size() PUNK_COUNT; end
|
51
|
+
|
52
|
+
def []( index )
|
53
|
+
@zoom == 1 ? crop( index ) : scale( index, @zoom )
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def crop( index )
|
58
|
+
y, x = index.divmod( PUNK_ROWS )
|
59
|
+
@punks.crop( x*PUNK_WIDTH, y*PUNK_HEIGHT, PUNK_WIDTH, PUNK_HEIGHT )
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def scale( index, zoom )
|
64
|
+
punk = ChunkyPNG::Image.new( PUNK_WIDTH*zoom, PUNK_HEIGHT*zoom,
|
65
|
+
ChunkyPNG::Color::WHITE )
|
66
|
+
|
67
|
+
## (x,y) offset in big all-in-one punks image
|
68
|
+
y, x = index.divmod( PUNK_ROWS )
|
69
|
+
|
70
|
+
## copy all 24x24 pixels
|
71
|
+
PUNK_WIDTH.times do |i|
|
72
|
+
PUNK_HEIGHT.times do |j|
|
73
|
+
pixel = @punks[i+x*PUNK_WIDTH, j+y*PUNK_HEIGHT]
|
74
|
+
zoom.times do |n|
|
75
|
+
zoom.times do |m|
|
76
|
+
punk[n+zoom*i,m+zoom*j] = pixel
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
punk
|
82
|
+
end
|
83
|
+
end ## class Image
|
84
|
+
end ## module Cryptopunks
|
@@ -0,0 +1,148 @@
|
|
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
|
+
## convenience helpers for types (5)
|
141
|
+
def alien?() @type.name=='Alien'; end
|
142
|
+
def ape?() @type.name=='Ape'; end
|
143
|
+
def zombie?() @type.name=='Zombie'; end
|
144
|
+
def female?() @type.name=='Female'; end
|
145
|
+
def male?() @type.name=='Male'; end
|
146
|
+
end # class Metadata
|
147
|
+
|
148
|
+
end # module Cryptopunks
|
data/lib/cryptopunks/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cryptopunks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: crypto-lite
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: csvreader
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rdoc
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,6 +106,10 @@ files:
|
|
92
106
|
- bin/cryptopunk
|
93
107
|
- bin/punk
|
94
108
|
- lib/cryptopunks.rb
|
109
|
+
- lib/cryptopunks/attributes.rb
|
110
|
+
- lib/cryptopunks/dataset.rb
|
111
|
+
- lib/cryptopunks/image.rb
|
112
|
+
- lib/cryptopunks/structs.rb
|
95
113
|
- lib/cryptopunks/version.rb
|
96
114
|
homepage: https://github.com/rubycoco/blockchain
|
97
115
|
licenses:
|