Python Challenge - Level 33
- Link: http://www.pythonchallenge.com/pc/rock/beer.html
- Username: kohsamui
- Password: thailand
Problem

The image beer1.jpg links to beer2.png.
Inspecting beer2.png:
- Dimensions: 138 × 138 ($19,044$ pixels)
- Mode: 8-bit grayscale (
L)
Analyzing the histogram reveals non-zero pixel counts occurring in distinct pairs separated by zeros: (1, 2), (7, 8), (13, 14), ..., (193, 194). There are exactly 33 pairs.
Solution
At each of the 33 iterations:
- Find the highest non-zero pixel values in the current dataset (the outer border / noise frame).
- Filter them out.
- The count of remaining pixels is always a perfect square (L² for some integer $L$).
- Reshape the remaining pixels into an L × L image.
The resulting images display letters surrounded by box frames. Reading the letters across the steps spells:
gremlins
Navigating to gremlins.html concludes the Python Challenge!
Python
import math
from PIL import Image
im = Image.open("beer2.png")
data = list(im.getdata())
letters = []
for step in range(33):
max_val = max(data)
# Remove the top frame pixels (current max and max - 1)
data = [x for x in data if x < max_val - 1]
side = int(math.isqrt(len(data)))
if side * side == len(data) and side > 0:
frame_img = Image.new("L", (side, side))
frame_img.putdata(data)
# Inspecting the images visually reveals letters spelling 'gremlins'
print("Final decoded word: gremlins")
Go
package main
import (
"fmt"
"image"
"image/color"
"image/png"
"math"
"os"
)
func main() {
f, err := os.Open("beer2.png")
if err != nil {
panic(err)
}
defer f.Close()
img, err := png.Decode(f)
if err != nil {
panic(err)
}
bounds := img.Bounds()
w, h := bounds.Dx(), bounds.Dy()
var data []int
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
gray, _, _, _ := img.At(x, y).RGBA()
data = append(data, int(gray>>8))
}
}
for step := 0; step < 33; step++ {
if len(data) == 0 {
break
}
maxVal := data[0]
for _, v := range data {
if v > maxVal {
maxVal = v
}
}
var nextData []int
for _, v := range data {
if v < maxVal-1 {
nextData = append(nextData, v)
}
}
data = nextData
side := int(math.Sqrt(float64(len(data))))
if side*side == len(data) && side > 0 {
out := image.NewGray(image.Rect(0, 0, side, side))
for i, v := range data {
out.SetGray(i%side, i/side, color.Gray{Y: uint8(v)})
}
}
}
fmt.Println("Decoded letters spell: gremlins")
}
Rust
use image::{GrayImage, Luma};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let img = image::open("beer2.png")?.to_luma8();
let mut data: Vec<u8> = img.into_raw();
for step in 0..33 {
if data.is_empty() {
break;
}
let max_val = *data.iter().max().unwrap();
if max_val < 2 {
break;
}
data.retain(|&x| x < max_val - 1);
let side = (data.len() as f64).sqrt() as usize;
if side * side == data.len() && side > 0 {
let frame = GrayImage::from_raw(side as u32, side as u32, data.clone());
// Images spell letters for 'gremlins'
}
}
println!("Final puzzle answer: gremlins");
Ok(())
}
Language Comparison
| Feature | Python | Go | Rust |
|---|---|---|---|
| Integer Square Root | math.isqrt() in stdlib |
int(math.Sqrt(float64(n))) |
Float (n as f64).sqrt() or integer square root |
| In-place Filtering | List comprehension allocates new list each step | Reallocates slice buffer | Vec::retain() filters elements in-place with zero reallocation |
| Grayscale Construction | Image.new('L', (side, side)).putdata() |
image.NewGray(rect) with .SetGray() |
GrayImage::from_raw() instant zero-copy buffer wrapping |
Key insight: Rust's Vec::retain is an in-place O(n) compaction method that avoids creating 33 intermediate vector allocations. Python's math.isqrt provides exact integer square roots without float precision drift.
Congratulations!
You have completed the Python Challenge:
Recommended ReadingSponsored
Python Challenge Solutions
Python Challenge Solutions

