たまカーチャンのお部屋(さんでーぷろぐらま館)

仕様変更も納期も関係ない 趣味の素人タマさんの悪戦苦闘ぷろぐらみんぐBLOG

ちょっと張り切って

所信表明

連日、長いのを書いてみましたが、継続は力なりということで「内容は短く-そのかわり更新は頻繁に」を目標に、ちまちまと続けていこうと思います。

ということで

今日のネタは、「謎のねとげオプション一覧」でいきます。

謎のねとげ

タマさんがかつてやっていた(過去形)オンラインRPGは、環境設定*1をテキストファイルで保持しています。

テキストエディタで開いてたら見えるようなものにリバースエンジニアリングも何もあったものではないので、このテキストを読み込んで一覧表示するようなものをさくっと作ってみました。

今回のキモは

の2点になります。

使い勝手を考えると

  • どこのフォルダから起動しても動くようにしたい
  • このゲームの提供元は
    • 悪のがんほー
    • 悪のはんげ
    • 悪の○○○
  • と3カ所もあり、ユーザがどこからログインしているかが分からない*2
  • インストール先はレジストリに記録されている

ということでレジストリの情報を取得し、そこにある.iniファイルを読み込むことができればOKということですね。

そしてこんなのが出来上がりました*3

「謎ねとげ環境表示」取得

ちなみに、このゲームのクライアントをインストールしていない場合は終了以外の操作はできませんので、ただのサンプルということで(^^;;;
そして、さすがにJavaではレジストリにアクセスしたり.iniファイルをマッピングしたりするのは無理でした*4

*1:ゲームデータじゃなく、ウィンドウの大きさとか音声ボリュームとかエフェクトの有無とかね

*2:クライアントも最大3つインストールされます

*3:追記:リンクが切れてますが、ネトゲの方も運用終了になったので放置しております⇒なんか又始まったのでリンク復活させました

*4:一度書いたらどこでも動くかわりに、あんまりOSに密着したことはできないんよねぇ……

昨日のコードは

Java&C++の両方ともスクラッチから書いていますが、実のところ、さんでーぷろぐらむで主に使ってるのはどっちでもなくC#だったりします*1

でもって、このお題をC#で書くとどうなるかと言うと*2

using System;
using ExifUtils;

namespace Approximate {
  internal class Program {
    private static void Main(string[] args) {
      decimal dNum = 3.14159265358979323846264338327950m;
      if (args.Length == 0) {
        Console.WriteLine("引数が無い場合はπを引数とします");
      } else {
        if (!decimal.TryParse(args[0], out dNum)) {
          Console.WriteLine("入力が不正です");
          return;
        }
      }
      Rational<int> r = Rational<int>.Approximate(dNum);
      Console.WriteLine(string.Format("近似値 = {0}", r.ToString()));
    }
  }
}

これで

引数が無い場合はπを引数とします
近似値 = 355/113

と出てくるわけで、C#ではこのお題自体が車輪の再発明にしかならなかったり(^^;)


ところで、妙にデフォルト値の\piの桁数が長かったりしますが、これは↓が全部わるいということで(^^;;;;

www.youtube.com

*1:C#が現れる前はさすがにC++がメインでしたけど(^^;)

*2:コード中の使用ライブラリExifUtilsとは、 誤push防止用退避 のことです。動かしてみたい方はプロジェクトの依存関係に読み込んでください

早速なにか作ってみます

作った

対戦表 タマさん 黒猫 たぬたぬ
タマさん
-
×
黒猫
×
-
たぬたぬ
×
-

……誰がはてな記法のサンプル作れと……

いえ、3年の空白期間の間に面白いものが作れるようになったなぁと(^^;)

気を取り直して……

Javaとしては、導入してその次には例のアレをやることになりそうなものですが、
さすがに今更ハロワ*1から入ると一部の方から石投げられそうなので、少し前にチャットで上がったネタを書いてみます。

任意の小数を、なるべく近い分数で表せ

例えば、0.111111...なら1/9とか1.08なら27/25とかですね。

これにも色々な解法があるのでしょうけど、私が思いついたのは高校の頃に覚えた「分数の和で小数を表記する」方法でした。

\piを例に挙げると

\pi=3.141592...=3+0.141592...=3+\frac{1}{7.262546...}
=3+\frac{1}{7+0.062546...}=3+\frac{1}{7+\frac{1}{15.988}}
\approx 3+\frac{1}{7+\frac{1}{16}}=\frac{355}{113}

ということで、おおよそ3\frac{16}{113}になるというものです*2

手順としては

  • 小数を整数部分Aと小数点以下Bに分ける
  • Bの逆数をとる(必ず1以上になる)
  • その逆数を更に整数部分と小数点以下に分ける
  • 以下繰り返し

で、出てきた整数部分を配列にでも入れておいて、それを使って分数計算を行うことにしました。
f:id:tamaka:20130220224609p:plain
↑これの赤○のところを順番に配列に入れておく

そして、できたコードがこんなの*3

import java.util.*;
import java.lang.Number;

/**
 * @author TAMA
 */
public class ApproximateRational {

  /**
   * @param args
   * @throws FractionException
   */
  public static void main(String[] args) throws FractionException {
    double dVal = 3.14159265358979323846264338327950;
/*    BigDecimal dVal = new  BigDecimal("3.14159265358979323846264338327950"); */
    if (args.length == 0) {
      System.out.println("引数が無い場合はπを引数とします");
    } else {
      dVal = Double.parseDouble(args[0]);
/*      dVal = new BigDecimal(args[0]); */
    }
    double dNum = dVal;
    ArrayList<Fraction> aFr = new ArrayList<Fraction>();
    int nDownNum = (int) Math.floor(dNum);
    aFr.add(new Fraction(nDownNum));
    dNum = dNum - nDownNum;
    for (int i = 0; i < 10; i++) {
      dNum = 1.0 / dNum;
      aFr.add(new Fraction((int) Math.floor(dNum)));
      dNum = dNum - Math.floor(dNum);
    }
    for (int i = 0; i < aFr.size(); i++) {
      Fraction r = aFr.get(i);
      for (int j = i; j >= 1; j--) {
        r = r.invert().add(aFr.get(j - 1));
      }
      System.out.print("第" + (i + 1) + "次近似 = " + r + "(");
      System.out.print(r.doubleValue());
      System.out.println(")[誤差:" + (dVal - r.doubleValue()) + "]");
    }
  }
}

で、出力が

引数が無い場合はπを引数とします
第1近似値 = 3(3.0)[誤差:0.14159265358979312]
第2近似値 = 22/7(3.142857142857143)[誤差:-0.0012644892673496777]
第3近似値 = 333/106(3.141509433962264)[誤差:8.32196275291075E-5]
第4近似値 = 355/113(3.1415929203539825)[誤差:-2.667641894049666E-7]
第5近似値 = 103993/33102(3.1415926530119025)[誤差:5.778906242426274E-10]

この手の誤差の出やすい実数演算の時は、一番精度の高い型を使うのがよさげなんですが、書き始めて5分で挫折しました*4
なぜかと申しますと
まともに加減乗除ができません(TT)
今回使わせていただいたFractionもそうなのですが、加減乗除が全部関数呼び出しってなにものですか(;;)

こんな感じで書きたかったし、こっちの方がすっきりしてると思う……。

#include <iostream>
#include <vector>
#include <boost/rational.hpp>
#include <format>

std::vector<int> v;
v.push_back(static_cast<int>(floorl(dNum)));
dNum -= floorl(dNum));
for (int i = 0; i < 10; i++) {
  dNum = 1.0 / dNum;
  v.push_back(static_cast<int>(floorl(dNum)));
  dNum -= floorl(dNum);
}
for (unsigned int i = 0; i < v.size(); i++) {
  boost::rational<int>r(v[i]);
  for (int j = i; j >= 1; j--) {
    r = v[j -1] + 1 / r;
  }
  std::cout << "第" << i + 1 << "次近似 = " << r << "(";
  std::cout << format("%.16g") % rational_cast<double>(r);
  std::cout << ")[誤差:" << dVol - rational_cast<double>(r) << "]" << std::endl;
}

なんか、のっけからもの凄い坂道が出てきたような気がする*5……。

*1:職業安定所ではない

*2:そしてこのお題を選んだ理由のもう一つは見ての通りで、はてな記法で分数が書けるということなのでサンプルとして書いてみました。

*3:コード中にFractionという謎のクラスが出てきますが、これは BLUEPIXY 様の分数クラスを使わせていただきました。それにしてもJavaにはスタンドアロンでは分数クラス無いんかい……

*4:リスト中のコメント:BigDecimalにその痕跡が残っております

*5:boostを使用してますが、たぶんバージョンが変わったら動かないような気もします

Javaはじめました

嫁に行き、家族が増え、趣味の笛吹きや怪しいサークルからも足を洗って早三年。
お仕事に育児に追っかけ回されていましたが、何とか自分の時間も確保できるようになってきました。

時間に拘束されない&お金のかからない趣味*1は続いていますが、元からプログラミング*2大好きタマさんだったりするので、新しい何かをやってみようと思い立ち、どうせならということで全く知らないJavaに目を付けたわけです。
……というよりぶっちゃけた話*3

相談できる知り合いにJava使いしかいなかった……

さて、Javaの開発環境といえばEclipseと相場が決まっているらしいので、早速導入してみました。
私のPCにはVisual Studio Express 2012というものしか入っていないので、この際「全部盛り」のPleiadesを、えいやっと放り込んで、えいやっと起動してみる……みる……みる……。


起動したとたんに落ちるぞこれ(TT)
32ビットでも64ビットでも関係なしにおちるぞこれ(TT)(TT)
のっけから縁起悪すぎ

で、あーでもないこーでもないと色々とやってみたところ

4.2Juno(最新)を3.7Indigoにしてみたら動いた

……あれですか、Junoというのはもしかして人柱バージョンですか……
ということで前途多難ながらタマさんのJavaお勉強は始まるのでした。

P.S.
それにしても
f:id:tamaka:20130218234650p:plain
なんでこんなにいっぱいバージョンが並んでるんじゃ~(TT)

*1:チェスだの将棋観戦だの

*2:PCを使ってて不便なことの解消とか面白いお題を見つけて組んでみるとかなのでとても人に使ってもらえるシロモノではないのですが

*3:10年来の師と仰ぐSEさんはどっかのお国に長期出張中だったりします