着手できる場所のチェックと石を取る処理を追加

ちゃんとテストしていない。

# -*- coding: utf-8 -*-
import sys
import codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

class Untouchable(Exception): pass

class Goban(object):

    invalid = 99
    empty = 0
    black = 1
    white = -1
    around_moves = ((1, 0), (-1, 0), (0, 1), (0, -1))

    def __init__(self, size):
        self.size = size
        self.data = [ [ 0 for i in range(size) ] for j in range(size) ]
    
    def render(self, output=sys.stdout):
        for i, line in enumerate(self.data):
            for j, point in enumerate(line):
                if point == self.empty:
                    output.write(self._empty_str(i, j))
                elif point == self.black:
                    output.write(u'●')
                else:
                    output.write(u'○')
            output.write('\n')
    
    def get_opponent_color(self, color):
        return color * -1

    def touch(self, x, y, color):
        if not self.touchable(x, y, color):
            raise Untouchable()
        self.data[y][x] = color
        gottens = set()
        opponent = self.get_opponent_color(color)
        for move_x, move_y in self.around_moves:
            next_x = x + move_x
            next_y = y + move_y
            if self.get_state(next_x, next_y) is not opponent: continue
            if not self.check_alive(next_x, next_y, opponent):
                self._get_stones(next_x, next_y, gottens)
        for gotten_x, gotten_y in gottens:
            self.data[gotten_y][gotten_x] = self.empty
        return len(gottens)

    def _get_stones(self, x, y, gottens):
        color = self.get_state(x, y)
        targets = [(x, y)]
        gottens.add((x, y))
        target = targets.pop()
        while target is not None:
            x, y = target
            for move_x, move_y in self.around_moves:
                next_x = x + move_x
                next_y = y + move_y
                if self.get_state(next_x, next_y) is not color: continue
                if (next_x, next_y) not in gottens:
                    targets.append((next_x, next_y))
                    gottens.add((next_x, next_y))
            target = targets.pop() if targets else None

    def touchable(self, x, y, color):
        if self.get_state(x, y) is not self.empty:
            return False
        else:
            if not self.check_alive(x, y, color):
                return False
        return True

    def get_state(self, x, y):
        if x < 0 or self.size <= x or y < 0 or self.size <= y:
            return self.invalid
        return self.data[y][x]

    def check_alive(self, x, y, color):
        targets = [(x, y)]
        target = targets.pop()
        while target is not None:
            for move_x, move_y in self.around_moves:
                next_x = x + move_x
                next_y = y + move_y
                state = self.get_state(next_x, next_y)
                if state is self.empty:
                    return True
                elif state is color:
                    targets.append((next_y, next_y))
            target = targets.pop() if targets else None
        return False

    def _empty_str(self, x, y):
        if x == 0:
            if y == 0:
                return u'┏'
            elif y == self.size - 1:
                return u'┓'
            else:
                return u'┯'
        elif x == self.size - 1:
            if y == 0:
                return u'┗'
            elif y == self.size - 1:
                return u'┛'
            else:
                return u'┷'
        else:
            if y == 0:
                return u'┠'
            elif y == self.size - 1:
                return u'┨'
            else:
                return u'┼'
        
def test():
    goban_size = 19
    goban = Goban(goban_size)
    goban.touch(3, 3, Goban.black)
    goban.touch(15, 15, Goban.white)
    goban.touch(0, 1, Goban.black)
    goban.render()
    goban.touch(0, 0, Goban.white)
    goban.render()
    gotten = goban.touch(1, 0, Goban.black)
    goban.render()
    print gotten, 'got.'

if __name__ == '__main__':
    test()

実行。

┏┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┓
●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼○┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┗┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┛
○┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┓
●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼○┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┗┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┛
┏●┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┯┓
●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼●┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼○┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┠┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┨
┗┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┷┛
1 got.

Pythonで碁盤を表す文字列を生成

なんでこんなものを書いてるか、の説明はあとで。

# -*- coding: utf-8 -*-
import sys
import codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

class Goban(object):

    empty = 0
    black = 1
    white = 2

    def __init__(self, size):
        self.size = size
        self.data = [ [ 0 for i in range(size) ] for j in range(size) ]
    
    def render(self, output=sys.stdout):
        for i, line in enumerate(self.data):
            for j, point in enumerate(line):
                if point == self.empty:
                    output.write(self._empty_str(i, j))
                elif point == self.black:
                    output.write(u'●')
                else:
                    output.write(u'○')
            output.write('\n')
    
    def touch(self, x, y, color):
        self.data[y][x] = color

    def _empty_str(self, x, y):
        if x == 0:
            if y == 0:
                return u'┏'
            elif y == self.size - 1:
                return u'┓'
            else:
                return u'┯'
        elif x == self.size - 1:
            if y == 0:
                return u'┗'
            elif y == self.size - 1:
                return u'┛'
            else:
                return u'┷'
        else:
            if y == 0:
                return u'┠'
            elif y == self.size - 1:
                return u'┨'
            else:
                return u'┼'
        
def test():
    goban_size = 19
    goban = Goban(goban_size)
    goban.touch(4, 4, Goban.black)
    goban.touch(15, 15, Goban.white)
    goban.touch(0, 1, Goban.black)
    goban.render()

if __name__ == '__main__':
    test()

TopCoder SRM 498

6回目のSRM参戦。

Div2 Easy: AdditionGame(Java

はいはい。スピード勝負。

public class AdditionGame {

	public int getMaximumPoints(int A, int B, int C, int N) {
		int res = 0;
		for (int i = 0; i < N; i++) {
			int max = Math.max(Math.max(A, B), C);
			if (max >= 1) {
				res += max;
				if (max == A) A--;
				else if (max == B) B--;
				else if (max == C) C--;
			}
		}
		return res;
	}
	
}

switch/case文を使おうとしたら定数じゃないとダメって怒られた。ふだん使わないから知らんかった。(Pythonにはswitch/case文がない)

Div2 Medium: FoxSequence(Java

最初C#で書きかけてJavaで書き直すというアホなことをしてしまった。。。
比較的素直に書いていけばよさそうな問題ですが・・・

public class FoxSequence {

	public String isValid(int[] seq) {
		int diff = seq[1] - seq[0];
		if (diff <= 0) return "NO";
		int status = 1;
		for (int i = 2; i < seq.length; i++) {
			int newdiff = seq[i] - seq[i-1];
			if (status == 1 || status == 4) {
				if (newdiff <= 0) {
					status++;
				} else if (newdiff != diff) {
					return "NO";
				}
			} else if (status == 2 || status == 5) {
				if (newdiff >= 0) {
					status++;
					if (status == 3 && newdiff != 0) {
						status++;
					}
				} else if (newdiff != diff) {
					return "NO";
				}
			} else if (status == 3) {
				if (newdiff != 0) {
					status++;
				}
			}
			diff = newdiff;
		}
		if (status != 5) return "NO";
		return "YES";
	}
	
}

おそるおそる提出したところ、不安が的中してChallenge Phaseで被撃墜。

int seq = {1, 2, 2, 2, 3, 2};

のようなケースで落ちます。
条件式のミス。

追記: 他にも {1} や {1, 2, 3, 4, 5, 4, 3, 3, 3, 2, 1} の時がだめです。一行修正・二行追加。

Div2 Hard: NinePuzzle(Java

めずらしく時間が余ったのでだめもとでHard問題にもチャレンジ。
んー、これって入れ替えとか考える必要あるのかなー?
こんな簡単なわけないから間違ってるだろうけど、一応出してみるか・・・。

public class NinePuzzle {

	public int getMinimumCost(String init, String goal) {
		int res = 0;
		char[] colors = {'R', 'G', 'B', 'Y'};
		for (char color : colors) {
			int initnum = 0;
			int goalnum = 0;
			for (int i = 0; i < init.length(); i++) {
				if (init.charAt(i) == color) initnum++;
				if (goal.charAt(i) == color) goalnum++;
			}
			res += Math.abs(initnum - goalnum);
		}
		return res / 2;
	}
	
}

なんと正解。746.08ptゲット!

結果

977.65ptで179位。
レーティングは859から947に回復し、緑色コーダーに復帰。

TopCoder SRM 496をC#で練習

参加できてなかったので本番のつもりで練習。

Div2 Medium: ColoredStrokes

using System;

class ColoredStrokes
{
    public int getLeast(string[] picture)
    {
        int res = 0;
        int width = picture[0].Length;
        int height = picture.Length;
        for (int i = 0; i < height; i++)
        {
            bool onHStroke = false;
            for (int j = 0; j < width; j++)
            {
                if (picture[i][j] == 'R' || picture[i][j] == 'G')
                {
                    if (!onHStroke)
                    {
                        res++;
                        onHStroke = true;
                    }
                }
                else
                {
                    onHStroke = false;
                }
            }

        }
        for (int i = 0; i < width; i++)
        {
            bool onVStroke = false;
            for (int j = 0; j < height; j++)
            {
                if (picture[j][i] == 'B' || picture[j][i] == 'G')
                {
                    if (!onVStroke)
                    {
                        res++;
                        onVStroke = true;
                    }
                }
                else
                {
                    onVStroke = false;
                }
            }

        }
        return res;
    }
}

縦と横でそれぞれストローク数をカウントするだけ。
二重ループ一個で書けるかな・・・。
271.27ptでsubmitしてシステムテストもクリア。

SRM 497の回答をC#で書き直してみた

id:chokudai先生の真似してC#で参戦できないかなーと思案中。
Parallels上のWindows 7にVisual C# Expressを入れてみました。プロジェクト名がnamespaceになったり余計なusingが入るのって制御できないのかなー。

Div2 Easy: Filtering

using System;

class Filtering
{
    public int[] designFilter(int[] sizes, string outcome)
    {
        int max = int.MinValue;
        int min = int.MaxValue;
        for (int i = 0; i < sizes.Length; i++)
        {
            if (outcome[i] == 'A')
            {
                max = Math.Max(max, sizes[i]);
                min = Math.Min(min, sizes[i]);
            }
        }
        for (int i = 0; i < sizes.Length; i++)
        {
            if (outcome[i] == 'R' && min <= sizes[i] && sizes[i] <= max)
            {
                return new int[0];
            }

        }
        int[] res = { min, max };
        return res;
    }
}

Div2 Medium: PermutationSignature

using System;

class PermutationSignature
{
    public int[] reconstruct(string signature)
    {
        int[] res = new int[signature.Length + 1];
        int[] stack = new int[signature.Length + 1];
        int stackpos = 0;
        int pos = 0;
        int cur = 1;
        stack[stackpos++] = cur++;
        for (int i = 0; i < signature.Length; i++)
        {
            if (signature[i] == 'I')
            {
                while (stackpos != 0) res[pos++] = stack[--stackpos];
            }
            stack[stackpos++] = cur++;
        }
        while (stackpos != 0) res[pos++] = stack[--stackpos];
        return res;
    }
}

うむ

自分としてはきれいに書けて満足。クラスライブラリを全然知らないからスタックも配列で実装しました。
Hard問題にもそろそろ挑戦しようかな。

TopCoder SRM 497

コード貼るならはてなでしょってことで、ひさびさにこちらに書きます。
5回目のTopCoder挑戦です。これまでの記録はこちら。 http://www.topcoder.com/tc?module=MemberProfile&cr=22907785&tab=alg

Div2 Easy: Filtering

public class Filtering {

	public int[] designFilter(int[] sizes, String outcome) {
		int minA = Integer.MAX_VALUE;
		int maxA = -1;
		int minR = Integer.MAX_VALUE;
		int maxR = -1;
		char[] chars = outcome.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			char c = chars[i];
			if (c == 'A') {
				minA = Math.min(minA, sizes[i]);
				maxA = Math.max(maxA, sizes[i]);
			} else {
				minR = Math.min(minR, sizes[i]);
				maxR = Math.max(maxA, sizes[i]);
			}
		}
		
		int[] ret = {minA, maxA};
		if (minR != Integer.MAX_VALUE && minA < minR) {
			ret = new int[0];
		} else if (maxR != -1 && maxR < maxA && minR < maxR ) {
			ret = new int[0];
		}
		return ret;
	}
	
}

Easyのわりに問題文長い。。理解に時間がかかった。
maxRの更新箇所でコピペの修正ミスがあり、Easyでは初のFailed。
そもそもRは最小値と最大値を取る必要なかった。

Div2 Medium: PermutationSignature

いい加減に解きたいMedium問題。

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Stack;

public class PermutationSignature {

	public int[] reconstruct(String signature) {
		int[] ret = new int[signature.length()+1];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = i+1;
		}
		char[] chars = signature.toCharArray();
		List<Integer> res = new ArrayList<Integer>();
		Stack<Integer> stack = new Stack<Integer>();
		stack.push(1);
		for (int i = 0; i < chars.length; i++) {
			if (chars[i] == 'I') {
				while (stack.size() != 0) {
					res.add(stack.pop());
				}
			}
			stack.push(ret[i+1]);
		}
		while (stack.size() != 0) res.add(stack.pop());
		for (int i = 0; i < ret.length; i++) {
			ret[i] = res.get(i).intValue();
		}
		return ret;
	}

}

境界条件ではまって提出が間に合わなかった。10分ほどオーバーして書き上げたのがこれ。
合ってると思うのですがいかに。

というわけで

初のEasy失敗でスコア0・・・。最悪の結果に終わり、レーティングは854まで落ちてしまいました。
次回から挽回です!

復習会

Div1をめざすには、やっぱり復習が大切ですよね。
今回はマリーチの開発メンバー三人で渋谷のルノアールに集まり、SRMの後に復習会をしました。Division Summaryから正解者のコードを開き、みんなで内容を理解したり、お互いのコードを見せ合ったり、と。
次回からは、SRMとは別の日の休日などに復習会をしようと思います。外の人も歓迎なので、またTwitterなどでお知らせいたします。

名前解決がパフォーマンスに与える影響

http://blog.miraclelinux.com/asianpen/2008/05/python-code-rea.html#more
でも実験されていますが、もう少し細かいケースで試してみました。

scope_test.py

def with_local(num):
    l = []
    l_value = True
    for i in xrange(num):
        l.append(l_value)

def with_nested(num):
    l = []
    n_value = True
    def nested(num):
        for i in xrange(num):
            l.append(n_value)
    nested(num)

m_value = True
def with_module(num):
    l = []
    for i in xrange(num):
        l.append(m_value)

def with_builtin(num):
    l = []
    for i in xrange(num):
        l.append(True)

if __name__ == "__main__":
    import sys
    from timeit import Timer
    t = sys.argv[1]
    num = sys.argv[2]
    if t == "local":
        def_name = "with_local"
    elif t == "nested":
        def_name = "with_nested"
    elif t == "module":
        def_name = "with_module"
    elif t == "builtin":
        def_name = "with_builtin"
    t = Timer("%s(%s)" % (def_name, num),
        "from __main__ import %s" % def_name)
    print def_name, t.timeit(1)

手元のcoLinux(on ThinkPadX61 Core2 Duo 2GHz)で実行。まずは100万回。

[michisu@colinux] $ python scope_test.py local 1000000                                               [~/python-code-reading]
with_local 0.941817998886
[michisu@colinux] $ python scope_test.py nested 1000000                                              [~/python-code-reading]
with_nested 1.04100513458
[michisu@colinux] $ python scope_test.py module 1000000                                              [~/python-code-reading]
with_module 1.03532505035
[michisu@colinux] $ python scope_test.py builtin 1000000                                             [~/python-code-reading]
with_builtin 1.08331394196

1000万回。

[michisu@colinux] $ python scope_test.py local 10000000                                              [~/python-code-reading]
with_local 9.48914504051
[michisu@colinux] $ python scope_test.py nested 10000000                                             [~/python-code-reading]
with_nested 10.4752728939
[michisu@colinux] $ python scope_test.py module 10000000                                             [~/python-code-reading]
with_module 10.4114220142
[michisu@colinux] $ python scope_test.py builtin 10000000                                            [~/python-code-reading]
with_builtin 10.6834070683

ビルトイン型をそのまま使うとやはり少し遅い。
1000万回回しても1秒程度しか差が出ないので、Setのような汎用コレクションクラスのコードでもない限り、普段は気にしなくても良さそうだけど。(これをつねにやるぐらいならCを使えって話になる)

ある名前がコードブロック内で使われると、その名前を最も近傍から囲うようなスコープ (最内スコープ: nearest enclosing scope) を使って束縛の解決を行います。

http://www.python.jp/doc/2.4/ref/naming.html

ということで、最内スコープによる名前の解決がもっとも高速になり、順次外のスコープが使われるのでルックアップが遅くなると思われます。
上のケースではnestedがmoduleより常に少しだけ遅いのが面白いですが、この理由はよく分かりません。

追記。タイトル変更しました。「スコープの変数解決が」→「名前解決が」
追記2。地の文で回数の桁が間違ってたので直しました。