logo

Python Challenge - Level 28

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:

  1. Extract green channel values.
  2. Group consecutive pairs of bytes (g_2k, g_2k+1).
  3. Compute the absolute difference |g_2k - g_2k+1|.
  4. The majority of differences are 42 (ASCII *). Filter out all values of 42.
  5. 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:

http://www.pythonchallenge.com/pc/ring/guido.html