suna8’s blog

還暦を過ぎたジジーの気まぐれ日記

森村誠一氏を偲ぶ

2023年7月24日にミステリー作家の森村誠一氏が亡くなりました。享年90。

中日新聞デジタル版より

氏の作品に出合ったのは、ほぼ半世紀前の高校生の時、学校の近くの本屋でなにげに手にした『高層の死角』という文庫本でした。背表紙にはタイトルや作者名以外に”乱歩賞受賞”と印刷されており、当時、江戸川乱歩の作品も読んでいたので、そのつながりで興味を持ちました。

その作品を読んだことがきっかけで”本格ミステリー”の世界の面白さを知った私は、氏の作品を手当たり次第に読みました。時には徹夜してまで。

結局、氏の作品は社会人になってからも増え続け、自室の本棚の大部分を占めるまでになりました。もちろん他の作家の作品も買ってはいましたが、森村氏の文庫本は相当数に及んだのです。本棚に並べると主に角川文庫の青表紙が目立っていたせいもあり、本屋でも自然に青い色を探してしまう毎日でした。

森村誠一氏の文庫本の蔵書

森村氏は作家活動を開始した当初はホテルや山岳をテーマにしたミステリーが中心でしたが、やがて人間性や社会問題も扱うようになり、幅広い分野で作品を提供してくれました。後年には時代小説なども書かれていましたが、私自身は分野的に好みではなかったので、氏のすべての作品を読破するまでにはいきませんでした。

私の読書歴に影響を与えた本の一部 (断捨離対象外)

残念ながら断捨離の一環で去年に氏の作品の蔵書を手放してしまいましたが、手元には『高層の死角』だけは保存しています。ただ、紙の書籍は全体的に字が小さいので、機会を見て『高層の死角』を始めとする“もう1度読みたい作品”に関して再度電子ブック版を購入し、フォントサイズを大きくして読み直してみたいと思っています。

森村誠一さん、安らかにお眠りください。

 

やっぱり趣味のプログラミングは楽しい(Part6/end)

Part5より

 

実は別のプログラムソースを元に、ChatGPTにリバースエンジニアリングさせたことがありました。要は、ソースコードから「仕様書」を作らせたのです。この時はかなりの精度で”生成”できたので、今回完成したプログラムについても作らせることにしました。

 

以下のソースプログラムから、プログラム仕様書を作成してください。
・プログラムの概要
・ゲームの遊び方
・メソッドの一覧
・変数一覧表
・仕様の拡張案

/*
 * イスカンダルのトーフ屋ゲーム
 *         original copyright 1978 by N.Tsuda
 *        convert & arrange to Java version by T.Sunahachi 2023
 */

import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;

public class TofuGame {

    static int STARTGOLD = 5000;     // 開始時の所持金
    static int GOALGOLD = 30000;     // 目標金額
    static int TOFUBAIKA = 50;     // トーフの売価
    static int TOFUGENKA = 40;     // トーフの原価
    static int SELLMAX = {500, 200, 100};     // 天気ごとの最大売上個数
    static String TENKIWORDS = {"晴れ", "曇り", "雨"};    // 天気を表す言葉
    
    static int hare;                 // 晴れの確率
    static int kumori;             // 曇りの確率
    static int ame;                 // 雨の確率

    private static Random random = new Random();

    // メインメソッド
    public static void main(String args) {

        Scanner scanner = new Scanner(System.in);
        String input;

(以下略)

 

すると、こんな感じの文章が出来上がってきたのですが……(レイアウトが乱れているのは、HatenaBlogのエディタの勝手な挙動のせい)

プログラム仕様書

  1. プログラムの概要:
    このプログラムは「イスカンダルのトーフ屋ゲーム」というゲームを実行するものです。プレイヤーはトーフ屋を経営し、目標金額を達成することを目指します。プレイヤーとコンピュータが交互にトーフを作り、天候に応じてトーフを売ります。売上に基づいて所持金が増減し、先に目標金額に到達したプレイヤーが勝利となります。

  2. ゲームの遊び方:
    a. プログラムを実行すると、「イスカンダルのトーフ屋ゲーム (Java版)」と表示されます。
    b. ルール説明を表示するかどうか尋ねられます。"y"を入力するとルールが表示されます。
    c. ゲームが開始され、プレイヤーとコンピュータが交互にプレイします。
    d. ゲーム終了後、プレイを続けるかどうか尋ねられます。"y"を入力するとゲームが継続されます。
    e. 目標金額に到達したプレイヤーが勝利となり、ゲームが終了します。

  3. メソッドの一覧:

    • main(String args): プログラムのエントリーポイント。ゲームのメイン処理を呼び出します。
    • displayRules(): ゲームのルール説明を表示します。
    • gameMain(): トーフ屋ゲームのメイン処理を実行します。
    • tenkiYohou(): 天気予報を表示します。
    • randomNum(int max): 指定された最大値までの乱数を生成します。
    • inpTofu(int limit): プレイヤーが作成するトーフの個数を入力します。
    • compTofu(int limit, int player): コンピュータが作成するトーフの個数を決定します。
    • nextTenki(): 翌日の天候を決定します。
    • soldTofu(int tenki, int make): 売れるトーフの個数を計算します。
  4. 変数一覧表:

    • STARTGOLD: 開始時の所持金 (初期値: 5000)
    • GOALGOLD: 目標金額 (初期値: 30000)
    • TOFUBAIKA: トーフの売価 (初期値: 50)
    • TOFUGENKA: トーフの原価 (初期値: 40)
    • SELLMAX: 天気ごとの最大売上個数を格納した配列 (初期値: [500, 200, 100])
    • TENKIWORDS: 天気を表す言葉を格納した配列 (初期値: ["晴れ", "曇り", "雨"])
    • hare: 晴れの確率
    • kumori: 曇りの確率
    • ame: 雨の確率
    • random: 乱数生成に使用するRandomクラスのインスタンス
    • scanner: 入力を受け取るためのScannerクラスのインスタンス
    • input: ユーザーの入力を格納する変数
    • playerMake: プレイヤーが作成するトーフの個数
    • computerMake: コンピュータが作成するトーフの個数
    • playerSold: プレイヤーが売ったトーフの個数
    • computerSold: コンピュータが売ったトーフの個数
    • playerProfit: プレイヤーの利益
    • computerProfit: コンピュータの利益
    • playerGold: プレイヤーの所持金
    • computerGold: コンピュータの所持金
  5. 仕様書の拡張案:

    1. 難易度設定の追加
      現在のプログラムでは、トーフの作成数や売上個数などの要素がランダムに決定されています。拡張として、プレイヤーがゲームの難易度を選択できるようにします。難易度によって、作成数や売上個数などのパラメータを調整します。

    2. 複数日のプレイ
      現在のプログラムでは、ゲームは1日ごとに繰り返されますが、拡張として複数日のプレイをサポートします。プレイヤーは指定された日数分だけゲームを進めることができます。各日の結果や利益を記録し、最終日に最終的な所持金や勝敗を表示します。

    3. イベントの追加
      ゲームにさらなるバラエティを加えるために、イベントを追加します。イベントはランダムに発生し、天候や売上数に影響を与えることがあります。例えば、イベントが発生すると晴れの確率が上昇したり、売上数が増加したりすることがあります。イベントの内容や影響は多様化させることで、ゲームのエンターテイメント性を向上させます。

    4. UIの改善
      現在のプログラムはコンソールベースで動作しますが、拡張としてグラフィカルユーザーインターフェースGUI)を導入します。GUIを使用することで、より直感的な操作や視覚的な情報表示を実現し、ユーザーエクスペリエンスを向上させます。

    5. プレイヤーのカスタマイズ
      プレイヤーが自身のトーフ屋をカスタマイズできる機能を追加します。プレイヤーは店舗のデザインやトーフの品質、販売戦略などを選択できます。これにより、プレイヤーの戦略や選択の影響がゲームに反映され、より自己のプレイスタイルに合ったゲーム体験を提供します。            

      (ChatGPT)

今回の『イスカンダルのトーフ屋ゲーム』に関しても、まあまあの仕様書が出来上がったようです。ゲームの遊び方の部分がイマイチですが、それ以外は完璧と言えるかと思います。

特に仕様の拡張案(いつのまにか”仕様書の拡張案”に変わっている)に関しては、どんどん提案してくるようで、ChatGPTはこのあたりが得意分野と言うことがわかります。

 

つづきをお願いします

 

拡張案のつづきです:

  1. リーダーボードの追加
    プレイヤーのスコアや成績を追跡するリーダーボード機能を実装します。プレイヤーは自分の成績を保存し、他のプレイヤーと競い合うことができます。リーダーボードは、最も高い売上や最長連続営業日数などの指標でランキングされます。これにより、プレイヤー同士の競争やゲームへのモチベーションが高まります。

  2. 成長要素の追加
    プレイヤーがゲーム内で成長する要素を追加します。例えば、トーフのレシピを改良したり、新しい販売戦略を学んだりすることができます。成長要素は、プレイヤーの経験ポイントやレベルといった形で表現され、新たなチャレンジや報酬を提供します。

  3. 追加のトーフバリエーション
    現在のプログラムでは、トーフの種類は限られています。拡張として、さまざまなトーフのバリエーションを追加します。豆腐以外の材料を使った特別なトーフや、季節限定のフレーバーなど、プレイヤーにより多様な選択肢を提供します。

  4. インゲーム通貨とアイテムショップ
    プレイヤーがゲーム内で獲得した通貨を使用して、特別なアイテムやアップグレードを購入できるアイテムショップを実装します。アイテムショップには、効率的なトーフ作成ツールやマーケティングキャンペーンなど、プレイヤーの進行をサポートするアイテムが用意されます。

  5. マルチプレイヤーモード
    複数のプレイヤーがオンライン上で一緒にプレイできるマルチプレイヤーモードを追加します。プレイヤー同士が協力したり競争したりすることができます。共同経営のトーフ屋やトーフ作りの対戦など、さまざまなプレイスタイルに対応します。

  6. イベントや特別なイベントアイテムの追加 定期的なイベントを開催し、プレイヤーに特別な報酬やアイテムを提供します。例えば、季節イベントやトーフ祭りなどのイベントを開催し、限定のレシピや装飾品を手に入れることができます。これにより、プレイヤーはゲーム内での長期的な興味を維持しやすくなります。

  7. チャレンジモードの追加
    プレイヤーに特定の目標や制限が与えられるチャレンジモードを追加します。例えば、時間内にできるだけ多くのトーフを作る、予算制限内で最高の利益を上げるなどのチャレンジがあります。チャレンジモードは、プレイヤーの戦略力やスキルを試す機会となります。

  8. ストーリーラインの拡張
    ゲームにストーリーラインを追加し、プレイヤーがトーフビジネスの世界で冒険をする要素を提供します。プレイヤーはトーフ業界の新たな展開やキャラクターとの出会い、困難な選択を経験することができます。ストーリーラインはゲームの興奮と没入感を高め、プレイヤーにより深い体験をもたらします。

  9. コミュニティ機能の強化
    プレイヤー同士がコミュニケーションを取りやすい環境を提供します。フレンドリストやチャット機能などのコミュニティツールを実装し、プレイヤーが情報や助言を共有し、協力したり競い合ったりすることができるようにします。コミュニティの活発化は、ゲームの楽しさと持続性を高める効果があります。

  10. ゲームのバランス調整と改善
    フィードバックやデータ分析を通じて、ゲームのバランスを見直し、不均衡な要素を改善します。プレイヤーのフィードバックを重視し、ゲームプレイの公正さと面白さを向上させるために、(以下略)

    (ChatGPT)

今回の結論として、(無料利用の範囲での)ChatGPTはプログラミングの世界においては下記のようなことが言えると思います。

 

という感じで今回のシリーズ?は終わりたいと思います。

やっぱり趣味のプログラミングは楽しい(Part5)

Part4より

 

やはりここまでのプログラミング作業はChatGPTには重荷だったようで、本来の”生成AI”の能力範囲を超えた使い方だとの結論に達しました。分かってはいましたが……

結局、中途半端な出来損ないJavaソースをEclipseに貼り付け、それを自力で直すことにしましたが、内容がほとんどでたらめだったので、その骨組み(メソッド)を利用しただけで、元々のBASICソースを参照しながら大半のコードを書き換える結果となりました。まあ、こういう作業も嫌いじゃなく、若いころを思い出しながら地道にプログラムを作り込んで行きました。ほんと、20歳前後のころは寝ることを惜しんでパソコンの前でしこしことコード(言語はBASIC)を打ち込んでいたのです。

作ったプログラムは専門雑誌に何度か投稿し掲載もされ、自己満足していた自分がいました。(このあたりの話は長くなるのでここでは割愛)

月刊ASCII 1981年1月号に掲載されたプログラム

実を言うとJava言語は3年前に必要にかられて始めたばかりで、当初は「最近の言語はめんどくさいな」などと思いながら触っていたのですが、慣れてくると開発環境(Eclipse)に馴染んできたせいもあって、徐々に苦痛が快感に変化していったのです。

特にゲームソフトを作りこんでいる最中は夢中になって、若いころと同じように時間を忘れて触っている自分がいたりもします。まさか還暦を過ぎた自分が新たな言語にハマるとは思ってもみませんでした。

 

ということで、完成したJava言語にによる『イスカンダルのトーフ屋ゲーム』は下記のようになりました。ここで公開しても意味はないでしょうけど(本来はGitHubなどで公開すべき?)、とりあえずのメモ代わりに貼り付けておきます。(字数は足りるかな?)

 

Part6へつづく

 

/*
 * イスカンダルのトーフ屋ゲーム
 *         original copyright 1978 by N.Tsuda
 *        convert & arrange to Java version by T.Sunahachi 2023
 */

import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;

public class TofuGame {

    static int STARTGOLD = 5000;     // 開始時の所持金
    static int GOALGOLD = 30000;     // 目標金額
    static int TOFUBAIKA = 50;     // トーフの売価
    static int TOFUGENKA = 40;     // トーフの原価
    static int SELLMAX = {500, 200, 100};     // 天気ごとの最大売上個数
    static String
TENKIWORDS = {"晴れ", "曇り", "雨"};    // 天気を表す言葉
    
    static int hare;                 // 晴れの確率
    static int kumori;             // 曇りの確率
    static int ame;                 // 雨の確率

    private static Random random = new Random();

    // メインメソッド
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        String input;

        // 開始時の処理
        System.out.println("イスカンダルのトーフ屋ゲーム (Java版)");
        System.out.print("ルール説明しますか?[y/n]:");
        input = scanner.nextLine();
        if (input.equalsIgnoreCase("y")) {
            displayRules();
        }
        
        do {
            // ゲームのメイン処理
            System.out.println("===== ゲームスタート =====");            
            gameMain();
            // 継続確認処理
            System.out.println();
            System.out.print("プレイを続けますか?[y/n]:");
            input = scanner.nextLine();
            System.out.println();
            if (input.equalsIgnoreCase("n")) {
                System.out.print("おつかれさまでした。");
                break;
            }
        } while (true);
    }

    // 説明文の表示処理
    private static void displayRules() {
        System.out.println();
        System.out.println("ここはイスカンダル星。あなたはここでトーフ屋を経営し、");
        System.out.println("地球への帰還費用を作り出さなくてはいけません。");
        System.out.println("でもお向かいには、コンピュータが経営するトーフ屋があります。。。");
        System.out.println();
        System.out.println("トーフの原価は1個" + TOFUGENKA + "円、販売価格は" + TOFUBAIKA + "円です。");
        System.out.println("1日に売れる個数は天候に左右されます。");
        System.out.println("晴れると" + SELLMAX[0] + "個、曇りだと" + SELLMAX[1] + "個、雨のときは" + SELLMAX[2] + "個まで売れます。");
        System.out.println("トーフは日持ちしないので、売れ残った分はすべて廃棄します。");
        System.out.println("所持金" + STARTGOLD + "円から始めて、早く" + GOALGOLD + "円を超えた方が勝ちです。");
        System.out.println();
    }

    // トーフ屋ゲームのメイン処理
    private static void gameMain() {

        int playerMake;                 // プレイヤーの作成個数
        int computerMake;                 // コンピュータの作成個数
        int playerSold;                 // プレイヤーの売上個数
        int computerSold;                 // コンピュータの売上個数
        int playerProfit;                 // プレイヤーの利益
        int computerProfit;             // コンピュータの利益
        
        int playerGold = STARTGOLD;     // プレイヤーの所持金
        int computerGold = STARTGOLD;     // コンピュータの所持金

        do {
            // 所持金の表示処理
            System.out.println();
            System.out.println("あなたの所持金は " + playerGold + "円 です。");
            System.out.println("わたしの所持金は " + computerGold + "円 です。");
            System.out.println();

            // 勝負の判定処理
            if (playerGold >= GOALGOLD || computerGold >= GOALGOLD) {
                if (playerGold >= computerGold) {
                    System.out.println("***** あなたの勝ちです! *****");
                } else {
                    System.out.println("***** コンピュータの勝ちです *****");
                }
                return;
            }

            // 天気予報の表示処理
            tenkiYohou();

            // トーフの作成処理 
            playerMake = inpTofu(playerGold / TOFUGENKA);
            computerMake = compTofu(computerGold / TOFUGENKA, playerMake);

            // 翌日の天気の決定・表示処理
            int tenki = nextTenki();

            // 結果表示処理
            playerSold = soldTofu(tenki, playerMake);
            computerSold = soldTofu(tenki, computerMake);
            System.out.println("あなたは " + playerMake + "個 作り、" + playerSold + "個 売れました。");
            System.out.println("わたしは " + computerMake + "個 作り、" + computerSold + "個 売れました。");
            playerProfit = playerSold * TOFUBAIKA - playerMake * TOFUGENKA;
            computerProfit = computerSold * TOFUBAIKA - computerMake * TOFUGENKA;
            System.out.println();
            System.out.println("あなたの利益は " + playerProfit + "円 です。");
            System.out.println("わたしの利益は " + computerProfit + "円 です。");

            // 所持金の計算
            playerGold += playerProfit;
            computerGold += computerProfit;

        } while (true);
    }

    // 天気予報の表示
    private static void tenkiYohou() {

        System.out.println("***** 天気予報 *****");

        hare = randomNum(100);
        kumori = randomNum(100 - hare);
        ame = 100 - hare - kumori;

        System.out.println("晴の確率は " + hare + " %です。");
        System.out.println("曇の確率は " + kumori + " %です。");
        System.out.println("雨の確率は " + ame + " %です。");
    }

    // 0~指定数までの乱数を発生させる
    private static int randomNum(int max) {
        if (max == 0) return 0;
        return random.nextInt(max);
    }

    // トーフの作成数の入力処理
    private static int inpTofu(int limit) {

        System.out.println();
        System.out.println("現在、あなたが作ることのできるトーフは" + limit + "個までです。");
        Scanner scanner = new Scanner(System.in);
        int num = 0;
        
        do {
            System.out.print("作る個数を入力してください:");
            try {
                num = scanner.nextInt();
            } catch(InputMismatchException e) { 
                System.out.println("数字を正しく入力してください。");
                scanner.nextLine();        
                // ↑ dummy method! (https://qiita.com/Choco_Late/items/5b6a9fdc98b53194a643)
                continue;
            }
            if (num >= 1 && num <= limit) {
                break;
            } else {
                System.out.println("作ることのできる範囲外の個数です。再入力してください。");
            }
        } while (true);

        return num;
    }

    // コンピュータの作成個数
    private static int compTofu(int limit, int player) {

        int num;
        String watashi;
        if (ame >= 40) {
            num = SELLMAX[2];        // 雨を想定
        } else if (kumori < 40 - ame) {
            num = SELLMAX[0];        // 晴れを想定
        } else {
            num = SELLMAX[1];        // 曇りを想定                
        }
        if (num > limit) {
            num = limit;
        }
        if (num == player) {
            watashi = "も";
        } else {
            watashi = "は";
        }
        System.out.println("わたし" + watashi + " " + num + "個 トーフを作りました。");
        return num;
    }

    // 天気の決定&表示処理
    private static int nextTenki() {

        int tenkiVal;

        // 1~100のサイコロを振る
        int dice = random.nextInt(100) + 1;
        if (dice <= hare) {
            tenkiVal = 0;
        } else if (dice <= hare + kumori) {
            tenkiVal = 1;
        } else {
            tenkiVal = 2;
        }

        System.out.println();
        System.out.println("ーーーーーーーー 次の日 ーーーーーーーー");

        // ウェイト処理
        try {
            for (int i = 0; i < 3; i++) {
                System.out.print("・");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println();

        System.out.println("天候は " + TENKIWORDS[tenkiVal] + " です。");
        return tenkiVal;

    }

    // 売上個数の計算
    private static int soldTofu(int tenki, int make) {

        int sold = make;
        if (make > SELLMAX[tenki]) {
            sold = SELLMAX[tenki];
        }
        return sold;

    }
}

 

やっぱり趣味のプログラミングは楽しい(Part4)

Part3より

 

やはりというか、ChatGPTは万能ではないということで(もともと文章を生成する機能に特化したものだし)諦めればいいのに、しつこく再度トライすることにしました。

RubyでだめならPrologがあるさ」

ということで、今度はこんな感じで、ChatGPTにお願いしました。

 

以下のPrologのプログラムをJava言語に書き換えてください。

%;;;; -*-   coding: utf-8 -*-
%
% tofu.prolog  -  イスカンダルのトーフ屋ゲーム(Prolog版)
%   by たけおか (take at takeoka.net)
%   ver.1.0
%   2012/DEC/09
%  for SB-Prolog or GNU Prolog
%
% Copyright (C) 1978 - 2000 by Nobuhide Tsuda
% http://vivi.dyndns.org/tofu/tofu.html
% 著作権は津田伸秀さん (ntsuda@beam.ne.jp) にあります。
%
% この Prolog版は、たけおか しょうぞうが、
% 永野圭一郎氏の(gano@is.s.u-tokyo.ac.jp) perl版 tofu.plを元に
% 書き直したものです。
%
%
% consult('/home/take/src/tofuya/tofu-prolog/tofu.prolog').
% ?- tofu.

% millisleep
%#+sbcl (defun millisleep(x) (sleep (/ x 1000)))
millisleep(X) :- XX is X/1000.0, sleep(XX).


% 乱数初期化
:- dynamic(irandom/2).
defRand :- retractall(irandom(_,_)),defRand1.
defRand :- defRand1.

defRand1 :-
    current_prolog_flag(prolog_name,'GNU Prolog'),
    %%for GnuProlog
    !,asserta((irandom(R,N) :- !,random(RR),R is floor(RR * N + 0.0))).
defRand1 :-
    %%for SWIProlog
    !,asserta((irandom(R,N) :- (!,R is random(N)))).

% 0 - 100の整数乱数
random100(R) :- irandom(R, 101).

(以下略)

 

以下はJava言語に書き換えたプログラムです。

import java.util.Random;
import java.util.Scanner;

public class TofuGame {
    private static Random random = new Random();

    private static void millisleep(long milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static int random100() {
        return random.nextInt(101);
    }

    private static int calc(int comp, int hare, int ame) {
        int x;
        if (hare >= 50) {
            x = 500;
        } else if (ame > 30) {
            x = 100;
        } else {
            x = 300;
        }
        return Math.min(comp / 40, x);
    }

    private static void disp(String str) {
        System.out.print(str);
    }

    private static void displn(String str) {
        System.out.println(str);
    }

    private static void dispShojikin(String name, int gold) {
        int ggg = gold / 1000;
        System.out.print("   " + name + " " + gold + " 円  ");
        for (int i = 0; i < ggg; i++) {
            System.out.print("■");
        }
        for (int i = 0; i < Math.max(30 - ggg, 0); i++) {
            System.out.print("□");
        }
        System.out.println();
    }

    private static void dispYohou(int hare, int kumori, int ame) {
        int hhh = hare * 10 / 25;
        int kkk = kumori * 10 / 25;
        int aaa = 1000 / 25 - (hhh + kkk);
        System.out.println();
        System.out.println("明日の天気予報: 晴れ " + hare + "%  くもり " + kumori + "%  雨 " + ame + "%");
        for (int i = 0; i < hhh; i++) {
            System.out.print("◎");
        }
        for (int i = 0; i < kkk; i++) {
            System.out.print("・");
        }
        for (int i = 0; i < aaa; i++) {
            System.out.print("●");
        }
        System.out.println();
    }

    private static int inpTofu(int limit) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("トーフを何個作りますか?(1〜" + limit + ")> ");
            int x = scanner.nextInt();
            if (x > 0 && x <= limit) {
                return x;
            }
        }
    }

    private static int profit(int n, int sold) {
        return Math.min(sold, n) * 50 - (n * 40);
    }

    private static void dispResult(int player, int comp) {
        if (player > comp) {
            System.out.println("あなたの勝ちです。");
        } else if (player < comp) {
            System.out.println("コンピュータの勝ちです。");
        } else {
            System.out.println("引き分けです。");
        }
    }

    private static void tofu() {
        defRand();
        displn("イスカンダルのトーフ屋ゲーム (Java版)");

 

(中略)

 

このように修正することで、プレイヤーとコンピュータのトーフの作成、天候の設定、利益の計算などが行われます。ゲームは所持金が30,000円以上になるかどちらかの勝利条件が満たされるまで続きます。

(ChatGPT)

 

と、うまく行きそうに思えたのですが、必要となるメソッドが何故か抜けているのです。

 

calc メソッドを表示して

申し訳ありませんが、提供されたコードには calc メソッドが含まれていません。おそらく、別のコードやメソッドを参照している可能性があります。もしくは、calc メソッドについて詳細な説明を教えていただければ、その内容に基づいてお手伝いできるかもしれません。ご提供いただいた情報が不十分な場合は、追加の詳細を教えていただけますか?

(ChatGPT)

 

いやいや、元のソースコードにcalcメソッドに相当する部分は含まれていますよ。どこかで抜け落ちたのだろうということで、今度はChatGPTが生成したJava言語のソースを貼り付けてみたのです。

 

このJavaのコードを正しく修正してください。

import java.util.Random;
import java.util.Scanner;

public class TofuGame {
    private static Random random = new Random();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("イスカンダルのトーフ屋ゲーム (Java版)");
        System.out.println("ルール説明しますか?[y/n]");
        String input = scanner.nextLine();
        if (input.equalsIgnoreCase("y")) {
            displayRules();
        }
        tofu();
        System.out.println("プレイを続けますか?[y/n]");
        input = scanner.nextLine();
        if (input.equalsIgnoreCase("y")) {
            main(args);
        }
    }

(以下略)

 

修正されたコードは以下の通りです。

(コードは略)

これで修正が完了しました。修正したコードを試してみてください。修正内容は、不要なmainメソッドを削除し、calcメソッドを追加している点です。また、関数名の日本語表記を英語表記に修正しています。

ご不明な点があれば、遠慮なくお知らせください。

(ChatGPT)

 

なんとなくうまく行ったような末尾の文章でしたが、結局は未完成のプログラムでしかなかったのです。当然でしょうね。もともと仕様が不明だと言って抜け落ちていたcalcメソッドを何の情報も与えない状況で”勝手に生成”したのですから。

 

Part5へつづく

やっぱり趣味のプログラミングは楽しい(Part3)

Part2より

 

ところが、結論を先に書けば、やっぱりというか今回はなかなかうまくいかなかったのです。まずは、こんな質問をしてみました。

イスカンダルのとうふ屋ゲーム」を知っていますか?

申し訳ありませんが、「イスカンダルのとうふ屋ゲーム」については、私の知識には含まれていません。可能性のある理由として、そのゲームが非常に新しいか、または地域的な人気があるゲームである可能性があります。詳細について教えていただければ、お手伝いできるかもしれませんが、保証することはできません。他の質問やお手伝いできることがあれば、どうぞお知らせください。

(ChatGPT)

 

まあ、知っているほうがおかしいとは思いましたが。日本人の99.9999%の人は知らないと思うし(数字は適当)、ということで、本題のほうにはいったわけですが。

以下のRubyで書かれたプログラムをJava言語に変換してください。

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
##■イスカンダルのトーフ屋ゲーム■ (外部仕様より再現)
##Copyright (C) 1978-2013 by N.Tsuda
##Reference: http://vivi.dyndns.org/tofu/tofu.html
##背景: あなたはイスカンダル星で遭難し、帰りの費用を稼ぐためにトーフをなるべくたくさん売ってお金を稼がなければならない。
##最初に所持金1000円が与えられる。
##30000円儲けることができれば、めでたくイスカンダルから脱出することができる。
##トーフは製造に一個あたり10円かかり、一個あたり12円で売ることができる。
##トーフは晴れの日は100個、曇りの日は50個、雨の日は10個売れる。
##売れなかった分は損失となる。
##あなたは天気予報を見て、明日いくつのトーフを製造するかを決めねばならない。
##
##A Tofu vendor surviving in Iscandar
##Copyright (C) 1978-2013 by N.Tsuda
##Reference: http://vivi.dyndns.org/tofu/tofu.html
##Background: You are a castaway in planet Iscandar in outer space, and you have to gain money by making and selling Tofu in order to go back to your mother planet.
##Initially you have 1,000 yen. The goal is to gain 30,000 yen for your traveling fee.
##One Tofu costs 10 yen for production, and the unit price is 12 yen.
##The sales of Tofu depends on weather: you can sell 100 Tofu on a fine day, 50 on a cloudy day, and 10 on a rainy day.
##Watch weather forecast and determine the quantity of Tofu you are going to make.

class Iscandar
  COST    = 10
  PRICE   = 12
  RATE    = {
    fine:   100,
    cloudy:  50,
    rainy:   10
  }
  RED = "\e[31m"
  GRN = "\e[32m"
  SO  = "\e[m"

(以下略)

 

以下は、提供されたRubyプログラムをJava言語に変換したものです。ただし、Javaの出力はターミナルではなくコンソール上に表示されるため、一部の出力部分には調整が必要です。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

public class Iscandar {
    private static final int COST = 10;
    private static final int PRICE = 12;
    private static final Map<String, Integer> RATE = new HashMap<String, Integer>() {{
        put("fine", 100);
        put("cloudy", 50);
        put("rainy", 10);
    }};
    private static final String RED = "\u001B[31m";
    private static final String GRN = "\u001B[32m";
    private static final String SO = "\u001B[m";

    private Map<String, Integer> forecast;
    private int total;

    public Iscandar() {
        forecast = new HashMap<>();
        total = 1000;

        showComment();
    }

    private void showComment() {
        String filename = Iscandar.class.getSimpleName() + ".java";
        // コメント行のみを抽出して表示することで対応します
        // 必要に応じてファイルの読み込みを行ってください
        System.out.println("##イスカンダルのトーフ屋ゲーム## (外部仕様より再現)");
        System.out.println("##Reference: http://vivi.dyndns.org/tofu/tofu.html");
        System.out.println("##背景: あなたはイスカンダル星で遭難し、帰りの費用を稼ぐためにトーフをなるべくたくさん売ってお金を稼がなければならない。");
        System.out.println("##最初に所持金1000円が与えられる。");
        System.out.println("##30000円儲けることができれば、めでたくイスカンダルから脱出することができる。");
        System.out.println("##トーフは製造に一個あたり10円かかり、一個あたり12円で売ることができる。");
        System.out.println("##トーフは晴れの日は100個、曇りの日は50個、雨の日は10個売れる。");
        System.out.println("##売れなかった分は損失となる。");
        System.out.println("##あなたは天気予報を見て、明日いくつのトーフを製造するかを決めねばならない。");
        System.out.println();
    }

    private Map<String, Integer> calcProbab() {
        Map<String, Integer> probab = new HashMap<>();
        Random random = new Random();
        probab.put("fine", random.nextInt(100));
        probab.put("cloudy", random.nextInt(100 - probab.get("fine")));
        probab.put("rainy", 100

(ChatGPT)

 

例によって途中で切れています。そこで、いつものように続きを表示させて、すべて表示されたと思われるソースコードEclipseに貼り付けました。エラーは出なかったものの実行させると、思ったような動きにはならずゲームルール的に滅茶苦茶な動作をしたのです。

 

Part4につづく

やっぱり趣味のプログラミングは楽しい(Part2)

Part1より

 

イスカンダルのトーフ屋ゲーム

このゲームは月刊アスキー1978年6月号に掲載されたBASICプログラムで、この当時はまだ日本ではマイコンブームにはなりつつありましたが、製品としてパソコンの形をしたものはほとんどなく、翌年の1979年に発表・発売されたNECPC-8001の登場を待たねばなりませんでした。なので、このゲームもオリジナルのマイコンで動かしていたようです。

イスカンダルのトーフ屋ゲームは筆者が1978年に考案し、ASCII誌上で発表したもので,一種の経営戦略シュミレーションゲームです.当時のコンピュータの演算能力は現在からは考えられないくらい貧弱で(当時の筆者のマシンは8ビット6800CPU, クロック1MHz, メモリ4~32Kバイト,HDD, FDD はなく,テープレコーダに300bpsでプログラムを保存していました),アプリケーションと言えば単純な数当てゲームのようなものばかりでしたので,このトーフ屋ゲームは単純な設定とは言え,コンピュータと対戦できるということで大変好評でした.

(プログラムの作者のWebページ=イスカンダルのトーフ屋ゲーム (vivi.dyndns.org)より抜粋引用

上記のような背景もあって、オリジナルのソースプログラムは、私が調べた限りネット上には見つからず、ChatGPTを利用してのJava言語への変換を試すことはできません。そもそもオリジナルはBASIC言語、それも構造化される前の旧仕様であり、当該ソースGOTO文を多用した”スパゲッティプログラム”(失礼)のため、おそらくまともには変換できないだろうとは思います。

唯一手元に残っている『イスカンダルのトーフ屋ゲーム』が掲載されていた月刊アスキー1978年6月号のページの写真

上記の写真は、以前所持していた月刊アスキーの該当ページの写真で、『イスカンダルのトーフ屋ゲーム』がずっと気になっていることが分かる証拠でもあります。不鮮明ですが、ギリギリでBASICのソースコードを確認することが出来ますが、このままではJava言語のような構造化言語に書き換えることは難しい(できないことはない)と思いました。

そこで、前出の作者ページの中にある他言語(他環境)への移植プログラムソースを元ネタにしようと各リンク先を探ってみたところ、かなりのデッドリンクとなっていました。かなり古い情報を掲載していてそのままアップデートされていない状態のようです。

ちなみにそのページの最終更新日を調べてみると、下記のように2015年の12月になっているので、もうすでに8年近く放置されていることが分かります。(結局は、リンク一覧の最新日付と一致するので、最後のリンクの追加日を見ればよかったという話になるわけだが……)

ブラウザの開発ツール上でJavaScriptを利用してそのページの最終更新日を知ることが出来るようだ

結局、デッドリンクになっていない貴重なリンク先を巡って、使えそうなソースコードを探してみることにしました。こう言ったら怒られそうですが「帯に短したすきに長し」状態で、なかなか適当なソースコードが見つかりませんでした。

その中で、下記の2つが使えそうでした。

1.Shozo Hatta氏Ruby
2.たけおか氏Prolog

いずれも私自身が経験したことのない言語だったので、ソースコードを見てもピンと来ませんでした。でも、コメントを参考にしたりコードを眺めてみるとそれぞれ下記の仕様のようです。

1.オリジナルの外部仕様からリバースした形で元の仕様に近い移植
2.オリジナルからの移植ではなく別の人が移植したperl版から移植したもの

オリジナルの再現性は1のほうが高いようでしたが、2のほうが仕様としてはかなり拡張されており、さらには日本語化もされルール説明も付加されるなど親切設計になっていました。

横道に逸れますが、後学のためにRuby言語とProlog言語の特徴を調べてみました。

Ruby

Rubyは日本人の技術者が開発した日本製のプログラミング言語であり、Webアプリケーションの開発に用いられることが多い言語です。データやそこに作用する手続きの集合体をオブジェクトとして表現するオブジェクト指向言語で、シンプルに短いコードで記述できるという特徴があります。

Ruby / ルビーとはどのような開発言語か。Rubyで出来ること・事例を紹介 | 株式会社GeNEE より

Prolog

Prologは名称の由来の通り、論理を使ってプログラミングをする言語です。少し難しい言い回しですが、「Prologは数理論理学に基づいた“論理型言語 ”である」と表現できるでしょう。Prologは、論理的思考が得意なのです。また、ALGOL、BASIC、C言語などに代表される手続き型言語とは異なり、Prologは宣言型言語です。 Prologでは、対象の性質を宣言することでプログラムを構成していきます。

Prologとは?プログラミング言語を初心者にもわかりやすく解説 | プログラミング入門 (programming-world.net) より

オブジェクト指向言語であるJavaに移植するには、Rubyのほうがやりやすそうですが果たしてどうでしょうか。いずれにしても、使えそうなコードはこれぐらいなので、さっそくChatGPTに変換を頼むことにしました。さて。。。

 
Part3へつづく)

やっぱり趣味のプログラミングは楽しい(Part1)

訳あって3年前からとある学校でJava言語を教えているのですが、学生相手とは言え仕事でプログラミングするのは作業的で、あまり面白くなかったりします。まあ、その内容がゲームの場合はそれなりに楽しくもありますが、教材として扱う以上、あまり凝ったものや自分の趣味に偏ったものは採用できないのです。

中日新聞朝刊(2023年6月3日)の社会面より

昨日、東海地方の大雨の影響で午後から学校(上記の学校とは別)が休校になったために、暇になった私は帰宅後にEclipseを起動してあれこれいじっていました。

なんか面白いゲームでもないかな?

そう思った時には、流行りのChatGPTの出番となります。

 

Java言語でカードゲームを作って

続きを読む