smellman's Broken Diary

クソみたいなもんです

Python Conference Japan 2012 の Top of Pythonista決定戦で一位になりました

Python Conference Japan 2012 に参加していたのですが、会場で開催されていた Top of Pythonista 決定戦で一位になりました。
Rubyプログラマなのに...
Pythonは学生時代に勉強しながら、アルバイト先のユーティリティ作成や卒研の実装とかをしていたのですが、社会人になってから一回ちょっとしたユーティリティを書いた以外ではほとんど使わず、勉強もおろそかにしていて柴田さんのみんPyぐらいしか読んでないような状態でした。
というわけで、かれこれ6年ぶりぐらいの本気のPythonプログラミングでしたが、非常に楽しかったです。
問題はまだ表に出てないかもしれないけど、いちおう僕が書いたプログラムを晒しておきます。printデバッグとかの部分で僕の苦労した箇所がわかります(汗

import sys
import json
from functools import total_ordering

def run(input, output, width, height):
    f = open(input)
    json_file = f.read()
    f.close()
    objs = json.loads(json_file)
    parser = Parser(objs, output, int(width), int(height))
    parser.run()

class Parser:
    def __init__(self, objs, output, width, height):
        self.logos = Logos(objs)
        self.matrix = Matrix(width, height)
        self.output = output
        
    def run(self):
        while self.matrix.next_pos():
            range_x = self.matrix.next_range_x()
            range_y = self.matrix.next_range_y()
            logo = self.logos.min_item(range_x, range_y)
            if (logo == None):
                self.matrix.set_notput(range_x)
            else:
                self.matrix.put(logo)
                self.logos.remove(logo)
            #self.matrix.show()
        result = self.matrix.to_json()
        f = open(self.output, 'w')
        f.write(result)
        f.close()
        return
            
class Matrix:
    empty = 0
    puted = 1
    notput = 2
    
    def __init__(self, width, height):
        self.width = width
        self.height = height
        #self.matrix = width * [ height * [self.empty]]
        self.matrix = self.makematrix()
        self.start_pos = tuple([0, 0])
        self.putlogos = []
    def makematrix(self):
        matrix = []
        for x in range(0, self.width):
            _matrix = []
            for y in range(0, self.height):
                _matrix.append(self.empty)
            matrix.append(_matrix)
        return matrix
            

    def next_pos(self):
        #print("next_pos")
        for y in range(0, self.height):
            for x in range(0, self.width):
                #print(x, y, self.matrix[x][y])
                if (self.matrix[x][y] == self.empty):
                    self.start_pos = tuple([x, y])
                    #print("start_pos", self.start_pos)
                    return True
        return False

    def next_range_y(self):
        range_y = 0
        x = self.start_pos[0]
        for y in range(self.start_pos[1], self.height):
            if (self.matrix[x][y] != self.empty):
                return range_y
            range_y = range_y + 1
        return range_y

    def next_range_x(self):
        y = self.start_pos[1]
        range_x = 0
        for x in range(self.start_pos[0], self.width):
            if (self.matrix[x][y] != self.empty):
                return range_x
            range_x = range_x + 1
        return range_x
            
    def put(self, logo):
        rect_to = tuple([self.start_pos[0] + logo.width, self.start_pos[1] + logo.height])
        self.draw(self.puted, rect_to)
        self.putlogos.append(tuple([logo, self.start_pos[0], self.start_pos[1]]))
        return
    
    def set_notput(self, range_x):
        #print("run set_nonput")
        nonput_point = self.get_nonput_point(range_x)
        if (nonput_point == None):
            return False
        #print(nonput_point)
        self.draw(self.notput, nonput_point)
        return True

    def draw(self, type, rect_to):
        #print(["draw ", self.start_pos, rect_to, type])
        #print("check point ", "271 1", self.matrix[271][1])
        for x in range(self.start_pos[0], rect_to[0] + 1):
            #print("line ", x)
            for y in range(self.start_pos[1], rect_to[1] + 1):
                #print("draw! ", x, y, type)
                self.matrix[x][y] = type
        #print("end draw")
        

    def get_nonput_point(self, range_x):
        x = self.start_pos[0] + range_x + 1#何かある
        if (x > self.width):
            rect_to = tuple([self.width - 1, self.start_pos[1]])
            self.draw(self.notput, rect_to)
            return None
        #print(self.start_pos)
        #print(range_x)
        for y in range(self.start_pos[1], self.height):
            if (self.matrix[x][y] == self.empty):
                return tuple([x - 1, y - 1])
        return None

    def show(self):
        print(self.matrix)

    def to_json(self):
        array = []
        for item in self.putlogos:
            dict = {}
            dict["id"] = item[0].id
            dict["x"] = item[1]
            dict["y"] = item[2]
            array.append(dict)
        return json.dumps(array)
                           

class Logos:
    def __init__(self, objs):
        self.logos = []
        for logoitem in objs:
            logo = Logo(logoitem)
            self.logos.append(logo)
        self.logos.sort()
        self.logos.reverse()

    def remove(self, logo):
        self.logos.remove(logo)

    def min_item(self, width, height):
        for logo in self.logos:
            if logo.width < width:
                if logo.height < height:
                    return logo
        return None
        
@total_ordering
class Logo:
    def __init__(self, logoitem):
        self.height = logoitem["height"]
        self.width = logoitem["width"]
        self.score = logoitem["score"]
        self.name = logoitem["name"]
        self.id = logoitem["id"]
        self.range = self.height * self.width

    # sort
    def __eq__(self, other):
        if (other == None):
            return False
        return (self.score == other.score) and (self.range == other.range)
    # 小さいものを逆にする
    def __lt__(self, other):
        if (self.score == other.score):
            return (self.range > other.range)
        return (self.score < other.score)

    # print
    def show(self):
        print("id: %d, name: %s, score:%d, width: %d, height: %d" % (self.id, self.name, self.score, self.width, self.height))

if __name__ == '__main__':
    #print(len(sys.argv))
    if (len(sys.argv) != 5):
        print("Usage: python3 %s input output width height" % sys.argv[0])
        exit()
    run(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

個人的に一番時間がかかったのがprint文のSyntax Errorです。Python3からprint文に()が必要なのに気づくのにすごく時間かかりました。一番苦労したのが文法とか!!!
あと、最初二次元配列の作り方間違えてしまいひどい目にあいました。コメントにそのまんま失敗例が残ってるので笑ってやってくだせぇorz