私がプログラミングを好きなのは、探求心と創造性を満たしてくれるからです。
このページ、記事を書くとどんどん たまっていくんです。
うまく分割する方法を考えないといけないんだけど、いい方法が見つからないので保留中です。
今年の5月から書くままに書きためている…
~ ChatGPT で英会話を学ぶ ~
「デスクトップ版 ChatGPT」と「Google 翻訳」を使って、誰でも英会話を学べるのでやってみましょう。
私も今までこんなにいっぱい英語をしゃべったことはなく、まったく初めての体験となりました。
ChatGPTってすごいですね。
英会話をやってみるためには以下の手順を行ってください。
※ ChatGPT のアカウント(サインアップ)は必要なので、事前に行う必要があります。
「Download for macOS」か「Download for Windows」のボタンを押します。
ここでは Windows の場合で手順を進めます。
アプリのダウンロードとインストールが開始されます。
下部のアイコンのは、「マイク入力を現在受け付けている」ことを示しています。
このアイコンだと「現在マイク入力できない状態」です。
After you say something in English, could you please translate it into Japanese?
I want to learn English conversation, so please do that.
(英語で話した後、続けて日本語の訳を言っていただけますか? 英会話を学びたいのでお願いします。)あなたはどこから来ましたか? | Where are you from? |
もっとゆっくり話してください。 | Please speak more slowly. |
何か話題を考えてくださいよ。 | Please think of a topic. |
それより、私の話を聞いてほしいのですが? | Instead, I want you to listen to what I have to say. |
(最近感動したことや、やろうとしていることを思い出して話す) | (翻訳に入力します) |
Google 翻訳のスピーカーを参考にして発音したいときは、ChatGPT 画面のマイクボタンを押してマイクをオフにします。そうすれば、練習中の発音を ChatGPT に聞き取られないようにできます。
翻訳のスピーカーは2回目の再生は少しゆっくりとしゃべってくれるようです。
慣れてきたら、翻訳のスピーカーによる発音と「ハモる」ように同時にしゃべってみてください。
★★欲を言えば、ChatGPT が話す英語は、音声を出しつつ英文も画面に表示してくれたらより勉強になると思います。
Windows 11 にて、
字幕エリアはドラッグして位置を変更できます。また4隅のどれか一つのカドをドラッグすればサイズを変更できます。
英語+日本語で回答をもらっているので、「英語(米国)」にしていると日本語の部分がローマ字みたいになるし、英語も必ずしもきちんと表示されるわけではないので、ほんとにおまけみたいなものですが参考にはなるかもしれません。
歯車アイコン>設定>マイクオーディオを含める を利用すれば自分の発音も英語として表示してくれます。これは自分がまともな発音をしているかどうかを確認することになるので、とても有効です。
ChatGPT のマイクをオフにして、正しく認識されるまで何度も同じ発音を繰り返すと良いでしょう。
ただ機械的に棒読みで読み上げ発音するのではなく、気持ちを込めながらしゃべると、より良くなると思います。
上記の手順をすぐに使えるように下記にまとめました。いつでも英会話してみましょう。
駅のニューデイズのかたわらで展開されている農協の野菜売り場で「銀杏」が売られていて良さそうだったので買いました。50 粒くらい入っていて 350 円。
母に食べ方を聞いて、その通りにして食べたら普通にうまかったです。
カラに切り込みを入れて、フライパンで弱火、フタをして10分ころがす。塩を付けて食べる。食べ過ぎると良くないので 1 日 10 粒くらいまで。
「推しの子」再放送、「進撃の巨人」再放送、「呪術廻戦」再放送、、といった感じ。
新作では、「ダンダダン」を観ています。人気アニメばかり観ているので普通と言えますかね。
「65」。6500万年前の地球に不時着した男と少女。宣伝トレーラーは SF で面白そうだったのに、男と少女のやり取りが甘ったるくて苦しかった。舞台設定もいろいろ甘かった。レンタル 300円。
「インターセクション(吹替)」。リュック・ベッソン製作。複数の男女のそれぞれの交差を普通に描いた映画。出来が良くて見せる映画だった。YouTube 期間限定無料公開。8カ月前から無料公開が続いているみたいで、いつまでなのかが不明です。
RPG の試作品3です。スクリーンショットなので動作はしません。
前回の試作品2をボツにして、手順書(「RPGプログラミング手順書」)を使って再び1から作り直しています。
何度も何度も RPG を作り直していると、RPG の根源的なしくみが少しずつ見えてきます。
たとえば、「RPG は "行動" と "結果" を中心にプログラムできる」なんて、ちょっと早合点気味に思ったりしました。
それが本当に早合点なのか、それとも本当に根源的なしくみなのかは、もうちょっと進めないと分かりません。
2024年11月26日追記:
やはり早合点でした。
2024年12月7日追記:
そう考えて戦闘画面のプログラムや、フィールド上のコマンドのプログラムに、直に各コマンド実行を書こうとしましたが、たとえば普通の道具を使うコマンドと、相手を攻撃する道具を使うコマンドを、どのように分けて書くのかが見当がつかず、結局、actionz、resultz を使う方法に戻しました。
その際の各変数にアクセスするスコープの問題については、アクセスしたい変数は全部 env というオブジェクトに収納して、env を各関数に引数で渡すことにしました。
下記はその考えをサンプルで示そうと思って作った JavaScript プログラムです。(行動のことをてふだと呼んでいます)
※ このプログラム中の actionz や resultz というような「末尾に z を使った表記」は、「連想配列は ~z、普通の配列は ~s」という独自の命名規則によるものです。
▼上記プログラムの実行結果
RPG ではいろいろなコマンドを実行し、それに対していろいろな結果があります。
上記 2. 3. のように、異なる行動で、同じ結果が得られる場合があります。
また、薬草などの道具は、フィールドを歩いているときのコマンドメニューで使うことができ、そしてまた、戦闘画面でも使うことができます。
つまり、使った結果が同じならば、異なる行動で結果を共通化させることができ、また、フィールドでも戦闘画面でも同じ「行動と結果」がありえるので異なる画面で共通化させることができます。
さらには、プレイヤーがフィールド上を動き回ることも、一種の行動であり、そこに結果を設定することもありえます。たとえば、
それから、上記のサンプルプログラムでも示していますが、
行動 → 結果1 → 結果2
というように結果が連鎖する場合もあります。たとえば、
「敵を攻撃して、敵はダメージを受ける。さらにその結果 敵が体力0になり倒れる」
という場合です。
RPG は行動だらけのゲームであり、行動だらけゆえに結果も大量にあります。
だからこの行動と結果を基本的なしくみとして とらえることができれば、プログラムはより簡潔になるのではと、期待しています。
下記は実際に「RPG の試作品3」で記述し始めた「行動と結果」のプログラムの一部です。
//--- actionz
actionz = {
"道具を使う" : async function( $command ) {
let darega = $command.darega;
let dougu = $command.dougu;
await narrate( `${darega.name}は${dougu.name}を使った!@k\n` );
await resultz[ "結果ひとり回復" ]( dareni );
let index = darega.dougus.indexOf( dougu );
darega.dougus.splice( index, 1 );
},
"誰かに道具を使う" : async function( $command ) {
let darega = $command.darega;
let dareni = $command.dareni;
let dougu = $command.dougu;
await narrate( `${darega.name}は${dareni.name}に${dougu.name}を使った!@k\n` );
await resultz[ "結果ひとり回復" ]( dareni );
let index = darega.dougus.indexOf( dougu );
darega.dougus.splice( index, 1 );
},
"こうげきする" : async function( $command ) {
let darega = $command.darega;
let dareni = $command.dareni;
await narrate( `${darega.name}は${dareni.name}をこうげきした!@k\n` );
await resultz[ "結果ひとりダメージ" ]( dareni );
},
} // actionz
//--- resultz
resultz = {
"結果ひとり回復" : async function( $dare ) {
let heal = 3 + Math.floor( Math.random() * 10 );
let healStr = han2zen( heal );
await narrate( `${$dare.name}のHPが${healStr}回復した!\n@k` );
$dare.HP += heal;
},
"結果ひとりダメージ" : async function( $dare ) {
let damage = Math.floor( Math.random() * 10 );
let damageStr = han2zen( damage );
await narrate( `${$dare.name}は${damageStr}のダメージを受けた!\n@k` );
$dare.HP -= damage;
//check.
if( $dare.HP <= 0 ) {
$dare.HP = 0;
await narrate( `${$dare.name}は倒れた!@k` );
}
},
} // resultz
このおかげで、戦闘画面のロジックの微妙に長く冗長だった部分が、ばっさりと消えて1行だけになってしまいました。
▼戦闘画面のロジックの微妙に長く冗長だった部分
▼ばっさりと消えて1行だけに
でもまだ使い始めたばかりなのでどうなるかは分かりません。
『簡潔だけど意味が不明』なコードよりは、『冗長だけど意味がよく分かる』コードのほうが良いです。
でも一番良いのは『簡潔で意味も良く分かる』という両立できているコードです。
「行動と結果」の仕組みが本当に良いものかどうかはまだこれからです。
※ 以下は上記「2024年12月7日追記:」で書いたご案内の、「再び actionz、resultz を使うようにした現状のプログラム」です。actionz は actionFunctionz、resultz は resultFunctionz という名前にしています。
色分け Red:文字列、Green:コメント、Cyan:順次・分岐・繰り返し・小プログラム、Orange:キーワード、Yellow:引数
//=== actionFunctionz
let actionFunctionz = {
// #1
"たたかう" : async function( $env, $argz ) {
let actor = $argz.actor;
let target = $argz.target;
await narrate( `${actor.name}は${target.name}をこうげきした!@k\n` );
let argz = {
target : target,
power : Math.floor( Math.random() * 10 ),
}
await resultFunctionz[ "結果一人ダメージ" ] ( $env, argz );
},
// #2
"どうぐ" : async function( $env, $argz ) {
let comName;
if( $env.name == "キャンプ" ) {
// キャンプでは「つかう、わたす、すてる」のどれか。
comName = $argz.dousuru;
}
else if( $env.name == "戦闘画面" ) {
// 戦闘画面ではすぐに「つかう」
comName = "つかう";
}
else {
err( "actionFunctionz['どうぐ']()", "$env.name が不正です.", $env.name, "$env.name や if文の条件を確認." );
}
await $argz.dougu[ comName ] ( $env, $argz );
},
} // actionFunctionz
//=== resultFunctionz
let resultFunctionz = {
// #1
"結果ひとり回復" : async function( $env, $argz ) {
let target = $argz.target;
//check.
if( target.HP <= 0 ) {
await narrate( `${target.name}は死んでいるので回復できない!\n@k` );
return;
}
let heal = $argz.power;
await narrate( `${target.name}のHPが${heal}ポイント回復した!\n@k` );
target.HP += heal;
},
// #2
"結果一人ダメージ" : async function( $env, $argz ) {
let target = $argz.target;
//check. 相手はすでに死んでいる
if( target.HP <= 0 ) {
await narrate( `しかし${target.name}はすでに死んでいる!\n@k` );
return;
}
let damage = $argz.power + ( Math.floor( Math.random() * 6 ) - 3 );
//check. 修正
if( damage < 0 ) damage = 0;
await narrate( `${target.name}は${damage}ポイントのダメージを受けた!\n@k` );
target.HP -= damage;
//check. 倒した
if( target.HP <= 0 ) {
target.HP = 0;
await narrate( `${target.name}は倒れた!@k\n` );
//check. モンスターの場合は
if( $env.monsters.includes( target ) ) {
target.visibility = false;
}
await resultFunctionz[ "全滅チェック" ] ( $env );
}
},
// #3
"結果全員ダメージ" : async function( $env, $argz ) {
for( let target of $argz.targets ) {
let argz = {
target : target,
power : $argz.power,
}
await resultFunctionz[ "結果一人ダメージ" ] ( $env, argz );
}
},
// #4
"全滅チェック" : async function( $env, $argz ) {
if( $env.name == "キャンプ" ) {
if( getAlives( $env.players ).length == 0 ) {
await narrate( `\n@s200${$env.players[ 0 ].name}たちは全滅した...@k` );
}
}
else if( $env.name == "戦闘画面" ) {
if( getAlives( $env.players ).length == 0 ) {
await narrate( `\n@s200${$env.players[ 0 ].name}たちは全滅した...@k` );
$env.endOfBattle = true;
}
else if( getAlives( $env.monsters ).length == 0 ) {
await narrate( `@s10モンスターたちをやっつけた!@k` );
$env.endOfBattle = true;
}
}
else {
err( "resultFunctionz['全滅チェック']()", "$env.name が不正です.", $env.name, "$env.name や if文の条件を確認." );
}
},
} // resultFunctionz