Hootsuiteを1週間使ってみた

TwitterのクライアントはiPhoneでEchofon使ってるってことでMacでもEchofonを使ってたのですが、Hootsuiteがよさそうだってことで1週間ほど使ってみました。

いい所

  • マルチカラムの表示は見やすい

一つの画面でタイムラインとリプライ、リスト等が見れるのでわざわざページ移動したり、タブ変更したりみたいな手間がなくて使いやすかったです。

最近Facebook使いはじめたので、同時に投稿できるってのは便利でした。Facebook側のTwitterアプリ使えば連携できるみたいなんですが、何故か認証でコケてしまって使えないので助かりました。

悪い所

  • いちいち投稿するサービス、アカウントを選択しないといけない

TwitterFacebook複数アカウント使ってると(ひょっとすると一つでも?)postするときにどのアカウントでするか、を選択しないといけないのですが、これをpostするたびに選択しないといけないので不便でした。デフォルトで投稿するアカウントの設定とかも見当らなかったです。

  • ちょっと重い

多機能っていうのがウリらしいので仕方ないのかもしれませんが、動作が多少重かったです。

  • キーボード操作…

ブラウザベースに文句言うのもアレな上に激しく個人的なことなんですが、キーボードで全ての操作にアクセスできると嬉しいです。やっぱホームポジションから離れたくありません。

まとめ

結論としては、常用クライアントにはなりませんでした。postする度にアカウント選択しないといけないっていうのが一番のネックでした。でもマルチカラムの表示は非常に見やすくてよかったです。yoonoっていうクライアントが同じようなマルチカラム表示のクライアントのようなのでまたまたしばらく使ってみようと思います。

SRM275 Div2 Easy - HingedDoor

今日もDiv2 Easyから。

HingedDoor

いちいち英語載せても邪魔なので問題文はリンク参照(ログインしないと見れません)。
で、適当訳。

ヒンジ付きのドアの開いた角度と減衰率が与えられたとき、閉じた状態になるまでの往復の回数(片道を1回とカウント)を返す。ただし角度が5度以下になったら閉じた状態となる。

ということで何も捻るところはないのでそのまま実装。

プログラム

今回はクラスだけ。

class HingedDoor {
public:
  int numSwings (int initialAngle, int reduction) {
    double presentAngle = initialAngle;
    int count = 0;

    while (presentAngle > 5.0) {
      presentAngle /= reduction;
      count++;
    }

    return count;
  }
};

所感

今回は配列を扱わなかったので詰まることなく数分で書けました。配列・文字列をうまく扱えるようになるのが今の自分の課題です。

昨日の問題ですが、TopCoderのページにある問題文から考えてたので最初配列で考えてたんですけど、ArenaでC++の問題文見たら入力がvectorでした。これからはArenaで問題文を読みましょう。あとプラグイン便利すぎワロタ。

SRM168 Div2 Easy : StairClimb

書いてる途中で全選択→BSのコンボを決めてしまった…

TopCoder参戦のための練習として過去問から適当に問題を選んでC++で書いていきます。俺のC++経験は"Hello, World"止まりなので、C++の勉強も兼ねてます。

この記事を読んで「ここはこうした方がいい」とか「こんなコード書く奴はクソ」とか思ったらバシバシ指摘して頂けるとありがたいです。よろしくお願いします。

問題

Div2 Easyという一番簡単なところから。

You are climbing a staircase. The staircase consists of some number of flights of stairs separated by landings. A flight is a a continuous series of stairs from one landing to another. You are a reasonably tall athletic person, so you can climb a certain number of stairs in one stride. However, after each flight, there is a landing which you cannot skip because you need to turn around for the next flight (which continues in the opposite direction).


You will be given the number of stairs in each flight in a int[] flights. Element 0 of flights represents the number of stairs in the first flight, element 1 is the number of stairs in the second flight, etc. You will also be given an int stairsPerStride, which is how many continuous stairs you climb in each stride. If it takes two strides to turn around at a landing, return the number of strides to get to the top of the staircase. You do not need to turn at the top of the staircase.

適当に訳すと

踊り場の間の階段の段数教えてやるから、何歩で頂上まで行けるか教えれ。お前背高いから結構段飛ばしでいけるよな。でも踊り場はいくらお前でも2歩かかるんだわ。

ということで特に何も考えずそのまま実装。その実装がしんどかったんだけれども…。

プログラム

stairClimb.cpp

#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <utility>
#include <set>
#include <cctype>
#include <queue>
#include <stack>
#include <fstream>
#include <cstring>
using namespace std;

class StairClimb {
public:
  int stridesTaken(vector<int> flights, int stairsPerStride) {
    int i, n = flights.size();
    int strides = 0;

    for (i = 0; i < n; i++) {
      flights[i] % stairsPerStride == 0 ? strides += flights[i] / stairsPerStride : strides += flights[i] / stairsPerStride + 1;
    }

    strides += (n - 1) * 2;
    
    return strides;
  }
};
  
int main(int argc, char** argv) {
  vector<int> f;
  int sps = atoi(argv[1]);
  
  for (int i = 2; i < argc; i++) {
    int j = atoi(argv[i]);
    f.push_back(j);
  }

  StairClimb sc;
  int strds = sc.stridesTaken(f, sps);

  cout << strds << endl;
}

実行例

./stairClimb.cpp 5 25 18 24 5 24 24 17 23 10 9 22 25 27 22 16 17 12                                               
102

所感

とにかくC++で書くのがキツかった。Twitterでかなり恥ずかしいPostを連発していた。最初配列で書こうとして、配列の要素数をsizeof()で取ろうとしたところ、関数の引数で渡した場合はポインタで渡されるので要素数が全然取れずにハマった。vector使うとあっさりできて感動した。あとincludeしてるライブラリは id:cou929_la さんのからかっぱらってきただけなのでさっぱり理解していない。

makeplex salon : 麻雀プログラム

久しぶりのブログ更新。よく見ると半年近く前に「再開します」とか高らかに宣言してるけど速攻で終了してました。僕はそういう人間です。

以前チラっと見て「あー俺こんなの無理っす無理っすムリポインザーギっす」とかほざいて何も手をつけなかったのだけど id:cou929_la さんがやってるのに刺激を受けてやってみました。

あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) - ITmedia エンタープライズ

自分の場合は方針決めに40分程度、コーディングに3時間ちょっと、計4時間弱かかりました。制限時間は3時間とのことで余裕でオーバーです。自分の実力はこんなもんだ、ということで要修行ですね。

問題

13枚の手牌が与えられたときにテンパイなら待ちを全て出力、テンパイしてないなら何も出力しない。萬子のチンイツ限定でいいとのこと。

解き終わってから他の方の解答を見てたら七対子考慮してたりしましたけど僕はアホなので一切そういうことを考えずに実装しました。今日実装してみようと思いましたが、そのうち気が向いたらやることにします。

方針

大きな方針としては、与えられた手牌から刻子、順子になり得る牌を候補として列挙して、候補を組み合わせて待ちを絞り込んでいきます。

七対子を除外した場合、待ちは

  • 刻子or順子*4 + 単騎待ち
  • 刻子or順子*3 + アタマ + リャンメンorペンチャン待ち
  • 刻子or順子*3 + アタマ + シャボ待ち

となります。

なのでまず列挙した候補の中から刻子or順子を4つ選ぶ場合と3つ選ぶ場合に場合分けして、それぞれで手牌の数を超えないかチェックしていきます。4つの場合は手牌の数を超えなければ残りの牌を待ちとして出力。3つの場合はさらにアタマの有無をチェックして、頭があってかつリャンメンorペンチャン又はシャボ待ちになっているかを調べます。

言語はできないなりに得意な言語、ということでPerlで書きました。

あとテストデータは適当にランダムに作ってやれー!と思ってたらほとんどテンパイじゃないものばかりで何の役にも立ちませんでした。

プログラム

majong.pl

#!/usr/bin/env perl

use strict;
use warnings;

# 入力
my $input = shift;
my @tehai = (0, 0, 0, 0, 0, 0, 0, 0, 0);
my @machi = ();

# 'random'が引数にくるとランダムな手牌を生成
if ($input eq 'random') {
    my $hai_num = 0;

    while ($hai_num < 13) {
        my $hai = int(rand(9));

        if ($tehai[$hai] < 4) {
            $tehai[$hai]++;
            $hai_num++;
        }
    }
}
else {
    my @sample = split //, $input;
    for (my $i=0; $i<@sample; $i++) {
        $tehai[$sample[$i]-1]++;
    }
}

print "tehai:\t";
for (my $i=0; $i<@tehai; $i++) {
    for (my $j=0; $j<$tehai[$i]; $j++) {
        print $i+1;
    }
}
print "\n";

my @candi = &getCandidate(\@tehai);
my @tmp_tehai = (0, 0, 0, 0, 0, 0, 0, 0, 0);

for (my $i=0; $i<@candi; $i++) {
    for (my $j=$i+1; $j<@candi; $j++) {
        for (my $k=$j+1; $k<@candi; $k++) {

            # 順子・刻子が4つの場合
            for (my $l=$k+1; $l<@candi; $l++) {
                my @tmp_hai = split //, $candi[$i] . $candi[$j] . $candi[$k] . $candi[$l];

                for (my $l=0; $l<@tmp_hai; $l++) {
                    $tmp_tehai[$tmp_hai[$l]-1]++;
                }

                # 入力の牌の数を超えていないかチェック
                my $check = 1;
                my $machi;
                for (my $l=0; $l<9; $l++) {
                    $tmp_tehai[$l] = $tehai[$l] - $tmp_tehai[$l];
                    if ($tmp_tehai[$l] < 0) {
                        $check = 0;
                    }
                    elsif ($tmp_tehai[$l] > 0) {
                        $machi = $l + 1;
                    }
                }

                if ($check) {
                    print "($candi[$i])($candi[$j])($candi[$k])($candi[$l])[$machi]\n";
                }

                @tmp_tehai = (0, 0, 0, 0, 0, 0, 0, 0, 0);
            }

            # 順子・刻子が3つの場合
            my @tmp_hai = split //, $candi[$i] . $candi[$j] . $candi[$k];

            for (my $l=0; $l<@tmp_hai; $l++) {
                $tmp_tehai[$tmp_hai[$l]-1]++;
            }

            # 入力の牌の数を超えていないかチェック
            my $check = 1;
            for (my $l=0; $l<9; $l++) {
                $tmp_tehai[$l] = $tehai[$l] - $tmp_tehai[$l];
                if ($tmp_tehai[$l] < 0) {
                    $check = 0;
                }
            }
            
            if ($check) {
                my ($head, $wait) = &getHead(\@tmp_tehai);

                if ($head && $wait) {
                    for (my $l=0; $l<@$head; $l++) {
                        for (my $m=0; $m<@$wait; $m++) {
                            my @tmp_hai2 = split //, $$head[$l] . $$wait[$m];
                            my @tmp_tehai2 = (0, 0, 0, 0, 0, 0, 0, 0, 0);

                            for (my $n=0; $n<@tmp_hai2; $n++) {
                                $tmp_tehai2[$tmp_hai2[$n]-1]++;
                            }

                            # 入力の牌の数を超えていないかチェック
                            for (my $n=0; $n<9; $n++) {
                                $tmp_tehai2[$n] = $tmp_tehai[$n] - $tmp_tehai2[$n];
                                
                                if ($tmp_tehai2[$n] < 0) {
                                    $check = 0;
                                }
                            }
                            
                            print "($candi[$i])($candi[$j])($candi[$k])($$head[$l])[$$wait[$m]]\n" if ($check);
                        }
                    }
                }
                
                if (@$head == 2) {
                    print "($candi[$i])($candi[$j])($candi[$k])($$head[0])[$$head[1]]\n";
                    print "($candi[$i])($candi[$j])($candi[$k])($$head[1])[$$head[0]]\n";
                }
            }

            @tmp_tehai = (0, 0, 0, 0, 0, 0, 0, 0, 0);
        }
    }
}

# 順子、刻子候補の取得
sub getCandidate {
    my @tehai = @{shift()};
    my @candi = ();

    # 刻子
    for (my $i=0; $i<9; $i++) {
        if ($tehai[$i] >= 3) {
            push @candi, $i+1 . $i+1 . $i+1;
        }
    }

    # 順子
    for (my $i=0; $i<7; $i++) {
        if ($tehai[$i] != 0 &&
            $tehai[$i+1] != 0 &&
            $tehai[$i+2] != 0) {
            my @list = ($tehai[$i], $tehai[$i+1], $tehai[$i+2]);
            my $min = &getMin(\@list);
            for (my $j=0; $j<$min; $j++) {
                push @candi, $i+1 . $i+2 . $i+3;
            }
        }
    }

    return @candi;
}

# 対子と待ちの取得
sub getHead {
    my @tehai = @{shift()};
    my @head = ();
    my @wait = ();
    
    for (my $i=0; $i<9; $i++) {
        # 対子
        if ($tehai[$i] >= 2) {
            push @head, $i+1 . $i+1;
        }

        # 待ち        
        # リャンメンorペンチャン
        if ($i < 8 &&
            $tehai[$i] != 0 &&
            $tehai[$i+1] != 0) {
            my @list = ($tehai[$i], $tehai[$i+1]);
            my $min = &getMin(\@list);
            for (my $j=0; $j<$min; $j++) {
                push @wait, $i+1 . $i+2;
            }
        }
        # カンチャン
        elsif ($i < 7 &&
               $tehai[$i] != 0 &&
               $tehai[$i+2] != 0) {
            my @list = ($tehai[$i], $tehai[$i+2]);
            my $min = &getMin(\@list);
            for (my $j=0; $j<$min; $j++) {
                push @wait, $i+1 . $i+3;
            }
        } 
    }

    return \@head, \@wait;
}

sub getMin {
    my @list = @{shift()};

    my $min = shift @list;

    for my $v (@list) {
        $min = $v if ($min > $v);
    }

    return $min;
}

問題点

実はこのプログラムには重大なバグがありまして、それは待ちを重複して出力してしまうような入力が存在する、ということです。

perl majong.pl 1223344888999
tehai:	1223344888999
(888)(999)(123)(234)[4]
(888)(999)(123)(234)[4]
(888)(999)(123)(44)[23]
(888)(999)(234)(234)[1]

実は候補を列挙するときに重複する要素(この場合234)は別の候補として列挙しちゃってるので、こういう入力の場合は重複して出力してしまいます。昨晩および今晩の段階では有効な解決法を見出せなかったので放置してます。候補を配列じゃなくてハッシュにすればなんとかなるかもしれません。

感想

方針を考えてるときは案外いけるんじゃないかと思ってましたが、いざ実装に入ると自分にコーディング能力がないことがよーくわかりました。しかし、こういうパズル的プログラミングが自分には非常に面白かったです。ネットで色々問題見つけて挑戦したいと思います。C++を勉強してTopCoderに挑戦したいなあ。

仕分けどうたらこうたら

スパコンがどうのこうの、という話がありますが、こういう話がオープンにできるようになったことはいいことだと思います。個人的には科研費の削減には反対ですが。

全然関係ないけどベヨネッタやりてー。箱○の方が評判よさそうですね。

しばらく放置していたのですが

またブログを書き始めてみます。

最近はMacbookiPhoneを買って充実したガジェット生活を送っています。便利ですねーiPhone。まだ有料アプリには手を出していないのですが、これは買ってしまいそうです。

あとは某掲示板のために、Catalystについてちょくちょく勉強しています。まだ2、3個アプリを作ってみただけですが、大体の流れが少しだけわかってきました。