ここは、技術情報、身の回りに起こった出来事を、「もしかしたらみんなの役に立つかもしれない」と思って書き留めておく場所です。

Image

【paiza】初恋プログラミング研究会やってみた【PHP/JavaScript解答例】

2024年6月10日
 

piazaというプログラマ向け学習等のサービスを行っているサイトがあります。

最近見たら、新しいものが何個か追加されていたので、遊んでみました。

前回の

【PHP】ゲーム STEINS;GATE 電脳言語のオルダーソンループ プログラミング【解答例】

もこの流れです。

今回は、新しいものもやってみようかと。

 

概要

ゲームのタイトルは

初恋プログラミング研究会 ~海に行こうよ~

です。

エピソードを選ぶとプログラミング問題が出題され、正解するとストーリーが流れる

といった遊び方となっています。

申し訳ないのですが、テキストを読んでいないので、結局海に行ったのかどうか分かっておりません

(二人の行方は君の目で確かめてくれ!)

 

このページでは、私が書いたPHPでの解答例を出していきたいとます。追記でJavaScriptでの解答も載せてみました。

 

色々なコードを見ると勉強になるので、私以外の人のPHP・JavaScriptの勉強になれば幸いです。

コードを書くにあたって考慮した点も、一緒に記述しています。

エピソード1は選択問題のため、記載していません。

 

エピソード問題2 帰り道に交わした約束

概要

  • 問題:手紙に書かれた文字列から「SUNSET」に続く3文字を抽出する。

 

PHP解答例(シンプル版)

<?php
$S = trim(fgets(STDIN));
$a = explode('SUNSET', $S . $S);
echo substr($a[1], 0, 3);
?> 

JavaScript解答例(シンプル版)

const S = require('fs').readFileSync('/dev/stdin','utf-8');
const a = (S + S).split('SUNSET');
console.log(a[1].substr(0,3));

 

「足りない文字の分は文字列 S の 1 文字目に戻り」という記述があるため、慣れたプログラマはおそらく入力文字列を二つ結合すると思われます。

なぜなら、1文字目に戻る処理が面倒だからです。

文字列検索ですが、strposで「SUNSET」を検索してもよいです。

ただ、こういった処理の場合、私はexplodeで分割することにしています。

分割したら$a[0]に「SUNSET」前の文字列、$a[1]に「SUNSET」後の文字列が入りますのでsubstrで3文字切り出して終わりです。

 

PHP解答例(短縮版)

<?php
$S = trim(fgets(STDIN));
echo substr(explode('SUNSET', $S . $S)[1], 0, 3);
?> 

JavaScript解答例(短縮版)

const S = require('fs').readFileSync('/dev/stdin','utf-8');
console.log((S + S).split('SUNSET')[1].substr(0,3));

 

シンプル版を短縮させたものです。

 

PHP解答例(高効率版)

<?php
$a = explode('SUNSET', trim(fgets(STDIN)));
echo substr($a[1] . $a[0], 0, 3);
?> 

JavaScript解答例(高効率版)

const a = require('fs').readFileSync('/dev/stdin','utf-8').split('SUNSET');
console.log((a[1] + a[0]).substr(0,3));

 

分割してから結合すれば同じだし、効率良くない?というものです。

 

PHP解答例(正規表現版)

<?php
preg_match('/(.*)SUNSET(.*)/', trim(fgets(STDIN)), $m);
echo substr($m[2] . $m[1], 0, 3);
?> 

JavaScript解答例(正規表現版)

const m = require('fs').readFileSync('/dev/stdin','utf-8').match(/(.*)SUNSET(.*)/);
console.log((m[2] + m[1]).substr(0,3));

 

割と使うかもね というものです。

 

エピソード問題3 海に広がる特別な想い

概要

  • 問題:フィボナッチ数列のn番目の結果を出力する。

 

PHP解答例(ループ版)

<?php
$n = trim(fgets(STDIN));
$a = 1;
$b = 1;
for ($i = 1; $i < $n; $i ++) {
	[$a, $b] = [$b, $a + $b];
}
echo $a;
?> 

JavaScript解答例(ループ版)

const n = require('fs').readFileSync('/dev/stdin','utf-8');
let a = 1;
let b = 1;
for (let i = 1; i < n; i ++) {
	[a, b] = [b, a + b];
}
console.log(a);

 

フィボナッチ数列を計算して出力してください…

フィボナッチ数列をどうやって計算するのかだけだと思います。

 

PHP解答例(再帰版1)

<?php
function f($i, $a, $b) {
	global $n;
	return ($n <= $i) ? $a : f($i + 1, $b, $a + $b);
}
$n = trim(fgets(STDIN));
echo f(1, 1, 1);
?> 

 

JavaScript解答例(再帰版1)

const n = require('fs').readFileSync('/dev/stdin','utf-8');
const f = (i, a, b) => (n <= i ? a : f(i + 1, b, a + b));
console.log(f(1, 1, 1));

 

ループじゃなくて再帰でやりましょうというやつです。

 

解答例(再帰版2)

<?php
function f($a, $b) {
	global $i, $n;
	return ($n <= $i ++) ? $a : f($b, $a + $b);
}
$n = trim(fgets(STDIN));
$i = 1;
echo f(1, 1);
?> 

JavaScript解答例(再帰版2)

const n = require('fs').readFileSync('/dev/stdin','utf-8');
let i = 1;
const f = (a, b) => ((n <= i ++) ? a : f(b, a + b));
console.log(f(1, 1));

 

再帰版1でスタックにカウント積む必要ある?と思う人に。

 

ランキング 2月14日に貰える何か

 

概要

  • 問題:五目並べの勝敗を出力する。

 

PHP解答例(解説用)

<?php
$bd = str_replace(['.', "\n"], ['0', ''], stream_get_contents(STDIN));
$ptn = [17043521,1118480,32505856,1015808,31744,992,31];
$O = bindec(str_replace(['O','X'], ['1','0'], $bd));
$X = bindec(str_replace(['O','X'], ['0','1'], $bd));
foreach ($ptn as $v) {
	if (($O & $v) === $v) {
		echo 'O';
		exit;
	}
	if (($X & $v) === $v) {
		echo 'X';
		exit;
	}
}
echo 'D';
?> 

JavaScript解答例(解説用)

const bd = require('fs').readFileSync('/dev/stdin','utf-8').replace(/\n/g, '').replace(/\./g, '0');
const ptn = [17043521,1118480,32505856,1015808,31744,992,31];
const O = parseInt(bd.replace(/O/g, '1').replace(/X/g, '0'), 2);
const X = parseInt(bd.replace(/O/g, '0').replace(/X/g, '1'), 2);
for (let v of ptn) {
	if ((O & v) === v) {
		console.log('O');
		process.exit();
	}
	if ((X & v) === v) {
		console.log('X');
		process.exit();
	}
}
console.log('D');

 

解説用に簡単にしたものを掲載しています。

5x5のマス目の五目並べで勝ったということは、5個並んでいるということです。

(当たり前)

じゃあ、"O"か"X"を1に”.”を0にして11111があれば勝ったということじゃないですかね?

そうすれば、ビット演算で判定できるのでは?

みたいな考え方です。

5x5の盤面を全部くっつけた文字列で、1111100000000000000000000(2進数)であれば、一番上の行で揃ったという判定となります。これを10進数に直すと32505856となります。$ptnの3番目くらいにありますね。

こんな感じで斜めのパターンも入れて全部で7つが$ptnに入ってますので、あとは&で判定していくだけです。

もうちょっとコードを縮めて、ランキングでは7位となっています。

それ以上はアルゴリズム関係ない世界だと思われます。

 

まとめ

問題が少なく簡単すぎるので気軽に試すくらいで丁度いいかもしてません。

ランキング問題だけ極端でしたね。