Python Challenge - Level 28
- Link: http://www.pythonchallenge.com/pc/ring/bell.html
- Username: repeat
- Password: switch
Problem

The page says:
RING-RING-RING say it out loud
Saying "ring" out loud phonetically hints at the color green.
Visiting green.html confirms: "yes! green!".
Solution
Focusing on the Green channel of bell.png:
- Extract green channel values.
- Group consecutive pairs of bytes (g_2k, g_2k+1).
- Compute the absolute difference |g_2k - g_2k+1|.
- The majority of differences are 42 (ASCII
*). Filter out all values of 42. - Convert the remaining differences into ASCII characters.
The decoded string asks:
whodunnit().split()[0] ?
The author of Python is Guido van Rossum. His first name is guido.
Python
from PIL import Image
img = Image.open("bell.png")
_, green, _ = img.split()
pixels = list(green.getdata())
# Compute absolute difference between adjacent pairs
diffs = [abs(a - b) for a, b in zip(pixels[0::2], pixels[1::2])]
# Filter out the noise value 42
msg_bytes = bytes([d for d in diffs if d != 42])
print(msg_bytes.decode())
# "whodunnit().split()[0] ?"
Go
package main
import (
"fmt"
"image/png"
"os"
)
func absDiff(a, b uint8) uint8 {
if a > b {
return a - b
}
return b - a
}
func main() {
f, err := os.Open("bell.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 green []uint8
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
_, g, _, _ := img.At(x, y).RGBA()
green = append(green, uint8(g>>8))
}
}
var msg []byte
for i := 0; i < len(green)-1; i += 2 {
d := absDiff(green[i], green[i+1])
if d != 42 {
msg = append(msg, d)
}
}
fmt.Println(string(msg)) // "whodunnit().split()[0] ?"
}
Rust
use image::GenericImageView;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let img = image::open("bell.png")?;
let green_channel: Vec<u8> = img.pixels().map(|(_, _, p)| p[1]).collect();
let msg: Vec<u8> = green_channel
.chunks_exact(2)
.map(|chunk| (chunk[0] as i16 - chunk[1] as i16).abs() as u8)
.filter(|&d| d != 42)
.collect();
let text = String::from_utf8(msg)?;
println!("{text}"); // "whodunnit().split()[0] ?"
Ok(())
}
Language Comparison
| Feature | Python | Go | Rust |
|---|---|---|---|
| Channel Splitting | img.split() splits RGB image into 3 channel images |
Iterate pixels extracting g >> 8 |
Stream pixels with .map(|p| p[1]) |
| Pairwise Chunking | zip(pixels[0::2], pixels[1::2]) |
Step by 2 in slice loop: i += 2 |
chunks_exact(2) iterator |
| Integer Absolute Difference | abs(a - b) auto-promoted integers |
Explicit branch or cast to signed int | Cast to i16 to avoid unsigned underflow panic |
Key insight: In Rust, subtracting two u8 values directly (a - b) can panic in debug mode on unsigned underflow; casting to signed i16 before taking .abs() guarantees safety. In Python, integers are signed and unbounded by default.
Next Level
The first word of the creator's name is guido: