Q: Dear sir, how do I get rich in
bits-coinpunks?A: If we all buy
bits-coinpunks from one another at ever higher prices we'll all be rich beyond our wildest dreams.21 million bits-coin. 10 000 punks. Do the math.
Let's (re)create from zero / scratch a pixel-perfect copy of the 10 000 CryptoPunks collection (Anno 2017).
Yes, you can. Do-it-yourself (DIY) and
own 100% forever your home-made free clean-room copy
of the billion dollar (2400×2400) bitmap
that kicked-off
a trillion dollar get-rich-quick digital art
mania / bubble in 2021 -
selling "decentralized" blockchain tokens
database records
to ever greater fools at ever higher prices.
Let's copy and (re)use all punk (building) blocks in the basic series (24×24):
11 Archetypes:
Male 1/2/3/4
,
Female 1/2/3/4
,
Zombie
,
Ape
,
Alien
122 Attributes (by category and a-z):
- Hat -
Bandana (m/f)
, Beanie (m)
, Cap (m/f)
, Cap Forward (m)
, Cowboy Hat (m)
, Do-rag (m)
, Fedora (m)
, Headband (m/f)
, Hoodie (m)
, Knitted Cap (m/f)
, Pilot Helmet (f)
, Police Cap (m)
, Tassle Hat (f)
, Tiara (f)
, Top Hat (m)
- Hair -
Blonde Bob (f)
, Blonde Short (f)
, Clown Hair Green (m/f)
, Crazy Hair (m/f)
, Dark Hair (f)
, Frumpy Hair (m/f)
, Half Shaved (f)
, Messy Hair (m/f)
, Mohawk (m/f)
, Mohawk Dark (m/f)
, Mohawk Thin (m/f)
, Orange Side (f)
, Peak Spike (m)
, Pigtails (f)
, Pink With Hat (f)
, Purple Hair (m)
, Red Mohawk (f)
, Shaved Head (m)
, Straight Hair (f)
, Straight Hair Blonde (f)
, Straight Hair Dark (f)
, Stringy Hair (m/f)
, Vampire Hair (m)
, Wild Blonde (f)
, Wild Hair (m/f)
, Wild White Hair (f)
- Eyes -
3D Glasses (m/f)
, Big Shades (m/f)
, Classic Shades (m/f)
, Eye Mask (m/f)
, Eye Patch (m/f)
, Horned Rim Glasses (m/f)
, Nerd Glasses (m/f)
, Regular Shades (m/f)
, Small Shades (m)
, VR (m/f)
, Welding Goggles (f)
- Eyes (Makeup) -
Blue Eye Shadow (f)
, Clown Eyes Blue (m/f)
, Clown Eyes Green (m/f)
, Green Eye Shadow (f)
, Purple Eye Shadow (f)
- Blemishes -
Mole (m/f)
, Rosy Cheeks (m/f)
, Spots (m/f)
- Nose -
Clown Nose (m/f)
,
- Ears -
Earring (m/f)
- Mouth -
Buck Teeth (m)
, Frown (m)
, Smile (m)
- Mouth (Makeup) -
Black Lipstick (f)
, Hot Lipstick (f)
, Purple Lipstick (f)
- Mouth Prop -
Cigarette (m/f)
, Medical Mask (m/f)
, Pipe (m/f)
, Vape (m/f)
- Beard -
Big Beard (m)
, Chinstrap (m)
, Front Beard (m)
, Front Beard Dark (m)
, Goat (m)
, Handlebars (m)
, Luxurious Beard (m)
, Mustache (m)
, Muttonchops (m)
, Normal Beard (m)
, Normal Beard Black (m)
, Shadow Beard (m)
- Neck Accessory -
Choker (f)
, Gold Chain (m/f)
, Silver Chain (m/f)
(Source: Punk (Building) Blocks - Basic Series (24×24))
Let's wipe up a generate_punk
method that
pastes / composes together
the building blocks / attributes and returns
a ready-to-save image. Example:
# generate punk #0
punk = generate_punk( 'Female 2', 'Earring', 'Blonde Bob', 'Green Eye Shadow' )
punk.save( "./tmp/punk0.png" )
punk.zoom(20).save( "./tmp/[email protected]" )
# generate punk #1
punk = generate_punk( 'Male 1', 'Smile', 'Mohawk' )
punk.save( "./tmp/punk1.png" )
punk.zoom(20).save( "./tmp/[email protected]" )
Here we go - the billion dollar formula:
require 'pixelart'
def normalize( str )
## allow (ignore):
## space ( ),
## underscore (_),
## dash (-)
str.downcase.gsub( /[ _-]/, '' ).strip
end
def generate_punk( *values, dir: "./basic" )
punk_type = values[0]
attribute_names = values[1..-1]
punk_type = normalize( punk_type )
path = "#{dir}/#{punk_type}.png"
punk = Image.read( path )
m_or_f = if punk_type.index( 'female' )
'f'
else
'm'
end
attribute_names.each do |attribute_name|
next if attribute_name.nil? || attribute_name.empty? ## note: skip nil/empty attributes
attribute_name = normalize( attribute_name )
path = "#{dir}/#{m_or_f}/#{attribute_name}.png"
attribute = Image.read( path )
punk.compose!( attribute )
end
punk
end # method generate
Let's test drive punk #0 and punk 1 (see above) and voila! In the original 24×24 format:
And 20x (480×480):
Note: If you use your own building blocks
make sure your type and attribute names
match the filenames (without the .png
extension).
For the matching algorithm all names
get automatically downcased and all spaces deleted,
thus,
Male 1
will map to male1.png
and
3D Glasses
to 3dglasses.png
and
Knitted Cap
to knittedcap.png
and so on.
Let's read-in all meta data records for all 10 000 punks. See the punks.csv dataset that reads:
type, attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7
Female 2, Earring, Blonde Bob, Green Eye Shadow,,,,
Male 1, Smile, Mohawk,,,,,
Female 3, Wild Hair,,,,,,
Male 1, Wild Hair, Pipe, Nerd Glasses,,,,
Male 2, Goat, Earring, Wild Hair, Big Shades,,,
Female 2, Earring, Half Shaved, Purple Eye Shadow,,,,
Male 2, Do-rag,,,,,,
Female 2, Spots, Wild White Hair, Clown Eyes Blue,,,,
Male 1, Luxurious Beard, Messy Hair,,,,,
Male 2, Big Beard, Police Cap, Clown Nose,,,,
Female 1, Mohawk, Blue Eye Shadow,,,,,
Female 2, Black Lipstick, Straight Hair Dark, Clown Eyes Green,,,,
...
Let's try:
require 'csvreader'
recs = CsvHash.read( './punks.csv' )
puts "#{recs.size} punk(s)"
#=> 10 000 punk(s)
And to wrap up let's loop over all 10 000 punks and generate a 24×24 version and a 20x zoom, that is, 480×480. Let's go:
recs.each_with_index do |rec,i|
puts "==> punk #{i}:"
pp rec
values = rec.values
punk = generate_punk( *values )
name = "punk#{i}"
punk.save( "./o/#{name}.png" )
punk.zoom(20).save( "./o/#{name}@20x.png" )
end
Yes, that's it.
In the /o
directory you will now find
two images per punk - in the orginal format, that is, 24×24
and 20x, that is, 480×480 - and get:
o/
punk0.png
[email protected]
punk1.png
[email protected]
punk2.png
[email protected]
punk3.png
[email protected]
punk4.png
[email protected]
punk5.png
[email protected]
punk6.png
[email protected]
punk7.png
[email protected]
punk8.png
[email protected]
...
Let's open up the first hundred punks, that is, punk0.png
to punk99.png
:
And let's have a looksie at the biggie 20x (480×480) versions:
and so on.
Let's generate an all-in-one composite image holding the complete collection of 10 000 punks in a 100×100 grid.
punks = CompositeImage.new( 100, 100 )
recs.each_with_index do |rec,i|
values = rec.values
punk = generate_punk( *values )
punks << punk
end
punks.save( "./o/punks.png" )
Yes, that's it.
Open up punks.png
to have a looksie.
Note: Only showing the first one hundred punks as a preview. Download punks.png ~800k for the full monty.
Post them on the CryptoPunksDev reddit. Thanks.