[C++] 261 SDL : テトリス落下スピード選択 その1 ChatGPT

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

これまで開発してきたテトリスはベーシック版として、多機能版を別に開発することにしました。

まずはテトロミノの落下スピードを調整できるようにします。ボタンを3つレンダリングしています。

ChatGPTにヘッダファイルを見せた上でコードの作成方法を聞いてみました。

Q: <Game.hは省略> 以上のGame.hを踏まえて落下スピードを3段階に設定するにはどうしたらよいですか?

Tetrisの落下スピードを3段階に設定する場合は、Gameクラス内に落下スピードを管理する変数を追加し、GameInput構造体にボタン入力を追加して、それに応じて落下スピードを変更するようにUpdatePlaying関数を修正する必要があります。

以下は、例としてUpdatePlaying関数を修正したコードです。ここでは、bSlow、bNormal、bFastの3つのフラグ変数をGameInput構造体に追加して、それぞれがtrueの場合に対応する落下スピードを設定しています。

void Game::UpdatePlaying(const GameInput& gameInput) {
    if (gameInput.bPause) {
        m_gameState = kGameState_TitleScreen;
        return;
    }

    // 落下スピードの設定
    int framesPerFallStep;
    if (gameInput.bSlow) {
        framesPerFallStep = 48;
    } else if (gameInput.bNormal) {
        framesPerFallStep = 32;
    } else {
        framesPerFallStep = 16;
    }

    // 落下フレーム数の計算
    m_framesUntilFall -= 1;
    if (m_framesUntilFall == 0) {
        m_framesUntilFall = framesPerFallStep;
        m_activeTetromino.m_pos.y += 1;
        if (CheckTetrominoCollision(m_activeTetromino)) {
            // 衝突した場合、テトロミノをフィールドに追加する
            AddTetronimoToField(m_field, m_activeTetromino);
            // 行を消去する
            int numLinesCleared = ClearFullLines(m_field);
            m_numLinesCleared += numLinesCleared;
            m_level = m_numLinesCleared / 10;
            // 新しいテトロミノをスポーンする
            if (!SpawnTetromino()) {
                m_gameState = kGameState_GameOver;
                return;
            }
        }
    }

    // 入力に応じた処理
    if (gameInput.bMoveLeft) {
        if (!CheckTetrominoCollision(m_activeTetromino, -1, 0)) {
            m_activeTetromino.m_pos.x -= 1;
        }
    }
    if (gameInput.bMoveRight) {
        if (!CheckTetrominoCollision(m_activeTetromino, 1, 0)) {
            m_activeTetromino.m_pos.x += 1;
        }
    }
    if (gameInput.bRotateClockwise) {
        if (!CheckTetrominoCollision(m_activeTetromino, 0, 0, 1)) {
            m_activeTetromino.m_rotation = (m_activeTetromino.m_rotation + 1) % Tetromino::kNumRotations;
        }
    }
    if (gameInput.bRotateAnticlockwise) {
        if (!CheckTetrominoCollision(m_activeTetromino, 0, 0, -1)) {
            m_activeTetromino.m_rotation = (m_activeTetromino.m_rotation + Tetrom

[C++] 260 乱数生成方法 randomヘッダ

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

テトリスC++版ではMacOSのstdlib.hにあるrand関数をこれまで使っていましたが、偏りがひどく乱数生成器として用を成さないため、ChatGPTに教えてもらった<random>ヘッダに変更しました。

#include <random>

int TetroTypeGenerator()
{	
	int tetroType;

	if (GeneratorCount == 0){
		arr = {0, 1, 2, 3, 4, 5, 6};
	}

	while(1){
		// 乱数生成器を初期化する
		std::random_device seed_gen;
		std::mt19937 engine(seed_gen());
		
		// 0から6の7つの整数から一つランダムに選択する
		std::uniform_int_distribution<int> dist(0, 6);
		int tetroType = dist(engine);
		// tetroType = rand() % kNumTetrominoTypes; // 偏りがひどいので不採用
		cout << "tetroType生成器: " << tetroType << endl;

		if (arr.size() == 0){
			arr = {0, 1, 2, 3, 4, 5, 6};
			cout << "テトロミノ配列が空になったのでリセットしました" << endl;
		} else if (arr.size() == 1){
			tetroType = arr.front();
			arr = {};

			cout << "tetroType残り1: " << to_string(tetroType) << endl;
			GetScoreAndDate();
			makeRanking();
			GeneratorCount++;
			return tetroType;
		}

		for (auto it = arr.begin(); it != arr.end(); ) {
			if (*it == tetroType) {
				it = arr.erase(it);

				cout << "tetroType: " << to_string(tetroType) << endl;
				GetScoreAndDate();
				makeRanking();

				GeneratorCount++;
				return tetroType;

			} else {
				it++;
			}
		}
	}
}

ChatGPT
Q: MacOSのstdlib.hにあるrand関数の生成する乱数が偏っています。なぜですか?

MacOSのstdlib.hにあるrand関数は、線形合同法に基づいた乱数生成アルゴリズムを使用しています。このアルゴリズムは、周期が短く、生成される乱数の分布が均等ではないため、偏った乱数が生成される可能性があります。

具体的には、rand関数は0からRAND_MAXまでの範囲の整数を生成しますが、生成される乱数の分布が均等でないため、特定の値がより頻繁に生成される傾向があります。また、線形合同法による乱数生成アルゴリズムは、初期値に依存するため、同じ初期値を使用する場合は、同じ乱数の系列が生成されます。

そのため、rand関数が生成する乱数は、科学的なシミュレーションや暗号学的な目的などのように、高品質な乱数が必要な場合には適していません。代わりに、乱数生成器として、より高品質なアルゴリズムを使用することが推奨されます。たとえば、C++11以降では、<random>ヘッダーに、様々な種類の乱数生成器が用意されています。

[C++] 259 SDL : 暫定順位をカラー化 Tetris

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, ChatGPT Plus, NO IDE]

テトリスC++版のプレイ中に暫定順位を表示するのに合わせて、ランクインしている場合はカラー化してリアルタイムで表示するようにしました。

for文を使ってレンダリングする場合は、バイト数が一致するようにしないと表示がおかしくなります。今回は6〜9位と10位(1バイト多い)を別扱いにしています。

これで基本的な機能はほぼ実装したでしょうか。

void Game::DrawPlaying(Renderer& renderer)
{
<中略>

for (int i = 0 ; i < 5 ; i++){
		snprintf(text, sizeof(text), "High score %d: %s %s", i+1, to_string(scores[i]).c_str(), dates[i].c_str());
		if (fullDates[i] != startTime){
			renderer.DrawText(text, 20, 170 + 40*i, mFont, 0xffffffff );
		} else {
			renderer.DrawText( text, 20, 170 + 40*i, mFont, 0xFF4500ff );
		}
}

for (int i = 5 ; i < 9 ; i++){
		snprintf(text, sizeof(text), "High score  %d: %s %s", i+1, to_string(scores[i]).c_str(), dates[i].c_str());
		if (fullDates[i] != startTime){
			renderer.DrawText(text, 100, 370 + 30*(i-5), mFont2, 0xffffffff );
		} else {
			renderer.DrawText(text, 100, 370 + 30*(i-5), mFont2, 0xFF4500ff );
		}
}

snprintf(text, sizeof(text), "High score 10: %s %s", to_string(scores[9]).c_str(), dates[9].c_str());
if (fullDates[9] != startTime){
		renderer.DrawText(text, 100, 370 + 30*4, mFont2, 0xffffffff );
} else {
		renderer.DrawText(text, 100, 370 + 30*4, mFont2, 0xFF4500ff );
}

<以下略>