Created
April 11, 2026 01:34
-
-
Save tux-peng/09d2f8ea14891f11d12819814a68eeef to your computer and use it in GitHub Desktop.
Add letters, numbers and all 120 colours from crayola's 1996 box to classicube
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from PIL import Image, ImageDraw, ImageFont | |
| # The 120 Crayola colors mapped to their standard hex values | |
| CRAYOLA_COLORS = { | |
| # Reds & pinks | |
| "Almond": "#EFDECD", "Apricot": "#FDD9B5", "Bittersweet": "#FD7C6E", "Blush": "#DE5D83", | |
| "Brick Red": "#CB4154", "Carnation Pink": "#FFAACC", "Cerise": "#DD4492", "Chestnut": "#BC5D58", | |
| "Jazzberry Jam": "#CA3767", "Mahogany": "#CD4A4C", "Mango Tango": "#FF8243", "Mauvelous": "#EF98AA", | |
| "Melon": "#FDBCB4", "Peach": "#FFCBA4", "Pink Flamingo": "#FC74FD", "Pink Sherbert": "#F78FA7", | |
| "Radical Red": "#FF496C", "Razzmatazz": "#E3256B", "Red": "#EE204D", "Salmon": "#FF9BAA", | |
| "Scarlet": "#FC2847", "Sunset Orange": "#FD5E53", "Tickle Me Pink": "#FC89AC", | |
| "Violet Red": "#F75394", "Wild Strawberry": "#FF43A4", | |
| # Oranges | |
| "Atomic Tangerine": "#FFA474", "Burnt Orange": "#FF7034", "Burnt Sienna": "#EA7E5D", | |
| "Copper": "#DD9475", "Macaroni And Cheese": "#FFBD88", "Neon Carrot": "#FFA343", | |
| "Orange": "#FF7538", "Outrageous Orange": "#FF6E4A", "Red Orange": "#FF5349", | |
| "Tumbleweed": "#DEAA88", "Vivid Tangerine": "#FF9980", | |
| # Yellows | |
| "Banana Mania": "#FAE7B5", "Canary": "#FFFF99", "Dandelion": "#FED85D", | |
| "Desert Sand": "#EFCDB8", "Gold": "#E7C697", "Goldenrod": "#FCD975", | |
| "Laser Lemon": "#FEFE22", "Sunglow": "#FFCF48", "Tan": "#FAA76C", | |
| "Unmellow Yellow": "#FFFF66", "Yellow": "#FCE883", "Yellow Orange": "#FFAE42", | |
| # Yellow-greens & limes | |
| "Electric Lime": "#CEFF1D", "Green Yellow": "#F0E891", "Inchworm": "#B2EC5D", | |
| "Olive Green": "#BAB86C", "Spring Green": "#ECEABE", "Yellow Green": "#C5E384", | |
| # Greens | |
| "Asparagus": "#87A96B", "Fern": "#71BC78", "Forest Green": "#6DAE81", | |
| "Granny Smith Apple": "#A8E4A0", "Green": "#1CAC78", "Jungle Green": "#3BB08F", | |
| "Mountain Meadow": "#30BA8F", "Pine Green": "#158078", "Screamin Green": "#76FF7A", | |
| "Sea Green": "#9FE2BF", "Shamrock": "#45CEA2", "Tropical Rain Forest": "#17806D", | |
| # Blues & teals | |
| "Aquamarine": "#78DBE2", "Blue": "#1F75FE", "Blue Green": "#0D98BA", | |
| "Caribbean Green": "#1CD3A2", "Cerulean": "#1DACD6", "Cornflower": "#9ACEEB", | |
| "Denim": "#2B6CC4", "Midnight Blue": "#1A4876", "Navy Blue": "#1974D2", | |
| "Pacific Blue": "#1CA9C9", "Robin's Egg Blue": "#1FCECB", "Sky Blue": "#80DAEB", | |
| "Timberwolf": "#DBD7D2", "Turquoise Blue": "#77DDE7", | |
| # Purples & violets | |
| "Blue Bell": "#A2A2D0", "Blue Violet": "#7366BD", "Cadet Blue": "#B0B7C6", | |
| "Eggplant": "#6E5160", "Fuchsia": "#C364C5", "Indigo": "#5D76CB", | |
| "Lavender": "#FCB4D5", "Manatee": "#979AAA", "Mulberry": "#C54B8C", | |
| "Orchid": "#E6A8D7", "Outer Space": "#414A4C", "Periwinkle": "#C5D0E6", | |
| "Plum": "#8E4585", "Purple Heart": "#7442C8", "Purple Mountain Majesty": "#9D81BA", | |
| "Purple Pizzazz": "#FE4EDA", "Razzle Dazzle Rose": "#FF48D0", "Red Violet": "#C0448F", | |
| "Royal Purple": "#7851A9", "Shocking Pink": "#FB7EFD", "Thistle": "#EBC7DF", | |
| "Vivid Violet": "#8F509D", "Violet (Purple)": "#926EAE", "Wild Blue Yonder": "#A2ADD0", | |
| "Wisteria": "#CDB4DB", | |
| # Fluorescents | |
| "Hot Magenta": "#FF1DCE", "Magenta": "#F664AF", "Wild Watermelon": "#FD5B78", | |
| # Neutrals | |
| "Antique Brass": "#CD9575", "Blizzard Blue": "#ACE5EE", "Maroon": "#C8385A", | |
| "Sepia": "#A5694F", "Shadow": "#8A795D", | |
| "Beaver": "#9F8170", "Black": "#000000", "Brown": "#B4674D", | |
| "Fuzzy Wuzzy": "#CC6666", "Gray": "#95918C", "Silver": "#CDC5C2", "White": "#FFFFFF", | |
| } | |
| def hex_to_rgb(hex_code): | |
| hex_code = hex_code.lstrip('#') | |
| return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4)) | |
| def colorize_block(base_image, color_rgb): | |
| """Multiplies the base texture by the target color.""" | |
| colored = Image.new("RGBA", base_image.size) | |
| for x in range(base_image.width): | |
| for y in range(base_image.height): | |
| r, g, b, a = base_image.getpixel((x, y)) | |
| # Multiply blend mode | |
| new_r = int((r / 255.0) * color_rgb[0]) | |
| new_g = int((g / 255.0) * color_rgb[1]) | |
| new_b = int((b / 255.0) * color_rgb[2]) | |
| colored.putpixel((x, y), (new_r, new_g, new_b, a)) | |
| return colored | |
| def create_text_block(base_image, text): | |
| """Draws centered text on a base block.""" | |
| block = base_image.copy() | |
| draw = ImageDraw.Draw(block) | |
| # Attempt to load a default font, fallback to standard if not found | |
| try: | |
| font = ImageFont.truetype("arial.ttf", 12) | |
| except IOError: | |
| font = ImageFont.load_default() | |
| # Center the text | |
| bbox = draw.textbbox((0, 0), text, font=font) | |
| w = bbox[2] - bbox[0] | |
| h = bbox[3] - bbox[1] | |
| x = (16 - w) / 2 | |
| y = (16 - h) / 2 - 1 | |
| # Draw black outline then white text for visibility | |
| draw.text((x-1, y), text, font=font, fill="black") | |
| draw.text((x+1, y), text, font=font, fill="black") | |
| draw.text((x, y-1), text, font=font, fill="black") | |
| draw.text((x, y+1), text, font=font, fill="black") | |
| draw.text((x, y), text, font=font, fill="white") | |
| return block | |
| def is_slot_empty(img, tx, ty): | |
| """Checks if a 16x16 slot is empty (pure purple background in this terrain.png).""" | |
| # EXPLICIT FIX: Protect the original Magenta Wool at Row 4, Column 11 | |
| if tx == 11 and ty == 4: | |
| return False | |
| # Look at the center pixel of the tile to determine if it's the empty purple background | |
| r, g, b, a = img.getpixel((tx * 16 + 8, ty * 16 + 8)) | |
| if r > 200 and g < 150 and b > 200: | |
| return True | |
| return False | |
| def main(): | |
| # Load original terrain | |
| terrain = Image.open("terrain.png").convert("RGBA") | |
| # White wool is at Column 15, Row 4 | |
| white_wool = terrain.crop((240, 64, 256, 80)) | |
| # Stone block is at index 1 (Column 1, Row 0) for text backgrounds | |
| stone = terrain.crop((16, 0, 32, 16)) | |
| new_blocks = [] | |
| # 1. Generate A-Z | |
| for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": | |
| new_blocks.append(create_text_block(stone, char)) | |
| # 2. Generate 0-9 | |
| for char in "0123456789": | |
| new_blocks.append(create_text_block(stone, char)) | |
| # 3. Generate 64 Crayola Wools | |
| for color_name, hex_code in CRAYOLA_COLORS.items(): | |
| rgb = hex_to_rgb(hex_code) | |
| new_blocks.append(colorize_block(white_wool, rgb)) | |
| # 4. Pack into empty slots | |
| block_index = 0 | |
| total_new_blocks = len(new_blocks) | |
| # Scan grid (16x16) skipping row 15 (breaking animations) | |
| for ty in range(15): | |
| for tx in range(16): | |
| if block_index >= total_new_blocks: | |
| break | |
| if is_slot_empty(terrain, tx, ty): | |
| # Paste the new block into the empty slot | |
| terrain.paste(new_blocks[block_index], (tx * 16, ty * 16)) | |
| block_index += 1 | |
| if block_index < total_new_blocks: | |
| print(f"Warning: Ran out of empty slots! Placed {block_index}/{total_new_blocks} blocks.") | |
| else: | |
| print(f"Successfully added all {total_new_blocks} blocks!") | |
| terrain.save("terrain_extended.png") | |
| print("Saved as terrain_extended.png") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment