パズルの自動生成(画像版)

こういう問題を出力するスクリプトができた。70行。


で書き換えているのはこの5行だけ

    def item_renderer(i):
        def renderer(cont):
            return cont.trans(0.5, 0.5).pushpop(
                lambda cont:cont.rot(i * 3.141592 / 3).trans(0.3).circle(r=0.1)
            ).pushpop(
                lambda cont:cont.trans((0.25 * i) % 1 - 0.5).rect(width=0.25, height=0.25))
        return renderer

0〜8のiについてそれぞれ違った図を描画する関数を返していて、それを四角の中に描画してクリッピングしている。クリッピングが一番大変だった(笑)

あとはここをもっと抽象化していけば問題がたくさん作れるな。いまは円と四角しかないけど、一度作った部品は再利用が出来るし。

パズルの自動生成とかでやったように、ランダムにモデルを選んでパラメータを決めて僕が知らない問題を作り出すところまで早くやりたいのだけど、そこはぐっと我慢してまずは集積サイトを作るべきなのかなぁ。むー。



とりあえずIQTest.dkの問題でも模倣してみるか。

def i2xy(i):
    return i % 3, i / 3

def item_renderer(i):
    def renderer(cont):
        # http://www.iqtest.dk/main.swf Q2
        x, y = i2xy(i)
        for j in range(9):
            if y <= j <= x + y:
                fill = "gray"
            else:
                fill = "white"

            (cont.trans(0.5, 0.5).rot(CCW90 + 2 * pi * j / 9).trans(0.3)
             .circle(r=0.1, stroke_width=0.01, stroke="black", fill=fill))

        return cont
    return renderer


    def renderer(cont):
        # http://www.iqtest.dk/main.swf Q3
        x, y = i2xy(i)
        
        for j in range(y + 1):
            (cont.trans(0.2 + x * 0.3 + 0.1 * j - 0.05 * y).path(d="M 0 0 L 0 1", stroke_width=0.02))

        return cont
    return renderer


def draw_grid(cont, n):
    from string import Template
    tmpl = Template("M $v 0 L $v 1 M 0 $v L 1 $v")
    for i in range(1, n):
        v = 1.0 / n * i
        cont.path(d=tmpl.substitute(locals()), stroke_width=0.01)

def item_renderer(i):
    def renderer(cont):
        # http://www.iqtest.dk/main.swf Q5
        x, y = i2xy(i)
        cont.apply(draw_grid, 3)
        for j in range(x + 1):
            cont.trans(1.0 / 3 * (y % 3), 1.0 / 3 * j).rect(width=0.33, height=0.33, fill="url(#tube)")
        return cont
    return renderer

あと

    cont.define_linear_gradient(
        "tube",
        [(0, "black", 1), (0.5, "white", 1), (1, "black", 1)])


def item_renderer(i):
    def renderer(cont):
        # http://www.iqtest.dk/main.swf Q8
        x, y = i2xy(i)
        white = dict(fill="none", stroke_width=0.01, stroke="black")
        marks = [
            lambda cont: cont.trans(0.5, 0.5).circle(r=0.4, **white),
            lambda cont: cont.trans(0.1, 0.1).rect(width=0.8, height=0.8, **white),
            lambda cont: cont.path(d="M 0.5 0.2 L 0.1 0.9 L 0.9 0.9 Z", **white),
            lambda cont: cont.trans(0.5, 0.5).circle(r=0.4), # black circle
            lambda cont: cont.trans(0.1, 0.1).rect(width=0.8, height=0.8), # black square 
            lambda cont: cont.path(d="M 0.5 0.2 L 0.1 0.9 L 0.9 0.9 Z"), # black triangle
            ]
        if i < 9: 
            j = (x + y) % 3
        else:
            j = (2 + i - 9) % 6
        cont.apply(marks[j])
        return cont
    return renderer

誤答を作るのに苦労した。

from random import random
buf = [int(random() * 6) for _i in range(9)]
def item_renderer(i):
    def renderer(cont):
        # http://www.iqtest.dk/main.swf Q39
        cont.apply(draw_grid, 3)
        for y in range(3):
            for x in range(3):
                cont.trans(1.0 / 3 * x, 1.0 / 3 * y).scale(1.0 / 3).apply(
                    MARKS[(buf[(i + y * 3 + x) % 9] + i) % 6])

        return cont
    return renderer

さっきのmarksは再利用すると思ったので関数の外に移動した。+iをつけない若干簡単バージョンも作ってみた。

簡単な方

難しい方

オリジナル問題