Created
April 2, 2026 00:41
-
-
Save tux-peng/d00f51bf9f0cf1051eb0a9405e7c125a to your computer and use it in GitHub Desktop.
crayola 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 64 Crayola colors mapped to their standard hex values | |
| CRAYOLA_COLORS = { | |
| "Apricot": "#FDD9B5", "Asparagus": "#87A96B", "Bittersweet": "#FD7C6E", "Black": "#000000", | |
| "Blizzard Blue": "#ACE5EE", "Blue": "#1F75FE", "Blue Green": "#0D98BA", "Blue Violet": "#7366BD", | |
| "Brick Red": "#CB4154", "Brown": "#B4674D", "Burnt Sienna": "#EA7E5D", "Cadet Blue": "#B0B7C6", | |
| "Carnation Pink": "#FFAACC", "Cerulean": "#1DACD6", "Denim": "#2B6CC4", "Forest Green": "#6DAE81", | |
| "Fuchsia": "#C364C5", "Gold": "#E7C697", "Goldenrod": "#FCD975", "Granny Smith Apple": "#A8E4A0", | |
| "Gray": "#95918C", "Green": "#1CAC78", "Green Yellow": "#F0E891", "Hot Magenta": "#FF1DCE", | |
| "Laser Lemon": "#FEFE22", "Macaroni And Cheese": "#FFBD88", "Magenta": "#F664AF", "Mahogany": "#CD4A4C", | |
| "Mauvelous": "#EF98AA", "Melon": "#FDBCB4", "Midnight Blue": "#1A4876", "Olive Green": "#BAB86C", | |
| "Orange": "#FF7538", "Orchid": "#E6A8D7", "Outrageous Orange": "#FF6E4A", "Periwinkle": "#C5D0E6", | |
| "Pine Green": "#158078", "Purple Mountain Majesty": "#9D81BA", "Razzmatazz": "#E3256B", "Red": "#EE204D", | |
| "Red Orange": "#FF5349", "Red Violet": "#C0448F", "Robin’s Egg Blue": "#1FCECB", "Salmon": "#FF9BAA", | |
| "Screamin Green": "#76FF7A", "Sea Green": "#9FE2BF", "Silver": "#CDC5C2", "Sky Blue": "#80DAEB", | |
| "Spring Green": "#ECEABE", "Thistle": "#EBC7DF", "Tickle Me Pink": "#FC89AC", "Timberwolf": "#DBD7D2", | |
| "Tropical Rain Forest": "#17806D", "Tumbleweed": "#DEAA88", "Turquoise Blue": "#77DDE7", "Unmellow Yellow": "#FFFF66", | |
| "Violet (Purple)": "#926EAE", "Violet Red": "#F75394", "White": "#FFFFFF", "Wild Watermelon": "#FD5B78", | |
| "Wisteria": "#CDB4DB", "Yellow": "#FCE883", "Yellow Green": "#C5E384", "Yellow Orange": "#FFAE42" | |
| } | |
| 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