バイオリズム。2


この前のバイオリズムに年月変更操作を追記しました。
誕生日は変わらずマジックナンバーです。
題材とするアルゴリズムに含まれない部分は敢えて書いてません。


NDS の方だけは本体設定の誕生日から取得しようかと思ったのですが
元々誕生年が設定できない仕様な為、実装は止めました(月日だけじゃバイオリズムは割り出せないので。)。


本体設定の表示方法とかはそのうち書いておきます。
ヘッダ見る限りでは ARM9 から拾える感じでした。


但しメッセージとかニックネームの 2 バイト文字は UTF-16 だったりするので
この辺になると日本語ライブラリ作ってそれを使った説明とかになると思います。
結構前の Shift-JIS 決め打ちな作りかけが…。


devkitPro - NDS


・ビルド手順

1. 下記ソースを *.c ファイルに保存。

2. /* コンパイラ_ターゲットの選択設定 */ の部分にある
#define	_VC2010_WIN_ をコメントアウトして、
#define	_DEVKITPRO_NDS_ のコメントアウトを取り除く。

3. devkitPro/examples/nds/templates/arm9 等のテンプレートプロジェクトを流用して
./source/main.c を *.c と差し替える。

4. Makefile のリンクオプションに "-lm" を追記してビルド。
LIBS	:= 	-lnds9 -lm


Visual C++ 2010 - Win32 コンソール版


・ビルド手順

1. 下記ソースを *.c ファイルに保存。

2. /* コンパイラ_ターゲットの選択設定 */ の部分にある
#define	_DEVKITPRO_NDS_ をコメントアウトして、
#define	_VC2010_WIN_ のコメントアウトを取り除く。

3. ファイル-新規作成-プロジェクト-Win32 コンソール アプリケーション で空のプロジェクト作って
ソース ファイル-追加-既存の項目 で *.c を追加してビルド。


main.c

/*---------------------------------------------------------------------------------
	
	Biorhythm
	
	version 0.011
	Dec 11, 2011
	
	By REGEKATU
	
---------------------------------------------------------------------------------*/

/* コンパイラ_ターゲットの選択設定 */
//#define	_VC2010_WIN_
#define	_DEVKITPRO_NDS_


#ifdef	_VC2010_WIN_
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include <stdio.h>
#include <time.h>

#ifdef	_VC2010_WIN_
#include <Windows.h>
#endif
#ifdef	_DEVKITPRO_NDS_
#include <nds.h>
#endif


/* ブール型定義 */
#ifndef bool
#define bool unsigned char
#endif

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif

/* コンソール定数 */
#ifdef	_VC2010_WIN_
#define CONSOLE_WIDTH  80															/* 表示環境の横表示可能文字数*/
#define CONSOLE_HEIGHT 25															/* 表示環境の縦表示可能文字数*/
#endif
#ifdef	_DEVKITPRO_NDS_
#define CONSOLE_WIDTH  32															/* 表示環境の横表示可能文字数*/
#define CONSOLE_HEIGHT 24															/* 表示環境の縦表示可能文字数*/
#endif

typedef enum{    /* setColor 関数用色定義 */
	L_BLACK = 0, /* \x1b[30m 低輝度・黒色 */
	L_BLUE,      /* \x1b[34m 低輝度・青色 */
	L_GREEN,     /* \x1b[32m 低輝度・緑色 */
	L_CYAN,      /* \x1b[36m 低輝度・水色 */
	L_RED,       /* \x1b[31m 低輝度・赤色 */
	L_PURPLE,    /* \x1b[35m 低輝度・紫色 */
	L_YELLOW,    /* \x1b[33m 低輝度・黄色 */
	L_WHITE,     /* \x1b[37m 低輝度・白色 */
	
	H_BLACK,     /* \x1b[40m 高輝度・黒色 */
	H_BLUE,      /* \x1b[44m 高輝度・青色 */
	H_GREEN,     /* \x1b[42m 高輝度・緑色 */
	H_CYAN,      /* \x1b[46m 高輝度・水色 */
	H_RED,       /* \x1b[41m 高輝度・赤色 */
	H_PURPLE,    /* \x1b[45m 高輝度・紫色 */
	H_YELLOW,    /* \x1b[43m 高輝度・黄色 */
	H_WHITE      /* \x1b[47m 高輝度・白色 */
}COLOR;

/* バイオリズム定数 */
typedef enum{                 /* 生命の周期 */
	CYCLE_PHYSICAL      = 23, /* 身体の周期 */
	CYCLE_SENSITIVITY   = 28, /* 感情の周期 */
	CYCLE_INTELLECUTUAL = 33  /* 知性の周期 */
}CYCLE;

#define CURVE_TO_POS(c) ((int)(c * 5.3) * -1 + 5)									/* サインカーブ値を文字表示の位置情報に変換する */

typedef enum{ /* 状態遷移定数 */
	INIT = 0, /* プログラム初期化 */
	DRAW,     /* バイオリズム表示 */
	READY     /* 表示年月変更待ち */
}ACT;

/* コンソール変数 */
#ifdef	_VC2010_WIN_
HANDLE hOut;																		/* コンソールスクリーンバッファのハンドル */
COORD dwPos;																		/* コンソールスクリーンカーソル指定用構造体 */
CONSOLE_SCREEN_BUFFER_INFO csbi;													/* コンソールスクリーンバッファ情報の構造体 */
DWORD written;																		/* 書き込まれたセル数へのポインタ */
#endif


/* バイオリズム変数 */
int act;																			/* 状態遷移変数 */

int birth_year;																		/* 誕生年 */
int birth_month;																	/* 誕生月 */
int birth_day;																		/* 誕生日 */

int now_year;																		/* 現在表示年 */
int now_month;																		/* 現在表示月 */

int old_year;																		/* 前回表示年 */
int	old_month;																		/* 前回表示月 */


/* メイン関数 */
int main(void);																		/* メインプログラム */

/* バイオリズム関数 */
void drawBiorhythm(int birth_year, int birth_month, int birth_day, 					/* 指定した誕生日に対する指定年月の、バイオリズムを表示する */
					int year, int month);
double getBioCurve(int birth_year, int birth_month, int birth_day, 					/* 指定した誕生日に対する指定日の、指定周期に対するサインカーブ値を返す */
					int year, int month, int day, int cycle);

/* 日付関数 */
int getDateDiff(int year1, int month1, int day1, int year2, int month2, int day2);	/* 指定された2つの年月日の差を日数で返す */
int getDayOfYear(int year, int month, int day);										/* 指定された年月日の年間積算日を返す */
int getDaysInYear(int year);														/* 指定した年の日数を返す */
int getDaysInMonth(int year, int month);											/* 指定年の、指定した月の日数を返す */
bool isLeapYear(int year);															/* 指定した年が閏年かどうかを確認する */
void addYears(int year1, int* year2, int years);									/* 指定した年から指定した年数が経過した後の年を返す */
void addMonths(int year1, int month1, int* year2, int* month2, int months);			/* 指定した年月から指定した月数が経過した後の年月を返す */

/* コンソール関数 */
void setLocate(int x, int y);														/* 文字の表示位置を設定する */
void setColor(int fore, int back);													/* 文字の表示色、背景色を設定する */
void clearScreen(void);																/* コンソール画面を消去する */


//---------------------------------------------------------------------------------
int main(void){
//---------------------------------------------------------------------------------
	
	/* メインプログラム */
	
	time_t timer;
	struct tm tm;
	
#ifdef	_DEVKITPRO_NDS_
	/* キーリピートを設定する */
	unsigned long keys;
	keysSetRepeat(30, 1);
#endif

	/* コンソールを初期化する */
#ifdef	_VC2010_WIN_
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
#ifdef	_DEVKITPRO_NDS_
	consoleDemoInit();
#endif
	
	/* メインループ */
	while(1){
		
		/* ゲームを一定間隔で更新する */
#ifdef	_VC2010_WIN_
		Sleep(16);
#endif
#ifdef	_DEVKITPRO_NDS_
		swiWaitForVBlank();
#endif

#ifdef _DEVKITPRO_NDS_
		/* キー情報を更新する */
		scanKeys();
		keys = keysDownRepeat();
#endif

		switch(act){
			
		/* プログラム初期化 */
		case INIT:
			
			/* 誕生日を設定する */
			birth_year  = 2000;
			birth_month = 9;
			birth_day   = 13;
			
			/* 現在の日時を取得する */
			timer = time(NULL);
#ifdef _VC2010_WIN_
			localtime_s(&tm, (const time_t *)&timer);
#else
			tm = *localtime((const time_t *)&timer);
#endif
			
			/* 表示する年月を設定する */
			old_year  = now_year  = tm.tm_year + 1900;
			old_month = now_month = tm.tm_mon + 1;
			
			act = DRAW;
			break;
			
		/* バイオリズム表示 */
		case DRAW:
			
			/* バイオリズムを表示する */
			drawBiorhythm(birth_year, birth_month, birth_day, now_year, now_month);
			act = READY;
			break;
			
		/* 表示年月変更待ち */
		case READY:
			
			/* 先月に進める */
#ifdef _VC2010_WIN_
			if(GetAsyncKeyState(VK_LEFT) == 0xffff8001){
#endif
#ifdef _DEVKITPRO_NDS_
			if(keys & KEY_LEFT){
#endif
				old_year  = now_year;
				old_month = now_month;
				addMonths(old_year, old_month, &now_year, &now_month, -1);
				act = DRAW;
			}
			
			/* 次月に進める */
#ifdef _VC2010_WIN_
			if(GetAsyncKeyState(VK_RIGHT) == 0xffff8001){
#endif
#ifdef _DEVKITPRO_NDS_
			if(keys & KEY_RIGHT){
#endif
				old_year  = now_year;
				old_month = now_month;
				addMonths(old_year, old_month, &now_year, &now_month, 1);
				act = DRAW;
			}
			
			break;
			
		}
		
	}
	
}

//---------------------------------------------------------------------------------
void drawBiorhythm(int birth_year, int birth_month, int birth_day, int year, int month){
//---------------------------------------------------------------------------------
	
	/* 指定した誕生日に対する指定年月の、バイオリズムを表示する */
	
	int i, temp_year, temp_month;
	double curve;
	
	/* 画面を消去して、文字色を白に設定する */
	clearScreen();
	setColor(H_WHITE, L_BLACK);
	
	/* タイトル、バージョン、コピーライトを表示する */
	setLocate(1, 0);
	printf("Biorhythm ver 0.01 By REGEKATU");
	
	/* 誕生日を表示する */
	setLocate(1, 2);
	printf("your birthday       %4d/%2d/%2d", birth_year, birth_month, birth_day);
	
	/* 描画年月を表示する */
	setLocate(1, 4);
	printf("draw month & years  %4d/%2d", year, month);
	
	/* 日付を表示する */
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		if(i % 10 == 0){
			setLocate(i, 6);
			printf("%d", i / 10);
		}
	}
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		setLocate(i, 7);
		printf("%d", i % 10);
	}
	
	/* 罫線を表示する */
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		setLocate(i, 13);
		printf("-");
	}
	for(i = 0;i <= 10;i++){
		setLocate(0, i + 8);
		if(i == 0) printf("+");
		else if(i == 5) printf("0");
		else if(i == 10) printf("-");
		else             printf("|");
	}
	
	/* 身体の周期を赤で表示する */
	setColor(H_RED, L_BLACK);
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		curve = getBioCurve(birth_year, birth_month, birth_day, year, month, i, CYCLE_PHYSICAL);
		setLocate(i, CURVE_TO_POS(curve) + 8);
		printf("P");
	}
	
	/* 感情の周期を緑で表示する */
	setColor(H_GREEN, L_BLACK);
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		curve = getBioCurve(birth_year, birth_month, birth_day, year, month, i, CYCLE_SENSITIVITY);
		setLocate(i, CURVE_TO_POS(curve) + 8);
		printf("S");
	}
	
	/* 知性の周期を青で表示する */
	setColor(H_BLUE, L_BLACK);
	for(i = 1;i <= getDaysInMonth(year, month);i++){
		curve = getBioCurve(birth_year, birth_month, birth_day, year, month, i, CYCLE_INTELLECUTUAL);
		setLocate(i, CURVE_TO_POS(curve) + 8);
		printf("I");
	}
	
	/* 先月と次月のプロンプトを表示する */
	setColor(H_WHITE, L_BLACK);
	setLocate(1, 22);
	addMonths(year, month, &temp_year, &temp_month, -1);
	printf("<<%4d/%2d", temp_year, temp_month);
	setLocate(22, 22);
	addMonths(year, month, &temp_year, &temp_month, 1);
	printf("%4d/%2d>>", temp_year, temp_month);
	
}

//---------------------------------------------------------------------------------
double getBioCurve(int birth_year, int birth_month, int birth_day, int year, int month, int day, int cycle){
//---------------------------------------------------------------------------------
	
	/* 指定した誕生日に対する指定日の、指定周期に対するサインカーブ値を返す */
	
	return sin( (double)(getDateDiff(birth_year, birth_month, birth_day, year, month, day) % cycle) / cycle * 2 * M_PI );
	
}

//---------------------------------------------------------------------------------
int getDateDiff(int year1, int month1, int day1, int year2, int month2, int day2){
//---------------------------------------------------------------------------------
	
	/* 指定された2つの年月日の差を日数で返す */
	
	int days, i;
	
	/* 指定年1が指定年2より以前だった場合 */
	if(year1 < year2){
		
		/* 指定年月日1から、指定年1の末日までの日数を計算する */
		days = getDaysInYear(year1);
		days -= getDayOfYear(year1, month1, day1);
		
		/* 指定年1の翌年から、指定年2の前年までの日数を合計する */
		for(i = year1 + 1;i <= year2 - 1;i++)
			days += getDaysInYear(year1);
		
		/* 指定年2の指定月日2までの年間積算日を合計する */
		days += getDayOfYear(year2, month2, day2);
		
	/* 指定年1が指定年2以上だった場合 */
	}else{
		
		/* 指定月日2から指定月日1間の日数を計算する */
		days = getDayOfYear(year2, month2, day2) - getDayOfYear(year1, month1, day1);
		
	}
	
	/* 計算した日数を「数え」で返す */
	return days + 1;
	
}

//---------------------------------------------------------------------------------
int getDayOfYear(int year, int month, int day){
//---------------------------------------------------------------------------------
	
	/* 指定された年月日の年間積算日を返す */
	
	int days, i;
	
	/* 年始月から指定した月の前月までの日数を集計する */
	days = 0;
	for(i = 1;i <= month - 1;i++)
		days += getDaysInMonth(year, i);
	
	/* 前月までの日数と今月の日数を合計した値を返す */
	return days + day;
	
}

//---------------------------------------------------------------------------------
int getDaysInYear(int year){
//---------------------------------------------------------------------------------
	
	/* 指定した年の日数を返す */
	
	return (isLeapYear(year)) ? 366 : 365;
	
}

//---------------------------------------------------------------------------------
int getDaysInMonth(int year, int month){
//---------------------------------------------------------------------------------
	
	/* 指定年の、指定した月の日数を返す */
	
	/* 各月の日数を設定する */
	int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	
	/* 指定した年が閏年なら2月の日数をを29日に変更する */
	if(isLeapYear(year)) mon[2] = 29;
	
	return mon[month];
	
}

//---------------------------------------------------------------------------------
bool isLeapYear(int year){
//---------------------------------------------------------------------------------
	
	/* 指定した年が閏年かどうかを確認する */
	
	bool ret = false;
	
	/* 西暦年が4で割り切れる年は閏年である */
	if(year % 4 == 0) ret = true;
	
	/* 但し、西暦年が100で割り切れる場合は閏年ではない */
	if(year % 100 == 0) ret = false;
	
	/* 但し、西暦年が400で割り切れる場合は閏年である */
	if(year % 400 == 0) ret = true;
	
	return ret;
	
}

//---------------------------------------------------------------------------------
void addYears(int year1, int* year2, int years){
//---------------------------------------------------------------------------------
	
	/* 指定した年から指定した年数が経過した後の年を返す */
	
	*year2 = year1 + years;

}

//---------------------------------------------------------------------------------
void addMonths(int year1, int month1, int* year2, int* month2, int months){
//---------------------------------------------------------------------------------
	
	/* 指定した年月から指定した月数が経過した後の年月を返す */
	
	int ty1, tm1, ty2, tm2;
	
	/* 指定月数から年数を計算する */
	ty1 = months / 12;
	
	/* 指定月数から月数を計算する */
	tm1 = months % 12;
	
	/* 指定月数から計算した年数、月数を指定年月に加算する */
	ty2 = year1  + ty1;
	tm2 = month1 + tm1;
	
	/* 月の繰り下がり、繰り上がりがあったら加算年月を補正する */
	if(tm2 < 1){
		tm2 += 12;
		ty2 += -1;
	}else if(tm2 > 12){
		tm2 += -12;
		ty2 += 1;
	}
	
	/* 計算した結果年月を返す */
	*year2  = ty2;
	*month2 = tm2;
	
}

//---------------------------------------------------------------------------------
void setLocate(int x, int y){
//---------------------------------------------------------------------------------
	
	/* 文字の表示位置を設定する */
	
#ifdef	_VC2010_WIN_
	dwPos.X = (short)x;
	dwPos.Y = (short)y;
	SetConsoleCursorPosition(hOut, dwPos);
#else
	printf("\x01b[%d;%dH", y, x);
#endif
	
}

//---------------------------------------------------------------------------------
void setColor(int fore, int back) {
//---------------------------------------------------------------------------------
	
	/* 文字の表示色、背景色を設定する */
	
	/* 下位 4 ビットを色番号として使用する */
	fore &= 0x0f;
	back &= 0x0f;
	
#ifdef	_VC2010_WIN_
	SetConsoleTextAttribute( hOut, (WORD)fore + ((WORD)back << 4) );
#else
	static const int font_color[] = {
		30, 34, 32, 36, 31, 35, 33, 37, 
		40, 44, 42, 46, 41, 45, 43, 47
	};
	
	static const int back_color[] = {
		RGB15( 0,  0,  0), 	//30 normal black
		RGB15( 0,  0, 15), 	//34 normal blue
		RGB15( 0, 15,  0), 	//32 normal green
		RGB15( 0, 15, 15), 	//36 normal cyan
		RGB15(15,  0,  0), 	//31 normal red
		RGB15(15,  0, 15), 	//35 normal magenta
		RGB15(15, 15,  0), 	//33 normal yellow
		RGB15(24, 24, 24), 	//37 normal white
		
		RGB15(15, 15, 15), 	//40 bright black
		RGB15( 0,  0, 31), 	//44 bright blue
		RGB15( 0, 31,  0), 	//42 bright green
		RGB15( 0, 31, 31), 	//46 bright cyan
		RGB15(31,  0,  0), 	//41 bright red
		RGB15(31,  0, 31), 	//45 bright magenta
		RGB15(31, 31,  0), 	//43 bright yellow
		RGB15(31, 31, 31)	//47 & 39 bright white
	};
	
	printf("\x1b[%dm", font_color[fore]);
	BG_PALETTE_SUB[0] = back_color[back];
#endif
	
}

//---------------------------------------------------------------------------------
void clearScreen(void){
//---------------------------------------------------------------------------------
	
	/* コンソール画面を消去する */
	
#ifdef	_VC2010_WIN_
	dwPos.X = 0;
	dwPos.Y = 0;
	if(GetConsoleScreenBufferInfo(hOut, &csbi))
		FillConsoleOutputCharacter(hOut, ' ', 
			csbi.dwSize.X * csbi.dwSize.Y, dwPos, &written);
#else
	printf("\x1b[2J");
#endif
	
}

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