cryptopunks 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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 [ [#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 [ [#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:
|