こういう場所で質問するには相当な力量が要るよね

アバター
usao
記事: 1887
登録日時: 11年前

こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

ここがまだ賑やかだった頃にもわりとよく見た気がするけど…

概念的に複数種類の座標系を扱うプログラムを書いているときに
ある値がどの座標系での値なのか?というのを書いてる本人が把握できていないパターン!

に久々に出くわしたぜ.

最終的に描画する際の座標系(いわゆる[pixel]な世界)を除いては,
自分自身が勝手に(都合よく,必要に応じて)作り出した物なハズなのに,その話を自身でわかってないっていう.

そういうレベルでこういう場所で質問してる状況ってのは割と不幸だなあ,とか思う.
他者から「自分自身が勝手に(都合よく,必要に応じて)作り出した仕組み」に基づいた話が成されるとは限らない,という点において.

少なくとも自分で考えてたのとは全く異なる仕組みの話に誘導されてしまわないように強い意志を持つ必要があると思う.
そうじゃないと,何が正しくて何が違うのかがわからない世界に連れていかれてしまうことになるし,
何が正しくて何が違うのかを決める権限すら知らぬ間に持っていかれてしまう.

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

なんてことをぼんやりと思ったりしつつも
別に助けないどころか,どちらかと言えばそういう方向の話を横から入れていくスタイルw

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

タイトルの「こういう場所で質問するには相当な力量が要るよね」における
「相当な力量」というのは
プログラミングの力量というよりは,【誰がどんな話をいつ書いてくるかわからない中でうまいことやる力】というか…かな.
(前者も最低限は必要だろうが)

なんだろうね,話がある程度進んでる段階で私みたいなのが横から別の話を入れ込んできた際に,それをどうさばくか?みたいな.
それまで進んでいた話を平和に続行するためには,邪魔者にはお帰りいただく必要があると思うけど,それを穏便に(?)うまいことやるのは結構骨が折れるんじゃないかなぁ,とか.

逆に,1人しか来てないけどもその人の話だとちょっと違う感…みたいな場合もどうすりゃいいのか,これも厄介そうだぞ.

アバター
もるも
記事: 54
登録日時: 8年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by もるも » 2年前

質問してる方たちはかなりの初心者ですよね、慣れれば回答を持つより自分でググるなり何なりする方が早いですもの。

まぁ、ここは回答者の経験値上げのイメージのほうが強いかなぁ。故に手厳しい(;^ω^)

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

プログラミングの側の力量に関して言えば…

こういうところで質問するとコードが示されることがわりとよくあるけど,
他人のコードを読むのってのは自分で書くよりも難しい作業なわけなので,
「ガチ初心者です!」って状態で質問するといきなり高難度な状況が発生して地獄を見ることになり得る.
(示されたのがコードという形じゃなくても,まぁ状況は似たようなものだろうけど)

やりとりの中でわからん事柄だけがどんどん目の前に積みあがっていくようだと
「自分ひとりでやってた方が遥かにマシだったぜ!」ってことにもなりかねない.

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

int Add3( int arg ); //引数に3を足した値を返す(話を簡単にするため,オーバーフローがどうのいう話は度外視する)

とかいうのが必要になったとする.
この関数内の処理はどうればよいのか?
(何らかの意味で)良い/優れた 方法もあれば,そうでない方法もあるかもしれない.

で,実装に際して
「3回インクリメントすればいいよね!」という方法論を採用したとしよう.
そして実装し,テストしたら,どうやらまともに動かなかったとしよう.
で,コードを確認したらこうなってる↓

CODE:

int Add3( int arg )
{
  int ret = arg;
  ++ret;
  ++ret;
  return ret;
}
なんということでしょう.
「3回インクリメント」のつもりが,2回しかやってないという致命的なバグ!

…っていう話における「正しい修正」とは何か?
それは ++ret; をもう一個追加することで「3回インクリメント」の形とすることだ.
(ret++; でも許されるかもしれない)

ret の初期化のところを

CODE:

int ret = arg+1;
に変更するとか,return のところを

CODE:

return ret+1;
にしちゃうとかいうことではない.もちろん

CODE:

return arg+3;
もちがう.
これらは正しい修正ではない.間違いである.
関数が返す値に関しては正しいかもしれないが,「実装したはずの話になっていない」という点において間違っている.

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

(つづき)

もしもこのバグ(インクリメントが足りてないっていうのが原因だということ)がわからなくてどこぞに質問した際に

CODE:

inline
int Add3( int arg ){  return arg+3;  }
とかいう回答が他者から示されたとして,
これを採用するという行為は,自分が考えていた方法論(アルゴリズム)を投げ捨てるということだ.
それはバグ修正ではなくて,全く別の実装への乗り換え.
「なんで俺のコードは思った通りに動かないんだろう? どうすれば思った形になるのだろう?」っていう問題に関しては何も解決していないよね,っていう.

まぁ,それでも良いというのならば乗り換えても良いだろうが,その際には,乗り換え先の話を理解して→採用するべき.
自身が
  • 「arg+3」のところの「+」っていう記述は何なのか? 意味がわからん
  • 何かついてる「inline」ってのがわからん
  • 上記の説明をなんとなく受けたけども,何故「arg+3」で正しい解が得られるのか?というあたりがまだ掴めない
とかいう状態で「示されたコードはどうやら動くようだからそのままパクればいいや」みたいな,そういうのはもう自分の首をしめてるだけだと思うのだが…
【大元の問題はわからんまま,コードは自身にはさっぱりわからん物に変貌した】とかいう,そういう状態にあえて踏み込む勇者が後を絶たない不思議.
そんなことやってて辛くないのだろうか?
最後に編集したユーザー usao on 2022年3月25日(金) 13:54 [ 編集 2 回目 ]

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

今日からC言語をはじめてみるぜ! っていう段階でいきなり
ものすごく素晴らしいアルゴリズムを誰も文句のつけようがない形のコードで実装できてしまうぞ!
…なんていう天才でもなければ,
最初は
貧相な方法をクソみたいな形で実装することになるのだろうけども,
それがその時点における自身の全力なのであれば,その時点の自身にとっては「貧相」でも「クソ」でもない物であるハズ.
でも,一週間後か半年後にそれを見たら「なにこれ,ふざけてるの?」ってなる.

しっかりと段階的に行った学習,試行錯誤から得られた実感に基づく反省やら教訓やら…

こういう質問掲示板みたいなのには,そういう積み重ねを阻害する効果(初心者であるほど効果が強い副作用)があるような気がする.
私がプログラミングに触れ始めた頃にはこういう場所が(少なくとも自分の目の前には)無かった.とても幸運だったのかもしれない.

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

……とか何とか,久々に質問掲示板を覗いてて思ったりしたんだけど,
そんなのよりも真っ先に思った事:

「せめてもっと人がいる場所で質問すればいいのに!」

過疎丸出しな場所をあえて選ぶ理由がわからん.
とはいえ,他に選択肢がどれだけ存在するんだよ?って言われると,私はよく知らんのだけどね.
以下は勝手なイメージ.

・S〇:英語.
・S〇(JP):過疎地.
・ter〇tail:説教部屋.
・知〇袋:動物園.
・〇iita:ポエムサイト.

うーん,どこもなぁ.

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

話題のスネークゲームとかいうやつの基本的なところを同じ雰囲気のコード(全部外部変数みたいな)で書いてみた.

ただし,私はDXライブラリ使いではないので,

・描画系処理とメインループあたりはOpenCV使う形になってる.
・「乱数で 最小~最大 の値を得る」処理には std::mt19937 を使用.

CODE:

//std::mt19937を用いた乱数処理
#include <random>
class StlRnd
{
public:
	StlRnd() : m_MT( std::random_device().operator()() ){}
	StlRnd( std::mt19937::result_type Seed ) : m_MT( Seed ){}
public:
	template< class T_INT >
	T_INT GetInt( T_INT Min, T_INT Max ){	return std::uniform_int_distribution<T_INT>( Min,Max )( m_MT );	}
private:
	std::mt19937 m_MT;
};

namespace
{
	//----------
	//ゲームロジック用の型とか定数とか
	//----------
	const int FIELD_SIZE = 20;	//フィールドのサイズ(縦と横で同値)
	const int MAX_SNAKE_LENGTH = 8;	//蛇の長さの最大値

	//4方向の単位方向ベクトル
	const int DX[] = { -1, 0, 1, 0 };
	const int DY[] = {  0,-1, 0, 1 };
	enum Direction{ LEFT, UP, RIGHT, DOWN }; //direction : index for DX[],DY[]

	//(ゲーム世界での)位置
	struct Position
	{
		Position( int x=0, int y=0 ) : x{x}, y{y} {}
		bool operator==( const Position &rhs ) const {	return ( x==rhs.x && y==rhs.y );	}
		int x,y;
	};

	//----------
	//ゲームデータ
	//----------
	Position SnakeBody[MAX_SNAKE_LENGTH];	//蛇の体の座標用配列
	int SnakeHeadIndex;	//蛇の頭の位置は SnakeBodx[ SnakeHeadIndex ]
	int SnakeLength;	//蛇の長さ.1だと頭だけな状態.
	int SnakeMoveDir;	//蛇の移動方向( enum Direction の値を使う)
	Position FoodPos;	//餌の位置

	//----------
	//ゲーム処理
	//----------
	//餌をどこか(ただし蛇の頭とは違う場所)に配置する
	void ChangeFoodPos( StlRnd &Rnd )
	{
		do
		{
			FoodPos.x = Rnd.GetInt( 0, FIELD_SIZE-1 );
			FoodPos.y = Rnd.GetInt( 0, FIELD_SIZE-1 );
		}
		while( FoodPos==SnakeBody[SnakeHeadIndex] );
	}

	//ゲーム初期状態生成
	void Initialize( StlRnd &Rnd )
	{
		SnakeHeadIndex = 0;
		SnakeBody[SnakeHeadIndex] = Position( FIELD_SIZE/2, FIELD_SIZE/2 );
		SnakeLength = 1;
		SnakeMoveDir = Rnd.GetInt( 0, 3 );
		ChangeFoodPos( Rnd );
	}

	//位置がフィールド外かどうかをチェック
	inline bool IsOutOfField( const Position &Pos ){	return ( Pos.x<0 || FIELD_SIZE<=Pos.x || Pos.y<0 || FIELD_SIZE<=Pos.y );	}

	//ゲーム状態更新処理
	void Update( int InputedKey, StlRnd &Rnd )
	{
		//押されたキーに応じて蛇の移動方向を変更
		switch( InputedKey )
		{
		case 'a':	SnakeMoveDir = LEFT;	break;
		case 'd':	SnakeMoveDir = RIGHT;	break;
		case 'w':	SnakeMoveDir = UP;	break;
		case 's':	SnakeMoveDir = DOWN;	break;
		default:	break;
		}
		//蛇の頭の移動先座標
		Position DestPos{ SnakeBody[SnakeHeadIndex].x + DX[SnakeMoveDir], SnakeBody[SnakeHeadIndex].y + DY[SnakeMoveDir] };
		if( IsOutOfField(DestPos) )return;	//フィールド外には出ない
		//蛇の更新
		SnakeHeadIndex = (SnakeHeadIndex+1) % MAX_SNAKE_LENGTH;
		SnakeBody[ SnakeHeadIndex ] = DestPos;
		if( DestPos==FoodPos )	//餌を食べる処理
		{
			if( SnakeLength < MAX_SNAKE_LENGTH ){	++SnakeLength;	}	//蛇を長くする
			ChangeFoodPos( Rnd );	//餌を別のどこかに再配置
		}
	}

	//----------
	//描画系
	//----------
	const int CHIP_SIZE = 8;	//表示サイズ用倍率

	//ゲーム座標 → pixel単位の表示用座標
	cv::Point CvtToPixCoord( const Position &Pos )
	{	return cv::Point( Pos.x*CHIP_SIZE + CHIP_SIZE/2, Pos.y*CHIP_SIZE + CHIP_SIZE/2 );	}

	//ゲーム状態の描画
	void Draw( cv::Mat &Canvas )
	{
		//黒で全体をクリア
		Canvas = 0;
		{//蛇を描画
			int index = SnakeHeadIndex;
			for( int i=0; i<SnakeLength; ++i )
			{
				cv::circle( Canvas, CvtToPixCoord( SnakeBody[index] ), CHIP_SIZE/2, cv::Scalar(0,0,255), (i==0 ? -1 : 2) );
				index = ( index>0  ?  index-1  :  MAX_SNAKE_LENGTH-1 );
			}
		}
		//餌を描画
		cv::circle( Canvas, CvtToPixCoord(FoodPos), CHIP_SIZE/2, cv::Scalar(0,255,255), -1 );
	}
}

//main
int main()
{
	StlRnd Rnd;	//乱数生成器
	Initialize( Rnd );	//ゲーム初期化
	//メインループ
	cv::Mat ViewImg( CHIP_SIZE*FIELD_SIZE, CHIP_SIZE*FIELD_SIZE, CV_8UC3 );	//表示用画像バッファ
	while( true )
	{
		Draw( ViewImg );	//ゲーム状態を画像バッファに描画
		cv::imshow( "Test", ViewImg );	//画像バッファの内容をウィンドウに表示
		int key = cv::waitKey( 100 );	//ウェイトとキー入力
		if( key == 'q' )break;	//qキーが押されたら終了
		Update( key, Rnd );	//ゲーム状態更新
	}
	return 0;
}
最後に編集したユーザー usao on 2022年3月25日(金) 20:04 [ 編集 1 回目 ]

アバター
usao
記事: 1887
登録日時: 11年前

Re: こういう場所で質問するには相当な力量が要るよね

投稿記事 by usao » 2年前

(前記コードにはゲームオーバーの判定自体入ってないけども)
スネークゲームって,進行方向をいきなり逆方向に変えることはできない(そんなことしたら即死する)ハズだよなぁ…
ということで,方向転換部分を以下のようにでもすればよいのかな.

触ってみた感じ,蛇の長さが1(頭しかない初期状態)のときには例外的に逆方向を許す方がいいかな感があったので,そんな感じにしてみる.

CODE:

{//押されたキーに応じて蛇の移動方向を変更
	int RequestedDir = SnakeMoveDir;
	switch( InputedKey )
	{
	case 'a':	RequestedDir = LEFT;	break;
	case 'd':	RequestedDir = RIGHT;	break;
	case 'w':	RequestedDir = UP;	break;
	case 's':	RequestedDir = DOWN;	break;
	default:	break;
	}
	//蛇の長さが2以上のときは現在と逆方向へ方向転換できない
	if( SnakeLength<2  ||  ( (RequestedDir ^ SnakeMoveDir)&0x01 ) )
	{	SnakeMoveDir = RequestedDir;	}
}
うーん,これはenumの値に依存したクソコード^^