0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PostgreSQLで○×ゲーム(PL/pgSQLサンプルプログラム)

Last updated at Posted at 2024-06-27

PL/pgSQLサンプルプログラム

PL/pgSQL入門用のサンプルプログラムとして、○×ゲームを作成しました。

○×ゲームの仕組み

まず○×を書くマスを用意する必要がありますが、今回は○×をテーブルに記録します。
テーブルは行番号・列番号・マークの3列用意します。
行番号と列番号でマスを表現し、そのマスに記入されたマークを表現します。

テーブルとマス目.png

レコードそのまま表示してしまうと○×ゲームになりませんが、表示するときにマス目状に表示すればOKです。

○×ゲームのテーブル作成

CREATE TABLE marubatsu_field (
    row_id integer NOT NULL CHECK (1 <= row_id AND row_id <= 3),
    column_id integer NOT NULL CHECK (1 <= column_id AND column_id <= 3),
    mark char(1),
    PRIMARY KEY (row_id, column_id)
);

○×ゲームの関数

CREATE OR REPLACE FUNCTION marubatsu(rownum integer, columnnum integer) RETURNS text AS $$
DECLARE
    row_count integer;
    init_row_num CONSTANT integer := 3;
    init_column_num CONSTANT integer := 3;
    sente_mark marubatsu_field.mark%TYPE;
    gote_rec RECORD;
    display_cur CURSOR FOR
        SELECT row_id, column_id, COALESCE(mark, ' ') AS mark 
        FROM marubatsu_field ORDER BY row_id, column_id;
    display_text text := '';
BEGIN -- 実行部の開始
    -- 引数チェック
    IF rownum < 1 OR rownum > 3 THEN
        RAISE EXCEPTION '行指定の数字が有効範囲外を指定しています。';
    ELSIF columnnum < 1 OR columnnum > 3 THEN
        RAISE EXCEPTION '列指定の数字が有効範囲外を指定しています。';
    END IF;

    -- 行が初期化行数じゃなければ初期化
    SELECT COUNT(*) INTO row_count FROM marubatsu_field;
    IF row_count != (init_row_num * init_column_num) THEN
        DELETE FROM marubatsu_field;
        FOR i IN 1 .. init_row_num LOOP
            FOR j IN 1 .. init_column_num LOOP
                INSERT INTO marubatsu_field (row_id, column_id) VALUES (i, j);
            END LOOP;
        END LOOP;
    END IF;

    -- 先手が選んだ位置に○を入れる
    SELECT mark INTO sente_mark FROM marubatsu_field WHERE row_id = rownum AND column_id = columnnum;
    IF sente_mark IS NOT NULL THEN
        -- すでにマークが入っていたらメッセージ
        RAISE EXCEPTION '指定された位置はすでにマークされています。';
    ELSE
        UPDATE marubatsu_field SET mark = '' WHERE row_id = rownum AND column_id = columnnum;
    END IF;

    -- 後手がマークされていないところをランダムで選択して×を入れる
    BEGIN -- サブブロック開始
        SELECT * INTO gote_rec FROM marubatsu_field WHERE mark is null ORDER BY random() limit 1;
        IF NOT FOUND THEN
            -- 全部埋まっていたら例外を発生させる
            RAISE EXCEPTION SQLSTATE 'ABCDE' USING MESSAGE = 'もう書き込める場所がありません。';
        ELSE
            UPDATE marubatsu_field SET mark = '×' WHERE row_id = gote_rec.row_id AND column_id = gote_rec.column_id;
        END IF;
    EXCEPTION
        -- 全部埋まっていた場合に発生した例外をキャッチしてメッセージを表示
        WHEN SQLSTATE 'ABCDE' THEN
            RAISE NOTICE '%', SQLERRM;
    END; -- サブブロック終了

    -- 全てのレコードを取得して、表示用のテキストを作成する
    FOR rec IN display_cur LOOP
        CASE rec.column_id
            WHEN 1 THEN display_text := display_text || ' ' || rec.mark;
            WHEN 2 THEN display_text := display_text || ' | ' || rec.mark;
            WHEN 3 THEN display_text := display_text || ' | ' || rec.mark || ' ' || chr(10);
        END CASE;
    END LOOP;

    -- 表示用のテキストを返す
    RETURN display_text;

END; -- 実行部の終了
$$ LANGUAGE plpgsql;

おまけ

実は上記のソースには、勝利判定処理がありません。
修正して組み込んでみてもいいかもしれません。
また、あくまでもPL/pgSQLのサンプルなので、機能を説明するために冗長なソースになっているところもあります。もっと簡潔にできるようトライしてもいいでしょう。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?