cryptopunks 2.0.1 → 3.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -3
- data/Manifest.txt +0 -3
- data/README.md +666 -526
- data/Rakefile +40 -32
- data/bin/cryptopunk +17 -17
- data/bin/punk +17 -17
- data/lib/cryptopunks/attributes.rb +147 -147
- data/lib/cryptopunks/colors.rb +162 -162
- data/lib/cryptopunks/composite.rb +38 -38
- data/lib/cryptopunks/contract/punksdata-assets.rb +338 -338
- data/lib/cryptopunks/contract/punksdata-contract.rb +55 -55
- data/lib/cryptopunks/contract/punksdata-meta.rb +2107 -2107
- data/lib/cryptopunks/dataset.rb +67 -67
- data/lib/cryptopunks/image.rb +35 -33
- data/lib/cryptopunks/structs.rb +161 -161
- data/lib/cryptopunks/tool.rb +382 -275
- data/lib/cryptopunks/version.rb +27 -23
- data/lib/cryptopunks.rb +66 -109
- metadata +16 -19
- data/config/spritesheet.csv +0 -402
- data/config/spritesheet.png +0 -0
- data/lib/cryptopunks/generator.rb +0 -223
data/lib/cryptopunks/tool.rb
CHANGED
@@ -1,275 +1,382 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
class Tool
|
6
|
-
def run( args )
|
7
|
-
Toolii.run( args )
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
class Opts
|
14
|
-
def merge_gli_options!( options = {} )
|
15
|
-
# puts " update options:"
|
16
|
-
# puts options.inspect
|
17
|
-
|
18
|
-
@file = options[:file] if options[:file]
|
19
|
-
@outdir = options[:dir] if options[:dir]
|
20
|
-
|
21
|
-
@zoom = options[:zoom] if options[:zoom]
|
22
|
-
@offset = options[:offset] if options[:offset]
|
23
|
-
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
puts ""
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
opts.
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
1
|
+
|
2
|
+
module Punk
|
3
|
+
|
4
|
+
|
5
|
+
class Tool
|
6
|
+
def run( args )
|
7
|
+
Toolii.run( args )
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class Opts
|
14
|
+
def merge_gli_options!( options = {} )
|
15
|
+
# puts " update options:"
|
16
|
+
# puts options.inspect
|
17
|
+
|
18
|
+
@file = options[:file] if options[:file]
|
19
|
+
@outdir = options[:dir] if options[:dir]
|
20
|
+
|
21
|
+
@zoom = options[:zoom] if options[:zoom]
|
22
|
+
@offset = options[:offset] if options[:offset]
|
23
|
+
|
24
|
+
@seed = options[:seed] if options[:seed]
|
25
|
+
|
26
|
+
@verbose = true if options[:verbose] == true
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def verbose=(boolean) # add: alias for debug ??
|
31
|
+
@verbose = boolean
|
32
|
+
end
|
33
|
+
|
34
|
+
def verbose?
|
35
|
+
return false if @verbose.nil? # default verbose/debug flag is false
|
36
|
+
@verbose == true
|
37
|
+
end
|
38
|
+
|
39
|
+
def file() @file || './punks.png'; end
|
40
|
+
def file?() @file; end ## note: let's you check if file is set (or "untouched")
|
41
|
+
|
42
|
+
def zoom() @zoom || 1; end
|
43
|
+
def zoom?() @zoom; end
|
44
|
+
|
45
|
+
def offset() @offset || 0; end
|
46
|
+
def offset?() @offset; end
|
47
|
+
|
48
|
+
def outdir() @outdir || '.'; end
|
49
|
+
def outdir?() @outdir; end
|
50
|
+
|
51
|
+
### use a standard (default) seed - why? why not?
|
52
|
+
def seed() @seed || 4142; end
|
53
|
+
def seed?() @seed; end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
end # class Opts
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
## note: use gli "dsl" inside a class / namespace
|
62
|
+
class Toolii
|
63
|
+
extend GLI::App
|
64
|
+
|
65
|
+
opts = Opts.new
|
66
|
+
|
67
|
+
|
68
|
+
program_desc 'punk (or cryptopunk) command line tool'
|
69
|
+
|
70
|
+
version Pixelart::Module::Cryptopunks::VERSION
|
71
|
+
|
72
|
+
|
73
|
+
desc "Zoom factor x2, x4, x8, etc."
|
74
|
+
arg_name 'ZOOM'
|
75
|
+
default_value opts.zoom
|
76
|
+
flag [:z, :zoom], type: Integer
|
77
|
+
|
78
|
+
desc "Start counting at offset"
|
79
|
+
arg_name 'NUM'
|
80
|
+
default_value opts.offset
|
81
|
+
flag [:offset], type: Integer
|
82
|
+
|
83
|
+
|
84
|
+
desc "Seed for random number generation / shuffle"
|
85
|
+
arg_name 'NUM'
|
86
|
+
default_value opts.seed
|
87
|
+
flag [:seed], type: Integer
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
desc "Output directory"
|
92
|
+
arg_name 'DIR'
|
93
|
+
default_value opts.outdir
|
94
|
+
flag [:d, :dir,
|
95
|
+
:o, :out, :outdir], type: String
|
96
|
+
|
97
|
+
|
98
|
+
### todo/check: move option to -t/--tile command only - why? why not?
|
99
|
+
# desc "True Official Genuine CryptoPunks™ all-in-one composite image"
|
100
|
+
desc "All-in-one composite image"
|
101
|
+
arg_name 'FILE'
|
102
|
+
default_value opts.file
|
103
|
+
flag [:f, :file], type: String
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
### global option (required)
|
108
|
+
## todo: add check that path is valid?? possible?
|
109
|
+
desc '(Debug) Show debug messages'
|
110
|
+
switch [:verbose], negatable: false ## todo: use -w for short form? check ruby interpreter if in use too?
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
desc "Flip (vertically) all punk characters in all-in-one punk series composite (#{opts.file})"
|
115
|
+
command [:f, :flip] do |c|
|
116
|
+
c.action do |g,o,args|
|
117
|
+
puts "==> reading >#{opts.file}<..."
|
118
|
+
punks = ImageComposite.read( opts.file )
|
119
|
+
|
120
|
+
## note: for now always assume 24x24
|
121
|
+
cols = punks.width / 24
|
122
|
+
rows = punks.height / 24
|
123
|
+
tile_width = 24
|
124
|
+
tile_height = 24
|
125
|
+
|
126
|
+
phunks = ImageComposite.new( cols, rows,
|
127
|
+
width: tile_width,
|
128
|
+
height: tile_height )
|
129
|
+
|
130
|
+
punks.each do |punk|
|
131
|
+
phunks << punk.flip_vertically
|
132
|
+
end
|
133
|
+
|
134
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
135
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
136
|
+
|
137
|
+
## note: allways assume .png extension for now
|
138
|
+
basename = File.basename( opts.file, File.extname( opts.file ) )
|
139
|
+
path = "#{opts.outdir}/#{basename}-flipped.png"
|
140
|
+
puts "==> saving phunks flipped one-by-one by hand to >#{path}<..."
|
141
|
+
|
142
|
+
phunks.save( path )
|
143
|
+
puts 'Done.'
|
144
|
+
end # action
|
145
|
+
end # command flip
|
146
|
+
|
147
|
+
|
148
|
+
desc "Shuffle all punk characters (randomly) in all-in-one punk series composite (#{opts.file})"
|
149
|
+
command [:s, :shuffle] do |c|
|
150
|
+
c.action do |g,o,args|
|
151
|
+
puts "==> reading >#{opts.file}<..."
|
152
|
+
punks = ImageComposite.read( opts.file )
|
153
|
+
|
154
|
+
## note: for now always assume 24x24
|
155
|
+
cols = punks.width / 24
|
156
|
+
rows = punks.height / 24
|
157
|
+
tile_width = 24
|
158
|
+
tile_height = 24
|
159
|
+
|
160
|
+
phunks = ImageComposite.new( cols, rows,
|
161
|
+
width: tile_width,
|
162
|
+
height: tile_height )
|
163
|
+
|
164
|
+
tiles = cols * rows
|
165
|
+
indexes = (0..tiles-1).to_a
|
166
|
+
srand( opts.seed ) ## note: for new reset **global** random seed and use (builtin) Array#shuffle
|
167
|
+
puts " using random generation number seed >#{opts.seed}< for shuffle"
|
168
|
+
indexes = indexes.shuffle
|
169
|
+
|
170
|
+
###
|
171
|
+
# seed 4142 ends in [..., 7566, 828, 8987, 9777]
|
172
|
+
# 333 ends in [..., 6067, 9635, 973, 8172]
|
173
|
+
|
174
|
+
|
175
|
+
indexes.each_with_index do |old_index,new_index|
|
176
|
+
puts " ##{old_index} now ##{new_index}"
|
177
|
+
phunks << punks[old_index]
|
178
|
+
end
|
179
|
+
|
180
|
+
puts " all #{tiles} old index numbers (zero-based) for reference using seed #{opts.seed}:"
|
181
|
+
puts indexes.inspect
|
182
|
+
|
183
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
184
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
185
|
+
|
186
|
+
## note: allways assume .png extension for now
|
187
|
+
basename = File.basename( opts.file, File.extname( opts.file ) )
|
188
|
+
path = "#{opts.outdir}/#{basename}-#{opts.seed}.png"
|
189
|
+
puts "==> saving p(h)unks shuffled one-by-one by hand to >#{path}<..."
|
190
|
+
|
191
|
+
phunks.save( path )
|
192
|
+
puts 'Done.'
|
193
|
+
end # action
|
194
|
+
end # command shuffle
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
desc "Get punk characters via image tiles from all-in-one punk series composite (#{opts.file}) - for IDs use 0 to 9999"
|
203
|
+
command [:t, :tile] do |c|
|
204
|
+
c.action do |g,o,args|
|
205
|
+
|
206
|
+
# puts "opts:"
|
207
|
+
# puts opts.inspect
|
208
|
+
|
209
|
+
puts "==> reading >#{opts.file}<..."
|
210
|
+
punks = ImageComposite.read( opts.file )
|
211
|
+
|
212
|
+
|
213
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
214
|
+
|
215
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
216
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
217
|
+
|
218
|
+
args.each_with_index do |arg,index|
|
219
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
220
|
+
|
221
|
+
punk = punks[ punk_index ]
|
222
|
+
|
223
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
224
|
+
|
225
|
+
## if zoom - add x2,x4 or such
|
226
|
+
if opts.zoom != 1
|
227
|
+
punk = punk.zoom( opts.zoom )
|
228
|
+
punk_name << "@#{opts.zoom}x"
|
229
|
+
end
|
230
|
+
|
231
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
232
|
+
puts "==> (#{index+1}/#{args.size}) saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
233
|
+
|
234
|
+
punk.save( path )
|
235
|
+
end
|
236
|
+
puts 'Done.'
|
237
|
+
end # action
|
238
|
+
end # command tile
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
desc 'Generate punk characters from text attributes (from scratch / zero) via builtin punk spritesheet'
|
243
|
+
command [:g, :gen, :generate] do |c|
|
244
|
+
c.action do |g,o,args|
|
245
|
+
|
246
|
+
puts "==> generating >#{args.join( ' + ' )}<..."
|
247
|
+
punk = Image.generate( *args )
|
248
|
+
|
249
|
+
puts " setting zoom to #{opts.zoom}x" if opts.zoom != 1
|
250
|
+
|
251
|
+
## make sure outdir exits (default is current working dir e.g. .)
|
252
|
+
FileUtils.mkdir_p( opts.outdir ) unless Dir.exist?( opts.outdir )
|
253
|
+
|
254
|
+
punk_index = 0 ## assume base 10 decimal
|
255
|
+
punk_name = "punk-" + "%04d" % (punk_index + opts.offset)
|
256
|
+
|
257
|
+
## if zoom - add x2,x4 or such
|
258
|
+
if opts.zoom != 1
|
259
|
+
punk = punk.zoom( opts.zoom )
|
260
|
+
punk_name << "@#{opts.zoom}x"
|
261
|
+
end
|
262
|
+
|
263
|
+
path = "#{opts.outdir}/#{punk_name}.png"
|
264
|
+
puts "==> saving punk ##{punk_index+opts.offset} to >#{path}<..."
|
265
|
+
|
266
|
+
punk.save( path )
|
267
|
+
puts 'Done.'
|
268
|
+
end # action
|
269
|
+
end # command generate
|
270
|
+
|
271
|
+
|
272
|
+
desc 'Query (builtin off-chain) punk contract for punk text attributes by IDs - use 0 to 9999'
|
273
|
+
command [:q, :query] do |c|
|
274
|
+
c.action do |g,o,args|
|
275
|
+
|
276
|
+
# puts "opts:"
|
277
|
+
# puts opts.inspect
|
278
|
+
|
279
|
+
args.each_with_index do |arg,index|
|
280
|
+
punk_index = arg.to_i( 10 ) ## assume base 10 decimal
|
281
|
+
|
282
|
+
puts "==> (#{index+1}/#{args.size}) punk ##{punk_index}..."
|
283
|
+
|
284
|
+
attribute_names = CryptopunksData.punk_attributes( punk_index )
|
285
|
+
## downcase name and change spaces to underscore
|
286
|
+
attribute_names = attribute_names.map do |name|
|
287
|
+
name.downcase.gsub( ' ', '_' )
|
288
|
+
end
|
289
|
+
|
290
|
+
print " "
|
291
|
+
print attribute_names.join( ' ' )
|
292
|
+
print "\n"
|
293
|
+
end
|
294
|
+
puts 'Done.'
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
|
300
|
+
desc 'List all punk archetype and attribute names from builtin punk spritesheet'
|
301
|
+
command [:l, :ls, :list] do |c|
|
302
|
+
c.action do |g,o,args|
|
303
|
+
|
304
|
+
generator = Image.generator
|
305
|
+
|
306
|
+
puts "==> Archetypes"
|
307
|
+
generator.meta.each do |rec|
|
308
|
+
next unless rec.archetype?
|
309
|
+
|
310
|
+
print " "
|
311
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
312
|
+
print " - #{rec.type}"
|
313
|
+
print "\n"
|
314
|
+
end
|
315
|
+
|
316
|
+
puts ""
|
317
|
+
puts "==> Attributes"
|
318
|
+
generator.meta.each do |rec|
|
319
|
+
next unless rec.attribute?
|
320
|
+
|
321
|
+
print " "
|
322
|
+
print "%-30s" % "#{rec.name} / (#{rec.gender})"
|
323
|
+
print " - #{rec.type}"
|
324
|
+
print "\n"
|
325
|
+
end
|
326
|
+
|
327
|
+
puts ""
|
328
|
+
puts " See github.com/cryptopunksnotdead/punks.spritesheet for more."
|
329
|
+
puts ""
|
330
|
+
|
331
|
+
puts 'Done.'
|
332
|
+
end # action
|
333
|
+
end # command list
|
334
|
+
|
335
|
+
|
336
|
+
|
337
|
+
pre do |g,c,o,args|
|
338
|
+
opts.merge_gli_options!( g )
|
339
|
+
opts.merge_gli_options!( o )
|
340
|
+
|
341
|
+
if opts.verbose?
|
342
|
+
## LogUtils::Logger.root.level = :debug
|
343
|
+
end
|
344
|
+
|
345
|
+
## logger.debug "Executing #{c.name}"
|
346
|
+
true
|
347
|
+
end
|
348
|
+
|
349
|
+
post do |global,c,o,args|
|
350
|
+
## logger.debug "Executed #{c.name}"
|
351
|
+
true
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
on_error do |e|
|
356
|
+
|
357
|
+
if opts.verbose?
|
358
|
+
puts e.backtrace
|
359
|
+
end
|
360
|
+
|
361
|
+
if e.is_a?( SystemExit )
|
362
|
+
puts
|
363
|
+
puts "*** error: system exit with status code ( #{e.status} )"
|
364
|
+
exit( e.status ) ## try exit again to make sure error code gets passed along!!!
|
365
|
+
else
|
366
|
+
puts
|
367
|
+
puts "*** error: #{e.message}"
|
368
|
+
end
|
369
|
+
|
370
|
+
## note: was false # skip default error handling
|
371
|
+
|
372
|
+
## note: try true - false WILL SWALLOW exit codes and such
|
373
|
+
## - looks like it's still returning 0 (e.g. on unknown option or such)!!!!
|
374
|
+
true
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
### exit run(ARGV) ## note: use Toolii.run( ARGV ) outside of class
|
379
|
+
end # class Toolii
|
380
|
+
end # module Punk
|
381
|
+
|
382
|
+
|