Yuki Nakata's Blog

スーパー中ちゃん♪

カテゴリ: Linux

linux glibcのrand関数のソースコードを読んでみましょう。

どうやって乱数を生成しているのか調べてみましょう。全く怖くありません。

まず乱数についてですが、真性乱数と擬似乱数に分類されます。glibcの乱数は周期性があるため擬似乱数となります。優秀な擬似乱数としてはメルセンヌ・ツイスタ乱数があります。暗号に使われるのは真性乱数の方ですね。難しそうな説明はここまで。w

glibc ダウンロードディレクトリ glibc-2.22.tar.gz

rand関数は解凍してstdlibディレクトリのrandom.cとrandom_r.cに書かれています。

ソースコードを読む、というよりパクって実装して動かしてみると理解できます。

まず今は使われていない昔のrand関数を書いていきます。
oldrand.c

#include <stdio.h>
#include <stdlib.h>

/* x**31 + x**3 + 1.  */
#define    TYPE_3        3
#define    BREAK_3        128
#define    DEG_3        31
#define    SEP_3        3

//実はこの乱数テーブルをいじっているだけ 昔の乱数は2番目の-1726662223しか使っていない
static int32_t  randtbl[DEG_3 + 1] =
  {
    TYPE_3,

    -1726662223, 379960547, 1735697613, 1040273694, 1313901226,
    1627687941, -179304937, -2073333483, 1780058412, -1989503057,
    -615974602, 344556628, 939512070, -1249116260, 1507946756,
    -812545463, 154635395, 1388815473, -1926676823, 525320961,
    -1009028674, 968117788, -123449607, 1284210865, 435012392,
    -2017506339, -911064859, -370259173, 1132637927, 1398500161,
    -205601318,
  };

static struct random_data unsafe_state =
  {
    .fptr = &randtbl[SEP_3 + 1],
    .rptr = &randtbl[1],

    .state = &randtbl[1],

    .rand_type = TYPE_3,
    .rand_deg = DEG_3,
    .rand_sep = SEP_3,

    .end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])]
};

void oldrand(struct random_data *buf, int *result)
{
  int *state;

  state = buf->state;
  int val = state[0];
  val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;  //randtbl[1]をごちゃごちゃいじっているだけ
  state[0] = val;
  *result = val;
}

int main()
{
  int i, result;

  for (i = 0; i < 10; i++) {
    oldrand(&unsafe_state, &result);
    printf("oldrand = %d\n", result);
  }

  return 0;
}

$ gcc oldrand.c -o oldrand
./oldrand
oldrand = 897822358
oldrand = 105331223
oldrand = 617672196
oldrand = 1888665581
oldrand = 1128537634
oldrand = 1434149043
oldrand = 980477552
oldrand = 1927835113
oldrand = 1772747374
oldrand = 1723946255

これが昔のrand関数です。randtblという乱数テーブルを用意していますが、たった一つしか使っていません。
val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
ここで数字をいじっているだけです。

次は現在使われている乱数
myrand.c

#include <stdio.h>
#include <stdlib.h>

/* x**31 + x**3 + 1.  */
#define    TYPE_0        0
#define    TYPE_3        3
#define    BREAK_3        128
#define    DEG_3        31
#define    SEP_3        3

//実はこの乱数テーブルをいじっているだけ ポインタを使って順送りしているだけ。最後まで行ったら最初に戻る
static int32_t randtbl[DEG_3 + 1] =
  {
    TYPE_3,

    -1726662223, 379960547, 1735697613, 1040273694, 1313901226,
    1627687941, -179304937, -2073333483, 1780058412, -1989503057,
    -615974602, 344556628, 939512070, -1249116260, 1507946756,
    -812545463, 154635395, 1388815473, -1926676823, 525320961,
    -1009028674, 968117788, -123449607, 1284210865, 435012392,
    -2017506339, -911064859, -370259173, 1132637927, 1398500161,
    -205601318,
  };

static struct random_data unsafe_state =
  {
    .fptr = &randtbl[SEP_3 + 1],
    .rptr = &randtbl[1],

    .state = &randtbl[1],

    .rand_type = TYPE_3,
    .rand_deg = DEG_3,
    .rand_sep = SEP_3,

    .end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])]
};

void myrand(struct random_data *buf, int *result)
{
  int *state;


      int32_t *fptr = buf->fptr;
      int32_t *rptr = buf->rptr;
      int32_t *end_ptr = buf->end_ptr;
      int32_t val;

      val = *fptr += *rptr;
      /* Chucking least random bit.  */
      *result = (val >> 1) & 0x7fffffff;  //ここで数字をいじる。他はポインタの書き換えでしかない
      ++fptr;
      if (fptr >= end_ptr) {
           fptr = state;
            ++rptr;
        } else {
           ++rptr;
          if (rptr >= end_ptr)
           rptr = state;
        }
      buf->fptr = fptr;
      buf->rptr = rptr;

}

int main()
{
  int i, result;

  for (i = 0; i < 10; i++) {
    myrand(&unsafe_state, &result);
    printf("oldrand = %d\n", result);
  }

  return 0;
}
$ gcc myrand.c -o myrand
$ ./myrand
oldrand = 1804289383
oldrand = 846930886
oldrand = 1681692777
oldrand = 1714636915
oldrand = 1957747793
oldrand = 424238335
oldrand = 719885386
oldrand = 1649760492
oldrand = 596516649
oldrand = 1189641421

難しそうですがやっていることはポインタを使って読み込むrandtblのアドレスを書き換えているだけです。

もちろん、これだけだと毎回同じ乱数が生成されます。

srandは与えられた種を元にrandtblのデータを書き換えているだけです。

それで毎回異なる乱数を生成することができるようになります。

仕組みは恐ろしく簡単です。

それよりも注意が必要なのは関数の実態がわかりづらいことです。

random.cの中でsrandが関連付けられています。

weak_alias (__srandom, srand)  とあります。

srandを呼び出すと__srandomに変換されますよ、という意味。

さらに__srandom_r関数へと変換されて、random_r.cファイルの中で定義されています。

つまりsrand 関数は内部では __srandom_r 関数になります。

仕組みはこんなもんです。この調子でglibcのソースコードを読んでいきましょう。

virtualboxのディスク容量を変更の仕方を説明しています。

virtualboxで仮想OSをインストールした後で、ディスク容量を大きくしたくなることがあります。

仮想ハードディスクのサイズを変更するにはVBoxManageと云うコマンドを打たないといけません。

macの場合、VBoxManageは/Applications/VirtualBox.app/Contents/MacOS/
に入っています。

試しにubuntu-12.04のディスク容量を15GBから倍の30GBにやってみます。
41
まずは確認。設定からストレージを選びます。ディスク容量が15GBであることを確認。
これを30GBにします。vdiファイルのパスを覚えておきます。

$ cd /Applications/VirtualBox.app/Contents/MacOS/

$ VBoxManage modifyhd ~/VirtualBox\ VMs/ubuntu-12.04/ubuntu-12.04.vdi --resize 30720

$ VBoxManage modifyhd vdiのパス --resize MByte
サイズはメガバイトで指定します。30GBの場合、30 * 1024で計算できます。
 18
サイズがちゃんと30GBになっています。成功ですね。
 

virtualboxで共有フォルダの使い方を説明しています。

私の環境ですが、マックにvirtualboxを使ってlinuxをインストールしています。

macとlinuxでフォルダを共有してファイルを受渡しできたら便利ですよね。

さっそくやっていきましょう。

57
まずマックでフォルダを作ります。sharedfolderという名前のフォルダを作成しました。

05
virtualboxを起動し、linuxを選択し、設定ボタンをクリック。

共有フォルダのタブをクリックし、共有フォルダのパスを追加します。

27
フォルダーのパスでその他を選び、先ほど作成したフォルダーを指定します。

マックの設定はこれだけです。
 
次はリナックス側の設定です。

ハマるとしたら唯一ここです。

ユーザーをvirtualboxグループに加えなければいけません。

$ sudo gpasswd -a user vboxsf
userに自分のユーザー名を指定してください。

Screenshot from 2016-01-22 08-46-05
共有フォルダは /media/sf_sharedfolderとなります。
sf_が接頭語として付きます。 

Summoning Warsのゲームをプレイしています。

このページはチョコチョコ更新しています。

レベルは19になりました。3番目の村dwarvenwallにいます。ゴブリンとガイコツさんと戦っています。

本当にdiablo2みたいなゲームです。

ネットで調べても全くクエスト情報がありません。

このゲームやっている人はいるのか?

クエストはだいたい3種類に分類されます。
ボスを倒す。
その辺の敵をすべて倒す。
アイテムを取ってくる。

大抵このどれかです。詰まったときは下を見てください。

以下ネタバレ






最初の村のクエスト
A friend in need 足を怪我した人がいるから助けてあげて。村の北の木のところにいる。周辺のゴブリンを倒すと姿を現す。クリックして話かけると解決。

Maylons Grave お墓の中 ゴブリンからの依頼 ゾンビに追っかけられている。墓の奥にいるボスのガイコツをやっつける。 

2番目の村 Joringsbridge 周辺のクエスト

Herb collector 村の左のフィールドにある木の実を10個取ってくる。村人のGertlinde Kampferに話しかけると解決 
  
goblin_raid ゴブリンがJoringsbridgeを襲おうと計画している。ストーンヘンジにいるボスゴブリンを3匹倒す。 村人Segeant Luttererに話しかけると解決。報酬として3000Gもらえる。

The besieged farm 農夫がゴブリンに襲われて困っている。村の西側にいるゴブリンを全部倒す。村人Sergeant Luttererに話しかけると解決。

The Ambush 2番目の村から北 -> 西 -> 西のフィールドにいるボスを倒す。村人Sergeant Lutterereに話しかけると解決。

wounded soldier 洞窟内の大きい蜘蛛と犬を倒す。洞窟内のぽつーんと一人でいる人に話すと解決。

3番目の村dwarfenwallでは時折岩が頭上に落ちてきます。村を占拠しているガイコツさんの仕業っぽいです。
wonded soldierのクエスト完了後、村の北にいくとガイコツさんと対決することができるようになります。
2000体以上のガイコツさんが出てきますよ。ポーションをたくさん持って倒しましょう。





 

の続きです。

Summoning Warsをプレイしてみました。このゲームやっぱり面白いよ。

スクリーンショットの摂り方がわからず、取れませんでした。

遊び方を解説しておきます。

職業は魔法使いmagician を選択しました。初心者は戦士を選ぶのが無難。

地下牢みたいなとこに閉じ込められている所から始まります。始めに門番2人に話しかけます。
 
いきなりでかいボスみたいなのと戦わされます。訳が分からず、何回かここで死にました。笑

2、3発攻撃したら、場面が変わりました。おそらく武器をちゃんと装備しているかのチェックかと思われます。

長い話を聞かされて、 フィールドに切り替わったらゲーム開始です。


遊び方
まずショートカットキーの使い方を覚えなければいけません。簡単ですが。

q クエスト 未完了、完了 クエストの履歴が見られます。

i インベントリー 装備品、アイテム

T スキルツリー 魔法使いの場合 覚えている魔法ツリー

m マップ マップが見られます。

c キャラクター キャラクターのステータス 強さなど

こんなもん。

体力の回復のさせ方は赤いポーションを使って回復するしかないようです。
いずれ魔法で回復できるようになるかも。
村の右下の道具屋さん30ゴールドで売っています。 

武器や防具
お店がありますが、まったく買う必要がありません。
敵を倒すと、たくさん落とします。
クリックするとインベトリーに追加されます。すぐに一杯になるので武器屋さんに売りにいきましょう。
売ることでお金を稼げます。すぐにお金は溜まりますよ。
武器や防具の中にはレベルが上がらないと装備できないものもあります。
赤い字でレベル何々以上と書いてあるのがそうです。

クエスト
始めの村を出るところにいる門番に話しかけられます。初めてのクエスト。 クエストとはお使いですな。
足を怪我している奴がいるから助けてやってほしい。
マップの中央の木のところにいます。ゴブリンをやっつけると姿を現します。
人に話しかけてクリックするとクエスト完了。
クエストといっても要はクリックするだけです。ディアブロ2も全く一緒だった。

移動は左クリック。

攻撃は左クリックと右クリックの両方に割り当てることができます。

レベルが上がるとステータスを向上させることができます。

また魔法使いの場合には魔法を覚えることができるようになります。
覚えた魔法は左クリックや右クリックをして割り当てましょう。

2時間ほどプレイしてレベルが4になりました。

英語はわからなくても大丈夫。

本当にDiablo2っぽいです。是非遊びましょう。

 

そういえばこのサイトはlinuxブログでしたーー。

お正月ですしリナックスのゲームを紹介したいと思います。

その名もSummoning Warsだ!。

diablo2ようなハックアンドスラッシュ ゲームです。

ディアブロ2も英語が全くわからないままクリアしました。

多分ですけど、武器や防具を手に入ながら、洞窟やフィールドを探検して、遅いかかるモンスターをやっつけていくゲームです。

面白くないわけがない!

まずlinuxのゲームのインストールから。

インストールの仕方が変わっています。

私は最新のubuntu-15.10の環境で実行しています。
Screenshot from 2016-01-08 19-39-54
playdebのサイトに行って Install Playdeb Package のplaydebをクリック。

 
クリックしてopenで実行します。(保存じゃないよ。)
Screenshot from 2016-01-08 19-37-47
ソフトウェアセンターが勝手に開くので、playdebをインストールします。

コマンドでgetdebのインストール
$ wget -q -O - http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add -

$ sudo sh -c 'echo "deb http://archive.getdeb.net/ubuntu wily-getdeb apps" >> /etc/apt/sources.list.d/getdeb.list'

$ sudo apt-get update

$ sudo apt-get install sumwars
sumwarsのインストール

$ sumwars
sumwarsで起動
Screenshot from 2016-01-08 20-22-04
やったー起動できたー。これだけで感動した!

実はですね、このゲームを起動させるだけで1週間以上かかってるんですよ笑。遊んでください。

面白かったらいいですね。私もこれからプレイしてみます。

linuxゲーム Summoning Warsで遊ぼう 超面白いぞ! その2 遊び方を解説しています。

linuxのカスタマイズをしていましたが、辞めました。

linuxのカスタマイズといっても、パッケージをインストールしたり、削除するだけです。

仕組みはなんとなくわかりました。

時間が結構かかるし、面倒くさいだけです。

まともにやっていたら1日潰れます。

普通に公開されているディストリビューションをそのままインストールした方が早いです。笑

今はlubuntuを使っています。w

軽量でさくさく動く。

うん、これで良かった。 

↑このページのトップヘ