解いてみた

ITmedia「あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定」ってのを解いてみた。
馬鹿正直なコードすぎて恥ずかしい。
もっとかっこいいコードが書きたい。
あとバグ出したんで3時間以上かかってます。
同じ牌が4枚しかないこと、チートイツについては考慮してません。


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MachiChecker {
public static void main(String[] args) {
//標準入力を読んで空改行じゃなければ待ちチェック
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
while (true) {
String line = reader.readLine();
if (line != null && line.length() > 0) {
showMachi(line);
} else {
System.exit(0);
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}

public static void showMachi(String str) {
//入力をソートして配列化。13番目の要素(待ち)は0とする
final int[] pais = new int[14];
for (int i = 0; i < 13; i++) {
pais[i] = Integer.parseInt(str.substring(i, i + 1));
}
pais[13] = 99;
Arrays.sort(pais);
pais[13] = 0;

//順子と刻子をすべて取り出して、ソート
List paiSets = findJuntu(pais);
paiSets.addAll(findKoutu(pais));
Collections.sort(paiSets, new Comparator() {
@Override
public int compare(PaiSet o1, PaiSet o2) {
int val1 = pais[o1.pai1] * 100 + pais[o1.pai2] * 10 + pais[o1.pai3];
int val2 = pais[o2.pai1] * 100 + pais[o2.pai2] * 10 + pais[o2.pai3];
return val1 - val2;
}
});

//すべての頭を取り出す
List atamas = findAtama(pais);
//見つかった待ち文字列
Set found = new HashSet();

//牌のインデックスのセット
Set nums = new HashSet();
for (Atama atama : atamas) {
nums.add(atama.pai1);
nums.add(atama.pai2);
//順子、刻子の組み合わせで牌のインデックスがかぶらない物をfoundに追加
for (int i = 0; i < paiSets.size(); i++) {
PaiSet set1 = paiSets.get(i);
if (addNumbers(nums, set1)) {
for (int j = i + 1; j < paiSets.size(); j++) {
PaiSet set2 = paiSets.get(j);
if (addNumbers(nums, set2)) {
for (int k = j + 1; k < paiSets.size(); k++) {
PaiSet set3 = paiSets.get(k);
if (addNumbers(nums, set3)) {
for (int l = k + 1; l < paiSets.size(); l++) {
PaiSet set4 = paiSets.get(l);
if (addNumbers(nums, set4)) {
found.add(atama.toString(pais) + set1.toString(pais)
+ set2.toString(pais) + set3.toString(pais) + set4.toString(pais));
removeNumbers(nums, set4);
}
}
removeNumbers(nums, set3);
}
}
removeNumbers(nums, set2);
}
}
removeNumbers(nums, set1);
}
}
nums.remove(atama.pai1);
nums.remove(atama.pai2);
}

//結果表示
for (String yaku : found) {
System.out.println(yaku);
}
}

private static List findJuntu(int[] pais) {
List rtn = new ArrayList();
for (int i = 0; i < pais.length; i++) {
for (int j = i + 1; j < pais.length; j++) {
for (int k = j + 1; k < pais.length; k++) {
if ((pais[i] == pais[j] - 1 && pais[j] == pais[k] - 1)
|| (pais[i] == pais[j] - 1 && k == 13)
|| (pais[i] == pais[j] - 2 && k == 13)) {
rtn.add(new PaiSet(i, j, k));
}
}
}
}
return rtn;
}

private static List findKoutu(int[] pais) {
List rtn = new ArrayList();
for (int i = 0; i < pais.length; i++) {
for (int j = i + 1; j < pais.length; j++) {
for (int k = j + 1; k < pais.length; k++) {
if ((pais[i] == pais[k] && pais[j] == pais[k])
|| (pais[i] == pais[j] && k == 13 && j != 13)) {
rtn.add(new PaiSet(i, j, k));
}
}
}
}
return rtn;
}

private static List findAtama(int[] pais) {
List rtn = new ArrayList();
for (int i = 0; i < pais.length; i++) {
for (int j = i + 1; j < pais.length; j++) {
if (pais[i] == pais[j] || j == 13) {
rtn.add(new Atama(i, j));
}
}
}
return rtn;
}

private static boolean addNumbers(Set set, PaiSet paiSet) {
if(set.contains(paiSet.pai1) || set.contains(paiSet.pai2) || set.contains(paiSet.pai3)) {
return false;
}
set.add(paiSet.pai1);
set.add(paiSet.pai2);
set.add(paiSet.pai3);
return true;
}

private static void removeNumbers(Set set, PaiSet paiSet) {
set.remove(paiSet.pai1);
set.remove(paiSet.pai2);
set.remove(paiSet.pai3);
}
}

class Atama {
public int pai1;
public int pai2;

public Atama(int pai1, int pai2) {
this.pai1 = pai1;
this.pai2 = pai2;
}

public String toString(int[] pais) {
if(pai1 != 13 && pai2 != 13) {
return "("+ pais[pai1] + "" + pais[pai2] + ")";
} else {
return "["+ pais[pai1] + "]";
}
}
}

class PaiSet {
public int pai1;
public int pai2;
public int pai3;

public PaiSet(int pai1, int pai2, int pai3) {
this.pai1 = pai1;
this.pai2 = pai2;
this.pai3 = pai3;
}

public String toString(int[] pais) {
if(pai3 != 13) {
return "("+ pais[pai1] + "" + pais[pai2] + "" + pais[pai3] + ")";
} else {
return "["+ pais[pai1] + "" + pais[pai2] + "]";
}
}
}

結果


1112224588899
(99)(111)(222)[45](888)
1122335556799
(55)(123)(123)(567)[99]
(99)(123)(123)[55](567)
(99)(123)(123)(555)[67]
1112223335559
[9](111)(222)(333)(555)
[9](123)(123)(123)(555)
1223344888999
[4](123)(234)(888)(999)
[1](234)(234)(888)(999)
(44)(123)[23](888)(999)
1112345678999
(11)(123)[45](678)(999)
(99)(111)(234)[56](789)
[2](111)(345)(678)(999)
(99)(111)(234)(567)[89]
(11)[12](345)(678)(999)
(11)(123)(456)[78](999)
(11)(123)(456)(789)[99]
[5](111)(234)(678)(999)
(99)[11](123)(456)(789)
(99)(111)[23](456)(789)
[8](111)(234)(567)(999)

なっげー
かっこわるーい

一応解説すると、入力された数字を14要素の数値の配列にし(最後の要素はワイルドカードとして考える)、
そこからあり得る全ての頭、順子、刻子をリストアップして、牌がかぶらない組み合わせを
表示してます。
九蓮宝燈の場合、頭が19、順子と刻子が42あり、それらを全部組み合わせると
19*42*41*40*39/(1*2*3*4)=2126670
通りの組み合わせになりますが、牌がかぶる組み合わせは試さずに切り捨ててるので
(ソースのネストしまくって汚い辺り)実際のところは28676回の試行で済み、
数ミリ秒(Core2Duo 2.13GHzのMac。数回目の試行でHotSpotで最適化された後の数字)
と比較的実用レベルの速度におさまりました。

追記:ちょっとバグってたんで直しました。
さらに追記:カンチャン待ちを忘れてたんで追加しました。
さらに追記:よく見たら余計なコードがあったんで整理

ふーらふら

相変わらずジムには足繁く通っているのだが、仕事が始まってから時間が足りず、
一回1時間程度しか練習できていない。
そうなると最後になる筋トレが省かれることが多かったのだが、
久しぶりに目いっぱい、2時間半みっちり練習したら筋トレで体がガタガタになった。
最近、体力がかなり落ちてるみたいで、マスなんかやった日には
あっという間にスタミナが切れるし。
まずいなあ・・・
ちょっと前から新しいトレーナーの人が入ったのだが、初めてミットをやってもらった。
慎重180センチを超える大柄な人で、すごくがっちりした体をしている。
ミットはうまいトレーナーほどいい音がするものだが(ミットをパンチに当ててくるので)、
この人は極端にいい音がして、あまりにも強い力でミットをパンチに当ててくるので
僕のジャブ程度だと打ち負けていた。
すごいもんだ。

ThinkPad

知らなかったが、最近のThinkPadはドライバの自動アップデータが付いていて、
それとLANのドライバさえあれば数クリックでドライバを最新状態にすることができる。
こりゃ便利。
インストールの手間がずいぶん省けた。

ハードディスク交換

週末を使ってノートのハードディスクを換装してみた。
80ギガのうち、Cドライブに6.5ギガ切って残りをDドライブにしたのだが、
DドライブをFAT32でフォーマットしようとしたら、大きすぎてできず、
仕方ないんでNTFSにしてフォーマットした。
OSを標準のWindowsXPからWindows2000にダウングレードしたのだが、
不便になったと感じられることが何一つ見つからないのが笑える。
動作も軽くなったし2000の方が断然いい。
ともかくこれで40ギガほど空きができたので、Winnyでエr、もとい、
でかい開発ツールのインストールや、
VJの素材集めにいそしめます。