2025年4月11日金曜日

アロー関数(無名関数)&三項演算子を学んだ話

 結構雑に無名関数は使ってたんだけどまだ知識がレガシーだったので備忘

一旦コード書きますね。

const isInput = bo.tagName.toLowerCase() === 'input';
const getText = () => isInput ? bo.value : bo.textContent;
const setText = (text) => isInput ? (bo.value = text) : (bo.textContent = text);

最近のコードがこれです。

そもそも無名関数って何?アロー関数って何?

「そもそも無名関数って何?」という人もいると思いますが、これはなんてことはない、名前を付けない関数のことです。
アロー関数は無名関数の一部です。ほぼニアリーイコールです。

通常の関数は 「function 関数名(引数) { 処理; return 戻り値; }」 という形です。そして、定義した関数を 「変数 = 関数名(引数)」 のように呼び出して使います。

たとえば:

function aaa(x) {
    let b = x + 1;
    return b;
}
console.log(aaa(10)); // 11


さて、無名関数の説明をする前に1つだけ補足すると、「関数は変数に代入することができます」:

変数 = function 関数名(引数) { 処理; return 戻り値; };

たとえば、こんな風に関数を定義して変数に代入できます:

const bbb = function aaa(x) {
    let b = x + 1;
    return b;
};
console.log(bbb(10)); // 11


変数 bbb に代入したことで、bbb は関数として呼び出すことができます。つまり:

変数 = function 関数名(引数) { 処理; return 戻り値; };

というのは、変数に「関数」が入ってるということです。以降、代入先を「関数」と書きますが、実際は const 変数名 = ... の形で使います。

そして、関数を代入するとき、定義した関数の名前(ここでは aaa)が不要なら、いっそ名前を省略しようというのが無名関数の第一段階目です:

関数 = function(引数) { 処理; return 戻り値; };

たとえば:
const bbb = function(x) {
    let b = x + 1;
    return b;
};
console.log(bbb(10)); // 11


関数名を省略して、直接 function(x) と書く形です。これで、名前をつけない関数、つまり無名関数が作れます。

ちなみに、このように関数内で別の関数を定義することもできます。

たとえば:

function outer() {
    const inner = function(x) {
        let b = x + 1;
        return b;
    };
    return inner(10);
}
console.log(outer()); // 11

ここでは inner が outer の中で定義されていて、外からは見えません。

第二段階として、今度は function を省略します。代わりにアロー(=>)を使って、アロー右側が関数の処理であることを明示します:

関数 = (引数) => { 処理; return 戻り値; };

function がなくなるだけで、かなりスッキリしますね。

たとえば:

const bbb = (x) => {
    let b = x + 1;
    return b;
};
console.log(bbb(10)); // 11


ここからは、1行で書く場合に限りますが、さらに短く記述することが可能です。第三段階は:

関数 = (引数) => 処理

たとえば:

const aaa = (x) => x + 1;
console.log(aaa(10)); // 11


通常、処理結果をいったん変数に入れて、それを戻り値として返します。でも、1行の場合は『処理結果がそのまま戻り値になる』と分かるので、return を省略できます。また、1行だけなら『=> の後が戻り値の式』と確定するので、{} も外せます。

{} を外すと、1つの式しか書けなくなるため、return を書くとエラーになります。つまり、『処理結果が直接戻り値になる』ので、return は不要となります。

無名関数の話をまとめると・・・

⓪function aaa(x) {let b = x + 1; return b;}
①const bbb = function aaa(x) { let b = x + 1; return b; };
②const bbb = function (x)  { let b = x + 1; return b; };
③const bbb = (x) => { let b = x + 1; return b; };
④const aaa = (x) => x + 1;

要点としては
①関数は変数に代入できる
②関数名が不要になる
③アロー関数で functionも省略 できる
④1行限定で括弧とreturnも省略できる

三項演算子

【条件 ? 真の場合 : 偽の場合】
簡単に言えば「if、else」文の短縮記述ですね。
感覚としてはエクセル関数のifの記述に似ています。

最初に記述した「isInput ? (bo.value = text) : (bo.textContent = text);」はisInput がtrueかfalseになります。
これによって真ならコロンの左側が、偽なら右側の代入処理が行われます。

もう一つ「const getText = () => isInput ? bo.value : bo.textContent;」
こちらはreturnがあります。アロー関数を使って三項演算子を使った場合、上記なら真の場合、インプットボタンのバリューが返り、偽ならボタンのテキストが返ります。

アロー関数の一行記述の場合、直接returnが返ってしまいますが簡単な計算や簡単な分岐returnぐらいなら可能という事です。

2025年4月7日月曜日

node.jsのmjs(esm)とcjsってなーに?

mjs(esm)とcjsの違い

処理速度が爆速なので重たい処理をスクリプトで作るならnode.jsだなって事をこの前記事にしたんですけど、自分のコードを変換して貰った時に「{"type": "commonjs"}」って内容の「package.json」を作ってねって言われたんですよ。

色々調べた結果node.jsは現在二つに分岐していて、esmとcjsの二つの記述方法があるという事が分かりました。

cjs

まずcjsは「CommonJS」の略で、要するに一般的なjavascriptの記述方法です。

mjs

そして、esmはECMAScriptModules(エクマスクリプトモジュール)の略で「Ecmaインターナショナル」といものがあり、それによって標準化されたJavaScriptの国際規格という事らしい。

このECMAは大元をたどると「欧州電子計算機工業会(英語: European Computer Manufacturers Association)」の略だったが今は関係ないただの単語としてだけ文字が残って「Ecma」みたいな記述で良いらしい。

要するにEcmaという国際規格にしたjavascriptって事です

2024年今現在のnodeの仕様

v12以前「.jsのデフォルトはCommonJSだった時代」
v14以降「.cjsやpackage.jsonでの明示が必要になった」
v22以降「.jsのデフォルトがESMになった」←今ここ

というわけで、一旦最初の疑問「json」って何?って所はおおよそ理解しました。
そしてnodeが規格を判定する時に最初に判定するのは拡張子らしいので仮にどっちがデフォルトであっても拡張子をcjsにするか、mjsとすれば確実のようです。
一括でファイルのタイプを管理するときは「json」ファイルを作ればOK!

javascriptって今どうなってるの?

cjsは古い記述方法と言ってしまえばそれまでですが、過去のjavascriptと現在のjavascriptを結ぶ規格。
一言で言えば、web記述ベースのnode特化コード。
esmはwebとnodeを共通化しようという未来規格です。nodeがこちらをデフォルトにしたのはその意識が強いという事でしょう。
今の所cjsの記述されたパッケージなども多いですが今後はesmが主流になるのかもしれないです。
つまり、esm「ブラウザネイティブ」かcjs「Node.js特化」かの違い

まとめ
ESM: ブラウザ+Node.jsの未来標準、非同期・静的解析が強み。
CJS: Node.jsの歴史的規格、同期的でサーバーサイドに強い。
ESMがモダン、CJSがレガシー。

今からjavascriptを勉強するならesmベースで勉強しつつ、cjsに対応できると良いという感じ。

2025年4月6日日曜日

LEFT ALIVE(レフトアライブ)を遊んでみた

 まず、デフォルト難易度が難しすぎる
「STANDARD:スタンダード」でとにかく死にまくる
購入前にどんなゲームかPS公式の『LEFT ALIVE』 ストーリートレーラーも見て

確認した上で面白そうと思ったんですが、想像以上の難しさ。
フロントミッションやった事ないけど面白そうだなと思って購入しましたが開始1時間くらいでかなりそういうゲームじゃないという洗礼を受けます。
ぱっと見ステルスゲームに見えますしジャンルとしてはまぁ間違いでもないんでしょうけどサバイバルゲーム・・・いや、「死にゲー」です!!

ちなみに人によっては気にすると思うので言語対応は「日本語は字幕のみ」です。
言語パックとかはありません。ちょっとよそ見をすると内容が抜けちゃうのでムービーもちゃんと見ないといけません。

■デフォルトの難易度が高すぎる

難易度は5種類
・CASUAL:カジュアル:お手軽
・LIGHT:ライト:簡単
・STANDARD:スタンダード:標準
・ADVANCED:アドバンスト:上級
・HOPELESS:ホープレス:絶望

もしこれからやるのであれば絶対にカジュアルで遊ぶのをオススメします
このゲーム標準という意味は「地獄を見る覚悟がある」です。
普通のプレイヤーならスタンダードを選ぶと思いますが敵は全員超固いです。
ヘッドショットですら4・5発必要になります。
敵の射撃精度もかなり高いです。発見されてる状態なら少し横切った僅かな時間でも射貫いて来ます。
殆どの場合は敵同士が複数でカバーしており単体の敵を削って道を開けるのも難しい。
しかも全体的に敵が多く、ヴァンツァーというロボの射程に入ると地獄です。
敵の感知能力が高くこちらを視認していなくても近くにいると探索モードに入ります。しかも大抵の場合プレイヤー側にほぼ真っすぐ来ます。
敵が軍人で耐久が高いは分かりますが、じゃあ主人公も軍人や警察だったりするのに、耐久力ぺらっぺらなのは・・・。仮に敵のスーツにそこまで耐久があるなら敵から奪えてもいいでしょ。

後から知ったのですがこれでも敵は弱体化してこの難易度なんです。
初回プレイした人たちの阿鼻叫喚が聞こえてきそうです。

このアプデ情報の通りカジュアルは難しすぎて後から追加された難易度です。

難易度 内容 備考
CASUAL ■シューティングライクなプレイを楽しみたい方向け
・プレイヤーの攻撃力が増加し、受けるダメージが大幅に低減されます
・所持できる銃弾の最大数が増加します。
・ヘッドショットおよび背後からの打撃攻撃の威力が強化され、一撃で大きなダメージを与えられます。
・搭乗するヴァンツァーの耐久値が強化されています。
アプデ実装された
LIGHT ■ほどよい緊張感を楽しみたい方向け
・プレイヤーの受けるダメージが低減されます。
・所持できる銃弾の最大数が増加します。
・敵兵の体力が低下しています。
・ヘッドショット及び背後からの打撃攻撃を行った場合でも致命的なダメージを与える事は出来ません
-
STANDARD ■緊張感と達成感を楽しみたい方向け
・敵兵の体力と攻撃力が、標準的な値に設定される
・ヘッドショット及び背後からの打撃攻撃を行った場合でも致命的なダメージを与える事は出来ません
デフォルト難易度
ADVANCED ・敵兵の体力と攻撃力が、高めに設定されています。
・ヘッドショット及び背後からの打撃攻撃を行った場合でも致命的なダメージを与える事は出来ません
・所持できる銃弾の最大数が制限されます。
-
HOPELESS ・敵兵の体力と攻撃力が、非常に高めに設定されています。
・ヘッドショット及び背後からの打撃攻撃を行った場合でも致命的なダメージを与える事は出来ません
・所持できる銃弾の最大数が大幅に制限されます。
-

カジュアル以外には以下文言があります

「敵の行動の特性には、いくつかのバリエーションがあります。
よく観察すれば、安全にその場を切り抜ける方法が見つかるかもしれません。
敵と戦い、倒すことは、ひとつの手段でしかありません。
可能な限り、戦うことを避ける方法を考えましょう」

自分がプレイしていての敵の行動の気づきとしては敵は「索敵をする敵」と「持ち場に滞在し続けようとする敵」に分かれていて、かつドローンは反応次第一気に集まります。

スタンダードプレイでもドローンはをつって狩る索敵をつって倒す持ち場から動かないならヒットアンドアウェイで大抵あんとかなりますが弾も減るし何よりも時間が掛かりすぎる。
観察すれば「まぁまぁ打開できますが本当にただただ無駄に時間が掛かりすぎる。」

一応打開行動としては「ショットガン超近距離」なら一発で倒せます。
スライディングで敵に触れれば敵が吹っ飛びダウン状態となり、パイプ等打撃武器で基本的には一撃で倒せます。

ただ、スライディング&パイプで一撃で倒せない敵が登場したところで「スタンダード」プレイを私は諦めました。
本当に時間が掛かり過ぎてやりがいとかよりも苦痛やストレスの方がはるかに上回る。
一発撃ちアラートが消えるまで(警戒解除まで)待つなど地味な方法などで命からがら進行しても達成感や面白さよりも、やっと終わった疲労感の方が強い。


■操作性や仕様が良くない

■スライディングが敵方向を勝手に避ける
しかもスライディングの仕様も本当に良く無くて敵に真っすぐ向かってダッシュしてる状態からスライディングしているのに敵をよけてスライディングが発生する(なんなの本当に…)。
初期ボタンがL3なので押し込み時に横にぶれてるのかと思って屈むを×ボタンにしましたがやはり単純にスライディング発生時に方向が調整されるらしく上手く当たりませんでしたので仕様みたいです。

■壁端のカバーアクションがガバガバすぎる
壁に張り付いてる状態で移動してるのに壁端でキャラがはみ出している
敵もそれに気づくし隠れているのに銃弾が当たります
じゃあちょっと奥めに隠れるとカバーから銃を構える動作が出来ません。
壁端で銃を構えようとしてるんだからどうにかして欲しい。

■壁張り付きと前転
壁張り付きが「×ボタン」、前転が「×ボタン+方向キー」緊迫してプレイしてるため移動から直ぐに物陰に隠れたい。移動して物陰に隠れ・・・前転!敵の目の前でハチの巣などがぽんぽん起こります。
吸着も微妙な判定があり、勝手に壁張り付きが解除されたり、隠れてのが見つかり撃たれてる時に移動キーを押しても壁張り付きが解除されずハチの巣にされたり・・・
小さい障害物と建物が隣接してる時に、小さい障害物に壁張り付きをしてる状態で建物側に移動キーが入るとなんと立ち上がってしまう。当然見つかるし撃たれる。

■打撃武器の耐久性
唯一の救済にも見えるスライディング&殴打ですが耐久値が無くなると使えなくなります。
キツイ・・・。

■配置変更で周囲に敵がいきなり復活する
救出するためにどうにかやっと敵を排除していったのに「敵の配置が変更されました」という無慈悲な通知とともに敵が周囲に現れる。えぐい。

■残骸などを拾う時にどれを拾うかメニューが邪魔
いいから全部拾え!あんまり立ち止まってる時間はないんだって!!という場面がちらほらあります。実際問題として一部だけ拾うをいう事もなかったのでこのメニュー表示要らなすぎる。仮に重量オーバーなら重い方を残して拾って欲しい。

■射線が通らない
無駄なリアルの追及というか照準がどう考えても射線が通っているのに障害物に邪魔させる。「赤×」がでてるのは辛うじてわかるけど出ない時もあるので一方的に的に敵の銃弾ばかり当たる

■リソースを残そうとした場合の行動
スタンダードの難易度で回復アイテムや銃弾など止血パッチすら使うのが勿体ない
リソースを節約したい場合プレイヤーがどうするかというと死ぬ事です
デフォルトの難易度が高すぎて、ゾンビアタックをして一番リソース消費が少なかったのを採用するだけです
生きようと思いません。次の自分が何とかしてくれる。
出血したら止血パッチなんて使わずに今回はしょうがなかったなぁと思うだけでした。
ポーズメニューにロードが無く、タイトル画面に戻るしかなくそれも面倒なのでじゃあ死ぬか…テンポ悪いなぁという感じ。

■オートセーブは基本意味が無い
たまにセーブポイントじゃない所から再開されますが死んだらほぼほぼセーブポイントのセーブがロードされます。
また、逆にへんな場所でされたオートセーブは死亡時にリトライとexitなのですがロードが無いのが結構ストレス。なんで何かあったらタイトル画面に戻されるんだ・・・。

■敵の感知範囲が広い
ちょっと視界に入ったり、視界に居なくても走ったり、歩いても(たぶん音に反応している?)気付かれる。
また兵士が周囲に居ない戦車の真後ろなのに反応したりする

■索敵センサーの微妙さ

敵をマークする索敵センサーがありますが点滅形式で見づらいのはまあ良しとしても、時間がたつとマークは消失します。作れる索敵センサーに限りがあるのにえぐすぎる。しかも先に述べた通り敵の行動を観察に時間がかかります。
どうするか。セーブポイントからの極力しなない様に偵察と特攻です。
アイテムの場所、敵の数と場所を探るのに一旦捨て身で確認して死に戻り対応です。

「索敵センサーを作るのは材料と時間の無駄」です。この時点でゲーム的にはこれでいいのかという感じはします。

■救出が困難すぎる

更にゲーム中には救出対象が登場します。
プレイヤーを生かすのだけでも何時間も掛けて移動してるのにかなり難しいです
更に更に救出対象の場所に到達するまでに時間制限があるパターンがあって、上で述べた通り無駄に時間がかかるのに時間制限とかたどり着けません。

先に述べた通り、敵の配置変更が30分とか1時間かけて周囲をクリアリングしたのに敵が復活して萎えてしまう。

■スタンダードからカジュアルに難易度変更すると

別ゲーです!スタンダードからやっていれば超人になった気分になります。
敵の銃弾を受けてもハチの巣にされてもこんなに喰らわないのかよってくらいプレイヤーが固くなります。さらにヘッドショットでちゃんと敵が一発で死にます。

クリエーターがこれくらいの難易度が普通と作った難易度で遊べないのは心苦しい所はあるけども、チャプター4まではスタンダードで行ったので許して…。
あとアップデートで追加されたカジュアルですが英断だと思います。
遊びやすいし、探索がちゃんと出来るようになって落ちてるアーカイブなどもちゃんと拾えるし、市民の救出なども出来るようになりました。

■ゲームとしてフォロー出来る所はあるけれど…

このゲームはステルスゲームではなくサバイバルゲームというジャンルになっています。
不利な状況からどうやって打開するのか、自分がピンチの時に誰かを助ける事が出来るのか、持ってる銃弾やアイテムのリソースをどう振り分けてプレイするか
と、言ったテーマがあるようです。
これだけ聞くと確かにと思う事もあるし面白そうです。
トレーラーを見た時にフロントミッション知らなくても面白そうと思ったので方向が完全におかしいわけじゃないはず。

ただ、蓋を開けてみればデフォルトの難易度スタンダードがあまりにも不利すぎて圧倒的死にゲー、圧倒的理不尽。
敵も同じ人間なのに(ボスでもない一般兵が)プレイヤーより圧倒的に強すぎる。
ヘッドショット4発も耐えられるとか人間じゃない。

元からファンの人たちはこういうのに慣れているのかと思ったら結構散々な事を言われていて可哀想。

キャラデザやモデリング、シナリオや落ちてるアーカイブとかストーリーも良いと思います。
問題があるとすれば難易度。しいてはゲームデザインでしょうか。難易度部分設計が厳しすぎた。
カジュアルを遊ぶと分かりますが難易度が緩和されるだけでかなり評価が良くなると思います。
折角索敵センサーでマーキングした敵が分からなくなる所だったり流血要素、エリアをクリアしたのに逆に配置変更で敵がリセットされるなど結構難易度爆上げです。
難易度を上げる調整をするにしても操作性をわざと悪くして調整する方法はただの理不尽調整です。カバーアクションで身を乗り出してないのにはみ出てて敵に見つかる・カバーアクション中なのに銃弾があたる。そして画面に対してこちらからの射線が非常に分かり難く射線が通らない。
「プレイヤー側に隠れる意思があって隠れる動作もしているのにも関わらず敵が反応する」「プレイヤーの画面では確かに敵にポインタがあるにも関わらず射線が通っていない、しかもその射線がどうなっているのかも非常に分かり難い」プレイヤーが努力しているのにシステムが阻害するのはゲーム的に不満の方が大きい。
逆にこれと相対するようにスライディングが雑に強すぎるよくわからない調整。

難しさに対する回答が返ってこない感じで達成感よりも「ただ理不尽」からくる疲労感の方が強くなっていてうーん。
ゲーム作るのって難しいですね。

■カジュアルは普通に面白かった

操作性・仕様の問題などはまぁありますがカジュアルなら許せる範囲かなと思います。
スタンダードだと進行条件が厳しすぎて諦めるような所もどうにか出来ます。

先に述べた通りキャラデザやモデリング、シナリオや落ちてるアーカイブとかストーリーも良かったです。最後のヴァンツァーにいっぱい乗れる所も良かったですし、最後の方でシナリオがちゃんと畳まれ行くのも良かった。
マルチエンディングみたいなのですが既にスタンダードをやった時のあの苦痛感を思うと1周で十分かなぁという感想。

前評判とか知らずに単にトレーラーをみて面白そうと思ったゲームでしたが「カジュアルなら楽しめる」と思います。普通におすすめ出来ます。
難易度がピーキーすぎるのでスタンダード以上で初プレイするならかなり覚悟が必要です。

2025年4月4日金曜日

C#プログラムが突然起動時に終了「sfc /scannow」した話

 まぁタイトル通り。
過去に作成済みのLOMの能力値組み合わせを探すだけのプログラムですがなんか突然起動した瞬間に動かなくなりました。
過去に起動実績があるのでプログラムソースの中身は問題ない事は確定
最初はウィルススキャンで隔離されているのかと思ったけれど弾かれた形跡はなく色々探した所イベントビューアのWindowsログのapplicationログにエラーを発見。

ーーー

障害が発生しているアプリケーション名: wp_put11.exe、バージョン: 0.0.0.0、タイム スタンプ: 0x67e8c34c

障害が発生しているモジュール名: KERNELBASE.dll、バージョン: 10.0.19041.5607、タイム スタンプ: 0x18768d24

例外コード: 0xc06d007e

障害オフセット: 0x000000000003b699

障害が発生しているプロセス ID: 0x28e8

障害が発生しているアプリケーションの開始時刻: 0x01dba54932b292ea

障害が発生しているアプリケーション パス: C:\Users\master\Desktop\C♯\v11\wp_put11.exe

障害が発生しているモジュール パス: C:\WINDOWS\System32\KERNELBASE.dll

レポート ID: cac20f27-c118-49d7-9450-b52568853519

障害が発生しているパッケージの完全な名前: 

障害が発生しているパッケージに関連するアプリケーション ID: 

ーーー

「ははーんなるほどね!(全く分からない)」

System32に居るからシステムファイルのKERNELBASE.dllが悪いのかなぁとも思って少し調べたところこれ自体は実行したプログラムとOS間をかけ橋しているだけで問題はなく

流れ的には「プログラム実行」→KERNELBASE.dll「実行されたやつなんかエラーっすね」
と、返してるだけらしい。

問題は「0xc06d007e(モジュールが見つからない)」こっち
今回はWindowsデフォの「C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe」で直接コンパイルしていて呼び出しも「using System; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; using System.Threading;」だけ。

すべて.NET Frameworkの標準ライブラリに含まれるもので、外部DLL不要。そもそも動作確認済みのプログラムで開始直後受け付け待ちにすら到達できない(処理中エラーじゃない)のであとはー・・・「NET Framework 4.0のランタイムが逝ったか・・・?」

こんな時は\sfc コマンド!/
いつも通り名前から実行で出したcmdに「sfc /scannow」実行したら
このコマンド管理者しか実行しちゃダメなんですよって丁寧に怒られたので
「C:\Windows\System32」に移動してcmd.exeを右クリックで管理者実行

ーーー

C:\WINDOWS\system32>sfc /scannow

システム スキャンを開始しています。これにはしばらく時間がかかります。
システム スキャンの検証フェーズを開始しています。
検証 100% が完了しました。
Windows リソース保護により、破損したファイルが見つかりましたが、それらは正常に修復されました。
オンライン修復の場合、詳細は次の場所にある CBS ログ ファイルに含まれています
windir\ Logs\CBS\CBS.log (たとえば C:\Windows\Logs\CBS\CBS.log)。オフライン修復の場合、詳細は /OFFLOGFILE フラグによって指定したログ ファイルに含まれています。

ーーー

再実行!即強制終了!「例外コード: 0xc06d007e」!
(´・ω・`)ソンナー
ーーー

C:\WINDOWS\system32>DISM /Online /Cleanup-Image /RestoreHealth
展開イメージのサービスと管理ツール
バージョン: 10.0.19041.3636
イメージのバージョン: 10.0.19045.5608

[===========================84.9%=================         ]

[===========================87.3%==================        ]

[===========================89.9%====================      ]

[==========================100.0%==========================] 復元操作は正常に完了しました。

操作は正常に完了しました。

ーーー


再実行!即強制終了!「例外コード: 0xc06d007e」!
(´・ω・`)ドウシテ・・・


2025年3月24日月曜日

LOMのステータスを総当たり探査した話(技術者向け)

 たまには技術ノートでも書こうかなという話
今回のお題はlomのベストパターン探査です。

VBS:
Function CalcStats(w)
s=array(5,5,5,5,5,5,5)
s(0)=s(0)+0.75*w(0)+1 *w(1)+1 *w(2)+1.25*w(3)+1.25*w(4)+1.75*w(5)+0.75*w(6)+0.5 *w(7)+1.25*w(8)+0.75*w(9)+0.5 *w(10)
s(1)=s(1)+1.25*w(0)+1 *w(1)+0.75*w(2)+1 *w(3)+0.75*w(4)+0.75*w(5)+1.25*w(6)+0.75*w(7)+0.75*w(8)+1.25*w(9)+1.75*w(10)
s(2)=s(2)+0.75*w(0)+1 *w(1)+1 *w(2)+0.75*w(3)+1.25*w(4)+0.5 *w(5)+1 *w(6)+0.75*w(7)+0.75*w(8)+0.5 *w(9)+0.75*w(10)
s(3)=s(3)+0.75*w(0)+0.75*w(1)+0.75*w(2)+0.75*w(3)+0.75*w(4)+0.75*w(5)+0.75*w(6)+1.25*w(7)+0.5 *w(8)+0.75*w(9)+0.75*w(10)
s(4)=s(4)+0.5 *w(0)+0.75*w(1)+0.75*w(2)+0.75*w(3)+0.5 *w(4)+0.75*w(5)+0.5 *w(6)+0.5 *w(7)+1 *w(8)+0.5 *w(9)+0.5 *w(10)
s(5)=s(5)+1 *w(0)+0.75*w(1)+1 *w(2)+0.5 *w(3)+0.75*w(4)+0.75*w(5)+0.75*w(6)+0.75*w(7)+0.75*w(8)+0.75*w(9)+0.75*w(10)
s(6)=s(6)+1 *w(0)+0.75*w(1)+0.5 *w(2)+0.75*w(3)+0.5 *w(4)+0.75*w(5)+1 *w(6)+1.25*w(7)+0.75*w(8)+1.25*w(9)+1 *w(10)
for i=0 to ubound(s) s(i)=fix(s(i) if s(i)>99 then s(i)=99 next CalcStats=s end function function t619(s) t=0 for i=0 to ubound(s):t=t+s(i):next if t>=619 then t619=true else t619=false end function function s79t615(s) f=true t=0 for i=0 to ubound(s) if s(i)<79 then f=false t=t+s(i) next if t<615 then f=false s79t615=f end function msgbox s79t615(CalcStats(array(1,81,1,0,0,0,0,6,9,0,0)))
msgbox t619(CalcStats(array(1,81,1,0,0,0,0,6,9,0,0)))

考えなくちゃいけないのは計算と探査判定
探査方法の考え方としては、武器別にステータス配列を作って配列を合算しても良いんだけど配列同士の合算が面倒くさい。
考え方を変えて関数で各武器の回数だけを貰ってその分だけ能力値を掛ける。
これをCscriptで多重forに放り投げるだけ。

Jscript:

function CalcStats(w) {
  var s = [5, 5, 5, 5, 5, 5, 5];
  s[0]=s[0]+0.75*w[0]+1*w[1]+1*w[2]+1.25*w[3]+1.25*w[4]+1.75*w[5]+0.75*w[6]+0.5*w[7]+1.25*w[8]+0.75*w[9]+0.5*w[10];
  s[1]=s[1]+1.25*w[0]+1*w[1]+0.75*w[2]+1*w[3]+0.75*w[4]+0.75*w[5]+1.25*w[6]+0.75*w[7]+0.75*w[8]+1.25*w[9]+1.75*w[10];
  s[2]=s[2]+0.75*w[0]+1*w[1]+1*w[2]+0.75*w[3]+1.25*w[4]+0.5*w[5]+1*w[6]+0.75*w[7]+0.75*w[8]+0.5*w[9]+0.75*w[10];
  s[3]=s[3]+0.75*w[0]+0.75*w[1]+0.75*w[2]+0.75*w[3]+0.75*w[4]+0.75*w[5]+0.75*w[6]+1.25*w[7]+0.5*w[8]+0.75*w[9]+0.75*w[10];
  s[4]=s[4]+0.5*w[0]+0.75*w[1]+0.75*w[2]+0.75*w[3]+0.5*w[4]+0.75*w[5]+0.5*w[6]+0.5*w[7]+1*w[8]+0.5*w[9]+0.5*w[10];
  s[5]=s[5]+1*w[0]+0.75*w[1]+1*w[2]+0.5*w[3]+0.75*w[4]+0.75*w[5]+0.75*w[6]+0.75*w[7]+0.75*w[8]+0.75*w[9]+0.75*w[10];
  s[6]=s[6]+1*w[0]+0.75*w[1]+0.5*w[2]+0.75*w[3]+0.5*w[4]+0.75*w[5]+1*w[6]+1.25*w[7]+0.75*w[8]+1.25*w[9]+1*w[10];
  for (var i = 0; i < s.length; i++) {
      s[i] = Math.floor(s[i]);
      if (s[i] > 99) s[i] = 99;
  }
  return s;
}

function t619(s) {
  var t = 0;
  for (var i = 0; i < s.length; i++) {t += s[i];}
  return t >= 619;
}
function s79t615(s) {
  var f = true;
  var t = 0;
  for (var i = 0; i < s.length; i++) {
     if (s[i] < 79) f = false;
     t += s[i];
  }
  if (t < 615) f = false;
  return f;
}
function s80t607(s) {
  var f = true;
  var t = 0;
  for (var i = 0; i < s.length; i++) {
      if (s[i] < 80) f = false;
      t += s[i];
  }
  if (t < 607) f = false;
  return f;
}

速度比較用にJscriptにそのまま書き直したものがこれ。同じくCscriptで実行したけど速度的に大きな差は無かった。
別にいらなかったんだけど全ステ80以上パターンも探査に追加この時点では607が最低値で分かっていたのでそれを指定。
ちなみにあとで総当たりさせると合計607は三千パターン以上ある為後で合計範囲を610以上に変更した。
619のif elseはそもそも比較演算すればboolなので修正他はそのまま。

javascript(node.js実行):

function CalcStats(w) {
  let s = [5, 5, 5, 5, 5, 5, 5];
  s[0]=s[0]+0.75*w[0]+1*w[1]+1*w[2]+1.25*w[3]+1.25*w[4]+1.75*w[5]+0.75*w[6]+0.5*w[7]+1.25*w[8]+0.75*w[9]+0.5*w[10];
  s[1]=s[1]+1.25*w[0]+1*w[1]+0.75*w[2]+1*w[3]+0.75*w[4]+0.75*w[5]+1.25*w[6]+0.75*w[7]+0.75*w[8]+1.25*w[9]+1.75*w[10];
  s[2]=s[2]+0.75*w[0]+1*w[1]+1*w[2]+0.75*w[3]+1.25*w[4]+0.5*w[5]+1*w[6]+0.75*w[7]+0.75*w[8]+0.5*w[9]+0.75*w[10];
  s[3]=s[3]+0.75*w[0]+0.75*w[1]+0.75*w[2]+0.75*w[3]+0.75*w[4]+0.75*w[5]+0.75*w[6]+1.25*w[7]+0.5*w[8]+0.75*w[9]+0.75*w[10];
  s[4]=s[4]+0.5*w[0]+0.75*w[1]+0.75*w[2]+0.75*w[3]+0.5*w[4]+0.75*w[5]+0.5*w[6]+0.5*w[7]+1*w[8]+0.5*w[9]+0.5*w[10];
  s[5]=s[5]+1*w[0]+0.75*w[1]+1*w[2]+0.5*w[3]+0.75*w[4]+0.75*w[5]+0.75*w[6]+0.75*w[7]+0.75*w[8]+0.75*w[9]+0.75*w[10];
  s[6]=s[6]+1*w[0]+0.75*w[1]+0.5*w[2]+0.75*w[3]+0.5*w[4]+0.75*w[5]+1*w[6]+1.25*w[7]+0.75*w[8]+1.25*w[9]+1*w[10];
  for (let i = 0; i < s.length; i++) {
  s[i] = Math.floor(s[i]);
  if (s[i] > 99) s[i] = 99;
  }
  return s;
  }
function t619(s) {
  return s.reduce((t, val) => t + val, 0) >= 619;
}
function s79t615(s) {
  const t = s.reduce((sum, val) => sum + val, 0);
  return s.every(val => val >= 79) && t >= 615;
}
  function s80t607(s) {
  const t = s.reduce((sum, val) => sum + val, 0);
  return s.every(val => val >= 80) && t >= 607;
}

前回の記事で初めてnode.jsをインストールしたんだけどjavascriptがコーディング出来てローカル処理をしたいなら絶対インストールした方がいい。
処理速度は100倍以上速くなる。
Jscriptでは変数定義にvarしか使えなかったけどjavascriptなのでletやconstに修正して、判定関数も処理を整理。

この後さらにこれをC#に書き換えて並列処理で高速化するんだけど、現状で片手剣30回前後まで絞っても目新しいパターン結果は出なかったので省略。

発見パターンのうち有益なパターンは「LOMリマスター:レベルアップ能力値シミュレーター」の方を参照してください。
※Lvシステムは変わってないのでリマスターじゃない場合も同じです

結局の所、能力調整的に微妙な…いわゆる最大パターン619は6パターンしかありません。
逆に一番いいパターン、79以上615は27パターン程あります。

(オマケとして探査した80以上の合計上限はどう頑張っても611が最大で、79より合計が下がるので微妙。ちなみにこの611パターンは1つしか見つかってないです。80以上610なら18パターンほど見つかってます。)

■大まかな探査時間
片手剣の回数をベースに杖とグラブは無制限、他は10回という制限で
片手剣の使用回数を62回にした場合はだいたい以下の通り。

個別の時間差
 0% →   9%: 13:07:43 - 12:28:35 = 39分 8秒 = 2,348秒
 9% →  18%: 13:40:55 - 13:07:43 = 33分12秒 = 1,992秒
18% →  27%: 14:09:01 - 13:40:55 = 28分 6秒 = 1,686秒
27% →  36%: 14:34:30 - 14:09:01 = 25分29秒 = 1,529秒
36% →  45%: 14:54:26 - 14:34:30 = 19分56秒 = 1,196秒
45% →  54%: 15:10:44 - 14:54:26 = 16分18秒 =   978秒
54% →  63%: 15:24:24 - 15:10:44 = 13分40秒 =   820秒
63% →  72%: 15:35:43 - 15:24:24 = 11分19秒 =   679秒
72% →  81%: 15:45:03 - 15:35:43 =  9分20秒 =   560秒
81% →  90%: 15:52:29 - 15:45:03 =  7分26秒 =   446秒
90% → 100%: 15:58:26 - 15:52:29 =  5分57秒 =   357秒 

ループの外側の数値が大きくなるほど使える回数は減るので内側に向かってどんどん処理時間が早まっていく。
だいたい50前後くらいなら少し放置しておけばリストアップ出来る。
これより片手剣の回数が減ると一日放置とかになるかも

■C#その後:
・並列でも全てをなげちゃうとCPU90%くらいで数日帰ってこないことになるので片手剣の回数を指定出来るように修正、関数として分離(全体を回したい時はこれにfor)
全体に投げてた並列処理をfor内部を並列化して内部のforを高速化

・条件を変更するたびにコンパイルが面倒になったので設置textから判定条件を読み込めるように修正
File.ReadAllLines(conditionsFile);で読み込んで改行でsplitして
読み込むテキストは改行区切りで「80,80,80,80,80,80,80,610」「81,81,81,81,81,81,81,」「,,,,,,,619」こんな感じ。空部分は条件指定なし

・全部デスクトップに吐いて、読み込みもデスクトップだったのを全部実行exeから相対パスに出力と読み込みするように変更「Environment.GetFolderPath(Environment.SpecialFolder.Desktop)→System.Reflection.Assembly.GetExecutingAssembly().Location」とか

・各ループ上限を柔軟に調整したくなったのでリミットテキストを外部において武器の回数上限を変更出来るように修正

・引数で回数を指定して実行する事で入力待ちするのを省略するように変更
片手剣回数を関数分離して回したいときはforにしようと思ってたけど適当にスクリプトで引数付きで実行出来るようにした

などの修正をしたけど検証終わったらもう使わないよなぁという気持ち。
いやまぁ初見言語のC#の勉強にはなったけども

C#(for抜粋):

for (int w0 = 0; w0 <= w0Limit; w0++)
{
  int w2Limit = Math.Min(remaining - w0, loopLimits[2]);
  Console.WriteLine("\n片手剣 " + actualOneHandSword + "回 - 進捗: " + (w0 * 100 / w0Max) + "% (" + w0 + "/" + w0Max + ") - " + DateTime.Now);
  for (int w2 = 0; w2 <= w2Limit; w2++)
  {
    int w3Limit = Math.Min(remaining - w0 - w2, loopLimits[3]);
    for (int w3 = 0; w3 <= w3Limit; w3++)
    {
      int w4Limit = Math.Min(remaining - w0 - w2 - w3, loopLimits[4]);
      for (int w4 = 0; w4 <= w4Limit; w4++)
      {
        Console.Write(".");
        Console.Out.Flush();
        int w5Limit = Math.Min(remaining - w0 - w2 - w3 - w4, loopLimits[5]);
        for (int w5 = 0; w5 <= w5Limit; w5++)
        {
          int w6Limit = Math.Min(remaining - w0 - w2 - w3 - w4 - w5, loopLimits[6]);
          for (int w6 = 0; w6 <= w6Limit; w6++)
          {
            int w7Limit = Math.Min(remaining - w0 - w2 - w3 - w4 - w5 - w6, loopLimits[7]);
            Parallel.For(0, w7Limit + 1, w7 =>
            {
              int w8Limit = Math.Min(remaining - w0 - w2 - w3 - w4 - w5 - w6 - w7, loopLimits[8]);
              Parallel.For(0, w8Limit + 1, w8 =>
              {
                int w9Limit = Math.Min(remaining - w0 - w2 - w3 - w4 - w5 - w6 - w7 - w8, loopLimits[9]);
                for (int w9 = 0; w9 <= w9Limit; w9++)
                {
                  int w10 = remaining - w0 - w2 - w3 - w4 - w5 - w6 - w7 - w8 - w9;
                  if (w10 >= 0 && w10 <= loopLimits[10])
                  {
                    int[] localW = new int[11];
                    localW[0] = w0; localW[1] = actualOneHandSword; localW[2] = w2; localW[3] = w3; localW[4] = w4;
                    localW[5] = w5; localW[6] = w6; localW[7] = w7; localW[8] = w8; localW[9] = w9; localW[10] = w10;
                    bool localHasMatch = false;
                    StatsWrite(localW, actualOneHandSword, limitPrefix, ref localHasMatch);
                    if (localHasMatch) hasMatch = true;
                  }
                }
              });
            });
          }
        }
      }
    }
  }
}

C#のコードだけ記載しないのもあれだったから。総当たり処理部分はこんな感じ。
ループは最初forではなく再帰にしてたんだけどforの方が早いらしい事が分かったのでforに戻した。1秒でも早くというかミリ秒でも早く動いて欲しかった故に・・・。

判定処理をforに入れるとfor内が厚くるのでStatsWrite側に判定処理を入れています。Parallel.Forで並列化してるのは杖とグラブ部分。

2025年3月21日金曜日

node.jsを使ってみた話。

 そもそもnode.jsって何?

今回自分が知りたかった要点ですが「node.js」とは
・node.js=javascriptのローカル実行環境
・実行環境=インストールが必要
・javascriptの記述でローカル処理できる
・WSH(Jscriptやvbs)より
百倍以上速い!!

まぁ他にもあるんですが、今回押さえたい要点はこの4つです。

「node.js」名前だけ聞いたことあったんですが「jQuery」のようなjavascriptライブラリだと思ってたんですよ。
・・・ですが、全然そうではなくて実行環境だったんですよ。

「javascript」と「Jscript」と「node.js」の関係

■javascript
web上で動的に処理する時に使うスクリプト言語です
ファイルの拡張子は「.js」です

■Jscript
Windows上で(ローカル環境で)処理を自動化する時に使うjavascriptベースのスクリプト言語です。限りなくjavascriptに近い言語ですがjavascriptとは別物です。
ファイル拡張子は「.js」つまり拡張子までもjavascriptと同じです。

古いjavascriptがベースで、例えば変数定義はvarしかなくlet等は使えません。
また、今後新規にサポートされる予定もありません。むしろ消えかかっている泥船言語です。Microsoftは今後Windowsの自動化はパワーシェルを推奨してる感じです。

根本的な話をするとWSH(Windows Script Host)というWindows上で動かすスクリプト言語二つの内の一つがJscriptです(もう一つはvbs)。
バッチファイル(.bat)などより柔軟な自動化が出来る言語で、当然ながらWindows上でしか動作しません。
ただ逆に言えばWindowsであれば何かをインストールなど不要で直ぐにスクリプトが使えWindowsのこまごました自動化が出来ます。普通に便利です。

■node.js
「node.js」なんて名前だからてっきりファイルかと思うじゃないですか?
そうじゃないんです。
javascriptをローカルで使えるようにする「環境」なんです。

そうです、記述する言語は「javascript」です。
当然ファイルの拡張子も「.js」です。
しかも、ローカルファイルにアクセスなども出来るようになるんです。
「じゃあ、Jscript完全上位互換になるって事?」と思うでしょうがJscriptで使えるものが使えません。

例えば:
・WScript.Echo: Windows Scripting Host(WSH)特有のメソッドで、Node.jsには存在しません。
・WScript.CreateObject("WScript.Shell"): WSHでシステムオブジェクトにアクセスするための機能。Node.jsでは代替手段が必要。
・new ActiveXObject("Scripting.FileSystemObject"): WindowsのCOMオブジェクトで、Node.jsではサポートされていません。

あと上には書きませんでしたがnode.jsはマルチプラットフォームです。
Windows以外の環境でもjavascriptが使えるって事になります。

node.jsはjavascriptで記述するのに何でそんなに早いの?

そもそもなんでscriptは遅いのかって話に直結します。
一般的にプログラムとスクリプトには壁があり、その大きな違いはコンパイル(やビルド)をするかしないかが大きな境界になります。
インタプリタとコンパイラという区分です。

・インタプリタではソースコードを一行ずつ変換し命令を処理・実行
・コンパイラは全ての命令をまとめて変換後に一括で処理・実行

処理速度だけで言えば機械が処理しやすいプログラム(コンパイラ)の方が圧倒的に早いです。
じゃあなんでスクリプトなんてものがあるかというと、確かにプログラムよりはかなり遅いですが、直ぐに実行できて直ぐに修正出来るという利点があります

小中規模であればスクリプトの方が対処する内容に対して即効性があるのです。
逆にプログラムはコンパイルを通さないと処理が実行出来ませんが大規模な処理は圧倒的に処理速度が重要になります。

node.jsはjavascriptをコンパイルしている

前段の通りコンパイルすると処理速度が爆発的に早くなります。
実はnode.jsはjavascriptをコンパイルする事で高速化しています。

更に詳しく言えば「Just-In-Time (JIT) コンパイル」をしています。
インタプリタで初期実行した後、ホットコードをマシンコードに変換という形です。
手動でコンパイルする必要がないがないが重たい処理だけ知らないうちにコンパイルしてくれるって事ですね。
また、イベント駆動型の非同期I/Oモデル故に大量処理に向いています。


凄い雑にまとめると「javascript」を超高速化出来るのがnode.jsです。
さて、ここから下は駄文です。


そもそもなんでのnode.js使ってみたの?

LOMのレベルアップのステータスを手動で調整してたけど面倒になったから総当たりで他にパターンが無いかの確認と上限を確定させるため。
まぁ一旦雑に総当たりで試してみたが当然遅い。そこで高速化する手立てを考えている時にnode.jsに行きついたわけです。

どれくらい早くなった?

総当たりをぶつけた訳ですが途中探査にJscriptで3時間かかった部分がnode.jsで実行したら1分で探査されました。
Jscript特有部分を少し直した以外は処理方法などは手を加えずに180倍速で処理できました(はっや・・・)。

ただあくまでも今回のしかも一部分を切り取った時間比較なのであれですが基本的には重い処理を作ったなら100倍くらいは早くなると見積もっていいと思います。


追記:
一日node.jsで回して片手剣56回までの探査で行ったので強制終了

「そういえばWindowsってデフォルト環境でC#のコンパイル出来るようになったんだよなぁ」って事を思い出してC#用にコードを変更
さらにC#なら並列で処理できるからもっと早くなるんじゃないか?
フルでぶん投げるけど元より全探査しようとは思ってない。
24時間回してみてどれくらい結果取れるかなぁって感じ←今ここ

余談:早いスクリプト言語って何?

「javascriptをnode.jsで実行(以下node)、javascript、Jscript、VBS、Python」の処理速度を比較した場合単純処理なら

node>>超えられない壁>>Jscript・VBS>javascript>Python

コンパイルがされる関係上百倍くらいは差がでるのでnodeを越える事は出来ません。
重たい処理をしたい場合、悪い事は言いませんのでjavascriptが出来るならnode.jsをインストールした方が良いです。
Pythonインストールするぐらいならnode.jsインストールしたほうがたぶん平和。

もうプログラムで良くない?

まぁ、そもそもWindowsデフォルト環境だけで何もインストールしたくない、もしくは環境的に新たに何かをインストールできない場合、基本的には「Jscript・VBS・javascript」しか選択肢がありません。

ただし、裏技的解決方法としてはC#で書き直す方法があります。
なんとWindowsはデフォルト環境でC#がコンパイルできます!

もう少し丁寧に言うとWindowsはデフォルトで「.NET Framework 」が入っておりC#のコンパイル環境が整っています。
つまり普通にプログラム組んじゃおうって事ですね。当然ながらめちゃ早いです。しかもC#の場合は「Parallel.For」でマルチプロセスで平行処理できる為、今回のような物量探査問題だけならばnodeより確実に早いです。

※一応注意点としてはC#のデフォルトコンパイル環境は「.NET Framework」のバージョンが古いので最新のC#コードが使えない事は留意してください。
例えば「$とか=>」が使えません。
■ラムダ式形式: =>を使った簡潔なメソッド定義はC# 6.0以降の機能。
■文字列補間($"...")がC# 6.0以降の機能


2025年3月13日木曜日

奇数か(偶数か)を判定する

前置き:

今回のお話は 𝕏のおすすめ欄にたまたま流れてきたポスト「1~100までの整数が奇数かどうかを判定するプログラムを書きました!」に起因します。

一般の人には面白さが伝わるか分からないけど『「もし奇数なtrueそうでなければfalse」つまり1なら奇数2なら偶数3なら偶s...って100個書けば良いんだ!』

HAHAHAそうだけどそうじゃないだろっていう面白さ…やっぱり伝わらないかも。

確かに定義が「1~100固定で奇数ならtrueを返し偶数ならfalse」なら間違いないくこれで答えが得られるけど汎用性が無さすぎる事が1つ、もう一つはそもそも処理本質はそこじゃない。っていう所。

「1なら奇数」...は既に答えを知っている場合の条件分岐で本来定義を考えるべきなのは奇数はどの条件で奇数になるのかって話。

このポストにはさらに元ネタがあって「コーディングのヒント」っていうこのベタif文をエクセル等のオートフィル(処理を繰り返す)によって脳筋コードを生成するってネタポストの再ネタポストって事ですね。

そして「【ゆる募】1~100までの整数が奇数かどうかを判定するプログラム大喜利を開催します☺ 言語不問✨」更にネタにした二番煎じ、三番煎じのネタポスト『無駄に洗練された無駄の無い無駄な動き』を考える会みたいな話です


「プログラムをする」=「ロジカルな思考になる事」

自身はプログラマーではないく精々スクリプターなので思考が雑だけれど基本的には同じで「コーディング」する事よりも「その本質はなんなのか」を考える思考ロジックがたぶん一般人とプログラマーの境界な気がします。

この記事を書くきっかけになったのはネタポストもあるけれど実は大喜利のリプにあった「入力が数値じゃないかもしれない」みたいなポストがあってそうじゃないんよなぁと思ってなんとなく(今はそういう話をしてるんではなくロジック部分のみの話なんよ)。

さておき、そもそも通常はどのようにコードを書くか


n=99;print(bool(n%2))


最初に考えたのはこんな感じです。ポスト元が1をtrueにしてるのでそれに合わせます。

奇数なら余りは1になり、偶数なら余りが0になります。

これをbool値「真(True)偽(False)」に変換します。

まぁでもよくよく考えたら「n%2==1」で直接bool値になりますね。

元のコードの通り関数にするなら


num=99
def is_odd(num:int)->bool:return num%2==1
print(is_odd(n))


こんな感じでしょうか。

この通り本来は奇数をだすならたったこれだけなのです。


大喜利の話

特に記載はないですがこの手の大喜利の要点は

結果が正しい事は前提で、より壮大に無駄にリソースを使い条件を満たそうという話です。

かつ限定条件が1~100なのでこの範囲じゃないと上手く動かないとかの方が面白い。

更に言えば単に意味のない処理を入れる訳ではなく必要な処理で構成されており、処理が抜けると動かない形であった方が良いですね。


まぁなのである意味一番元のベタif文100個並べるが完成されてるかもしれません

(判定を100回行い、なおかつ1行でも抜けると正しく動作しない)


もう一つの要点

それは定義を考えるという事です。

通常の奇数の定義は「数値に対して2で割って余りが1なら奇数」これが普通です。

このような大喜利はこの定義を別の条件に言い換え処理を考える遊びです。


例えば入力は1~100の整数という条件ですので「奇数は偶数と必ず交互に存在する」見たいな事を考えた場合「奇数から始まり、対象の数値まで真と偽を交互に代入」すれば奇数か偶数かを判定できる。

𝕏では花占いと表現してポストしたやつで


n=100;b=false;for(i=0;i<n;i++){if(b){b=false}else{b=true}}


こんな感じです。

Jscriptですがまぁjavascriptでも同じです。


更に別の奇数の定義を考えてみます。

交互に存在するなら「真偽交互に100個分回答を用意して、入力数値番目」にアクセスすれば直接真偽を得られる


n=99;WScript.StdOut.WriteLine(Boolean("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010".split("")[n]*1))


定義を元に少し変更します。

画像でポストするのが面倒だしコピペ出来ないし面倒。という事でコードを「全角140文字=半角280字で考える」

なので真偽配列を10二進数配列を作ります。

さらに配列は0から始まるけど入力値は1~100なので要素-1のアクセスが面倒なので配列の0番目の要素を追加し101個の配列を用意してやれば入力値で直接0か1が返ります。これをbool変換すれば奇数か判定できる。 コードはJscriptのCscriptです。


他にも大喜利の例題は「1,3,5,....99なら奇数」という50個分の判定ですがそもそも偶数奇数判定は「入力値の末尾1桁が1,3,5,7,9なら奇数」でいいじゃんと考える事も出来ます。

であれば


n=100;print([False,True,False,True,False,True,False,True,False,True][int(list(str(n))[-1])])


こんな感じで真偽100個配列を作るより奇数5つの配列でいい 今度はPythonで組んでみましたが先ほどやってる事は同じです。


あと他には「2で割って余りが1なら奇数」という表現を別の表現で考える方法もあります

「2で割った結果が小数になるならば奇数」みたいな感じです


n=100:msgbox cbool(ubound(split(n/2,".")))


例えば2で割ってそれを小数点で配列化、割り切れてれば配列数は0、割り切れてなければ1。

0・1をbool化すれば真偽判定できます


言語特有の考え方ですが先ほどの100個全ての真偽を配列にする方法を

n=100;print(bool(int(list("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010")[n])))

Python版で考えて、Pythonは進数変換が容易なのでこの二進数を16進数に圧縮しようかと思ったんですがそもそも


n=100;print(bool(int(list("01"*50+"0")[n])))


の圧縮率が高すぎてもうこれで良くない?みたいな事もあります。

「01」というパターンを50回繰り返すというPython記述便利。


さて、交互に繰り返される真偽・二進数・末尾一桁と来ましたが入力値が0かnot0になれば良いわけです。だとするとそもそも「十進数を二進数に変換して末尾一桁を真偽変換」すれば良いのではとも気づきます。


n=100;print(bin(n)[-1]=="1")


おっと、また短くなってしまった。Python久々に書いたけど便利だなぁ。

もっと無駄な形にしないとダメですね


割り算と余りの概念を脳筋で解決する方法も考えてみます

2個をワンセットとして対消滅させていき、数字君が生き残れれば奇数

分かりやすく言うと「数値を2ずつ引いていき、余れば奇数」


n=99:do while n>1:n=n-2:loop:msgbox n=1

vba・vbsならこんな感じです。これもやっぱり短いですね。
やはりベタifが無駄であり無駄という意味では酷いコードの完成度が高い。
でも𝕏の文字数制限内でそれ書くのは面倒臭い。
じゃあ、いっそ実行可能な形式でそれを生成すればいいよね

a=split("false/true","/"):c="n=数値"&vbcrlf:for i=1 to 100:c=c&"if n="&i&" then b="&a(i mod 2)& vbcrlf:next:c=c&"msgbox b"&vbcrlf
createobject("scripting.filesystemobject").createtextfile("n.vbs", 1).write c


𝕏の文字数制限内に収まりました
「数値」の所を1~100に置き換えればそのまま実行可能です。

if n=1 then b=true
if n=2 then b=false
...

をベタ入力したvbsファイルを生成するvbsです。
コードを生成するコードの時点で二度手間で、コード生成時にi mod 2で既に処理内で余りを出してる所にも二度手間感があります。
「でも何かを抜くと動かなくなる」というコードです。
まぁ一旦こんなところで終わりにしましょう。


意味はないけど意味がある事

今回の「奇数の真偽を出す」は別に意味のある事ではありません
ただ、こう言った処理定義の考え方変換や柔軟な発想、手段の引き出しを増やす事はあるほど良いです。

また、0と1で百個用意して配列要素で値をえる方法などは既に結果が変わらない場合などであれば配列をメモリに突っ込んで引き出した方がforなどを回すより高速に処理できるし、呼び出しが外にあればメインは要素1つ与えるだけで取り出せるメイン処理の簡素化が出来たりもします。

こういったものは別に大喜利である必要はありませんんが内容が簡単で初心者でも上級者でも一緒に頭の体操が出来るのが面白い。
簡単な問題なので「処理すべき事」と「結果がこうなる」が分かっているうえで他の人のコードを見れるから初心者からでも上級者がどんな工程を踏むのかが見れるのも良いです。

他にも「与えられた数値が素数かどうか判定する」「うるう年の判定」など条件を知っているが考え方が試されるものは色々あります。

余談:
上記コードは自分で考えたもので、記述出来るのがwscript、VBA、Pythonなどスクリプト系なのでコードがごった煮でごめんね:;(∩´﹏`∩);: