CPCTF参加記その1

2025/04/21

登録!参加!問題解くぞ!

順位とか考えてないので、点数低くてもいろんな問題を解くことを念頭にとりあえずLV.1から取り組みました。それぞれの問題について、自分の思考プロセスと解答について書いていきます。一度もプログラミング経験のない方や新入生向けに、詳しく説明していきます。

CTFの常識的なもの

問題を解いていく前にCTFの常識について軽く説明します。CTFはものすごく簡単にいうと、問題を解いたら得点と交換する文字列がもらえるのでそれをサイトに送って得点を稼ぐ、というちょっと変わった競技です。この文字列を**flag(フラッグ、またはフラグ)**と呼び、とりあえず問題を解いたらもらえるもの、と考えておくといいと思います。

flagについてですが、基本的に文字列で与えられます。またその文字列も形が決まっていることが多いです。今回の大会では**CPCTF{(英数字の文字列)}**という指定があります。だからなんだという感じもしますが、逆にこのように形が決まっていれば、何かしらの形で文字列をもらった時に、その文字列と解答が一致しているだろうということが推測できるという利点があります。また、実際にこの仕様を使う問題もあります。

もう一つflagについての特性があります。それはflagの中身(CPCTF{hogefuga}だとしたらhogefugaの部分)は何かしらの意味がある文字列になっていることが多いということです。もちろん、CPCTF{ScienceTokyo}などそんなわかりやすいものではありません。それを少しいじったCPCTF{Sc13nc3_T0ky0}のような、特定の英字を見た目が似ている数字(iは1、Eは3、oは0など)に置き換えたものが大半を占めます。このような記法をLeetと呼びます。ゲームなどでみたことがある人もいるのではないでしょうか?これによって、文字列が意味のあるものとなり獲得したflagが正しいものであるという確証を取ることができます。

では、実際に解いていきましょう!

45^2

整数Nが与えられるのでそれの二乗を出力せよという問題。yukicoderというサイトで正誤チェックを行うようです。ここではyukicoderについての解説は省きます。

さて、プログラミングについて全く知らない方は入力の受け取りってなんだ?となると思います。yukicoderに入出力についての記事があるので覗いてみましょう。初心者の方にはPython3かC++をお勧めします。以降の解説ではその二つを用いて解説します。(Pythonが一番わかりやすいです!)

自分のパソコン(ローカル環境と呼びます)でコードを動かすのは少し大変なので、yukicoderのオンライン実行を使って感覚を掴んでみましょう。

ここからはyukicoderでプログラムを動かす部分に入ります。作者の解説がわかりにくいかもしれないので、yukicoderの初めての方へを先に見ることをお勧めします。

一旦、次のコードをコピーして、ページ上部の言語の欄で対応したものを選びましょう。C++の方はそのまま(おそらくC++17~みたいな感じになっている)で、Pythonを使う方はPython3を選びましょう。

// C++
#include <iostream>
#include <string>

using namespace std;

int main() {
    int N;
    cin >> N;
    cout << N << endl;
    return 0;
}
# Python3
N = input()
print(N)

次に標準入力に好きな数(大きすぎるのはNG!)を入れてみて下さい。この時半角数字(0,1,2...)で入力し、全角数字(0,1,2...)では入力しないようにしましょう。半角数字は適当にキーを入力した時にアルファベットが大きくならなければ入力できます。別の言い方をすると、日本語入力の状態でなければ大丈夫です。

それでは実行ボタンを押してみましょう!右側の標準出力の欄に入力した数が出てくるはずです。もしも表示されなかったら、標準エラー出力に何かすごい文字列が出ていると思うので、入力したコードと入力したもの、エラーメッセージを全てコピーアンドペーストをしてChatGPTに投げてみましょう。だいたいこれで治ります。

さて、コードの解説に移ります。C++から解説していきます。(Pythonを知りたい方はこのセクションを飛ばして下さい)

C++

C++はいかんせん複雑なのでここではものすごく端折りますのでご了承ください。(詳しく知りたい方はGoogleで自分に合った記事を見つけてみて下さい。)

まず、最初の部分を見てみましょう。

// C++
#include <iostream>
#include <string>

using namespace std;

なんか呪文を唱えているようです。この呪文はとりあえず唱えておくと良いものだと考えておいて下さい。

では、次の部分を見ていきましょう。

int main() {
  中略
  return 0;
}

ここも呪文のようなものです。中略の部分にやりたいことを書くのがメインだと考えておくと良いでしょう。ここもかなり使い回します。

では、本題の中略の部分を見ていきましょう。

    int N;
    cin >> N;
    cout << N << endl;

どうやら何かしたいたびに最後にセミコロン(;)をつけないといけないようですね。これは覚えておきましょう。さて、初めの行の「int N;」についてですが、これは「整数の変数(英語でinteger)のNを作ります」、という意味です。もしもMという整数の変数を作りたければ「int M;」と書けばいいでしょう。

では次の行を見てみましょう。この行では「cin >> N;」と書いてあります。これは「Nに入力を入れます」という意味です。比較演算子の>を使っていますが、ここでは大きさとかを比べるという意味でこの記号は使われておらず、矢印的な存在で2個連結しています。2個連結していないとエラーになりますので注意しましょう。

やっとですね!最後の行を見てみましょう。「cout << N << endl;」と書いており、先ほどとは矢印の方向が逆になっています。矢印の方向は決まっているのでここも気をつけましょう。さて、ここではなんと矢印が2つも使われています。そうです、実は矢印は連結しても使えます。(なんならcinの方でも使えますがここでは割愛します。)coutは出力をしますというおまじないで、もちろん見た目の順番通りに出力も行ってくれます。Nがあるのはわかりますが、endlってなんぞや?となるでしょう。これもおまじないの一種で、改行を行うという意味があります。別になくてもいいんじゃない?と思う方もいると思いますが、実際にそうなので本当につけなくても大丈夫です。(cout << N;でも動きます!)ここでは便宜上、形を覚えてもらうためにつけています。

ここまでおまじないという言葉を言葉を何度も使って来ましたが、この概念は実は重要です。おまじないには見ての通り規則があり、元からあるおまじないの言葉を変に使うと、変なことがもちろん起きます。

ここでは難しいことを避けるために詳しくは説明しませんが、予約語という概念があり、言葉には予約制があって、それが被ることは許されません。なのでいくらあなたが英語好きでもintという名前の整数の変数を作りたくて「int int;」なんてしてしまうとパソコンが怒ります。これを避けるにはそのような言葉を覚えるのが一番ですが、とりあえず今は「何か新しいものを作りたければ一文字だけにする」と覚えていれば大丈夫です。

Python

Pythonの解説をしていきます。まずは最初の行を見てみましょう。

N = input()

ここではNという変数に入力したもの(input())を入れるという意味を示します。pythonはかなり自由な言語であり、ここでわざわざ入力した「もの」と伝えたのは数字以外でもひらがなやハングルなどを入れてもNにそれらが入ることができる、という特性を持ちます。実際に標準入力に色々入力してみると、正常に動くことが確かめられるはずです!

では次の行を見てみましょう。

print(N)

ここではNを出力するという意味を持ちます。print(表示したいもの)で色々表示できます。このようにprint(N)は関数であって、今まで数学で使って来たf(x)と同じようなもので、Nやxといったもの(引数と呼ばれます)を受け取って、printやfなどあらかじめ準備した処理を行う、といった特性を持ちます。

解いてみよう

ここまでの知識を用いて実際に解いてみましょう。やりたいことを再確認すると「入力を受け取って」、「その入力を二乗した数を作って」、「その二乗した数を返す」ことです。ステップごとに分けるとプログラミングはやりやすいです!

// C++
#include <iostream>
#include <string>

using namespace std;

int main() {
    int N;             // Nという変数を作って
    cin >> N;          // Nを受け取って
    int M;             // 二乗した数を入れるためのMという変数を作って
    M = N * N;         // Mに二乗した数を入れて
    cout << M << endl; // Mを出力
    return 0;
}
# Python3
N = input() # Nという変数に入力を入れて
M = N * N   # Mという変数に二乗した数を入れて
print(M)    # Mを出力

数学で使う演算子の+,-,x,÷はそれぞれ+,-,*,/で使うことができます。見ての通り、積と商の演算子だけ異なり、この記法は大体の言語でこれは共通です。

以上のコードでも解けますが、最後に少し今までの数学らしくない記法で解く方法を示します。

// C++
#include <iostream>
#include <string>

using namespace std;

int main() {
    int N;             // Nという変数を作って
    cin >> N;          // Nを受け取って
    N = N * N          // NにNを二乗した数を入れて
    cout << M << endl; // Nを出力
    return 0;
}
# Python3
N = input() # Nという変数に入力を入れて
N = N * N   # NにNを二乗した数を入れて
print(N)    # Mを出力

「N = N * N」というなんかすごいことが起きていますね。ですが、これは今まで見てきた数学の方程式とは異なります。こちらの方が簡潔ではありますが、しばらくの間は慣れないでしょう。これがプログラミング言語というものです。これからも少しずつ難しくなりますが今回の経験をもとに色々解いてみましょう!

お疲れ様でした!