PostgreSQL上で動くハノイの塔
SQLとPLPGSQLで作りました。
https://www.youtube.com/watch?v=HolUUY6kI9c
CREATE TABLE hanoi ( id INTEGER PRIMARY KEY, tower INTEGER NOT NULL ); CREATE OR REPLACE FUNCTION HELP(OUT function TEXT, OUT example TEXT, OUT description TEXT) RETURNS SETOF RECORD AS $$ BEGIN RETURN QUERY SELECT 'HELP()'::TEXT, 'SELECT * FROM HELP(); / SELECT HELP();'::TEXT, 'このヘルプです。'::TEXT; RETURN QUERY SELECT 'RESET(HEIGHT)'::TEXT, 'SELECT * FROM RESET(3); / SELECT RESET(3);'::TEXT, '高さHEIGHTのハノイの塔を作ります。'::TEXT; RETURN QUERY SELECT 'SHOW()'::TEXT, 'SELECT * FROM SHOW(); / SELECT SHOW();'::TEXT, '現在のハノイの塔の状態を表示します。'::TEXT; RETURN QUERY SELECT 'MOVE(FROM, TO)'::TEXT, 'SELECT * FROM MOVE(1, 2); / SELECT MOVE(1, 2);'::TEXT, 'FROMからTOに移動します。'::TEXT; END; $$ LANGUAGE PLPGSQL; CREATE OR REPLACE FUNCTION SHOW(IN message TEXT, OUT tower_1 INTEGER[], OUT tower_2 INTEGER[], OUT tower_3 INTEGER[], OUT message TEXT) RETURNS SETOF RECORD AS $$ SELECT (SELECT ARRAY_AGG(id) FROM (SELECT id FROM hanoi WHERE tower = 1 ORDER BY id) AS h), (SELECT ARRAY_AGG(id) FROM (SELECT id FROM hanoi WHERE tower = 2 ORDER BY id) AS h), (SELECT ARRAY_AGG(id) FROM (SELECT id FROM hanoi WHERE tower = 3 ORDER BY id) AS h), message ; $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION SHOW(OUT tower_1 INTEGER[], OUT tower_2 INTEGER[], OUT tower_3 INTEGER[], OUT message TEXT) RETURNS SETOF RECORD AS $$ SELECT * FROM SHOW(NULL); $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION RESET(IN height INTEGER, OUT tower_1 INTEGER[], OUT tower_2 INTEGER[], OUT tower_3 INTEGER[], OUT message TEXT) RETURNS SETOF RECORD AS $$ BEGIN DELETE FROM hanoi; IF height < 2 THEN RETURN QUERY SELECT * FROM SHOW('引数は高さ2以上でないといけません。'); RETURN; END IF; INSERT INTO hanoi (id, tower) SELECT generate_series, 1 FROM GENERATE_SERIES(1, height); RETURN QUERY SELECT * FROM SHOW(NULL); END; $$ LANGUAGE PLPGSQL; CREATE OR REPLACE FUNCTION MOVE(IN from_tower INTEGER, IN to_tower INTEGER, OUT tower_1 INTEGER[], OUT tower_2 INTEGER[], OUT tower_3 INTEGER[], OUT message TEXT) RETURNS SETOF RECORD AS $$ BEGIN DECLARE height INTEGER; BEGIN height := (SELECT MAX(id) FROM hanoi); IF NOT from_tower BETWEEN 1 AND height OR NOT to_tower BETWEEN 1 AND height THEN RETURN QUERY SELECT * FROM SHOW(FORMAT('引数は1以上%s以下でないといけません。', height)); RETURN; END IF; DECLARE target_id INTEGER; BEGIN target_id := (SELECT id FROM hanoi WHERE tower = from_tower ORDER BY id ASC LIMIT 1); IF target_id IS NULL THEN RETURN QUERY SELECT * FROM SHOW(FORMAT('タワー%sは空です。', from_tower)); RETURN; END IF; IF EXISTS(SELECT * FROM hanoi WHERE tower = to_tower AND id < target_id) THEN RETURN QUERY SELECT * FROM SHOW('対象のタワーに小さな数字があります。'); RETURN; END IF; UPDATE hanoi SET tower = to_tower WHERE id = target_id; IF (SELECT COUNT(*) = height FROM hanoi WHERE tower = 3) THEN RETURN QUERY SELECT * FROM SHOW('あなたの勝ち!'); ELSE RETURN QUERY SELECT * FROM SHOW(); END IF; END; END; END; $$ LANGUAGE PLPGSQL;













