読者です 読者をやめる 読者になる 読者になる

乱数生成ライブラリ。


「こんなライブラリいいな、出来たらいいな。」と、いつものノリで作ってみた。


以前エントリで自分の方向性を言及して以来、
毎度フルスクラッチか、一部コピペでわざわざ書き出すことが無駄なことだとやっと気付き、
コピペするようなコードなんかはそろそろ、ゲーム作成用の
自前ライブラリとして整備してこうと思い立った訳ですよ。


そういうことで手始めに乱数ライブラリを作成。
先週末頃、あまりゲームと関係ないコード(ラインアート)書いてたんですが
インスタンス生成して無尽蔵にラインの生成が可能になったところで、
標準ライブラリの乱数生成ではちょいと不都合が出てくることが分かって。これが作成の動機。
乱数なんかはゲーム作成時によく使いそうだからと思ってさっさと作ってみたのです。


作成前になぜ作りたいかという動機と、作るに辺り必要な機能とか割り出して、
ベタ書き→単純なモジュール化(srand 同等関数と rand 同等関数まで)→
マルチプルインスタンス化(create と destroy 作成)→デバッグもろもろ→出来上がり
という流れで作成。


マルチプルインスタンス化は出来上がったもの使う分には便利だけど
作る分には1工程増えてしまうという感じ。
でもゲームジャンルによっては直感的な構造で作れそうじゃない?なんて思ったり。
作ってみてから言えって話ですが(汗)。


作成動機
・ちょっとしたことには線形合成法でも別に構わない
・ちゃんとしたものを作るにあたっては乱数偏りを極力避けたい
・リプレイ等の機能を実現する場合、与えるシード値が同じなら同じ値が返って来なくては不都合
・1場面で複数の乱数を生成したいとき、シード値を別々で持ってもらわないと困るときもある


求める機能・機構
インスタンス発行という手段で動的に複数の乱数ジェネレータを生成出来るようにする
・標準ライブラリの乱数ジェネレータの生成する擬似乱数より偏りのない優れた乱数生成を実現する
・偏りの精度を重視しない場面のことも考慮して、線形合成法も使えるようにする


ソースを見て分かる通り何の捻りもない線形合成法と Xorshift による乱数生成ですが。
標準ライブラリのみで構成して NDS 以外でもそのまま使えるように狙ってみたり。
自前ライブラリはそんな感じの方向性でチマチマ継続してこうと思ってます。


Random library test


アーカイブは整理したら公開する予定なので先にソースだけ出しときます。


追記。
アップロードしたー!と思ったら5月(May)を3月(Mar)と書いてたことに気付いた。
面倒なので放置しとく(笑)。


Nitro Developer Style
http://page.freett.com/ntr/


main.c

/*---------------------------------------------------------------------------------
	
	Random library test
	
	version 0.01
	Mar 09, 2010
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <nds.h>
#include "libmy/Random.h"


//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	
	consoleDemoInit();
	printf("Random library test\n\nversion 0.01\nMar 09, 2010\n\nBy REGEKATSU\n\n");
	printf("USAGE:\nKEY_A=LCGS, KEY_B=XORSHIFT\nKEY_START=DESTROY & EXIT\n\n");
	
	Random random1 = Random_Create(GENERATOR_LCGS);
	Random random2 = Random_Create(GENERATOR_XORSHIFT);
	
	Random_SetSeed(random1, 1);
	Random_SetSeed(random2, 1);
	
	while(1){
		
		swiWaitForVBlank();
		scanKeys();
		
		if(keysDown() & KEY_A){
			printf("RANDOM LCGS:%d\n", (int)Random_GetValue(random1));
		}else if(keysDown() & KEY_B){
			printf("RANDOM XORSHIFT:%d\n", (int)Random_GetValue(random2));
		}
		
		if(keysDown() & KEY_START){
			printf("\nRANDOM DESTROY\n");
			random1 = Random_Destroy(random1);
			random2 = Random_Destroy(random2);
			return 0;
		}
		
	}
	
}

//---------------------------------------------------------------------------------


libmy/Random.h

/*---------------------------------------------------------------------------------
	
	Random library header
	
	Mar 09, 2010
	
	By. REGEKATSU
	
---------------------------------------------------------------------------------*/

#ifndef _RANDOM_H_
#define _RANDOM_H_


typedef enum{
	GENERATOR_LCGS = 0, //線形合成法による乱数生成
	GENERATOR_XORSHIFT, //Xorshift による乱数生成
	GENERATOR_TOTAL     //乱数ジェネレータ合計数(ジェネレータタイプとしての指定は不可。)
}GENERATOR_TYPE;


typedef struct _Random *Random;


#ifdef __cplusplus
extern "C" {
#endif


Random Random_Create(int generator_type);
Random Random_Destroy(Random random);
void Random_SetSeed(Random random, unsigned long seed);
unsigned long Random_GetValue(Random random);


#ifdef __cplusplus
}
#endif

#endif	// _RANDOM_H_


libmy/Random.c

/*---------------------------------------------------------------------------------
	
	Random library routine
	
	version 0.01
	Mar 09, 2010
	
	By. REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdlib.h>
#include "Random.h"


typedef struct _Random{
	
	//乱数生成方法(0:線形合成法, 1:Xorshift)
	int generator_type;
	
	//乱数生成関数用1(線形合成法)
	unsigned long next;
	
	//乱数生成関数用2(Xorshift)
	unsigned long vec128[4];
	
}_Random;


//乱数生成関数1(線形合成法)
void random_SetSeed(Random random, unsigned long seed);
unsigned long random_GetVal(Random random);

//乱数生成関数2(Xorshift)
void random_SetSeed128(Random random, unsigned long seed);
unsigned long random_GetVal128(Random random);


//乱数生成方法選択用関数ポインタ
void (*pSetSeed[GENERATOR_TOTAL]) (Random random, unsigned long seed) = {random_SetSeed, random_SetSeed128};
unsigned long (*pGetVal[GENERATOR_TOTAL]) (Random) = {random_GetVal, random_GetVal128};


//---------------------------------------------------------------------------------
Random Random_Create(int generator_type){
//---------------------------------------------------------------------------------
	
	Random random;
	
	random = (Random)malloc(sizeof(_Random));
	
	if(generator_type < GENERATOR_LCGS || generator_type >= GENERATOR_TOTAL){
		random->generator_type = GENERATOR_LCGS;
	}else{
		random->generator_type = generator_type;
	}
	
	random->next = 1;
	random->vec128[0] = 1812433254UL;
	random->vec128[1] = 3713160357UL;
	random->vec128[2] = 3109174145UL;
	random->vec128[3] = 64984499UL;
	
	return random;
	
}

//---------------------------------------------------------------------------------
Random Random_Destroy(Random random){
//---------------------------------------------------------------------------------
	
	if(random == NULL) return NULL;
	
	free(random);
	
	return NULL;
	
}

//---------------------------------------------------------------------------------
void Random_SetSeed(Random random, unsigned long seed){
//---------------------------------------------------------------------------------
	
	if(random == NULL) return;
	
	(*pSetSeed[random->generator_type])(random, seed);
	
}

//---------------------------------------------------------------------------------
unsigned long Random_GetValue(Random random){
//---------------------------------------------------------------------------------
	
	if(random == NULL) return 0;
	
	return (*pGetVal[random->generator_type])(random);
	
}

//---------------------------------------------------------------------------------
void random_SetSeed(Random random, unsigned long seed){
//---------------------------------------------------------------------------------
	
	//乱数生成関数1(線形合成法)
	
	if(random == NULL) return;
	
	//種を設定。
	random->next = seed;
	
}

//---------------------------------------------------------------------------------
unsigned long random_GetVal(Random random){
//---------------------------------------------------------------------------------
	
	//乱数生成関数1(線形合成法)
	
	if(random == NULL) return 0;
	
	//0..32767 の範囲の擬似乱数を返す。
	random->next = random->next * 1103515245 + 12345;
	return (random->next / 65536) % 32768;
	
}

//---------------------------------------------------------------------------------
void random_SetSeed128(Random random, unsigned long seed){
//---------------------------------------------------------------------------------
	
	//乱数生成関数2(Xorshift)
	
	if(random == NULL) return;
	
	random->vec128[0] = 1812433254UL;
	random->vec128[1] = 3713160357UL;
	random->vec128[2] = 3109174145UL;
	random->vec128[3] = 64984499UL;
	
	int i;
	for(i = 1;i <= 4;i++){
		random->vec128[i - 1] = seed = 1812433253UL * ( seed ^ (seed >> 30) ) + i;
	}
	
}

//---------------------------------------------------------------------------------
unsigned long random_GetVal128(Random random){
//---------------------------------------------------------------------------------
	
	//乱数生成関数2(Xorshift)
	
	if(random == NULL) return 0;
	
	unsigned long t, w;
	
	t = random->vec128[0];
	w = random->vec128[3];
	random->vec128[0] = random->vec128[1];
	random->vec128[1] = random->vec128[2];
	random->vec128[2] = w;
	t ^= t << 11;
	t ^= t >> 8;
	w ^= w >> 19;
	w ^= t;
	random->vec128[3] = w;
	
	return w;
	
}

//---------------------------------------------------------------------------------