007. smarty

关卡地址#

解决方案:#

思路:#

这一关源代码中没有任何提示信息,看来信息都在图片中了。

smarty

图片中间位置有一条灰度图像,灰度图取值范围与ASCII码范围一致,所以可以将灰度值转换为ASCII码。小灰度条的宽度都是7px(除了第一条是5px)。需要注意灰度图与图片右边界有21px空白。

行动起来吧。

经过灰度值转换得到如下信息:

smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]

将列表中数值转换后就是下一关的地址了。

代码:#

import helper
path="../../Data/007"
helper.ensureDir(path)

import urllib.request
img="http://www.pythonchallenge.com/pc/def/oxygen.png"
(filename, headers)=urllib.request.urlretrieve(img, path+"/oxygen.png")

# python3图像处理库Pillow(friendly PIL fork)
# 文档地址:https://pillow.readthedocs.io/en/5.3.x/
from PIL import Image
im=Image.open(filename)
(w,h)=im.size
px=im.load()

outstr=[]
# 21px是灰度图右边空白的宽度
for i in range(0,w-21,7):
    (r,g,b,a)=px[i,h/2]
    # 灰度图r,g,b三个分量值相等
    outstr.append(chr(r))
im.close()

msg=''.join(outstr)
print(msg)

import re
pattern=re.compile(r'[\d]+')

nextlevel=''.join(map(chr,map(int,pattern.findall(msg))))
print(nextlevel)
$path="../../Data/007"

. .\helper.ps1
New-Dir -Dir $path
$path=$(Resolve-Path $path).Path

$url="http://www.pythonchallenge.com/pc/def/oxygen.png"
$filename=$path+"/oxygen.png"
Invoke-WebRequest -Uri $url -OutFile $filename

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$img=[System.Drawing.Image]::FromFile($filename)

$msg=""
for ($i=0; $i -lt $img.Width-21; $i+=7) {
    $msg+=[char]$img.GetPixel($i,$img.Height/2).R
}
$img.Dispose()

Write-Output $msg

$outstr=""
$pattern=[regex]"\d+"
$pattern.Matches($msg) | ForEach-Object {$outstr+=[char][int]$_.Value}

Write-Output $outstr
package main

import (
	"fmt"
	"regexp"
	"strconv"
	_ "image/png"
)

func (c *Challenge) Challenge007() {
	path:="../../Data/007"
	EnsureDir(path)

	filename:=path+"/oxygen.png"
	channel:="http://www.pythonchallenge.com/pc/def/oxygen.png"
	Download(channel,filename)

	im:=OpenImage(filename)

	bounds := im.Bounds()

	msg := ""
	y:=(bounds.Max.Y-bounds.Min.Y)/2
	for x:=bounds.Min.X; x<bounds.Max.X-21; x+=7 {
		r,_,_,_ := im.At(x,y).RGBA()
		msg+=string(byte(r))
	}

	fmt.Println(msg)

	pattern:=regexp.MustCompile("/d+")
	matches:=pattern.FindAllString(msg, -1)

	outstr:=""
	for _,match := range matches {
		i,err:=strconv.Atoi(match)
		if err == nil {
			outstr+=string(byte(i))
		}
	}

	fmt.Println(outstr)
}

最终结果: integrity#

下一关地址#

009. connect the dots

关卡地址#

解决方案:#

思路:#

在图片中没有找到有用的信息,老规矩看源文件:

first+second=?

first:
146,399,163,403,170,393,169,391,166,386,170,381,170,371,170,355,169,346,167,335,170,329,170,320,170,
310,171,301,173,290,178,289,182,287,188,286,190,286,192,291,194,296,195,305,194,307,191,312,190,316,
190,321,192,331,193,338,196,341,197,346,199,352,198,360,197,366,197,373,196,380,197,383,196,387,192,
389,191,392,190,396,189,400,194,401,201,402,208,403,213,402,216,401,219,397,219,393,216,390,215,385,
215,379,213,373,213,365,212,360,210,353,210,347,212,338,213,329,214,319,215,311,215,306,216,296,218,
290,221,283,225,282,233,284,238,287,243,290,250,291,255,294,261,293,265,291,271,291,273,289,278,287,
279,285,281,280,284,278,284,276,287,277,289,283,291,286,294,291,296,295,299,300,301,304,304,320,305,
327,306,332,307,341,306,349,303,354,301,364,301,371,297,375,292,384,291,386,302,393,324,391,333,387,
328,375,329,367,329,353,330,341,331,328,336,319,338,310,341,304,341,285,341,278,343,269,344,262,346,
259,346,251,349,259,349,264,349,273,349,280,349,288,349,295,349,298,354,293,356,286,354,279,352,268,
352,257,351,249,350,234,351,211,352,197,354,185,353,171,351,154,348,147,342,137,339,132,330,122,327,
120,314,116,304,117,293,118,284,118,281,122,275,128,265,129,257,131,244,133,239,134,228,136,221,137,
214,138,209,135,201,132,192,130,184,131,175,129,170,131,159,134,157,134,160,130,170,125,176,114,176,
102,173,103,172,108,171,111,163,115,156,116,149,117,142,116,136,115,129,115,124,115,120,115,115,117,
113,120,109,122,102,122,100,121,95,121,89,115,87,110,82,109,84,118,89,123,93,129,100,130,108,132,110,
133,110,136,107,138,105,140,95,138,86,141,79,149,77,155,81,162,90,165,97,167,99,171,109,171,107,161,
111,156,113,170,115,185,118,208,117,223,121,239,128,251,133,259,136,266,139,276,143,290,148,310,151,
332,155,348,156,353,153,366,149,379,147,394,146,399

second:
156,141,165,135,169,131,176,130,187,134,191,140,191,146,186,150,179,155,175,157,168,157,163,157,159,
157,158,164,159,175,159,181,157,191,154,197,153,205,153,210,152,212,147,215,146,218,143,220,132,220,
125,217,119,209,116,196,115,185,114,172,114,167,112,161,109,165,107,170,99,171,97,167,89,164,81,162,
77,155,81,148,87,140,96,138,105,141,110,136,111,126,113,129,118,117,128,114,137,115,146,114,155,115,
158,121,157,128,156,134,157,136,156,136

根据标题,这关是需要把点连接起来,而点需要横纵坐标。由于first和second两者长度不相等,可以看出,first和second代表的不是一条线的横纵坐标集合。那么,first和second表示的应该是两条线了。

将两条线绘制出来之后是一头牛,那么下一关是cow?

错!你会得到如下提示:

hmm. it’s a male.

公牛?ox?是bull!

代码:#

import helper

firststr=helper.readFile("../../Data/009/first.txt")
secondstr=helper.readFile("../../Data/009/second.txt")

firstlist=[int(x.strip()) for x in firststr.split(',')]
secondlist=[int(x.strip()) for x in secondstr.split(',')]

import turtle
import tkinter

tk=tkinter.Tk()
tk.title("Challenge009")
canvas=tkinter.Canvas(tk, width=500, height=500)
canvas.pack(expand=1,fill="both")
pen=turtle.RawPen(canvas)
pen.hideturtle()

def drawPoints(plist):
    points=list(map(lambda x: (x[0]-250, 250-x[1]), zip(plist[::2], plist[1::2])))
    pen.up()
    pen.goto(points[0])
    pen.down()
    for x,y in points:
        pen.goto(x,y)

drawPoints(firstlist)
drawPoints(secondlist)
tk.mainloop()
function GetPoints {
    param (
        [string[]]
        $strs
    )

    [System.Collections.Generic.List[System.Drawing.Point]]$list=New-Object System.Collections.Generic.List[System.Drawing.Point]

    for ($i=0; $i -lt $strs.Length; $i+=2) {
        $x=[System.Convert]::ToInt32($strs[$i].Trim())
        $y=[System.Convert]::ToInt32($strs[$i+1].Trim())
        [System.Drawing.Point]$point=New-Object System.Drawing.Point $x,$y
        $list.Add($point)
    }

    return $list
}

$firststr=(Get-Content (Resolve-Path "../../Data/009/first.txt").Path -Raw).Replace("`r`n","").Split(',',[System.StringSplitOptions]::RemoveEmptyEntries)
$secondstr=(Get-Content (Resolve-Path "../../Data/009/second.txt").Path -Raw).Replace("`r`n","").Split(',',[System.StringSplitOptions]::RemoveEmptyEntries)

[System.Collections.Generic.List[System.Drawing.Point]]$firstline=GetPoints -strs $firststr
[System.Collections.Generic.List[System.Drawing.Point]]$secondline=GetPoints -strs $secondstr

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$img=New-Object System.Drawing.Bitmap 500,500
$graphics=[System.Drawing.Graphics]::FromImage($img)
$graphics.Clear([System.Drawing.Color]::White)
$pen=[System.Drawing.Pens]::Black

$graphics.DrawLines($pen, $firstline.ToArray())
$graphics.DrawLines($pen, $secondline.ToArray())

. .\helper.ps1
Show-Image -Title "Challenge009" -Image $img

$img.Dispose()
package main

import(
	"image"
	"image/color"
	"image/draw"
	"strings"
	"strconv"
)

func (c *Challenge) Challenge009() {
	firststr := ReadFile("../../Data/009/first.txt")
	secondstr := ReadFile("../../Data/009/second.txt")

	bg := image.White
	pen := color.Black

	rgba :=  image.NewRGBA(image.Rect(0, 0, 500, 500))
	draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Over)

	firstline := getPoints(firststr)
	secondline := getPoints(secondstr)

	drawPoints(rgba, pen, firstline)
	drawPoints(rgba, pen, secondline)

	pngfile := "../../Data/009/go.png"

	SaveImage(pngfile, rgba, "png")

	ShowImage(pngfile)
}

func getPoints(str string) []image.Point {
	var points []image.Point
	strlist := strings.Split(str, ",")
	for i := 0; i < len(strlist); i+=2 {
		x,_ := strconv.Atoi(strings.Trim(strlist[i], "\r\n "))
		y,_ := strconv.Atoi(strings.Trim(strlist[i+1], "\r\n "))
		points = append(points, image.Pt(x,y))
	}

	return points
}

func drawPoints(rgba *image.RGBA, color color.Color, points []image.Point) {
	for i := 0; i < len(points)-1; i++ {
		drawLine(points[i].X, points[i].Y, points[i+1].X, points[i+1].Y, rgba, color)
	}
}

func drawLine(x0, y0, x1, y1 int, rgba *image.RGBA, color color.Color) {
	dx := abs(x1 - x0)
	dy := abs(y1 - y0)

	sx, sy := 1, 1
	if x0>=x1 {
		sx = -1
	}
	if y0>=y1 {
		sy = -1
	}
	err := dx-dy

	for {
		rgba.Set(x0, y0, color)
		if x0 == x1 && y0 == y1 {
			return
		}
		e2 := err * 2
		if e2 > -dy {
			err -= dy
			x0 += sx
		}
		if e2 < dx {
			err += dx
			y0 += sy
		}
	}
}

func abs(x int) int {
	if x>=0 {
		return x
	} else {
		return -x
	}
}

最终结果: bull#

下一关地址#

011. odd even

关卡地址#

解决方案:#

思路:#

这一关的提示比较干净,一切都需要从图片中提取分析。

把所有像素值提取到文本,以下为前4行前8列像素数据:

(0, 20, 0)(142, 180, 105)(0, 20, 0)(139, 177, 100)(0, 20, 0)(143, 180, 103)(0, 20, 0)(138, 175, 98)
(148, 186, 111)(0, 20, 0)(148, 186, 109)(0, 21, 0)(144, 181, 104)(0, 20, 0)(144, 181, 104)(0, 20, 0)
(0, 20, 0)(158, 195, 118)(0, 20, 0)(148, 185, 108)(0, 22, 0)(152, 189, 112)(0, 19, 0)(150, 184, 110)
(145, 182, 105)(0, 22, 0)(158, 195, 118)(0, 20, 0)(155, 189, 113)(0, 19, 0)(146, 180, 104)(0, 20, 0)

可以看出,绿色(0, x, 0)与彩色有规律的穿插着,绿色像素分布在偶数行偶数列和奇数行奇数列,彩色像素分布在偶数行奇数列和奇数行偶数列。

014. walk around

关卡地址#

解决方案:#

思路:#

这一关大图下面有个小图,查看源代码后发现是10000*1的图片显示为100*100

100 × 100 pixels (Natural: 10000 × 1 pixels)

同时还有一个提示:

remember: 100*100 = (100+99+99+98) + (…

结合标题,需要将10000*1的图片重新组合为100*100的图片,排列规律如下:

—————————————100-—————————————
|————————————98——————————————|
|   |   ................. |  |
|   |   ................. |  |
|   |   ................. |  |
98  96  ................. 97 99
|   |   ................. |  |
|   |   ................. |  |
|   |   ................. |  |
|   |   ................. |  |
| ——————————97————————————|  |
————————————99————————————————

如果一行一行排列像素会得到bit这个结果,然后得到如下提示:

you took the wrong curve.

016. let me get this straight

关卡地址#

解决方案:#

思路:#

图片每一行都有一条7个像素组成的像素块,两端是1个白色像素,中间是5个粉紫色像素,色值是(255,0,255)。由于图像模式是P,使用8位映射到调色板。

经测试,连续5个粉紫色像素的值是195。

根据标题:

让我直说吧

将这些像素块排成一列,就会得到下一关地址啦。

代码:#

import helper
path="../../Data/016"
helper.ensureDir(path)

# ================================
# 使用http认证,下载文件
import urllib.request
helper.installHTTPBasicAuthOpener("huge", "file")

gif="http://www.pythonchallenge.com/pc/return/mozart.gif"
(filename, headers)=urllib.request.urlretrieve(gif, path+"/mozart.gif")
# ================================

from PIL import Image
im=Image.open(filename)
px=im.load()

newIm=Image.new(im.mode, im.size)
newPx=newIm.load()

(w,h)=im.size
for y in range(h):
    for x in range(w):
        if px[x,y] == 195:
            for xx in range(w):
                newPx[xx,y]=px[(xx+x)%w,y]
            continue

im.close()
newIm.show()
newIm.close()
$path="../../Data/016"

. .\helper.ps1
New-Dir -Dir $path
$path=$(Resolve-Path $path).Path

$gif="http://www.pythonchallenge.com/pc/return/mozart.gif"
$filename=$path+"/mozart.gif"
Get-FileWithAuth -Url $gif -Filename $filename -Username "huge" -Password "file"

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$img=[System.Drawing.Image]::FromFile($filename)
$newImg=New-Object System.Drawing.Bitmap $img.Width,$img.Height
$color=[System.Drawing.Color]::FromArgb(255,0,255)

for ($y = 0; $y -lt $img.Height; $y++) {
    for ($x = 0; $x -lt $img.Width; $x++) {
        if ($img.GetPixel($x,$y) -eq $color) {
            for ($xx = 0; $xx -lt $img.Width; $xx++) {
                $newImg.SetPixel($xx,$y,$img.GetPixel(($xx+$x)%$img.Width,$y))
            }
            continue
        }
    }
}

$img.Dispose()

Show-Image -Title "Challenge016" -Image $newImg
$newImg.Dispose()
package main

import(
	"image"
)

func (c *Challenge) Challenge016() {
	path:="../../Data/016"
	EnsureDir(path)

	gif:="http://www.pythonchallenge.com/pc/return/mozart.gif"
	filename:=path+"/mozart.gif"
	DownloadWithBasicAuth(gif, filename, "huge", "file")

	im:=OpenImage(filename)

	newImg:=straightImage(im)

	giffile := path+"/mozart.go.gif"
	SaveImage(giffile, newImg, "gif")
	
	ShowImage(giffile)
}

func straightImage(im image.Image) *image.RGBA {
	bounds:=im.Bounds()
	newImg:=image.NewRGBA(bounds)
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			r,g,b,_:=im.At(x,y).RGBA()
			if r==65535 && g==0 && b==65535 {
				for xx := bounds.Min.X; xx < bounds.Max.X; xx++ {
					newImg.Set(xx,y,im.At((xx+x)%bounds.Dx(),y))
				}
				continue
			}
		}
	}
	return newImg
}

最终结果: romance#

下一关地址#

❤️ 如果这篇文章对你有帮助,欢迎赞助支持我继续维护 ❤️

☕ Support me ⚡ 爱发电赞助